diff --git a/.dockerignore b/.dockerignore index e43a7f53..3f50295f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,7 @@ test/ scripts/ docs/ data/ -build/ \ No newline at end of file +build/ +docker/ +go.work +go.work.sum \ No newline at end of file diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 36e995e4..be4cd5e0 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -2,10 +2,12 @@ name: Create and publish a Docker image on: push: - tags: - - '**' branches: - - '**' + - '**' + tags-ignore: + - "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10 + - "v[0-9]+.[0-9]+.[0-9]+-beta[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" env: REGISTRY: ghcr.io @@ -20,16 +22,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Log in to the Container registry - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -37,12 +39,12 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - name: Build and push Docker image - uses: docker/build-push-action@v2.7.0 + uses: docker/build-push-action@v4 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3a04e351..bacb81e7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,9 +12,9 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: golangci-lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 with: version: latest only-new-issues: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 834143a3..7c2523f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,46 +2,74 @@ name: "Release" on: push: - branches: - - "RC[0-9]/**" tags: - "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10 - "v[0-9]+.[0-9]+.[0-9]+-beta[0-9]+" - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+" +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + jobs: - goreleaser: + build-and-push-image: runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: - - name: Checkout - uses: actions/checkout@v2.3.4 - with: - fetch-depth: 0 + - name: Checkout repository + uses: actions/checkout@v3 - - uses: actions/setup-go@v2 - with: - go-version: '1.19' + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 - # TODO:figure out how to add the supported SDK and tendermint versions - # here we need to run the following and save the results to ENV in the goreleaser action - # SDKVERSION := $(shell go list -m -u -f '{{.Version}}' github.com/cosmos/cosmos-sdk) - # TMVERSION := $(shell go list -m -u -f '{{.Version}}' github.com/tendermint/tendermint) - # Also see .goreleaser.yaml to wire up the environment variables + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - - run: echo ":rocket::rocket::rocket:" > ../release_notes.md - if: startsWith(github.ref, 'refs/tags/') + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Build - uses: goreleaser/goreleaser-action@v2 + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 with: - version: latest - args: build --skip-validate # skip validate skips initial sanity checks in order to be able to fully run + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Release - uses: goreleaser/goreleaser-action@v2 - if: startsWith(github.ref, 'refs/tags/') + - name: Build and push Docker image + uses: docker/build-push-action@v4 with: - version: latest - args: release --rm-dist --release-notes=../release_notes.md - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + context: . + platforms: linux/amd64,linux/arm64 + file: docker/horcrux/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + draft-release: + needs: build-and-push-image + runs-on: ubuntu-latest + permissions: write-all + steps: + - name: Copy Binary + run: | + docker create -it --entrypoint sh --name amd --platform linux/amd64 ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.ref_name }} + docker create -it --entrypoint sh --name arm --platform linux/arm64 ${{ env.REGISTRY }}/${{ github.repository }}:${{ github.ref_name }} + docker cp amd:/bin/horcrux ./horcrux_linux-amd64 + docker cp arm:/bin/horcrux ./horcrux_linux-arm64 + sha256sum ./horcrux_linux-amd64 > ./horcrux_sha256.txt + sha256sum ./horcrux_linux-arm64 >> ./horcrux_sha256.txt + + - name: Draft Release + uses: softprops/action-gh-release@v1 + with: + draft: true + files: | + horcrux_linux-amd64 + horcrux_linux-arm64 + horcrux_sha256.txt diff --git a/.github/workflows/strangelove-project-managment.yaml b/.github/workflows/strangelove-project-managment.yaml index d0bbabfa..0f794406 100644 --- a/.github/workflows/strangelove-project-managment.yaml +++ b/.github/workflows/strangelove-project-managment.yaml @@ -15,7 +15,7 @@ jobs: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened') steps: - name: Add Issue to "Motherboard" Project Board - uses: leonsteinhaeuser/project-beta-automations@v1.2.1 + uses: leonsteinhaeuser/project-beta-automations@v2.0.1 with: gh_app_secret_key: ${{ secrets.MB_SECRET_KEY }} gh_app_ID: ${{ secrets.MB_APP_ID }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d6fa69ef..09dfa872 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: Run Go Tests +name: Tests on: pull_request: @@ -13,31 +13,49 @@ on: - 'Makefile' jobs: - test: - name: run tests - runs-on: [self-hosted, linux] + unit: + runs-on: ubuntu-latest steps: # Install and setup go - - name: Set up Go 1.19 - uses: actions/setup-go@v2 + - name: Set up Go 1.21 + uses: actions/setup-go@v4 with: - go-version: 1.19 + go-version: '^1.21.4' # checkout horcrux - name: checkout horcrux - uses: actions/checkout@v2 - - # cleanup docker environment on self-hosted test runner - - name: prepare fresh docker environment - run: | - docker stop $(docker ps -a -q) || true - docker rm -f $(docker ps -a -q) || true - docker network prune -f || true - - # make sure proto files are up to date - - name: generate fresh signer proto .go files - run: make signer-proto + uses: actions/checkout@v3 # run tests - name: run horcrux tests run: make test + e2e: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + test: + - TestMultipleChainHorcrux + - TestChainPureHorcrux + - TestDownedSigners2of3 + - TestDownedSigners3of5 + - TestLeaderElection2of3 + - Test2Of3SignerThreeSentries + - Test2Of3SignerThreeSentriesUniqueConnection + - TestUpgradeValidatorToHorcrux + - TestSingleSignerTwoSentries + - TestHorcruxProxyGRPC + steps: + # Install and setup go + - name: Set up Go 1.21 + uses: actions/setup-go@v4 + with: + go-version: '^1.21.4' + + # checkout horcrux + - name: checkout horcrux + uses: actions/checkout@v3 + + # run test matrix + - name: run test + run: cd test && go test -v -timeout 30m -run ^${{ matrix.test }}$ . diff --git a/.gitignore b/.gitignore index eea6fcf2..4e302327 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,8 @@ build data private_share_* +*_shard.json +rsa_keys.json priv_validator_key.json -dist/ \ No newline at end of file +dist/ +go.work.sum \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index c0313dbf..8fe7a04a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,61 +1,106 @@ linters: enable: - bodyclose - - deadcode - - depguard - dogsled - # - errcheck - # - funlen - # - gochecknoglobals - # - gochecknoinits - goconst - gocritic - # - gocyclo - # - godox - gofmt - goimports - # - golint - gosec - gosimple - govet + - importas - ineffassign - # - interfacer - lll - misspell - # - maligned - nakedret - prealloc - # - scopelint + - revive - staticcheck - - structcheck - stylecheck - typecheck - unconvert - # - unparam - unused - - varcheck - # - whitespace - # - wsl - # - gocognit - nolintlint -issues: - exclude-rules: - - linters: - - lll - source: "https://" - max-same-issues: 50 - linters-settings: dogsled: max-blank-identifiers: 3 + importas: + no-extra-aliases: true + alias: + - pkg: github.com/cosmos/cosmos-sdk/crypto/codec + alias: cryptocodec + - pkg: github.com/cosmos/cosmos-sdk/crypto/types + alias: cryptotypes + - pkg: github.com/cosmos/cosmos-sdk/x/slashing/types + alias: slashingtypes + - pkg: github.com/cometbft/cometbft/types + alias: comet + - pkg: github.com/cometbft/cometbft/config + alias: cometconfig + - pkg: github.com/cometbft/cometbft/crypto + alias: cometcrypto + - pkg: github.com/cometbft/cometbft/crypto/ed25519 + alias: cometcryptoed25519 + - pkg: github.com/cometbft/cometbft/crypto/encoding + alias: cometcryptoencoding + - pkg: github.com/tendermint/go-amino + alias: amino + - pkg: github.com/cometbft/cometbft/libs/bytes + alias: cometbytes + - pkg: github.com/cometbft/cometbft/libs/json + alias: cometjson + - pkg: github.com/cometbft/cometbft/libs/log + alias: cometlog + - pkg: github.com/cometbft/cometbft/libs/net + alias: cometnet + - pkg: github.com/cometbft/cometbft/libs/os + alias: cometos + - pkg: github.com/cometbft/cometbft/libs/rand + alias: cometrand + - pkg: github.com/cometbft/cometbft/libs/service + alias: cometservice + - pkg: github.com/cometbft/cometbft/p2p/conn + alias: cometp2pconn + - pkg: github.com/cometbft/cometbft/privval + alias: cometprivval + - pkg: github.com/cometbft/cometbft/proto/tendermint/types + alias: cometproto + - pkg: github.com/cometbft/cometbft/proto/tendermint/crypto + alias: cometprotocrypto + - pkg: github.com/cometbft/cometbft/proto/tendermint/privval + alias: cometprotoprivval + - pkg: github.com/cometbft/cometbft/rpc/client + alias: cometrpcclient + - pkg: github.com/cometbft/cometbft/rpc/client/http + alias: cometrpchttp + - pkg: github.com/cometbft/cometbft/rpc/jsonrpc/client + alias: cometrpcjsonclient + - pkg: github.com/cometbft/cometbft/rpc/jsonrpc/types + alias: cometrpcjsontypes + - pkg: github.com/cometbft/cometbft/rpc/core/types + alias: cometrpctypes + - pkg: github.com/ecies/go/v2 + alias: ecies + - pkg: github.com/grpc-ecosystem/go-grpc-middleware/retry + alias: grpcretry + - pkg : github.com/kraken-hpc/go-fork + alias: fork + - pkg: github.com/armon/go-metrics/prometheus + alias: gmprometheus + - pkg: github.com/mitchellh/go-homedir + alias: homedir + - pkg: gitlab.com/unit410/threshold-ed25519/pkg + alias: tsed25519 + - pkg: github.com/Jille/raft-grpc-transport + alias: raftgrpctransport + - pkg: github.com/hashicorp/raft-boltdb/v2 + alias: boltdb + - pkg: math/rand + alias: mrand maligned: suggest-new: true - # govet: - # check-shadowing: true - # golint: - # min-confidence: 0 - # gocyclo: - # min-complexity: 10 + govet: misspell: locale: US diff --git a/Makefile b/Makefile index 77d210d9..2340aae8 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,11 @@ COMMIT := $(shell git log -1 --format='%H') all: install -LD_FLAGS = -X github.com/strangelove-ventures/horcrux/cmd/horcrux/cmd.Version=$(VERSION) \ - -X github.com/strangelove-ventures/horcrux/cmd/horcrux/cmd.Commit=$(COMMIT) +LD_FLAGS = -X github.com/strangelove-ventures/horcrux/v3/cmd/horcrux/cmd.Version=$(VERSION) \ + -X github.com/strangelove-ventures/horcrux/v3/cmd/horcrux/cmd.Commit=$(COMMIT) + +LD_FLAGS += $(LDFLAGS) +LD_FLAGS := $(strip $(LD_FLAGS)) BUILD_FLAGS := -ldflags '$(LD_FLAGS)' @@ -18,7 +21,7 @@ build-linux: @GOOS=linux GOARCH=amd64 go build --mod readonly $(BUILD_FLAGS) -o ./build/horcrux ./cmd/horcrux test: - @go test -race -timeout 20m -mod readonly -v ./... + @go test -race -timeout 30m -mod readonly -v ./... test-short: @go test -mod readonly -run TestDownedSigners2of3 -v ./... @@ -35,18 +38,22 @@ build-horcrux-docker: mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) mkfile_dir := $(dir $(mkfile_path)) -signer-proto: - docker run \ - --rm \ - -u $(shell id -u ${USER}):$(shell id -g ${USER}) \ - --mount type=bind,source=$(mkfile_dir)/signer/proto,target=/horcrux/signer/proto \ - --entrypoint protoc \ - namely/protoc-all \ - --go_out=/horcrux \ - --go_opt=paths=source_relative \ - --go-grpc_out=/horcrux \ - --go-grpc_opt=paths=source_relative \ - --proto_path /horcrux \ - $(shell find $(mkfile_dir) -name *.proto -printf "%P\n") - -.PHONY: all lint test race msan tools clean build \ No newline at end of file +DOCKER := $(shell which docker) +protoVer=0.11.2 +protoImageName=ghcr.io/cosmos/proto-builder:$(protoVer) +protoImage=$(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(protoImageName) + +proto-all: proto-format proto-lint proto-gen + +proto-gen: + @echo "Generating Protobuf files" + @$(protoImage) sh ./scripts/protocgen.sh + +proto-format: + @$(protoImage) find ./ -name "*.proto" -exec clang-format -i {} \; + +proto-lint: + @$(protoImage) buf lint --error-format=json + + +.PHONY: all lint test race msan tools clean build diff --git a/README.md b/README.md index 66f9e70b..5ac42d57 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Horcrux -Horcrux is a [multi-party-computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) signing service for Tendermint nodes +Horcrux is a [multi-party-computation (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) signing service for CometBFT (Formerly known as Tendermint) nodes ## Why use Horcrux? @@ -12,7 +12,7 @@ Take your validator infrastructure to the next level of security and availabilit ## Design -Validator operators for tendermint chains balance operational and risk tradeoffs to avoid penalties via slashing for liveliness faults or double signing blocks. +Validator operators balance operational and risk tradeoffs to avoid penalties via slashing for liveliness faults or double signing blocks. Traditional high-availability systems where the keys exist on hot spares risk double signing if there are failover detection bugs. Low-availability systems, or manual failover, risk downtime if manual intervention cannot respond in a timely manner. @@ -54,7 +54,7 @@ software or this license, under any kind of legal claim. ## References -- [Tendermint Validator Documentation](https://docs.tendermint.com/master/tendermint-core/validators.html) +- [CometBFT Validator Documentation](https://docs.cometbft.com/main/core/validators) - [Cosmos Hub Validator Documentation](https://hub.cosmos.network/master/validators/overview.html) - [Provably Secure Distributed Schnorr Signatures and a (t, n) Threshold Scheme for Implicit Certificates](http://cacr.uwaterloo.ca/techreports/2001/corr2001-13.ps) diff --git a/client/address.go b/client/address.go index 94315824..3a611021 100644 --- a/client/address.go +++ b/client/address.go @@ -9,7 +9,7 @@ import ( func SanitizeAddress(address string) (string, error) { u, err := url.Parse(address) if err != nil { - return "", fmt.Errorf("error parsing peer URL: %w", err) + return "", fmt.Errorf("error parsing URL: %w", err) } return u.Host, nil diff --git a/client/address_test.go b/client/address_test.go index 18b2e20f..3296bba1 100644 --- a/client/address_test.go +++ b/client/address_test.go @@ -3,7 +3,7 @@ package client_test import ( "testing" - "github.com/strangelove-ventures/horcrux/client" + "github.com/strangelove-ventures/horcrux/v3/client" "github.com/stretchr/testify/require" ) diff --git a/cmd/horcrux/cmd/address.go b/cmd/horcrux/cmd/address.go new file mode 100644 index 00000000..644d577a --- /dev/null +++ b/cmd/horcrux/cmd/address.go @@ -0,0 +1,111 @@ +package cmd + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strings" + + "github.com/cometbft/cometbft/crypto" + cometprivval "github.com/cometbft/cometbft/privval" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/spf13/cobra" + "github.com/strangelove-ventures/horcrux/v3/signer" +) + +type AddressCmdOutput struct { + HexAddress string + PubKey string + ValConsAddress string + ValConsPubAddress string +} + +func addressCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "address chain-id [bech32]", + Short: "Get public key hex address and valcons address", + Example: `horcrux cosigner address cosmos`, + SilenceUsage: true, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + + var pubKey crypto.PubKey + + chainID := args[0] + + switch config.Config.SignMode { + case signer.SignModeThreshold: + err := config.Config.ValidateThresholdModeConfig() + if err != nil { + return err + } + + keyFile, err := config.KeyFileExistsCosigner(chainID) + if err != nil { + return err + } + + key, err := signer.LoadCosignerEd25519Key(keyFile) + if err != nil { + return fmt.Errorf("error reading cosigner key: %w, check that key is present for chain ID: %s", err, chainID) + } + + pubKey = key.PubKey + case signer.SignModeSingle: + err := config.Config.ValidateSingleSignerConfig() + if err != nil { + return err + } + keyFile, err := config.KeyFileExistsSingleSigner(chainID) + if err != nil { + return fmt.Errorf("error reading priv-validator key: %w, check that key is present for chain ID: %s", err, chainID) + } + + filePV := cometprivval.LoadFilePVEmptyState(keyFile, "") + pubKey = filePV.Key.PubKey + default: + panic(fmt.Errorf("unexpected sign mode: %s", config.Config.SignMode)) + } + + pubKeyAddress := pubKey.Address() + + pubKeyJSON, err := signer.PubKey("", pubKey) + if err != nil { + return err + } + + output := AddressCmdOutput{ + HexAddress: strings.ToUpper(hex.EncodeToString(pubKeyAddress)), + PubKey: pubKeyJSON, + } + + if len(args) == 2 { + bech32ValConsAddress, err := bech32.ConvertAndEncode(args[1]+"valcons", pubKeyAddress) + if err != nil { + return err + } + output.ValConsAddress = bech32ValConsAddress + pubKeyBech32, err := signer.PubKey(args[1], pubKey) + if err != nil { + return err + } + output.ValConsPubAddress = pubKeyBech32 + } else { + bech32Hint := "Pass bech32 base prefix as argument to generate (e.g. cosmos)" + output.ValConsAddress = bech32Hint + output.ValConsPubAddress = bech32Hint + } + + jsonOut, err := json.Marshal(output) + if err != nil { + return err + } + + fmt.Println(string(jsonOut)) + + return nil + }, + } + + return cmd +} diff --git a/cmd/horcrux/cmd/config.go b/cmd/horcrux/cmd/config.go index 734ea10e..6cdc6695 100644 --- a/cmd/horcrux/cmd/config.go +++ b/cmd/horcrux/cmd/config.go @@ -1,136 +1,121 @@ package cmd import ( - "errors" "fmt" - "net" - "net/url" "os" - "path/filepath" - "strconv" - "strings" - "time" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/client" - "github.com/strangelove-ventures/horcrux/signer" - "gopkg.in/yaml.v2" + "github.com/strangelove-ventures/horcrux/v3/signer" ) -func init() { - nodesCmd.AddCommand(addNodesCmd()) - nodesCmd.AddCommand(removeNodesCmd()) - configCmd.AddCommand(nodesCmd) - - peersCmd.AddCommand(addPeersCmd()) - peersCmd.AddCommand(removePeersCmd()) - peersCmd.AddCommand(setSharesCmd()) - configCmd.AddCommand(peersCmd) +const ( + flagSignMode = "mode" + flagNode = "node" + flagCosigner = "cosigner" + flagDebugAddr = "debug-addr" + flagKeyDir = "key-dir" + flagRaftTimeout = "raft-timeout" + flagGRPCTimeout = "grpc-timeout" + flagOverwrite = "overwrite" + flagBare = "bare" + flagGRPCAddress = "gprc-address" + flagMaxReadSize = "max-read-size" +) - chainIDCmd.AddCommand(setChainIDCmd()) - configCmd.AddCommand(chainIDCmd) +func configCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "config", + Short: "Commands to configure the horcrux signer", + } - configCmd.AddCommand(initCmd()) - rootCmd.AddCommand(configCmd) -} + cmd.AddCommand(initCmd()) + cmd.AddCommand(migrateCmd()) -var configCmd = &cobra.Command{ - Use: "config", - Short: "Commands to configure the horcrux signer", + return cmd } func initCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "init [chain-id] [chain-nodes]", + Use: "init", Aliases: []string{"i"}, Short: "initialize configuration file and home directory if one doesn't already exist", - Long: "initialize configuration file, use flags for cosigner configuration.\n\n" + - "[chain-id] is the chain id of the chain to validate\n" + - "[chain-nodes] is a comma separated array of chain node addresses i.e.\n" + - "tcp://chain-node-1:1234,tcp://chain-node-2:1234", - Args: cobra.RangeArgs(1, 2), + Long: `initialize configuration file. +for threshold signer mode, --cosigner flags and --threshold flag are required. + `, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) (err error) { - cid := args[0] - var cn []ChainNode - if len(args) == 2 { - cn, err = chainNodesFromArg(args[1]) - if err != nil { - return err - } + cmdFlags := cmd.Flags() + + bare, _ := cmdFlags.GetBool(flagBare) + nodes, _ := cmdFlags.GetStringSlice(flagNode) + + cn, err := signer.ChainNodesFromFlag(nodes) + if err != nil { + return err } - cmdFlags := cmd.Flags() - overwrite, _ := cmdFlags.GetBool("overwrite") + overwrite, _ := cmdFlags.GetBool(flagOverwrite) if _, err := os.Stat(config.ConfigFile); !os.IsNotExist(err) && !overwrite { return fmt.Errorf("%s already exists. Provide the -o flag to overwrite the existing config", config.ConfigFile) } - var cfg DiskConfig - - cs, _ := cmdFlags.GetBool("cosigner") - keyFileFlag, _ := cmdFlags.GetString("keyfile") - var keyFile *string - if keyFileFlag != "" { - keyFile = &keyFileFlag - } - debugAddr, _ := cmdFlags.GetString("debug-addr") - if cs { - // Cosigner Config - p, _ := cmdFlags.GetString("peers") - threshold, _ := cmdFlags.GetInt("threshold") - timeout, _ := cmdFlags.GetString("timeout") - peers, err := peersFromFlag(p) - if err != nil { - return err - } - - listen, _ := cmdFlags.GetString("listen") - if listen == "" { - return errors.New("must input at least one node") - } - url, err := url.Parse(listen) - if err != nil { - return fmt.Errorf("error parsing listen address: %s, %v", listen, err) - } - host, _, err := net.SplitHostPort(url.Host) + var cfg signer.Config + + signMode, _ := cmdFlags.GetString(flagSignMode) + keyDirFlag, _ := cmdFlags.GetString(flagKeyDir) + var keyDir *string + if keyDirFlag != "" { + keyDir = &keyDirFlag + } + debugAddr, _ := cmdFlags.GetString(flagDebugAddr) + grpcAddr, _ := cmdFlags.GetString(flagGRPCAddress) + maxReadSize, _ := cmdFlags.GetInt(flagMaxReadSize) + if signMode == string(signer.SignModeThreshold) { + // Threshold Mode Config + cosignersFlag, _ := cmdFlags.GetStringSlice(flagCosigner) + threshold, _ := cmdFlags.GetInt(flagThreshold) + raftTimeout, _ := cmdFlags.GetString(flagRaftTimeout) + grpcTimeout, _ := cmdFlags.GetString(flagGRPCTimeout) + cosigners, err := signer.CosignersFromFlag(cosignersFlag) if err != nil { return err } - if host == "0.0.0.0" { - return errors.New("host cannot be 0.0.0.0, must be reachable from other peers") - } - cfg = DiskConfig{ - PrivValKeyFile: keyFile, - ChainID: cid, - CosignerConfig: &CosignerConfig{ - Threshold: threshold, - Shares: len(peers) + 1, - P2PListen: listen, - Peers: peers, - Timeout: timeout, + cfg = signer.Config{ + SignMode: signer.SignModeThreshold, + PrivValKeyDir: keyDir, + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: threshold, + Cosigners: cosigners, + GRPCTimeout: grpcTimeout, + RaftTimeout: raftTimeout, }, - ChainNodes: cn, - DebugAddr: debugAddr, + ChainNodes: cn, + DebugAddr: debugAddr, + GRPCAddr: grpcAddr, + MaxReadSize: maxReadSize, } - if err = validateCosignerConfig(cfg); err != nil { - return err + + if !bare { + if err = cfg.ValidateThresholdModeConfig(); err != nil { + return err + } } } else { // Single Signer Config - if len(cn) == 0 { - return fmt.Errorf("must input at least one node") + cfg = signer.Config{ + SignMode: signer.SignModeSingle, + PrivValKeyDir: keyDir, + ChainNodes: cn, + DebugAddr: debugAddr, + MaxReadSize: maxReadSize, } - cfg = DiskConfig{ - PrivValKeyFile: keyFile, - ChainID: cid, - ChainNodes: cn, - DebugAddr: debugAddr, - } - if err = validateSingleSignerConfig(cfg); err != nil { - return err + if !bare { + if err = cfg.ValidateSingleSignerConfig(); err != nil { + return err + } } } @@ -143,509 +128,44 @@ func initCmd() *cobra.Command { } // create the config file config.Config = cfg - if err = config.writeConfigFile(); err != nil { + if err = config.WriteConfigFile(); err != nil { return err } - // initialize state/{chainid}_priv_validator_state.json file - if _, err = signer.LoadOrCreateSignState(config.privValStateFile(cid)); err != nil { - return err - } - - // if node is a cosigner initialize state/{chainid}_priv_validator_state.json file - if cs { - if _, err = signer.LoadOrCreateSignState(config.shareStateFile(cid)); err != nil { - return err - } - } - fmt.Printf("Successfully initialized configuration: %s\n", config.ConfigFile) return nil }, } - cmd.Flags().BoolP("cosigner", "c", false, "set to initialize a cosigner node, requires --peers and --threshold") - cmd.Flags().StringP("peers", "p", "", "cosigner peer addresses in format tcp://{addr}:{port}|{share-id} \n"+ - "(i.e. \"tcp://node-1:2222|2,tcp://node-2:2222|3\")") - cmd.Flags().IntP("threshold", "t", 0, "indicate number of signatures required for threshold signature") - cmd.Flags().StringP("listen", "l", "", "listen address of the signer") - cmd.Flags().StringP("debug-addr", "d", "", "listen address for Debug and Prometheus metrics in format localhost:8543") - cmd.Flags().StringP("keyfile", "k", "", - "priv val key file path (full key for single signer, or key share for cosigner)") - cmd.Flags().String("timeout", "1500ms", "configure cosigner rpc server timeout value, \n"+ - "accepts valid duration strings for Go's time.ParseDuration() e.g. 1s, 1000ms, 1.5m") - cmd.Flags().BoolP("overwrite", "o", false, "set to overwrite an existing config.yaml") - return cmd -} - -func validateSingleSignerConfig(cfg DiskConfig) error { - if cfg.ChainID == "" { - return fmt.Errorf("chain-id cannot be empty") - } - if len(cfg.ChainNodes) == 0 { - return fmt.Errorf("need to have a node configured to sign for") - } - if err := validateChainNodes(cfg.ChainNodes); err != nil { - return err - } - return nil -} - -func validateCosignerConfig(cfg DiskConfig) error { - if cfg.ChainID == "" { - return fmt.Errorf("chain-id cannot be empty") - } - if cfg.CosignerConfig == nil { - return fmt.Errorf("cosigner config can't be empty") - } - if cfg.CosignerConfig.Threshold <= cfg.CosignerConfig.Shares/2 { - return fmt.Errorf("threshold (%d) must be greater than number of shares (%d) / 2", - cfg.CosignerConfig.Threshold, cfg.CosignerConfig.Shares) - } - if cfg.CosignerConfig.Shares < cfg.CosignerConfig.Threshold { - return fmt.Errorf("number of shares (%d) must be greater or equal to threshold (%d)", - cfg.CosignerConfig.Shares, cfg.CosignerConfig.Threshold) - } - - _, err := time.ParseDuration(cfg.CosignerConfig.Timeout) - if err != nil { - return fmt.Errorf("%s is not a valid duration string for --timeout ", cfg.CosignerConfig.Timeout) - } - if _, err := url.Parse(cfg.CosignerConfig.P2PListen); err != nil { - return fmt.Errorf("failed to parse p2p listen address") - } - if err := validateCosignerPeers(cfg.CosignerConfig.Peers, cfg.CosignerConfig.Shares); err != nil { - return err - } - if err := validateChainNodes(cfg.ChainNodes); err != nil { - return err - } - return nil -} - -var nodesCmd = &cobra.Command{ - Use: "nodes", - Short: "Commands to configure the chain nodes", -} - -func addNodesCmd() *cobra.Command { - return &cobra.Command{ - Use: "add [chain-nodes]", - Aliases: []string{"a"}, - Short: "add chain node(s) to the cosigner's configuration", - Long: "add chain node(s) to the cosigner's configuration.\n\n" + - "[chain-nodes] is a comma separated array of chain node addresses i.e.\n" + - "tcp://chain-node-1:1234,tcp://chain-node-2:1234", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argNodes, err := chainNodesFromArg(args[0]) - if err != nil { - return err - } - diff := diffSetChainNode(argNodes, config.Config.ChainNodes) - if len(diff) == 0 { - return errors.New("no new chain nodes in args") - } - diff = append(config.Config.ChainNodes, diff...) - if err := validateChainNodes(diff); err != nil { - return err - } - - // silence usage after all input has been validated - cmd.SilenceUsage = true - - config.Config.ChainNodes = diff - if err := config.writeConfigFile(); err != nil { - return err - } - return nil - }, - } -} - -func removeNodesCmd() *cobra.Command { - return &cobra.Command{ - Use: "remove [chain-nodes]", - Aliases: []string{"r"}, - Short: "remove chain node(s) from the cosigner's configuration", - Long: "remove chain node(s) from the cosigner's configuration.\n\n" + - "[chain-nodes] is a comma separated array of chain node addresses i.e.\n" + - "tcp://chain-node-1:1234,tcp://chain-node-2:1234", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argNodes, err := chainNodesFromArg(args[0]) - if err != nil { - return err - } - diff := diffSetChainNode(config.Config.ChainNodes, argNodes) - if len(diff) == 0 { - return errors.New("cannot remove all chain nodes from config, please leave at least one") - } - // If none of the chain nodes in the args are listed in the config, just continue - // without throwing an error, as the chain nodes in the config remain untouched. - if err := validateChainNodes(diff); err != nil { - return err - } - - // silence usage after all input has been validated - cmd.SilenceUsage = true - - config.Config.ChainNodes = diff - if err := config.writeConfigFile(); err != nil { - return err - } - return nil - }, - } -} - -// diffSetCosignerPeer returns the difference set for ChainNodes of setA-setB. -// Example: [1,2,3] & [2,3,4] => [1] -func diffSetChainNode(setA, setB []ChainNode) (diff []ChainNode) { - for _, a := range setA { - found := false - for _, b := range setB { - if a == b { - found = true - } - } - if !found { - diff = append(diff, a) - } - } - return -} - -var peersCmd = &cobra.Command{ - Use: "peers", - Short: "Commands to configure the peer nodes", -} - -func addPeersCmd() *cobra.Command { - return &cobra.Command{ - Use: "add [peer-nodes]", - Aliases: []string{"a"}, - Short: "add peer node(s) to the cosigner's configuration", - Long: "add peer node(s) to the cosigner's configuration.\n\n" + - "[peer-nodes] is a comma separated array of peer node addresses i.e.\n" + - "tcp://peer-node-1:1234,tcp://peer-node-2:1234", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - argPeers, err := peersFromFlag(args[0]) - if err != nil { - return err - } - diff := diffSetCosignerPeer(argPeers, config.Config.CosignerConfig.Peers) - if len(diff) == 0 { - return errors.New("no new peer nodes in args") - } - diff = append(config.Config.CosignerConfig.Peers, diff...) - config.Config.CosignerConfig.Shares = len(diff) + 1 - if err := validateCosignerPeers(diff, config.Config.CosignerConfig.Shares); err != nil { - return err - } - - // silence usage after all input has been validated - cmd.SilenceUsage = true - - config.Config.CosignerConfig.Peers = diff - if err := config.writeConfigFile(); err != nil { - return err - } - return nil - }, - } -} - -func removePeersCmd() *cobra.Command { - return &cobra.Command{ - Use: "remove [peer-node-ids]", - Aliases: []string{"r"}, - Short: "remove peer node(s) from the cosigner's configuration", - Long: "remove peer node(s) from the cosigner's configuration.\n\n" + - "[peer-node-ids] is a comma separated array of peer node IDs i.e.\n" + - "1,2", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - var argPeers []CosignerPeer - for _, peer := range config.Config.CosignerConfig.Peers { - for _, id := range strings.Split(args[0], ",") { - id, err := strconv.Atoi(id) - if err != nil { - return err - } - if peer.ShareID == id { - argPeers = append(argPeers, peer) - } - } - } - - diff := diffSetCosignerPeer(config.Config.CosignerConfig.Peers, argPeers) - if len(diff) == 0 { - return errors.New("cannot remove all peer nodes from config, please leave at least one") - } - - config.Config.CosignerConfig.Shares = len(diff) + 1 - // If none of the peer nodes in the args are listed in the config, just continue - // without throwing an error, as the peer nodes in the config remain untouched. - if err := validateCosignerPeers(diff, config.Config.CosignerConfig.Shares); err != nil { - return err - } - - // silence usage after all input has been validated - cmd.SilenceUsage = true - - config.Config.CosignerConfig.Peers = diff - if err := config.writeConfigFile(); err != nil { - return err - } - return nil - }, - } -} - -func setSharesCmd() *cobra.Command { - return &cobra.Command{ - Use: "set-shares [num-shares]", - Aliases: []string{"shares"}, - Short: "set the number of key shares", - Long: "set the number of key shares.\n\n" + - "[num-shares] is the number of generated key shares, used to limit the number of peers i.e." + - "3", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - numShares, err := strconv.Atoi(args[0]) - if err != nil { - return err - } - if err := validateCosignerPeers(config.Config.CosignerConfig.Peers, numShares); err != nil { - return err - } - - // silence usage after all input has been validated - cmd.SilenceUsage = true - - config.Config.CosignerConfig.Shares = numShares - if err := config.writeConfigFile(); err != nil { - return err - } - return nil - }, - } -} - -// diffSetCosignerPeer returns the difference set for CosignerPeers of setA-setB. -// Example: [1,2,3] & [2,3,4] => [1] -func diffSetCosignerPeer(setA, setB []CosignerPeer) (diff []CosignerPeer) { - for _, a := range setA { - found := false - for _, b := range setB { - if a == b { - found = true - } - } - if !found { - diff = append(diff, a) - } - } - return -} - -var chainIDCmd = &cobra.Command{ - Use: "chain-id", - Short: "Commands to configure the chain ID", -} - -func setChainIDCmd() *cobra.Command { - return &cobra.Command{ - Use: "set [chain-ID]", - Aliases: []string{"s"}, - Short: "set the chain ID", - Long: "set the chain ID.\n\n" + - "[chain-id] is a string i.e.\n" + - "cosmoshub-4", - SilenceUsage: true, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - oldChainID := config.Config.ChainID - newChainID := args[0] - pvOldPath := config.privValStateFile(oldChainID) - pvNewPath := config.privValStateFile(newChainID) - shareOldPath := config.shareStateFile(oldChainID) - shareNewPath := config.shareStateFile(newChainID) - - if _, err := os.Stat(pvOldPath); err == nil { - if err = os.Rename(pvOldPath, pvNewPath); err != nil { - return err - } - } - if _, err := os.Stat(shareOldPath); err == nil { - if err = os.Rename(shareOldPath, shareNewPath); err != nil { - return err - } - } - - config.Config.ChainID = args[0] - if err = config.writeConfigFile(); err != nil { - return err - } - return nil - }, - } -} - -// Config maps to the on-disk JSON format -type DiskConfig struct { - PrivValKeyFile *string `json:"key-file,omitempty" yaml:"key-file,omitempty"` - ChainID string `json:"chain-id" yaml:"chain-id"` - CosignerConfig *CosignerConfig `json:"cosigner,omitempty" yaml:"cosigner,omitempty"` - ChainNodes []ChainNode `json:"chain-nodes,omitempty" yaml:"chain-nodes,omitempty"` - DebugAddr string `json:"debug-addr,omitempty" yaml:"debug-addr,omitempty"` -} - -func (c *DiskConfig) Nodes() []signer.NodeConfig { - out := make([]signer.NodeConfig, len(c.ChainNodes)) - for i, n := range c.ChainNodes { - out[i] = signer.NodeConfig{Address: n.PrivValAddr} - } - return out -} - -func (c *DiskConfig) MustMarshalYaml() []byte { - out, err := yaml.Marshal(c) - if err != nil { - panic(err) - } - return out -} - -type RuntimeConfig struct { - HomeDir string - ConfigFile string - StateDir string - PidFile string - Config DiskConfig -} - -func (c *RuntimeConfig) keyFilePath(cosigner bool) string { - if c.Config.PrivValKeyFile != nil && *c.Config.PrivValKeyFile != "" { - return *c.Config.PrivValKeyFile - } - if cosigner { - return filepath.Join(c.HomeDir, "share.json") - } - return filepath.Join(c.HomeDir, "priv_validator_key.json") -} - -func (c RuntimeConfig) privValStateFile(chainID string) string { - return filepath.Join(c.StateDir, fmt.Sprintf("%s_priv_validator_state.json", chainID)) -} - -func (c RuntimeConfig) shareStateFile(chainID string) string { - return filepath.Join(c.StateDir, fmt.Sprintf("%s_share_sign_state.json", chainID)) -} - -func (c RuntimeConfig) writeConfigFile() error { - return os.WriteFile(c.ConfigFile, c.Config.MustMarshalYaml(), 0644) //nolint -} - -type CosignerConfig struct { - Threshold int `json:"threshold" yaml:"threshold"` - Shares int `json:"shares" yaml:"shares"` - P2PListen string `json:"p2p-listen" yaml:"p2p-listen"` - Peers []CosignerPeer `json:"peers" yaml:"peers"` - Timeout string `json:"rpc-timeout" yaml:"rpc-timeout"` -} - -func (cfg *CosignerConfig) LeaderElectMultiAddress() (string, error) { - addresses := make([]string, 1+len(cfg.Peers)) - addresses[0] = cfg.P2PListen - for i, peer := range cfg.Peers { - addresses[i+1] = peer.P2PAddr - } - return client.MultiAddress(addresses) -} - -func (c *DiskConfig) CosignerPeers() (out []signer.CosignerConfig) { - for _, p := range c.CosignerConfig.Peers { - out = append(out, signer.CosignerConfig{ID: p.ShareID, Address: p.P2PAddr}) - } - return -} -type CosignerPeer struct { - ShareID int `json:"share-id" yaml:"share-id"` - P2PAddr string `json:"p2p-addr" yaml:"p2p-addr"` -} + f := cmd.Flags() + f.StringP(flagSignMode, "m", string(signer.SignModeThreshold), + `sign mode, "threshold" (recommended) or "single" (unsupported). threshold mode requires --cosigner (multiple) and --threshold`, //nolint + ) + f.StringSliceP(flagNode, "n", []string{}, "chain nodes in format tcp://{node-addr}:{privval-port} \n"+ + "(e.g. --node tcp://sentry-1:1234 --node tcp://sentry-2:1234 --node tcp://sentry-3:1234 )") -func validateCosignerPeers(peers []CosignerPeer, shares int) error { - // Check IDs to make sure none are duplicated - if dupl := duplicatePeers(peers); len(dupl) != 0 { - return fmt.Errorf("found duplicate share IDs in args: %v", dupl) - } + f.StringSliceP(flagCosigner, "c", []string{}, + `cosigners in format tcp://{cosigner-addr}:{p2p-port} +(e.g. --cosigner tcp://horcrux-1:2222 --cosigner tcp://horcrux-2:2222 --cosigner tcp://horcrux-3:2222)`) - // Make sure that the peers' IDs match the number of shares. - for _, peer := range peers { - if peer.ShareID < 1 || peer.ShareID > shares { - return fmt.Errorf("peer ID %v in args is out of range, must be between 1 and %v", - peer.ShareID, shares) - } - } - - // Check that exactly {num-shares}-1 peers are in the peer list, assuming - // the remaining peer ID is the ID the local node is configured with. - if len(peers) != shares-1 { - return fmt.Errorf("incorrect number of peers. expected (%d shares - local node = %d peers)", - shares, shares-1) - } - return nil -} - -func duplicatePeers(peers []CosignerPeer) (duplicates []CosignerPeer) { - encountered := make(map[int]string) - for _, peer := range peers { - if _, found := encountered[peer.ShareID]; !found { - encountered[peer.ShareID] = peer.P2PAddr - } else { - duplicates = append(duplicates, CosignerPeer{peer.ShareID, peer.P2PAddr}) - } - } - return -} + f.IntP(flagThreshold, "t", 0, "number of shards required for threshold signature") -func peersFromFlag(peers string) (out []CosignerPeer, err error) { - for _, p := range strings.Split(peers, ",") { - ps := strings.Split(p, "|") - if len(ps) != 2 { - return nil, fmt.Errorf("invalid peer string %s", p) - } - shareid, err := strconv.ParseInt(ps[1], 10, 64) - if err != nil { - return nil, err - } - out = append(out, CosignerPeer{ShareID: int(shareid), P2PAddr: ps[0]}) - } - return -} - -type ChainNode struct { - PrivValAddr string `json:"priv-val-addr" yaml:"priv-val-addr"` -} - -func chainNodesFromArg(arg string) ([]ChainNode, error) { - cn := parseChainNodes(arg) - return cn, validateChainNodes(cn) -} - -func parseChainNodes(nodes string) (out []ChainNode) { - for _, n := range strings.Split(nodes, ",") { - out = append(out, ChainNode{PrivValAddr: n}) - } - return -} - -func validateChainNodes(nodes []ChainNode) (err error) { - for _, n := range nodes { - _, err = url.Parse(n.PrivValAddr) - } - return + f.StringP( + flagDebugAddr, "d", "", + "listen address for debug server and prometheus metrics in format localhost:8543", + ) + f.StringP(flagKeyDir, "k", "", "key directory if other than home directory") + f.String(flagRaftTimeout, "500ms", "cosigner raft timeout value, \n"+ + "accepts valid duration strings for Go's time.ParseDuration() e.g. 1s, 1000ms, 1.5m") + f.String(flagGRPCTimeout, "500ms", "cosigner grpc timeout value, \n"+ + "accepts valid duration strings for Go's time.ParseDuration() e.g. 1s, 1000ms, 1.5m") + f.BoolP(flagOverwrite, "o", false, "overwrite an existing config.yaml") + f.Bool( + flagBare, + false, + "allows initialization without providing any flags. If flags are provided, will not perform final validation", + ) + f.StringP(flagGRPCAddress, "g", "", "GRPC address if listener should be enabled") + f.Int(flagMaxReadSize, 1024*1024, "max read size for remote signer connection") + return cmd } diff --git a/cmd/horcrux/cmd/config_test.go b/cmd/horcrux/cmd/config_test.go index eea3952a..dc5e492b 100644 --- a/cmd/horcrux/cmd/config_test.go +++ b/cmd/horcrux/cmd/config_test.go @@ -6,621 +6,169 @@ import ( "path/filepath" "testing" - "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" "github.com/stretchr/testify/require" ) -const ( - chainID = "horcrux-1" -) - func TestConfigInitCmd(t *testing.T) { tmpHome := t.TempDir() tcs := []struct { - name string - home string - args []string - expectErr bool + name string + home string + args []string + expectErr string + expectConfig string }{ { - name: "valid init", - home: tmpHome + "_valid_init", + name: "valid init threshold", + home: tmpHome + "_valid_init_threshold", args: []string{ - chainID, - "tcp://10.168.0.1:1234", - "-c", - "-p", "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3", + "-n", "tcp://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + "-c", "tcp://10.168.1.1:2222", + "-c", "tcp://10.168.1.2:2222", + "-c", "tcp://10.168.1.3:2222", "-t", "2", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", - }, - expectErr: false, - }, - { - name: "invalid chain-nodes", - home: tmpHome + "_invalid_chain-nodes", + "--raft-timeout", "500ms", + "--grpc-timeout", "500ms", + }, + expectConfig: `signMode: threshold +thresholdMode: + threshold: 2 + cosigners: + - shardID: 1 + p2pAddr: tcp://10.168.1.1:2222 + - shardID: 2 + p2pAddr: tcp://10.168.1.2:2222 + - shardID: 3 + p2pAddr: tcp://10.168.1.3:2222 + grpcTimeout: 500ms + raftTimeout: 500ms +chainNodes: +- privValAddr: tcp://10.168.0.1:1234 +- privValAddr: tcp://10.168.0.2:1234 +debugAddr: "" +grpcAddr: "" +maxReadSize: 1048576 +`, + }, + { + name: "valid init single signer", + home: tmpHome + "_valid_init_single", args: []string{ - chainID, - "://10.168.0.1:1234", // Missing/malformed protocol scheme - "-c", - "-p", "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3", + "-m", "single", + "-n", "tcp://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + }, + expectConfig: `signMode: single +chainNodes: +- privValAddr: tcp://10.168.0.1:1234 +- privValAddr: tcp://10.168.0.2:1234 +debugAddr: "" +grpcAddr: "" +maxReadSize: 1048576 +`, + }, + { + name: "invalid chain-node", + home: tmpHome + "_invalid_chain-node", + args: []string{ + "-n", "://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + "-c", "tcp://10.168.1.1:2222", + "-c", "tcp://10.168.1.2:2222", + "-c", "tcp://10.168.1.3:2222", "-t", "2", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", + "--raft-timeout", "500ms", + "--grpc-timeout", "500ms", }, - expectErr: true, + expectErr: `parse "://10.168.0.1:1234": missing protocol scheme`, }, { - name: "invalid peer-nodes", - home: tmpHome + "_invalid_peer-nodes", + name: "invalid cosigner node", + home: tmpHome + "_invalid_cosigner-node", args: []string{ - chainID, - "tcp://10.168.0.1:1234", - "-c", - "-p", "tcp://10.168.1.2:2222,tcp://10.168.1.3:2222", // Missing share IDs + "-n", "tcp://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + "-c", "://10.168.1.1:2222", + "-c", "tcp://10.168.1.2:2222", + "-c", "tcp://10.168.1.3:2222", "-t", "2", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", - }, - expectErr: true, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - tmpConfig := filepath.Join(tc.home, ".horcrux") - - t.Setenv("HOME", tc.home) - err := os.MkdirAll(tc.home, 0777) - require.NoError(t, err) - - cmd := initCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs(tc.args) - err = cmd.Execute() - - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - - ss, err := signer.LoadSignState(filepath.Join(tmpConfig, "state", chainID+"_priv_validator_state.json")) - require.NoError(t, err) - require.Equal(t, int64(0), ss.Height) - require.Equal(t, int64(0), ss.Round) - require.Equal(t, int8(0), ss.Step) - require.Nil(t, ss.EphemeralPublic) - require.Nil(t, ss.Signature) - require.Nil(t, ss.SignBytes) - - ss, err = signer.LoadSignState(filepath.Join(tmpConfig, "state", chainID+"_share_sign_state.json")) - require.NoError(t, err) - require.Equal(t, int64(0), ss.Height) - require.Equal(t, int64(0), ss.Round) - require.Equal(t, int8(0), ss.Step) - require.Nil(t, ss.EphemeralPublic) - require.Nil(t, ss.Signature) - require.Nil(t, ss.SignBytes) - } - }) - } -} - -func TestConfigChainIDSetCmd(t *testing.T) { - t.Setenv("HOME", t.TempDir()) - - cmd := initCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs([]string{ - chainID, - "tcp://10.168.0.1:1234", - "-c", - "-p", "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3", - "-t", "2", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", - }) - err := cmd.Execute() - require.NoError(t, err) - - tcs := []struct { - name string - args []string - expectErr bool - }{ - { - name: "happy path", - args: []string{"horcrux-2"}, - expectErr: false, - }, - { - name: "missing chain-id", - args: []string{}, - expectErr: true, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - cmd := setChainIDCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs(tc.args) - err := cmd.Execute() - - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.Equal(t, tc.args[0], config.Config.ChainID) - } - }) - } -} - -func TestConfigNodesAddAndRemove(t *testing.T) { - t.Setenv("HOME", t.TempDir()) - - cmd := initCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs([]string{ - chainID, - "tcp://10.168.0.1:1234", - "-c", - "-p", "tcp://10.168.1.1:2222|1,tcp://10.168.1.2:2222|2", - "-t", "2", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", - }) - err := cmd.Execute() - require.NoError(t, err) - - tcs := []struct { - name string - cmd *cobra.Command - args []string - expectNodes []ChainNode - expectErr bool - }{ // Do NOT change the order of the test cases! - { - name: "add single new node", - cmd: addNodesCmd(), - args: []string{"tcp://10.168.0.2:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, - }, - expectErr: false, - }, - { - name: "remove single node", - cmd: removeNodesCmd(), - args: []string{"tcp://10.168.0.2:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: false, - }, - { - name: "add multiple new nodes", - cmd: addNodesCmd(), - args: []string{"tcp://10.168.0.2:1234,tcp://10.168.0.3:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, - {PrivValAddr: "tcp://10.168.0.3:1234"}, - }, - expectErr: false, - }, - { - name: "remove multiple peers", - cmd: removeNodesCmd(), - args: []string{"tcp://10.168.0.2:1234,tcp://10.168.0.3:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: false, - }, - { - name: "add invalid node", - cmd: addNodesCmd(), - args: []string{"://10.168.0.3:1234"}, // Missing/malformed protocol scheme - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: true, - }, - { - name: "remove invalid node", - cmd: removeNodesCmd(), - args: []string{"://10.168.0.3:1234"}, // Missing/malformed protocol scheme - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: true, - }, - { - name: "add existing node", - cmd: addNodesCmd(), - args: []string{"tcp://10.168.0.1:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: true, - }, - { - name: "remove non-existent node", - cmd: removeNodesCmd(), - args: []string{"tcp://10.168.0.99:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: false, - }, - { - name: "add one new and one existing node", - cmd: addNodesCmd(), - args: []string{"tcp://10.168.0.1:1234,tcp://10.168.0.2:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, - }, - expectErr: false, - }, - { - name: "remove one existing and one non-existent node", - cmd: removeNodesCmd(), - args: []string{"tcp://10.168.0.2:1234,tcp://10.168.0.3:1234"}, - expectNodes: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - expectErr: false, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - tc.cmd.SetOutput(io.Discard) - tc.cmd.SetArgs(tc.args) - err = tc.cmd.Execute() - - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - - require.Equal(t, tc.expectNodes, config.Config.ChainNodes) - }) - } -} - -func TestConfigPeersAddAndRemove(t *testing.T) { - t.Setenv("HOME", t.TempDir()) - - cmd := initCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs([]string{ - chainID, - "tcp://10.168.0.1:1234", - "-c", - "-p", "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3,tcp://10.168.1.4:2222|4", - "-t", "3", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", - }) - err := cmd.Execute() - require.NoError(t, err) - - tcs := []struct { - name string - cmd *cobra.Command - args []string - expectPeers []CosignerPeer - expectErr bool - }{ // Do NOT change the order of the test cases! - { - name: "remove single peer", - cmd: removePeersCmd(), - args: []string{"4"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - }, - expectErr: false, - }, - { - name: "add single peer", - cmd: addPeersCmd(), - args: []string{"tcp://10.168.1.4:2222|4"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - {ShareID: 4, P2PAddr: "tcp://10.168.1.4:2222"}, - }, - expectErr: false, - }, - { - name: "remove multiple peers", - cmd: removePeersCmd(), - args: []string{"3,4"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - }, - expectErr: false, - }, - { - name: "add multiple peers", - cmd: addPeersCmd(), - args: []string{"tcp://10.168.1.3:2222|3,tcp://10.168.1.4:2222|4"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - {ShareID: 4, P2PAddr: "tcp://10.168.1.4:2222"}, - }, - expectErr: false, - }, - { - name: "remove non-existent peer", - cmd: removePeersCmd(), - args: []string{"1"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - {ShareID: 4, P2PAddr: "tcp://10.168.1.4:2222"}, - }, - expectErr: false, - }, - { - name: "add existing peer", - cmd: addPeersCmd(), - args: []string{"tcp://10.168.1.3:2222|3"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - {ShareID: 4, P2PAddr: "tcp://10.168.1.4:2222"}, - }, - expectErr: true, - }, - { - name: "remove one existing and one non-existent peer", - cmd: removePeersCmd(), - args: []string{"1,4"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - }, - expectErr: false, - }, - { - name: "add one non-existent and one existing peer", - cmd: addPeersCmd(), - args: []string{"tcp://10.168.1.3:2222|3,tcp://10.168.1.4:2222|4"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - {ShareID: 4, P2PAddr: "tcp://10.168.1.4:2222"}, - }, - expectErr: false, - }, - { - name: "add peer with ID out of range", - cmd: addPeersCmd(), - args: []string{"tcp://10.168.1.5:2222|6"}, - expectPeers: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - {ShareID: 4, P2PAddr: "tcp://10.168.1.4:2222"}, - }, - expectErr: true, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - tc.cmd.SetOutput(io.Discard) - tc.cmd.SetArgs(tc.args) - err = tc.cmd.Execute() - - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - - require.Equal(t, tc.expectPeers, config.Config.CosignerConfig.Peers) - }) - } -} - -func TestDiffSetChainNode(t *testing.T) { - tcs := []struct { - name string - setA []ChainNode - setB []ChainNode - expectDiff []ChainNode - }{ - { - name: "1 new, no overlap", - setA: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - setB: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.2:1234"}, - }, - expectDiff: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - }, - { - name: "1 new, 1 overlap chain node", - setA: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, - }, - setB: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.2:1234"}, - {PrivValAddr: "tcp://10.168.0.3:1234"}, - }, - expectDiff: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - }, - { - name: "0 new, partial overlap", - setA: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - }, - setB: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, + "--raft-timeout", "500ms", + "--grpc-timeout", "500ms", }, - expectDiff: nil, + expectErr: `failed to parse cosigner (shard ID: 1) p2p address: parse "://10.168.1.1:2222": missing protocol scheme`, }, { - name: "0 new, all overlap", - setA: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, - }, - setB: []ChainNode{ - {PrivValAddr: "tcp://10.168.0.1:1234"}, - {PrivValAddr: "tcp://10.168.0.2:1234"}, - }, - expectDiff: nil, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - diff := diffSetChainNode(tc.setA, tc.setB) - require.Equal(t, diff, tc.expectDiff) - }) - } -} - -func TestDiffSetCosignerPeer(t *testing.T) { - tcs := []struct { - name string - setA []CosignerPeer - setB []CosignerPeer - expectDiff []CosignerPeer - }{ - { - name: "1 new, no overlap", - setA: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - }, - setB: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - }, - expectDiff: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - }, - }, - { - name: "1 new, 1 overlap peer node", - setA: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - }, - setB: []CosignerPeer{ - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - {ShareID: 3, P2PAddr: "tcp://10.168.1.3:2222"}, - }, - expectDiff: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, + name: "invalid threshold", + home: tmpHome + "_invalid_threshold", + args: []string{ + "-n", "tcp://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + "-c", "tcp://10.168.1.1:2222", + "-c", "tcp://10.168.1.2:2222", + "-c", "tcp://10.168.1.3:2222", + "-t", "1", + "--raft-timeout", "500ms", + "--grpc-timeout", "500ms", }, + expectErr: "threshold (1) must be greater than number of shards (3) / 2", }, { - name: "0 new, partial overlap", - setA: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - }, - setB: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, + name: "invalid raft timeout", + home: tmpHome + "_invalid_raft-timeout", + args: []string{ + "-n", "tcp://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + "-c", "tcp://10.168.1.1:2222", + "-c", "tcp://10.168.1.2:2222", + "-c", "tcp://10.168.1.3:2222", + "-t", "2", + "--raft-timeout", "1500", + "--grpc-timeout", "500ms", }, - expectDiff: nil, + expectErr: `invalid raftTimeout: time: missing unit in duration "1500"`, }, { - name: "0 new, all overlap", - setA: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, - }, - setB: []CosignerPeer{ - {ShareID: 1, P2PAddr: "tcp://10.168.1.1:2222"}, - {ShareID: 2, P2PAddr: "tcp://10.168.1.2:2222"}, + name: "invalid grpc timeout", + home: tmpHome + "_invalid_grpc-timeout", + args: []string{ + "-n", "tcp://10.168.0.1:1234", + "-n", "tcp://10.168.0.2:1234", + "-c", "tcp://10.168.1.1:2222", + "-c", "tcp://10.168.1.2:2222", + "-c", "tcp://10.168.1.3:2222", + "-t", "2", + "--raft-timeout", "500ms", + "--grpc-timeout", "1500", }, - expectDiff: nil, + expectErr: `invalid grpcTimeout: time: missing unit in duration "1500"`, }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - diff := diffSetCosignerPeer(tc.setA, tc.setB) - require.Equal(t, diff, tc.expectDiff) - }) - } -} - -func TestSetShares(t *testing.T) { - t.Setenv("HOME", t.TempDir()) - - cmd := initCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs([]string{ - chainID, - "tcp://10.168.0.1:1234", - "-c", - "-p", "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3", - "-t", "2", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", - }) - err := cmd.Execute() - require.NoError(t, err) + tmpConfig := filepath.Join(tc.home, ".horcrux") - tcs := []struct { - name string - args []string - expectShares int - expectErr bool - }{ // Do NOT change the order of the test cases! - { - name: "valid number of shares", - args: []string{"3"}, - expectShares: 3, - expectErr: false, - }, - { - name: "too few shares for number of peers", - args: []string{"1"}, - expectShares: 3, - expectErr: true, - }, - { - name: "invalid number of shares", - args: []string{"-1"}, - expectShares: 3, - expectErr: true, - }, - } + err := os.MkdirAll(tc.home, 0777) + require.NoError(t, err) - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - cmd := setSharesCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) - cmd.SetArgs(tc.args) + args := []string{"--home", tmpConfig, "config", "init"} + args = append(args, tc.args...) + cmd.SetArgs(args) err = cmd.Execute() - if tc.expectErr { + if tc.expectErr != "" { require.Error(t, err) - require.Equal(t, tc.expectShares, config.Config.CosignerConfig.Shares) + require.EqualError(t, err, tc.expectErr) } else { require.NoError(t, err) - require.Equal(t, tc.expectShares, config.Config.CosignerConfig.Shares) + + actualConfig, err := os.ReadFile(filepath.Join(tmpConfig, "config.yaml")) + require.NoError(t, err) + + require.Equal(t, tc.expectConfig, string(actualConfig)) } }) } diff --git a/cmd/horcrux/cmd/cosigner.go b/cmd/horcrux/cmd/cosigner.go deleted file mode 100644 index 07ecf25b..00000000 --- a/cmd/horcrux/cmd/cosigner.go +++ /dev/null @@ -1,256 +0,0 @@ -package cmd - -import ( - "encoding/hex" - "encoding/json" - "fmt" - "log" - "os" - "path/filepath" - "strings" - "time" - - "github.com/cosmos/cosmos-sdk/types/bech32" - "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" - tmlog "github.com/tendermint/tendermint/libs/log" - tmService "github.com/tendermint/tendermint/libs/service" - "github.com/tendermint/tendermint/types" -) - -func init() { - cosignerCmd.AddCommand(StartCosignerCmd()) - cosignerCmd.AddCommand(AddressCmd()) - rootCmd.AddCommand(cosignerCmd) -} - -var cosignerCmd = &cobra.Command{ - Use: "cosigner", - Short: "Threshold mpc signer for TM based nodes", -} - -type AddressCmdOutput struct { - HexAddress string - PubKey string - ValConsAddress string - ValConsPubAddress string -} - -func AddressCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "address [bech32]", - Short: "Get public key hex address and valcons address", - Example: `horcrux cosigner address cosmos`, - SilenceUsage: true, - Args: cobra.RangeArgs(0, 1), - RunE: func(cmd *cobra.Command, args []string) (err error) { - err = validateCosignerConfig(config.Config) - if err != nil { - return - } - - key, err := signer.LoadCosignerKey(config.keyFilePath(true)) - if err != nil { - return fmt.Errorf("error reading cosigner key: %s", err) - } - - pubKey := key.PubKey - pubKeyAddress := pubKey.Address() - - pubKeyJSON, err := signer.PubKey("", pubKey) - if err != nil { - return err - } - - output := AddressCmdOutput{ - HexAddress: strings.ToUpper(hex.EncodeToString(pubKeyAddress)), - PubKey: pubKeyJSON, - } - - if len(args) == 1 { - bech32ValConsAddress, err := bech32.ConvertAndEncode(args[0]+"valcons", pubKeyAddress) - if err != nil { - return err - } - output.ValConsAddress = bech32ValConsAddress - pubKeyBech32, err := signer.PubKey(args[0], pubKey) - if err != nil { - return err - } - output.ValConsPubAddress = pubKeyBech32 - } else { - bech32Hint := "Pass bech32 base prefix as argument to generate (e.g. cosmos)" - output.ValConsAddress = bech32Hint - output.ValConsPubAddress = bech32Hint - } - - jsonOut, err := json.Marshal(output) - if err != nil { - return err - } - - fmt.Println(string(jsonOut)) - - return nil - }, - } - - return cmd -} - -func StartCosignerCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "start", - Short: "Start cosigner process", - Args: cobra.NoArgs, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if err = signer.RequireNotRunning(config.PidFile); err != nil { - return err - } - - err = validateCosignerConfig(config.Config) - if err != nil { - return err - } - - var ( - // services to stop on shutdown - services []tmService.Service - pv types.PrivValidator - chainID = config.Config.ChainID - logger = tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "validator") - cfg signer.Config - ) - - cfg = signer.Config{ - Mode: "mpc", - PrivValKeyFile: config.keyFilePath(true), - PrivValStateDir: config.StateDir, - ChainID: config.Config.ChainID, - CosignerThreshold: config.Config.CosignerConfig.Threshold, - ListenAddress: config.Config.CosignerConfig.P2PListen, - Nodes: config.Config.Nodes(), - Cosigners: config.Config.CosignerPeers(), - } - - if err = cfg.KeyFileExists(); err != nil { - return err - } - - logger.Info("Tendermint Validator", "mode", cfg.Mode, - "priv-key", cfg.PrivValKeyFile, "priv-state-dir", cfg.PrivValStateDir) - - var val types.PrivValidator - - key, err := signer.LoadCosignerKey(cfg.PrivValKeyFile) - if err != nil { - return fmt.Errorf("error reading cosigner key: %s", err) - } - - // ok to auto initialize on disk since the cosigner share is the one that actually - // protects against double sign - this exists as a cache for the final signature - signState, err := signer.LoadOrCreateSignState(config.privValStateFile(chainID)) - if err != nil { - panic(err) - } - - // state for our cosigner share - // Not automatically initialized on disk to avoid double sign risk - shareSignState, err := signer.LoadSignState(config.shareStateFile(chainID)) - if err != nil { - panic(err) - } - - cosigners := []signer.Cosigner{} - - // add ourselves as a peer so localcosigner can handle GetEphSecPart requests - peers := []signer.CosignerPeer{{ - ID: key.ID, - PublicKey: key.RSAKey.PublicKey, - }} - - for _, cosignerConfig := range cfg.Cosigners { - cosigner := signer.NewRemoteCosigner(cosignerConfig.ID, cosignerConfig.Address) - cosigners = append(cosigners, cosigner) - - if cosignerConfig.ID < 1 || cosignerConfig.ID > len(key.CosignerKeys) { - log.Fatalf("Unexpected cosigner ID %d", cosignerConfig.ID) - } - - pubKey := key.CosignerKeys[cosignerConfig.ID-1] - peers = append(peers, signer.CosignerPeer{ - ID: cosigner.GetID(), - PublicKey: *pubKey, - }) - } - - total := len(cfg.Cosigners) + 1 - localCosignerConfig := signer.LocalCosignerConfig{ - CosignerKey: key, - SignState: &shareSignState, - RsaKey: key.RSAKey, - Address: cfg.ListenAddress, - Peers: peers, - Total: uint8(total), - Threshold: uint8(cfg.CosignerThreshold), - } - - localCosigner := signer.NewLocalCosigner(localCosignerConfig) - - timeout, err := time.ParseDuration(config.Config.CosignerConfig.Timeout) - if err != nil { - log.Fatalf("Error parsing configured timeout: %s. %v\n", config.Config.CosignerConfig.Timeout, err) - } - - raftDir := filepath.Join(config.HomeDir, "raft") - if err := os.MkdirAll(raftDir, 0700); err != nil { - log.Fatalf("Error creating raft directory: %v\n", err) - } - - // RAFT node ID is the cosigner ID - nodeID := fmt.Sprint(key.ID) - - // Start RAFT store listener - raftStore := signer.NewRaftStore(nodeID, - raftDir, cfg.ListenAddress, timeout, logger, localCosigner, cosigners) - if err := raftStore.Start(); err != nil { - log.Fatalf("Error starting raft store: %v\n", err) - } - services = append(services, raftStore) - - val = signer.NewThresholdValidator(&signer.ThresholdValidatorOpt{ - Pubkey: key.PubKey, - Threshold: cfg.CosignerThreshold, - SignState: signState, - Cosigner: localCosigner, - Peers: cosigners, - RaftStore: raftStore, - Logger: logger, - }) - - raftStore.SetThresholdValidator(val.(*signer.ThresholdValidator)) - - pv = &signer.PvGuard{PrivValidator: val} - - pubkey, err := pv.GetPubKey() - if err != nil { - log.Fatal(err) - } - logger.Info("Signer", "address", pubkey.Address()) - - go EnableDebugAndMetrics(cmd.Context()) - - services, err = signer.StartRemoteSigners(services, logger, cfg.ChainID, pv, cfg.Nodes) - if err != nil { - panic(err) - } - - signer.WaitAndTerminate(logger, services, config.PidFile) - - return nil - }, - } - - return cmd -} diff --git a/cmd/horcrux/cmd/key2shares.go b/cmd/horcrux/cmd/key2shares.go deleted file mode 100644 index 25226f10..00000000 --- a/cmd/horcrux/cmd/key2shares.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright © 2021 NAME HERE - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package cmd - -import ( - "fmt" - "strconv" - - "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" - "github.com/tendermint/tendermint/libs/os" -) - -func init() { - rootCmd.AddCommand(CreateCosignerSharesCmd()) -} - -// CreateCosignerSharesCmd is a cobra command for creating cosigner shares from a priv validator -func CreateCosignerSharesCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "create-shares [priv_validator.json] [threshold] [shares]", - Aliases: []string{"shard", "shares"}, - Args: validateCreateCosignerShares, - Short: "Create cosigner shares", - RunE: func(cmd *cobra.Command, args []string) (err error) { - threshold, shares := args[1], args[2] - t, err := strconv.ParseInt(threshold, 10, 64) - if err != nil { - return fmt.Errorf("error parsing threshold (%s): %w", threshold, err) - } - n, err := strconv.ParseInt(shares, 10, 64) - if err != nil { - return fmt.Errorf("error parsing shares (%s): %w", shares, err) - } - - csKeys, err := signer.CreateCosignerSharesFromFile(args[0], t, n) - if err != nil { - return err - } - - // silence usage after all input has been validated - cmd.SilenceUsage = true - - for _, c := range csKeys { - if err = signer.WriteCosignerShareFile(c, fmt.Sprintf("private_share_%d.json", c.ID)); err != nil { - return err - } - fmt.Printf("Created Share %d\n", c.ID) - } - return nil - }, - } - return cmd -} - -func validateCreateCosignerShares(cmd *cobra.Command, args []string) error { - if len(args) != 3 { - return fmt.Errorf("wrong num args exp(3) got(%d)", len(args)) - } - if !os.FileExists(args[0]) { - return fmt.Errorf("priv_validator.json file(%s) doesn't exist", args[0]) - } - threshold, shares := args[1], args[2] - t, err := strconv.ParseInt(threshold, 10, 64) - if err != nil { - return fmt.Errorf("error parsing threshold (%s): %w", threshold, err) - } - n, err := strconv.ParseInt(shares, 10, 64) - if err != nil { - return fmt.Errorf("error parsing shares (%s): %w", shares, err) - } - if t > n { - return fmt.Errorf("threshold cannot be greater than total shares, got [threshold](%d) > [shares](%d)", t, n) - } - if t <= n/2 { - return fmt.Errorf("threshold must be greater than total shares "+ - "divided by 2, got [threshold](%d) <= [shares](%d) / 2", t, n) - } - return nil -} diff --git a/cmd/horcrux/cmd/key2shares_test.go b/cmd/horcrux/cmd/key2shares_test.go deleted file mode 100644 index 4853a163..00000000 --- a/cmd/horcrux/cmd/key2shares_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package cmd - -import ( - "io" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/privval" -) - -func TestKey2Shares(t *testing.T) { - tmp := t.TempDir() - - privValidatorKeyFile := filepath.Join(tmp, "priv_validator_key.json") - privValidatorStateFile := filepath.Join(tmp, "priv_validator_state.json") - pv := privval.NewFilePV(ed25519.GenPrivKey(), privValidatorKeyFile, privValidatorStateFile) - pv.Save() - - tcs := []struct { - name string - args []string - expectErr bool - }{ - { - name: "valid threshold and shares", - args: []string{privValidatorKeyFile, "2", "3"}, - expectErr: false, - }, - { - name: "valid threshold and shares 2", - args: []string{privValidatorKeyFile, "3", "5"}, - expectErr: false, - }, - { - name: "threshold exactly half of shares", - args: []string{privValidatorKeyFile, "2", "4"}, - expectErr: true, - }, - { - name: "threshold less than half of shares", - args: []string{privValidatorKeyFile, "1", "3"}, - expectErr: true, - }, - { - name: "threshold exceeds shares", - args: []string{privValidatorKeyFile, "4", "3"}, - expectErr: true, - }, - { - name: "non-numeric threshold and shares", - args: []string{privValidatorKeyFile, "two", "three"}, - expectErr: true, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - cmd := CreateCosignerSharesCmd() - cmd.SetOutput(io.Discard) - cmd.SetArgs(tc.args) - err := cmd.Execute() - if tc.expectErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/cmd/horcrux/cmd/leader_election.go b/cmd/horcrux/cmd/leader_election.go index 68163f6b..9fb4dfd6 100644 --- a/cmd/horcrux/cmd/leader_election.go +++ b/cmd/horcrux/cmd/leader_election.go @@ -3,139 +3,180 @@ package cmd import ( "context" "fmt" - "log" "time" - _ "github.com/Jille/grpc-multi-resolver" - grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" + grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/client" - "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/v3/client" + "github.com/strangelove-ventures/horcrux/v3/signer" + "github.com/strangelove-ventures/horcrux/v3/signer/multiresolver" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) func init() { - rootCmd.AddCommand(leaderElectionCmd) - rootCmd.AddCommand(getLeaderCmd) + multiresolver.Register() } -var leaderElectionCmd = &cobra.Command{ - Use: "elect [node_id]", - Short: "Elect new raft leader", - Long: `To choose the next eligible leader, pass no argument. +func leaderElectionCmd() *cobra.Command { + return &cobra.Command{ + Use: "elect [node_id]", + Short: "Elect new raft leader", + Long: `To choose the next eligible leader, pass no argument. To choose a specific leader, pass that leader's ID as an argument. `, - Args: cobra.RangeArgs(0, 1), - Example: `horcrux elect # elect next eligible leader + Args: cobra.RangeArgs(0, 1), + Example: `horcrux elect # elect next eligible leader horcrux elect 2 # elect specific leader`, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if config.Config.CosignerConfig == nil { - return fmt.Errorf("cosigner configuration is not present in config file") - } - - if len(config.Config.CosignerConfig.Peers) == 0 { - return fmt.Errorf("cosigner configuration has no peers") - } - - serviceConfig := `{"healthCheckConfig": {"serviceName": "Leader"}, "loadBalancingConfig": [ { "round_robin": {} } ]}` - retryOpts := []grpc_retry.CallOption{ - grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100 * time.Millisecond)), - grpc_retry.WithMax(5), - } - - grpcAddress, err := config.Config.CosignerConfig.LeaderElectMultiAddress() - if err != nil { - return err - } - - fmt.Printf("Broadcasting to address: %s\n", grpcAddress) - conn, err := grpc.Dial(grpcAddress, - grpc.WithDefaultServiceConfig(serviceConfig), grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), - grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))) - if err != nil { - log.Fatalf("dialing failed: %v", err) - } - defer conn.Close() - - leaderID := "" - - if len(args) > 0 { - leaderID = args[0] - } - - ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) - defer cancelFunc() - - grpcClient := proto.NewCosignerGRPCClient(conn) - _, err = grpcClient.TransferLeadership( - ctx, - &proto.CosignerGRPCTransferLeadershipRequest{LeaderID: leaderID}, - ) - if err != nil { - return err - } - - res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) - if err != nil { - return err - } - - fmt.Printf("Leader election successful. New leader: %s\n", res.Leader) - - return nil - }, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + if config.Config.ThresholdModeConfig == nil { + return fmt.Errorf("threshold mode configuration is not present in config file") + } + + if len(config.Config.ThresholdModeConfig.Cosigners) == 0 { + return fmt.Errorf("threshold mode configuration has no cosigners") + } + + serviceConfig := `{"healthCheckConfig": {"serviceName": "Leader"}, "loadBalancingConfig": [ { "round_robin": {} } ]}` + retryOpts := []grpcretry.CallOption{ + grpcretry.WithBackoff(grpcretry.BackoffExponential(100 * time.Millisecond)), + grpcretry.WithMax(5), + } + + grpcAddress, err := config.Config.ThresholdModeConfig.LeaderElectMultiAddress() + if err != nil { + return err + } + + fmt.Printf("Broadcasting to address: %s\n", grpcAddress) + conn, err := grpc.Dial(grpcAddress, + grpc.WithDefaultServiceConfig(serviceConfig), grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + grpc.WithUnaryInterceptor(grpcretry.UnaryClientInterceptor(retryOpts...))) + if err != nil { + return fmt.Errorf("dialing failed: %v", err) + } + defer conn.Close() + + leaderID := "" + + if len(args) > 0 { + leaderID = args[0] + } + + ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelFunc() + + grpcClient := proto.NewCosignerClient(conn) + _, err = grpcClient.TransferLeadership( + ctx, + &proto.TransferLeadershipRequest{LeaderID: leaderID}, + ) + if err != nil { + return err + } + + res, err := grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) + if err != nil { + return err + } + + fmt.Printf("Leader election successful. New leader: %d\n", res.Leader) + + return nil + }, + } } -var getLeaderCmd = &cobra.Command{ - Use: "leader", - Short: "Get current raft leader", - Args: cobra.NoArgs, - Example: `horcrux leader`, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if config.Config.CosignerConfig == nil { - return fmt.Errorf("cosigner configuration is not present in config file") - } - - if len(config.Config.CosignerConfig.Peers) == 0 { - return fmt.Errorf("cosigner configuration has no peers") - } - - retryOpts := []grpc_retry.CallOption{ - grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100 * time.Millisecond)), - grpc_retry.WithMax(5), - } - - grpcAddress, err := client.SanitizeAddress(config.Config.CosignerConfig.P2PListen) - if err != nil { - return err - } - - fmt.Printf("Request address: %s\n", grpcAddress) - conn, err := grpc.Dial(grpcAddress, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), - grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))) - if err != nil { - log.Fatalf("dialing failed: %v", err) - } - defer conn.Close() - - ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) - defer cancelFunc() - - grpcClient := proto.NewCosignerGRPCClient(conn) - - res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) - if err != nil { - return err - } - - fmt.Printf("Current leader: %s\n", res.Leader) - - return nil - }, +func getLeaderCmd() *cobra.Command { + return &cobra.Command{ + Use: "leader", + Short: "Get current raft leader", + Args: cobra.NoArgs, + Example: `horcrux leader`, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + thresholdCfg := config.Config.ThresholdModeConfig + if thresholdCfg == nil { + return fmt.Errorf("threshold mode configuration is not present in config file") + } + + if len(thresholdCfg.Cosigners) == 0 { + return fmt.Errorf("threshold mode configuration has no cosigners") + } + + var id int + + keyFileECIES, err := config.KeyFileExistsCosignerECIES() + if err != nil { + keyFileRSA, err := config.KeyFileExistsCosignerRSA() + if err != nil { + return fmt.Errorf("cosigner encryption keys not found (%s) - (%s): %w", keyFileECIES, keyFileRSA, err) + } + + key, err := signer.LoadCosignerRSAKey(keyFileRSA) + if err != nil { + return fmt.Errorf("error reading cosigner key (%s): %w", keyFileRSA, err) + } + + id = key.ID + } else { + key, err := signer.LoadCosignerECIESKey(keyFileECIES) + if err != nil { + return fmt.Errorf("error reading cosigner key (%s): %w", keyFileECIES, err) + } + + id = key.ID + } + + var p2pListen string + + for _, c := range thresholdCfg.Cosigners { + if c.ShardID == id { + p2pListen = c.P2PAddr + } + } + + if p2pListen == "" { + return fmt.Errorf("cosigner config does not exist for our shard ID %d", id) + } + + retryOpts := []grpcretry.CallOption{ + grpcretry.WithBackoff(grpcretry.BackoffExponential(100 * time.Millisecond)), + grpcretry.WithMax(5), + } + + grpcAddress, err := client.SanitizeAddress(p2pListen) + if err != nil { + return err + } + + fmt.Printf("Request address: %s\n", grpcAddress) + conn, err := grpc.Dial(grpcAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + grpc.WithUnaryInterceptor(grpcretry.UnaryClientInterceptor(retryOpts...))) + if err != nil { + return fmt.Errorf("dialing failed: %v", err) + } + defer conn.Close() + + ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelFunc() + + grpcClient := proto.NewCosignerClient(conn) + + res, err := grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) + if err != nil { + return err + } + + fmt.Printf("Current leader: %d\n", res.Leader) + + return nil + }, + } + } diff --git a/cmd/horcrux/cmd/metrics.go b/cmd/horcrux/cmd/metrics.go index 1380d4e0..078d3be5 100644 --- a/cmd/horcrux/cmd/metrics.go +++ b/cmd/horcrux/cmd/metrics.go @@ -4,19 +4,19 @@ import ( "context" "errors" "fmt" + "io" "net/http" "net/http/pprof" - "os" "time" "github.com/armon/go-metrics" gmprometheus "github.com/armon/go-metrics/prometheus" + cometlog "github.com/cometbft/cometbft/libs/log" "github.com/prometheus/client_golang/prometheus/promhttp" - tmlog "github.com/tendermint/tendermint/libs/log" ) -func AddPrometheusMetrics(mux *http.ServeMux) { - logger := tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "metrics") +func AddPrometheusMetrics(mux *http.ServeMux, out io.Writer) { + logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(out)).With("module", "metrics") // Add metrics from raft's implementation of go-metrics cfg := gmprometheus.DefaultPrometheusOpts @@ -36,8 +36,8 @@ func AddPrometheusMetrics(mux *http.ServeMux) { } // EnableDebugAndMetrics - Initialization errors are not fatal, only logged -func EnableDebugAndMetrics(ctx context.Context) { - logger := tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "debugserver") +func EnableDebugAndMetrics(ctx context.Context, out io.Writer) { + logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(out)).With("module", "debugserver") // Configure Shared Debug HTTP Server for pprof and prometheus if len(config.Config.DebugAddr) == 0 { @@ -59,7 +59,7 @@ func EnableDebugAndMetrics(ctx context.Context) { mux.Handle("/", http.RedirectHandler("/debug/pprof", http.StatusSeeOther)) // Add prometheus metrics - AddPrometheusMetrics(mux) + AddPrometheusMetrics(mux, out) // Configure Debug Server Network Parameters srv := &http.Server{ diff --git a/cmd/horcrux/cmd/migrate.go b/cmd/horcrux/cmd/migrate.go new file mode 100644 index 00000000..d2a63963 --- /dev/null +++ b/cmd/horcrux/cmd/migrate.go @@ -0,0 +1,313 @@ +package cmd + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/json" + "fmt" + "os" + "path/filepath" + + cometcrypto "github.com/cometbft/cometbft/crypto" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" + cometprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/spf13/cobra" + "github.com/strangelove-ventures/horcrux/v3/signer" + amino "github.com/tendermint/go-amino" + "gopkg.in/yaml.v2" +) + +func legacyConfig() (*v2Config, error) { + configFile, err := os.ReadFile(config.ConfigFile) + if err != nil { + return nil, err + } + + legacyConfig := new(v2Config) + + if err := yaml.Unmarshal(configFile, &legacyConfig); err != nil { + return nil, fmt.Errorf("failed to read config file as legacy: %w", err) + } + + if err := legacyConfig.validate(); err != nil { + return nil, err + } + + return legacyConfig, nil +} + +type ( + v2Config struct { + ChainID string `json:"chain-id" yaml:"chain-id"` + PrivValKeyFile *string `json:"key-file,omitempty" yaml:"key-file,omitempty"` + Cosigner *v2CosignerConfig `json:"cosigner" yaml:"cosigner"` + ChainNodes []v2ChainNodeConfig `json:"chain-nodes,omitempty" yaml:"chain-nodes,omitempty"` + DebugAddr string `json:"debug-addr,omitempty" yaml:"debug-addr,omitempty"` + } + + v2CosignerConfig struct { + Threshold int `json:"threshold" yaml:"threshold"` + Shares int `json:"shares" yaml:"shares"` + P2PListen string `json:"p2p-listen" yaml:"p2p-listen"` + Peers []struct { + ShareID int `json:"share-id" yaml:"share-id"` + P2PAddr string `json:"p2p-addr" yaml:"p2p-addr"` + } `json:"peers" yaml:"peers"` + Timeout string `json:"rpc-timeout" yaml:"rpc-timeout"` + } + + v2ChainNodeConfig struct { + PrivValAddr string `json:"priv-val-addr" yaml:"priv-val-addr"` + } + + v2CosignerKey struct { + PubKey cometcrypto.PubKey `json:"pub_key"` + ShareKey []byte `json:"secret_share"` + RSAKey rsa.PrivateKey `json:"rsa_key"` + ID int `json:"id"` + RSAPubs []*rsa.PublicKey `json:"rsa_pubs"` + } +) + +func (c *v2Config) validate() error { + if c.ChainID == "" { + return fmt.Errorf("chain-id is empty") + } + + return nil +} + +func (key *v2CosignerKey) UnmarshalJSON(data []byte) error { + type Alias v2CosignerKey + + aux := &struct { + RSAKey []byte `json:"rsa_key"` + PubkeyBytes []byte `json:"pub_key"` + RSAPubs [][]byte `json:"rsa_pubs"` + *Alias + }{ + Alias: (*Alias)(key), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + privateKey, err := x509.ParsePKCS1PrivateKey(aux.RSAKey) + if err != nil { + return err + } + + var pubkey cometcrypto.PubKey + var protoPubkey cometprotocrypto.PublicKey + err = protoPubkey.Unmarshal(aux.PubkeyBytes) + + // Prior to the tendermint protobuf migration, the public key bytes in key files + // were encoded using the go-amino libraries via + // cdc.MarshalBinaryBare(CosignerEd25519Key.PubKey) + // + // To support reading the public key bytes from these key files, we fallback to + // amino unmarshalling if the protobuf unmarshalling fails + if err != nil { + var pub cometcryptoed25519.PubKey + codec := amino.NewCodec() + codec.RegisterInterface((*cometcrypto.PubKey)(nil), nil) + codec.RegisterConcrete(cometcryptoed25519.PubKey{}, "tendermint/PubKeyEd25519", nil) + errInner := codec.UnmarshalBinaryBare(aux.PubkeyBytes, &pub) + if errInner != nil { + return err + } + pubkey = pub + } else { + pubkey, err = cometcryptoencoding.PubKeyFromProto(protoPubkey) + if err != nil { + return err + } + } + + // unmarshal the public key bytes for each cosigner + key.RSAPubs = make([]*rsa.PublicKey, 0) + for _, bytes := range aux.RSAPubs { + cosignerRsaPubkey, err := x509.ParsePKCS1PublicKey(bytes) + if err != nil { + return err + } + key.RSAPubs = append(key.RSAPubs, cosignerRsaPubkey) + } + + key.RSAKey = *privateKey + key.PubKey = pubkey + return nil +} + +func (key *v2CosignerKey) validate() error { + if key.PubKey == nil || len(key.PubKey.Bytes()) == 0 { + return fmt.Errorf("pub_key cannot be empty") + } + if len(key.ShareKey) == 0 { + return fmt.Errorf("secret_share cannot be empty") + } + if err := key.RSAKey.Validate(); err != nil { + return fmt.Errorf("rsa_key is invalid: %w", err) + } + if key.ID == 0 { + return fmt.Errorf("id cannot be zero") + } + if len(key.RSAPubs) == 0 { + return fmt.Errorf("cosigner keys cannot be empty") + } + + return nil +} + +func migrateCmd() *cobra.Command { + return &cobra.Command{ + Use: "migrate [chain-id]", + Short: "Migrate config and key files from v2 to v3", + SilenceUsage: true, + Args: cobra.RangeArgs(0, 1), + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + + legacyCfg, legacyCfgErr := legacyConfig() + if legacyCfgErr != nil { + fmt.Fprintf( + cmd.OutOrStderr(), + "failed to load legacy config: %v, proceeding to attempt key migration", + legacyCfgErr, + ) + } + + var chainID string + + if len(args) == 1 { + chainID = args[0] + } else { + if legacyCfgErr != nil { + return fmt.Errorf("unable to migrate v2 config without chain-id. please provide [chain-id] argument") + } + + chainID = legacyCfg.ChainID + } + + var legacyCosignerKeyFile string + + if legacyCfgErr == nil && legacyCfg.PrivValKeyFile != nil && *legacyCfg.PrivValKeyFile != "" { + legacyCosignerKeyFile = *legacyCfg.PrivValKeyFile + dir := filepath.Dir(legacyCosignerKeyFile) + config.Config.PrivValKeyDir = &dir + } else { + legacyCosignerKeyFile = filepath.Join(config.HomeDir, "share.json") + } + + if _, err := os.Stat(legacyCosignerKeyFile); err != nil { + return fmt.Errorf("error loading v2 key file: %w", err) + } + + keyFile, err := os.ReadFile(legacyCosignerKeyFile) + if err != nil { + return err + } + + legacyCosignerKey := new(v2CosignerKey) + + if err := legacyCosignerKey.UnmarshalJSON(keyFile); err != nil { + return fmt.Errorf("failed to read key file as legacy: %w", err) + } + + if err := legacyCosignerKey.validate(); err != nil { + return err + } + + newEd25519Key := signer.CosignerEd25519Key{ + PubKey: legacyCosignerKey.PubKey, + PrivateShard: legacyCosignerKey.ShareKey, + ID: legacyCosignerKey.ID, + } + + newEd25519KeyBz, err := newEd25519Key.MarshalJSON() + if err != nil { + return fmt.Errorf("failed to marshal new Ed25519 key to json: %w", err) + } + + newEd25519Path := config.KeyFilePathCosigner(chainID) + if err := os.WriteFile(newEd25519Path, newEd25519KeyBz, 0600); err != nil { + return fmt.Errorf("failed to write new Ed25519 key to %s: %w", newEd25519Path, err) + } + + newRSAKey := signer.CosignerRSAKey{ + RSAKey: legacyCosignerKey.RSAKey, + ID: legacyCosignerKey.ID, + RSAPubs: legacyCosignerKey.RSAPubs, + } + + newRSAKeyBz, err := newRSAKey.MarshalJSON() + if err != nil { + return fmt.Errorf("failed to marshal new RSA key to json: %w", err) + } + + newRSAPath := config.KeyFilePathCosignerRSA() + if err := os.WriteFile(newRSAPath, newRSAKeyBz, 0600); err != nil { + return fmt.Errorf("failed to write new RSA key to %s: %w", newRSAPath, err) + } + + // only attempt config migration if legacy config exists + if legacyCfgErr == nil { + var migratedNodes signer.ChainNodes + + for _, n := range legacyCfg.ChainNodes { + migratedNodes = append(migratedNodes, signer.ChainNode{ + PrivValAddr: n.PrivValAddr, + }) + } + + config.Config.ChainNodes = migratedNodes + config.Config.DebugAddr = legacyCfg.DebugAddr + + signMode := signer.SignModeSingle + + if legacyCfg.Cosigner != nil { + signMode = signer.SignModeThreshold + + var migratedCosigners signer.CosignersConfig + + if legacyCfg.Cosigner.P2PListen != "" { + migratedCosigners = append( + migratedCosigners, + signer.CosignerConfig{ + ShardID: legacyCosignerKey.ID, + P2PAddr: legacyCfg.Cosigner.P2PListen, + }, + ) + } + + for _, c := range legacyCfg.Cosigner.Peers { + migratedCosigners = append(migratedCosigners, signer.CosignerConfig{ + ShardID: c.ShareID, + P2PAddr: c.P2PAddr, + }) + } + + config.Config.ThresholdModeConfig = &signer.ThresholdModeConfig{ + Threshold: legacyCfg.Cosigner.Threshold, + Cosigners: migratedCosigners, + GRPCTimeout: legacyCfg.Cosigner.Timeout, + RaftTimeout: legacyCfg.Cosigner.Timeout, + } + } + + config.Config.SignMode = signMode + config.Config.MaxReadSize = 1024 * 1024 + + if err := config.WriteConfigFile(); err != nil { + return err + } + } + + if err := os.Remove(legacyCosignerKeyFile); err != nil { + return fmt.Errorf("failed to remove legacy key file (%s): %w", legacyCosignerKeyFile, err) + } + + return nil + }, + } +} diff --git a/cmd/horcrux/cmd/migrate_test.go b/cmd/horcrux/cmd/migrate_test.go new file mode 100644 index 00000000..b356723d --- /dev/null +++ b/cmd/horcrux/cmd/migrate_test.go @@ -0,0 +1,232 @@ +package cmd + +import ( + "fmt" + "io" + "os" + "path/filepath" + "testing" + + "github.com/strangelove-ventures/horcrux/v3/cmd/horcrux/cmd/testdata" + "github.com/stretchr/testify/require" +) + +func TestMigrateV2toV3(t *testing.T) { + tmp := t.TempDir() + + configFile := filepath.Join(tmp, "config.yaml") + + err := os.WriteFile(configFile, testdata.ConfigV2, 0600) + require.NoError(t, err) + + keyShareFile := filepath.Join(tmp, "share.json") + + err = os.WriteFile(keyShareFile, testdata.CosignerKeyV2, 0600) + require.NoError(t, err) + + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := []string{"--home", tmp, "config", "migrate"} + cmd.SetArgs(args) + err = cmd.Execute() + require.NoError(t, err) + + require.NoFileExists(t, keyShareFile) + + newKeyShardFile := filepath.Join(tmp, "test_shard.json") + require.FileExists(t, newKeyShardFile) + + newRSAKeyFile := filepath.Join(tmp, "rsa_keys.json") + require.FileExists(t, newRSAKeyFile) + + newKeyShardFileBz, err := os.ReadFile(newKeyShardFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerEd25519KeyMigrated, string(newKeyShardFileBz)) + + newRSAKeyFileBz, err := os.ReadFile(newRSAKeyFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerRSAKeyMigrated, string(newRSAKeyFileBz)) + + newConfigFileBz, err := os.ReadFile(configFile) + require.NoError(t, err) + + require.Equal(t, testdata.ConfigMigrated, string(newConfigFileBz)) +} + +func appendToFile(file, append string) error { + f, err := os.OpenFile(file, + os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + _, err = f.WriteString(append) + return err +} + +func TestMigrateV2toV3DifferentKeyFilePath(t *testing.T) { + tmp := t.TempDir() + + keyDir := filepath.Join(tmp, "keys") + err := os.Mkdir(keyDir, 0700) + require.NoError(t, err) + + configFile := filepath.Join(tmp, "config.yaml") + + err = os.WriteFile(configFile, testdata.ConfigV2, 0600) + require.NoError(t, err) + + keyShareFile := filepath.Join(keyDir, "share.json") + + err = appendToFile(configFile, fmt.Sprintf("key-file: %s", keyShareFile)) + require.NoError(t, err) + + err = os.WriteFile(keyShareFile, testdata.CosignerKeyV2, 0600) + require.NoError(t, err) + + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := []string{"--home", tmp, "config", "migrate"} + cmd.SetArgs(args) + err = cmd.Execute() + require.NoError(t, err) + + require.NoFileExists(t, keyShareFile) + + newKeyShardFile := filepath.Join(keyDir, "test_shard.json") + require.FileExists(t, newKeyShardFile) + + newRSAKeyFile := filepath.Join(keyDir, "rsa_keys.json") + require.FileExists(t, newRSAKeyFile) + + newKeyShardFileBz, err := os.ReadFile(newKeyShardFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerEd25519KeyMigrated, string(newKeyShardFileBz)) + + newRSAKeyFileBz, err := os.ReadFile(newRSAKeyFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerRSAKeyMigrated, string(newRSAKeyFileBz)) + + newConfigFileBz, err := os.ReadFile(configFile) + require.NoError(t, err) + + require.Equal(t, fmt.Sprintf("keyDir: %s\n", keyDir)+testdata.ConfigMigrated, string(newConfigFileBz)) +} + +// Should migrate keys only if config has already been migrated +func TestMigrateV2toV3KeysOnly(t *testing.T) { + tmp := t.TempDir() + + keyShareFile := filepath.Join(tmp, "share.json") + + err := os.WriteFile(keyShareFile, testdata.CosignerKeyV2, 0600) + require.NoError(t, err) + + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := []string{"--home", tmp, "config", "migrate", "test"} + cmd.SetArgs(args) + err = cmd.Execute() + require.NoError(t, err) + + require.NoFileExists(t, keyShareFile) + + newKeyShardFile := filepath.Join(tmp, "test_shard.json") + require.FileExists(t, newKeyShardFile) + + newRSAKeyFile := filepath.Join(tmp, "rsa_keys.json") + require.FileExists(t, newRSAKeyFile) + + newKeyShardFileBz, err := os.ReadFile(newKeyShardFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerEd25519KeyMigrated, string(newKeyShardFileBz)) + + newRSAKeyFileBz, err := os.ReadFile(newRSAKeyFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerRSAKeyMigrated, string(newRSAKeyFileBz)) +} + +// Should not modify config that is already in v3 format +func TestMigrateV2toV3ConfigAlreadyMigrated(t *testing.T) { + tmp := t.TempDir() + + configFile := filepath.Join(tmp, "config.yaml") + + err := os.WriteFile(configFile, []byte(testdata.ConfigMigrated), 0600) + require.NoError(t, err) + + keyShareFile := filepath.Join(tmp, "share.json") + + err = os.WriteFile(keyShareFile, testdata.CosignerKeyV2, 0600) + require.NoError(t, err) + + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := []string{"--home", tmp, "config", "migrate", "test"} + cmd.SetArgs(args) + err = cmd.Execute() + require.NoError(t, err) + + require.NoFileExists(t, keyShareFile) + + newKeyShardFile := filepath.Join(tmp, "test_shard.json") + require.FileExists(t, newKeyShardFile) + + newRSAKeyFile := filepath.Join(tmp, "rsa_keys.json") + require.FileExists(t, newRSAKeyFile) + + newKeyShardFileBz, err := os.ReadFile(newKeyShardFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerEd25519KeyMigrated, string(newKeyShardFileBz)) + + newRSAKeyFileBz, err := os.ReadFile(newRSAKeyFile) + require.NoError(t, err) + + require.Equal(t, testdata.CosignerRSAKeyMigrated, string(newRSAKeyFileBz)) + + newConfigFileBz, err := os.ReadFile(configFile) + require.NoError(t, err) + + require.Equal(t, testdata.ConfigMigrated, string(newConfigFileBz)) +} + +// Should not modify config or keys that are already in v3 format +func TestMigrateV2toV3AlreadyMigrated(t *testing.T) { + tmp := t.TempDir() + + configFile := filepath.Join(tmp, "config.yaml") + + err := os.WriteFile(configFile, []byte(testdata.ConfigMigrated), 0600) + require.NoError(t, err) + + ed25519KeyShardFile := filepath.Join(tmp, "test_shard.json") + + err = os.WriteFile(ed25519KeyShardFile, []byte(testdata.CosignerEd25519KeyMigrated), 0600) + require.NoError(t, err) + + rsaKeyShardFile := filepath.Join(tmp, "rsa_keys.json") + + err = os.WriteFile(rsaKeyShardFile, []byte(testdata.CosignerRSAKeyMigrated), 0600) + require.NoError(t, err) + + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := []string{"--home", tmp, "config", "migrate", "test"} + cmd.SetArgs(args) + err = cmd.Execute() + require.Error(t, err) + require.EqualError( + t, err, + fmt.Sprintf( + "error loading v2 key file: stat %s: no such file or directory", + filepath.Join(tmp, "share.json"), + ), + ) +} diff --git a/cmd/horcrux/cmd/root.go b/cmd/horcrux/cmd/root.go index 9024058b..dd8e4ebc 100644 --- a/cmd/horcrux/cmd/root.go +++ b/cmd/horcrux/cmd/root.go @@ -8,23 +8,53 @@ import ( homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/strangelove-ventures/horcrux/v3/signer" "gopkg.in/yaml.v2" ) -var ( - homeDir string - config RuntimeConfig -) +var config signer.RuntimeConfig + +func rootCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "horcrux", + Short: "A tendermint remote signer with both threshold signer and single signer modes", + } + + cmd.AddCommand(configCmd()) + cmd.AddCommand(startCmd()) + cmd.AddCommand(addressCmd()) + cmd.AddCommand(createCosignerEd25519ShardsCmd()) + cmd.AddCommand(createCosignerECIESShardsCmd()) + + rsaCmd := createCosignerRSAShardsCmd() + rsaCmd.Deprecated = ` +ECIES is recommended for cosigner-to-cosigner encryption since it is faster for the same security as RSA. +Horcrux ECIES uses secp256k1 with a bit size of 256, which is considered to be as secure as RSA with +a bit size of 3072. Horcrux RSA uses a bit size of 4096. + +To use ECIES instead, run: +horcrux create-ecies-shards +` + cmd.AddCommand(rsaCmd) + cmd.AddCommand(leaderElectionCmd()) + cmd.AddCommand(getLeaderCmd()) + cmd.AddCommand(stateCmd()) + cmd.AddCommand(versionCmd()) + + cmd.PersistentFlags().StringVar( + &config.HomeDir, + "home", + "", + "Directory for config and data (default is $HOME/.horcrux)", + ) -var rootCmd = &cobra.Command{ - Use: "horcrux", - Short: "A tendermint remote signer with both single signer and threshold signer modes", + return cmd } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - if err := rootCmd.Execute(); err != nil { + if err := rootCmd().Execute(); err != nil { // Cobra will print the error os.Exit(1) } @@ -32,20 +62,19 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) - rootCmd.PersistentFlags().StringVar(&homeDir, "home", "", "Directory for config and data (default is $HOME/.horcrux)") } // initConfig reads in config file and ENV variables if set. func initConfig() { var home string - if homeDir == "" { + if config.HomeDir == "" { userHome, err := homedir.Dir() handleInitError(err) home = filepath.Join(userHome, ".horcrux") } else { - home = homeDir + home = config.HomeDir } - config = RuntimeConfig{ + config = signer.RuntimeConfig{ HomeDir: home, ConfigFile: filepath.Join(home, "config.yaml"), StateDir: filepath.Join(home, "state"), diff --git a/cmd/horcrux/cmd/shards.go b/cmd/horcrux/cmd/shards.go new file mode 100644 index 00000000..5af886d8 --- /dev/null +++ b/cmd/horcrux/cmd/shards.go @@ -0,0 +1,253 @@ +/* +Copyright © 2021 NAME HERE + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/strangelove-ventures/horcrux/v3/signer" +) + +func createCosignerDirectoryIfNecessary(out string, id int) (string, error) { + dir := filepath.Join(out, fmt.Sprintf("cosigner_%d", id)) + dirStat, err := os.Stat(dir) + if err != nil { + if !os.IsNotExist(err) { + return "", fmt.Errorf("unexpected error fetching info for cosigner directory: %w", err) + } + if err := os.Mkdir(dir, 0700); err != nil { + return "", fmt.Errorf("failed to make directory for cosigner files: %w", err) + } + return dir, nil + } + if !dirStat.IsDir() { + return "", fmt.Errorf("path must be a directory: %s", dir) + } + return dir, nil +} + +const ( + flagOutputDir = "out" + flagThreshold = "threshold" + flagShards = "shards" + flagKeyFile = "key-file" + flagChainID = "chain-id" +) + +func addOutputDirFlag(cmd *cobra.Command) { + cmd.Flags().StringP(flagOutputDir, "", "", "output directory") +} + +func addTotalShardsFlag(cmd *cobra.Command) { + cmd.Flags().Uint8(flagShards, 0, "total key shards") + _ = cmd.MarkFlagRequired(flagShards) +} + +// createCosignerEd25519ShardsCmd is a cobra command for creating +// cosigner shards from a full priv validator key. +func createCosignerEd25519ShardsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-ed25519-shards", + Args: cobra.NoArgs, + Short: "Create cosigner Ed25519 shards", + RunE: func(cmd *cobra.Command, args []string) (err error) { + flags := cmd.Flags() + + chainID, _ := flags.GetString(flagChainID) + keyFile, _ := flags.GetString(flagKeyFile) + threshold, _ := flags.GetUint8(flagThreshold) + shards, _ := flags.GetUint8(flagShards) + + var errs []error + + if keyFile == "" { + return fmt.Errorf("key-file flag must not be empty") + } + + if chainID == "" { + return fmt.Errorf("chain-id flag must not be empty") + } + + if threshold == 0 { + return fmt.Errorf("threshold flag must be > 0, <= --shards, and > --shards/2") + } + + if shards == 0 { + return fmt.Errorf("shards flag must be greater than zero") + } + + if _, err := os.Stat(keyFile); err != nil { + return fmt.Errorf("error accessing priv_validator_key file(%s): %w", keyFile, err) + } + + if threshold > shards { + return fmt.Errorf( + "threshold cannot be greater than total shards, got [threshold](%d) > [shards](%d)", + threshold, shards, + ) + } + + if threshold <= shards/2 { + return fmt.Errorf("threshold must be greater than total shards "+ + "divided by 2, got [threshold](%d) <= [shards](%d) / 2", threshold, shards) + } + + if len(errs) > 0 { + return nil + } + + csKeys, err := signer.CreateCosignerEd25519ShardsFromFile(keyFile, threshold, shards) + if err != nil { + return err + } + + out, _ := cmd.Flags().GetString(flagOutputDir) + if out != "" { + if err := os.MkdirAll(out, 0700); err != nil { + return err + } + } + + // silence usage after all input has been validated + cmd.SilenceUsage = true + + for _, c := range csKeys { + dir, err := createCosignerDirectoryIfNecessary(out, c.ID) + if err != nil { + return err + } + filename := filepath.Join(dir, fmt.Sprintf("%s_shard.json", chainID)) + if err = signer.WriteCosignerEd25519ShardFile(c, filename); err != nil { + return err + } + fmt.Fprintf(cmd.OutOrStdout(), "Created Ed25519 Shard %s\n", filename) + } + return nil + }, + } + + addOutputDirFlag(cmd) + addTotalShardsFlag(cmd) + + f := cmd.Flags() + f.Uint8(flagThreshold, 0, "threshold number of shards required to successfully sign") + _ = cmd.MarkFlagRequired(flagThreshold) + f.String(flagKeyFile, "", "priv_validator_key.json file to shard") + _ = cmd.MarkFlagRequired(flagKeyFile) + f.String(flagChainID, "", "key shards will sign for this chain ID") + _ = cmd.MarkFlagRequired(flagChainID) + + return cmd +} + +// createCosignerECIESShardsCmd is a cobra command for creating cosigner-to-cosigner encryption secp256k1 keys. +func createCosignerECIESShardsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-ecies-shards", + Args: cobra.NoArgs, + Short: "Create cosigner ECIES shards", + + RunE: func(cmd *cobra.Command, args []string) (err error) { + shards, _ := cmd.Flags().GetUint8(flagShards) + + if shards <= 0 { + return fmt.Errorf("shards must be greater than zero (%d): %w", shards, err) + } + + csKeys, err := signer.CreateCosignerECIESShards(int(shards)) + if err != nil { + return err + } + + out, _ := cmd.Flags().GetString(flagOutputDir) + if out != "" { + if err := os.MkdirAll(out, 0700); err != nil { + return err + } + } + + // silence usage after all input has been validated + cmd.SilenceUsage = true + + for _, c := range csKeys { + dir, err := createCosignerDirectoryIfNecessary(out, c.ID) + if err != nil { + return err + } + filename := filepath.Join(dir, "ecies_keys.json") + if err = signer.WriteCosignerECIESShardFile(c, filename); err != nil { + return err + } + fmt.Fprintf(cmd.OutOrStdout(), "Created ECIES Shard %s\n", filename) + } + return nil + }, + } + addTotalShardsFlag(cmd) + addOutputDirFlag(cmd) + return cmd +} + +// createCosignerRSAShardsCmd is a cobra command for creating cosigner-to-cosigner encryption RSA keys. +func createCosignerRSAShardsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create-rsa-shards", + Args: cobra.NoArgs, + Short: "Create cosigner RSA shards", + + RunE: func(cmd *cobra.Command, args []string) (err error) { + shards, _ := cmd.Flags().GetUint8(flagShards) + + if shards <= 0 { + return fmt.Errorf("shards must be greater than zero (%d): %w", shards, err) + } + + csKeys, err := signer.CreateCosignerRSAShards(int(shards)) + if err != nil { + return err + } + + out, _ := cmd.Flags().GetString(flagOutputDir) + if out != "" { + if err := os.MkdirAll(out, 0700); err != nil { + return err + } + } + + // silence usage after all input has been validated + cmd.SilenceUsage = true + + for _, c := range csKeys { + dir, err := createCosignerDirectoryIfNecessary(out, c.ID) + if err != nil { + return err + } + filename := filepath.Join(dir, "rsa_keys.json") + if err = signer.WriteCosignerRSAShardFile(c, filename); err != nil { + return err + } + fmt.Fprintf(cmd.OutOrStdout(), "Created RSA Shard %s\n", filename) + } + return nil + }, + } + addTotalShardsFlag(cmd) + addOutputDirFlag(cmd) + return cmd +} diff --git a/cmd/horcrux/cmd/shards_test.go b/cmd/horcrux/cmd/shards_test.go new file mode 100644 index 00000000..a640376c --- /dev/null +++ b/cmd/horcrux/cmd/shards_test.go @@ -0,0 +1,141 @@ +package cmd + +import ( + "io" + "path/filepath" + "testing" + + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/privval" + "github.com/stretchr/testify/require" +) + +const testChainID = "test" + +func TestEd25519Shards(t *testing.T) { + tmp := t.TempDir() + + privValidatorKeyFile := filepath.Join(tmp, "priv_validator_key.json") + privValidatorStateFile := filepath.Join(tmp, "priv_validator_state.json") + pv := privval.NewFilePV(ed25519.GenPrivKey(), privValidatorKeyFile, privValidatorStateFile) + pv.Save() + + tcs := []struct { + name string + args []string + expectErr bool + }{ + { + name: "valid threshold and shards", + args: []string{ + "--chain-id", testChainID, + "--key-file", privValidatorKeyFile, + "--threshold", "2", + "--shards", "3", + }, + expectErr: false, + }, + { + name: "valid threshold and shards 2", + args: []string{ + "--chain-id", testChainID, + "--key-file", privValidatorKeyFile, + "--threshold", "3", + "--shards", "5", + }, + expectErr: false, + }, + { + name: "threshold exactly half of shards", + args: []string{ + "--chain-id", testChainID, + "--key-file", privValidatorKeyFile, + "--threshold", "2", + "--shards", "4", + }, + expectErr: true, + }, + { + name: "threshold less than half of shards", + args: []string{ + "--chain-id", testChainID, + "--key-file", privValidatorKeyFile, + "--threshold", "1", + "--shards", "3", + }, + expectErr: true, + }, + { + name: "threshold exceeds shards", + args: []string{ + "--chain-id", testChainID, + "--key-file", privValidatorKeyFile, + "--threshold", "4", + "--shards", "3", + }, + expectErr: true, + }, + { + name: "non-numeric threshold and shards", + args: []string{ + "--chain-id", testChainID, + "--key-file", privValidatorKeyFile, + "--threshold", "two", + "--shards", "three", + }, + expectErr: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := append([]string{"create-ed25519-shards", "--home", tmp, "--out", tmp}, tc.args...) + cmd.SetArgs(args) + err := cmd.Execute() + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestRSAShards(t *testing.T) { + tmp := t.TempDir() + + tcs := []struct { + name string + args []string + expectErr bool + }{ + { + name: "valid shards", + args: []string{"--shards", "3"}, + expectErr: false, + }, + { + name: "invalid shards", + args: []string{"--shards", "0"}, + expectErr: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + cmd := rootCmd() + cmd.SetOutput(io.Discard) + args := append([]string{"create-rsa-shards", "--home", tmp, "--out", tmp}, tc.args...) + cmd.SetArgs(args) + err := cmd.Execute() + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/cmd/horcrux/cmd/signer.go b/cmd/horcrux/cmd/signer.go deleted file mode 100644 index d8023e47..00000000 --- a/cmd/horcrux/cmd/signer.go +++ /dev/null @@ -1,89 +0,0 @@ -package cmd - -import ( - "log" - "os" - - "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" - tmlog "github.com/tendermint/tendermint/libs/log" - tmService "github.com/tendermint/tendermint/libs/service" - "github.com/tendermint/tendermint/privval" - "github.com/tendermint/tendermint/types" -) - -func init() { - signerCmd.AddCommand(StartSignerCmd()) - rootCmd.AddCommand(signerCmd) -} - -var signerCmd = &cobra.Command{ - Use: "signer", - Short: "Remote tx signer for TM based nodes.", -} - -func StartSignerCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "start", - Short: "Start single signer process", - Args: cobra.NoArgs, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) (err error) { - if err = signer.RequireNotRunning(config.PidFile); err != nil { - return err - } - - err = validateSingleSignerConfig(config.Config) - if err != nil { - return err - } - - var ( - // services to stop on shutdown - services []tmService.Service - pv types.PrivValidator - chainID = config.Config.ChainID - logger = tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "validator") - cfg signer.Config - ) - - cfg = signer.Config{ - Mode: "single", - PrivValKeyFile: config.keyFilePath(false), - PrivValStateDir: config.StateDir, - ChainID: config.Config.ChainID, - Nodes: config.Config.Nodes(), - } - - if err = cfg.KeyFileExists(); err != nil { - return err - } - - logger.Info("Tendermint Validator", "mode", cfg.Mode, - "priv-key", cfg.PrivValKeyFile, "priv-state-dir", cfg.PrivValStateDir) - - pv = &signer.PvGuard{ - PrivValidator: privval.LoadFilePVEmptyState(cfg.PrivValKeyFile, config.privValStateFile(chainID)), - } - - pubkey, err := pv.GetPubKey() - if err != nil { - log.Fatal(err) - } - logger.Info("Signer", "pubkey", pubkey) - - go EnableDebugAndMetrics(cmd.Context()) - - services, err = signer.StartRemoteSigners(services, logger, cfg.ChainID, pv, cfg.Nodes) - if err != nil { - panic(err) - } - - signer.WaitAndTerminate(logger, services, config.PidFile) - - return nil - }, - } - - return cmd -} diff --git a/cmd/horcrux/cmd/single_signer.go b/cmd/horcrux/cmd/single_signer.go new file mode 100644 index 00000000..0cd1edd9 --- /dev/null +++ b/cmd/horcrux/cmd/single_signer.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "fmt" + "io" + + "github.com/strangelove-ventures/horcrux/v3/signer" +) + +const ( + flagAcceptRisk = "accept-risk" + + singleSignerWarning = `@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ WARNING: SINGLE-SIGNER MODE SHOULD NOT BE USED FOR MAINNET! @ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +Horcrux single-signer mode does not give the level of improved +key security and fault tolerance that Horcrux MPC/cosigner mode +provides. While it is a simpler deployment configuration, +single-signer should only be used for experimentation +as it is not officially supported by Strangelove.` +) + +func NewSingleSignerValidator( + out io.Writer, + acceptRisk bool, +) (*signer.SingleSignerValidator, error) { + fmt.Fprintln(out, singleSignerWarning) + + if !acceptRisk { + panic(fmt.Errorf("risk not accepted. --accept-risk flag required to run single signer mode")) + } + + if err := config.Config.ValidateSingleSignerConfig(); err != nil { + return nil, err + } + + return signer.NewSingleSignerValidator(&config), nil +} diff --git a/cmd/horcrux/cmd/start.go b/cmd/horcrux/cmd/start.go new file mode 100644 index 00000000..66a26a67 --- /dev/null +++ b/cmd/horcrux/cmd/start.go @@ -0,0 +1,88 @@ +package cmd + +import ( + "fmt" + "os" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/cometbft/cometbft/libs/service" + "github.com/spf13/cobra" + "github.com/strangelove-ventures/horcrux/v3/signer" +) + +func startCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Start horcrux signer process", + Args: cobra.NoArgs, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + out := cmd.OutOrStdout() + logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(out)) + + err := signer.RequireNotRunning(logger, config.PidFile) + if err != nil { + return err + } + + if _, err := legacyConfig(); err == nil { + return fmt.Errorf("this is a legacy config. run `horcrux config migrate` to migrate to the latest format") + } + + // create all directories up to the state directory + if err = os.MkdirAll(config.StateDir, 0700); err != nil { + return err + } + + logger.Info( + "Horcrux Validator", + "mode", config.Config.SignMode, + "priv-state-dir", config.StateDir, + ) + + acceptRisk, _ := cmd.Flags().GetBool(flagAcceptRisk) + + var val signer.PrivValidator + var services []service.Service + + switch config.Config.SignMode { + case signer.SignModeThreshold: + services, val, err = NewThresholdValidator(cmd.Context(), logger) + if err != nil { + return err + } + case signer.SignModeSingle: + val, err = NewSingleSignerValidator(out, acceptRisk) + if err != nil { + return err + } + default: + panic(fmt.Errorf("unexpected sign mode: %s", config.Config.SignMode)) + } + + if config.Config.GRPCAddr != "" { + grpcServer := signer.NewRemoteSignerGRPCServer(logger, val, config.Config.GRPCAddr) + services = append(services, grpcServer) + + if err := grpcServer.Start(); err != nil { + return fmt.Errorf("failed to start grpc server: %w", err) + } + } + + go EnableDebugAndMetrics(cmd.Context(), out) + + services, err = signer.StartRemoteSigners(services, logger, val, config.Config.Nodes(), config.Config.MaxReadSize) + if err != nil { + return fmt.Errorf("failed to start remote signer(s): %w", err) + } + + signer.WaitAndTerminate(logger, services, config.PidFile) + + return nil + }, + } + + cmd.Flags().Bool(flagAcceptRisk, false, "Single-signer-mode unsupported. Required to accept risk and proceed.") + + return cmd +} diff --git a/cmd/horcrux/cmd/state.go b/cmd/horcrux/cmd/state.go index b7b4c37c..63ee6a5e 100644 --- a/cmd/horcrux/cmd/state.go +++ b/cmd/horcrux/cmd/state.go @@ -4,18 +4,20 @@ import ( "bufio" "encoding/base64" "fmt" + "io" "os" "strconv" "strings" "time" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/v3/signer" - tmjson "github.com/tendermint/tendermint/libs/json" + cometjson "github.com/cometbft/cometbft/libs/json" + cometlog "github.com/cometbft/cometbft/libs/log" ) -// Snippet Taken from https://raw.githubusercontent.com/tendermint/tendermint/main/privval/file.go +// Snippet Taken from https://raw.githubusercontent.com/cometbft/cometbft/main/privval/file.go // FilePVLastSignState stores the mutable part of PrivValidator. type FilePVLastSignState struct { Height int64 `json:"height"` @@ -23,45 +25,49 @@ type FilePVLastSignState struct { Step int8 `json:"step"` } -func init() { - stateCmd.AddCommand(showStateCmd()) - stateCmd.AddCommand(setStateCmd()) - stateCmd.AddCommand(importStateCmd()) +func stateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "state", + Short: "Commands to configure the horcrux signer's state", + } - rootCmd.AddCommand(stateCmd) -} + cmd.AddCommand(showStateCmd()) + cmd.AddCommand(setStateCmd()) + cmd.AddCommand(importStateCmd()) -var stateCmd = &cobra.Command{ - Use: "state", - Short: "Commands to configure the horcrux signer's state", + return cmd } func showStateCmd() *cobra.Command { return &cobra.Command{ - Use: "show", + Use: "show [chain-id]", Aliases: []string{"s"}, - Short: "Show the priv validator and share sign state", - Args: cobra.ExactArgs(0), + Short: "Show the sign state for a specific chain-id", + Args: cobra.ExactArgs(1), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { + + chainID := args[0] + if _, err := os.Stat(config.HomeDir); os.IsNotExist(err) { return fmt.Errorf("%s does not exist, initialize config with horcrux config init and try again", config.HomeDir) } - pv, err := signer.LoadSignState(config.privValStateFile(config.Config.ChainID)) + pv, err := signer.LoadSignState(config.PrivValStateFile(chainID)) if err != nil { return err } - share, err := signer.LoadSignState(config.shareStateFile(config.Config.ChainID)) + cs, err := signer.LoadSignState(config.CosignerStateFile(chainID)) if err != nil { return err } - fmt.Println("Private Validator State:") - printSignState(pv) - fmt.Println("Share Sign State:") - printSignState(share) + out := cmd.OutOrStdout() + fmt.Fprintln(out, "Private Validator State:") + printSignState(out, pv) + fmt.Fprintln(out, "Share Sign State:") + printSignState(out, cs) return nil }, } @@ -69,12 +75,17 @@ func showStateCmd() *cobra.Command { func setStateCmd() *cobra.Command { return &cobra.Command{ - Use: "set [height]", + Use: "set chain-id height", Aliases: []string{"s"}, - Short: "Set the height for both the priv validator and the share sign state", - Args: cobra.ExactArgs(1), + Short: "Set the height for the sign state of a specific chain-id", + Args: cobra.ExactArgs(2), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { + chainID := args[0] + + out := cmd.OutOrStdout() + logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(out)) + if _, err := os.Stat(config.HomeDir); os.IsNotExist(err) { cmd.SilenceUsage = false return fmt.Errorf("%s does not exist, initialize config with horcrux config init and try again", config.HomeDir) @@ -82,29 +93,29 @@ func setStateCmd() *cobra.Command { // Resetting the priv_validator_state.json should only be allowed if the // signer is not running. - if err := signer.RequireNotRunning(config.PidFile); err != nil { + if err := signer.RequireNotRunning(logger, config.PidFile); err != nil { return err } - pv, err := signer.LoadSignState(config.privValStateFile(config.Config.ChainID)) + pv, err := signer.LoadOrCreateSignState(config.PrivValStateFile(chainID)) if err != nil { return err } - share, err := signer.LoadSignState(config.shareStateFile(config.Config.ChainID)) + cs, err := signer.LoadOrCreateSignState(config.CosignerStateFile(chainID)) if err != nil { return err } - height, err := strconv.ParseInt(args[0], 10, 64) + height, err := strconv.ParseInt(args[1], 10, 64) if err != nil { cmd.SilenceUsage = false return err } - fmt.Fprintf(cmd.OutOrStdout(), "Setting height %d\n", height) + fmt.Fprintf(out, "Setting height %d\n", height) - pv.EphemeralPublic, share.EphemeralPublic = nil, nil + pv.NoncePublic, cs.NoncePublic = nil, nil signState := signer.SignStateConsensus{ Height: height, Round: 0, @@ -112,12 +123,12 @@ func setStateCmd() *cobra.Command { Signature: nil, SignBytes: nil, } - err = pv.Save(signState, nil, false) + err = pv.Save(signState, nil) if err != nil { fmt.Printf("error saving privval sign state") return err } - err = share.Save(signState, nil, false) + err = cs.Save(signState, nil) if err != nil { fmt.Printf("error saving share sign state") return err @@ -129,43 +140,48 @@ func setStateCmd() *cobra.Command { func importStateCmd() *cobra.Command { return &cobra.Command{ - Use: "import [height]", + Use: "import chain-id", Aliases: []string{"i"}, Short: "Read the old priv_validator_state.json and set the height, round and step" + "(good for migrations but NOT shared state update)", - Args: cobra.ExactArgs(0), + Args: cobra.ExactArgs(1), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { + chainID := args[0] + if _, err := os.Stat(config.HomeDir); os.IsNotExist(err) { cmd.SilenceUsage = false return fmt.Errorf("%s does not exist, initialize config with horcrux config init and try again", config.HomeDir) } + out := cmd.OutOrStdout() + logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(out)) + // Resetting the priv_validator_state.json should only be allowed if the // signer is not running. - if err := signer.RequireNotRunning(config.PidFile); err != nil { + if err := signer.RequireNotRunning(logger, config.PidFile); err != nil { return err } // Recreate privValStateFile if necessary - pv, err := signer.LoadOrCreateSignState(config.privValStateFile(config.Config.ChainID)) + pv, err := signer.LoadOrCreateSignState(config.PrivValStateFile(chainID)) if err != nil { return err } // shareStateFile does not exist during default config init, so create if necessary - share, err := signer.LoadOrCreateSignState(config.shareStateFile(config.Config.ChainID)) + cs, err := signer.LoadOrCreateSignState(config.CosignerStateFile(chainID)) if err != nil { return err } // Allow user to paste in priv_validator_state.json - fmt.Println("IMPORTANT: Your validator should already be STOPPED. You must copy the latest state..") + fmt.Fprintln(out, "IMPORTANT: Your validator should already be STOPPED. You must copy the latest state..") <-time.After(2 * time.Second) - fmt.Println("") - fmt.Println("Paste your old priv_validator_state.json. Input a blank line after the pasted JSON to continue.") - fmt.Println("") + fmt.Fprintln(out, "") + fmt.Fprintln(out, "Paste your old priv_validator_state.json. Input a blank line after the pasted JSON to continue.") + fmt.Fprintln(out, "") var textBuffer strings.Builder @@ -180,13 +196,13 @@ func importStateCmd() *cobra.Command { pvState := &FilePVLastSignState{} - err = tmjson.Unmarshal([]byte(finalJSON), &pvState) + err = cometjson.Unmarshal([]byte(finalJSON), &pvState) if err != nil { fmt.Println("Error parsing priv_validator_state.json") return err } - pv.EphemeralPublic = nil + pv.NoncePublic = nil signState := signer.SignStateConsensus{ Height: pvState.Height, Round: int64(pvState.Round), @@ -200,12 +216,12 @@ func importStateCmd() *cobra.Command { " Step: %v\n", signState.Height, signState.Round, signState.Step) - err = pv.Save(signState, nil, false) + err = pv.Save(signState, nil) if err != nil { fmt.Printf("error saving privval sign state") return err } - err = share.Save(signState, nil, false) + err = cs.Save(signState, nil) if err != nil { fmt.Printf("error saving share sign state") return err @@ -216,19 +232,19 @@ func importStateCmd() *cobra.Command { } } -func printSignState(ss signer.SignState) { - fmt.Printf(" Height: %v\n"+ +func printSignState(out io.Writer, ss *signer.SignState) { + fmt.Fprintf(out, " Height: %v\n"+ " Round: %v\n"+ " Step: %v\n", ss.Height, ss.Round, ss.Step) - if ss.EphemeralPublic != nil { - fmt.Println(" Ephemeral Public Key:", base64.StdEncoding.EncodeToString(ss.EphemeralPublic)) + if ss.NoncePublic != nil { + fmt.Fprintln(out, " Nonce Public Key:", base64.StdEncoding.EncodeToString(ss.NoncePublic)) } if ss.Signature != nil { - fmt.Println(" Signature:", base64.StdEncoding.EncodeToString(ss.Signature)) + fmt.Fprintln(out, " Signature:", base64.StdEncoding.EncodeToString(ss.Signature)) } if ss.SignBytes != nil { - fmt.Println(" SignBytes:", ss.SignBytes) + fmt.Fprintln(out, " SignBytes:", ss.SignBytes) } } diff --git a/cmd/horcrux/cmd/state_test.go b/cmd/horcrux/cmd/state_test.go index 053e549e..e805e202 100644 --- a/cmd/horcrux/cmd/state_test.go +++ b/cmd/horcrux/cmd/state_test.go @@ -7,27 +7,25 @@ import ( "testing" "time" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/v3/signer" "github.com/stretchr/testify/require" ) func TestStateSetCmd(t *testing.T) { tmpHome := t.TempDir() tmpConfig := filepath.Join(tmpHome, ".horcrux") - chainid := "horcrux-1" + stateDir := filepath.Join(tmpHome, ".horcrux", "state") - t.Setenv("HOME", tmpHome) + chainID := "horcrux-1" - cmd := initCmd() + cmd := rootCmd() cmd.SetOutput(io.Discard) cmd.SetArgs([]string{ - chainid, - "tcp://10.168.0.1:1234", - "-c", + "--home", tmpConfig, + "config", "init", + "-n", "tcp://10.168.0.1:1234", "-t", "2", - "-p", "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3", - "-l", "tcp://10.168.1.1:2222", - "--timeout", "1500ms", + "-c", "tcp://10.168.1.1:2222,tcp://10.168.1.2:2222,tcp://10.168.1.3:2222", }) err := cmd.Execute() require.NoError(t, err) @@ -39,12 +37,12 @@ func TestStateSetCmd(t *testing.T) { }{ { name: "valid height", - args: []string{"123456789"}, + args: []string{chainID, "123456789"}, expectErr: false, }, { name: "invalid height", - args: []string{"-123456789"}, + args: []string{chainID, "-123456789"}, expectErr: true, }, } @@ -63,24 +61,24 @@ func TestStateSetCmd(t *testing.T) { } else { require.NoError(t, err) - height, err := strconv.ParseInt(tc.args[0], 10, 64) + height, err := strconv.ParseInt(tc.args[1], 10, 64) require.NoError(t, err) - ss, err := signer.LoadSignState(filepath.Join(tmpConfig, "state", chainid+"_priv_validator_state.json")) + ss, err := signer.LoadSignState(filepath.Join(stateDir, chainID+"_priv_validator_state.json")) require.NoError(t, err) require.Equal(t, height, ss.Height) require.Equal(t, int64(0), ss.Round) require.Equal(t, int8(0), ss.Step) - require.Nil(t, ss.EphemeralPublic) + require.Nil(t, ss.NoncePublic) require.Nil(t, ss.Signature) require.Nil(t, ss.SignBytes) - ss, err = signer.LoadSignState(filepath.Join(tmpConfig, "state", chainid+"_share_sign_state.json")) + ss, err = signer.LoadSignState(filepath.Join(stateDir, chainID+"_share_sign_state.json")) require.NoError(t, err) require.Equal(t, height, ss.Height) require.Equal(t, int64(0), ss.Round) require.Equal(t, int8(0), ss.Step) - require.Nil(t, ss.EphemeralPublic) + require.Nil(t, ss.NoncePublic) require.Nil(t, ss.Signature) require.Nil(t, ss.SignBytes) } diff --git a/cmd/horcrux/cmd/testdata/config-migrated.yaml b/cmd/horcrux/cmd/testdata/config-migrated.yaml new file mode 100644 index 00000000..978f8f5e --- /dev/null +++ b/cmd/horcrux/cmd/testdata/config-migrated.yaml @@ -0,0 +1,19 @@ +signMode: threshold +thresholdMode: + threshold: 2 + cosigners: + - shardID: 3 + p2pAddr: tcp://127.0.0.1:2224 + - shardID: 1 + p2pAddr: tcp://127.0.0.1:2222 + - shardID: 2 + p2pAddr: tcp://127.0.0.1:2223 + grpcTimeout: 1000ms + raftTimeout: 1000ms +chainNodes: +- privValAddr: tcp://127.0.0.1:1234 +- privValAddr: tcp://127.0.0.1:2345 +- privValAddr: tcp://127.0.0.1:3456 +debugAddr: "" +grpcAddr: "" +maxReadSize: 1048576 diff --git a/cmd/horcrux/cmd/testdata/config-v2.yaml b/cmd/horcrux/cmd/testdata/config-v2.yaml new file mode 100644 index 00000000..57fdf00c --- /dev/null +++ b/cmd/horcrux/cmd/testdata/config-v2.yaml @@ -0,0 +1,15 @@ +chain-id: test +cosigner: + threshold: 2 + shares: 3 + p2p-listen: tcp://127.0.0.1:2224 + peers: + - share-id: 1 + p2p-addr: tcp://127.0.0.1:2222 + - share-id: 2 + p2p-addr: tcp://127.0.0.1:2223 + rpc-timeout: 1000ms +chain-nodes: +- priv-val-addr: tcp://127.0.0.1:1234 +- priv-val-addr: tcp://127.0.0.1:2345 +- priv-val-addr: tcp://127.0.0.1:3456 diff --git a/cmd/horcrux/cmd/testdata/cosigner-key-migrated-ed25519.json b/cmd/horcrux/cmd/testdata/cosigner-key-migrated-ed25519.json new file mode 100644 index 00000000..ae8a9382 --- /dev/null +++ b/cmd/horcrux/cmd/testdata/cosigner-key-migrated-ed25519.json @@ -0,0 +1 @@ +{"pubKey":"CiBRLhKCqDU6Wufhj0TZK6jNO8LreArc+CKHKYitdTyYUg==","privateShard":"q0zuUQpplAfBJxoA2e2O1H7BdBgopzhPmL8mIWP8AQw=","id":3} \ No newline at end of file diff --git a/cmd/horcrux/cmd/testdata/cosigner-key-migrated-rsa.json b/cmd/horcrux/cmd/testdata/cosigner-key-migrated-rsa.json new file mode 100644 index 00000000..17bf9860 --- /dev/null +++ b/cmd/horcrux/cmd/testdata/cosigner-key-migrated-rsa.json @@ -0,0 +1 @@ +{"rsaKey":"MIIJKAIBAAKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQKCAgAZ/xLkK43QqwBmbRwGnLtrPO4eBW6re24kjjSfBfPuZ4aMOK4n8lTYaerGV4Z4e4AIdM8Hs6yfYWtK1XuoY9Q4Mxu1Qg0ubIuOsP18ctCbVGZpsnVesrFODNUbRi/MbnlSdKMyl+dkAGhVk+7c1/r5YVhK+Va0yr8fUvP2uobtyZRMm64XhDtSAtv+1BN2dLQhwPW+/cQmHXoO/C7cM+CAHpsyJ5MPcIAukqG/8H1Uks1yI9QNJsMMgzkjDtXOIv5oI2Dhex4fvUF6pFCYktHX8YPIBKZbEx0bM6pAcAJecmhNH78I6HKbcqBzGovrpHfdwBqIZgrQMfS7rTeKPDykNZ2aHb3xc2Tv1FOcAx+sVg58eEwyTzhTSriBJVWBlGfHbey09eojRhDMlrMgqTVArT25okKwUF+xl0eKqJlFvspSDN3XV6Yb0D35MXS8qQEFoTyM5b40mUIlgabR7cWu+jPB1sFU3P28NPHRG40fwnr89yYX3gevRPSrigewfVnkb3qMBa/fJ7Khuet7XxWYVCXwLDzRSSgHkEgkY3llc8J0Yg2HHdnNOSLqpRmnRED9li7Ol04Ps6sFB6nVszpEa6D6B7ADg7nVxsHbwrvKZ47Ut8dbGBm9Cpp4hFg9oGcvRdogz2bsjCU4Y90X3nfvR1YIkwoEkj/n5ZbuX1PPKQKCAQEA37ZzyFozo/Y2//heRxWgFPwVzBK27+DYc16WLtZxs+shaiXJ4Zn0DOI5x1VdC50sxHvlERcTQeQhSO+jnXQOkF6YQgylxLuYoFdo2l5jojn2vsHa4Klw9UatpFStCwz+PhD0wK7WhDoEKvwbvQ2wTa/wvDpnAuhhAEh2hMMuDkRpiVulYe40ywQW/NOrfUklAw1D/5NflDGPCYBYreP7pNtYbMRt9zLhOgQSvn9GqbpnnMj88gZDFxNO7jDiUsYO7yFsi01ALJ+T6AfVtKRyOEbjhpOBBpvlpwbUAMHuARjgcTWmvyWPbafIGpiaSX8ThtG8h78n/ITX6+NPF6fIfwKCAQEA6NFO9qLmYJWj8IaJaPKYBB1JdnFXOJvmPiJ4DS8BhLsSoEYsYE83ECXJwTI2F2l6MVyBXq+PPrQKm2vsUEWzzn/Fi+b7jEbjTv379yLylD1fjsMT0NHtN3vq09Yp3kNgTrd1IfCNxZ5A92Vh0f67PjHB3aMXeYd+ydBeIIlLVAgR6nUtkvGmFtuPunUlZiuB6UpMXDjPRN1VrddvQVTgSkPl9WB8wPcShxOz+wL4Hi5DII+ThFdAAh9pnIFaBF1Et/xMl2ss/7hcxqcIItQSBLotU0brPHMvoiSEjHWLuekw/b5noabkrfm8NOhB6Gjrq58oODe9ZrDjiaweh55jAwKCAQEApTqIgW29vlfXf27dkvrx5Q3au4MHAly7AVrW6XkROaVsZI3McYfXrLxZmFQACNfpfKVVJi441u27d7cmzOAu8YosQnw84vT7YVGt67rTM7pD99gN5OjAuSeekETKGeNa1FSJsNZxMe/3rBfQFO3LTVWpJByugINJQYBDqQLPPVJh8EVz/MSG0XsPz2Q2wK4JXBusIVOjwDxqPMZCuQwtjDFFOfBKl81IdCUWAwTWF/3JEQ+RYuAlJSHpphsMzb3iwdOZ67j+sPabs0A2ItliUxZobbj8DvmNwLNWWcjiFIVfH75UjdEcAg1tydbz/VyR+31lFY2l5ufm4h5dCEev2QKCAQAX543NAxLWbebkRlwLe4UiPwOQ9rg25sLwNEfRSrdEMpUKAcqCpP+JV+fsP0SQiNL0CIR7/Vie3ouMQ7uCznVUyYe2AqRnVcv3C1r4mA0CLX8HQH5jXXqWzNFiqMWpvY9A5dNQBcv4s3QGMtGlZxtAmolGQX2ii8f33r4bZx1l5mI4iYmBYfBkvmx2f5q0b9kp4+gNPAQEFRm7/Le+pIFW/ru4wwxsH7I2Tk6XgkmJh8R6rmM+HltDHIiSejGM6yqoHW6byXRYWUylVPcf5FhpRdhriYeTsFv+sPMvHM6Y6xmNpCQt0939AvxRDlveCg/Qkknl48s9pQHn29VSpW+TAoIBAE862157emwegrRYu66ENMMNLbF6YxBpL47iWC1NQ4/8aM5i58edv1VWUw5R441uvAGXk7uzsYkrxmp0nj6UANkjq6n06mayN90kbg4FvjAzEtbbAu8byap+B5xLSuetIc+dVqACKihQ09zwm3d/LFtbrgZ2KGiw/RfvMxxT0HYp9A7NdYT4nf2Aqa3UR4SuxWm4bWWLHHMGeS4N9MuwFP64jLgLrXzyB851Avuz9LJCpNAflE9SQUvTqGwpFvsgEwQcGH+5vcvcBENCYbAwq5hmMnzrAXsA1NnJNqn+oqXG8GIogG7DOa4965QL60TwDu9s/opzV2bMVhVtxDqKSfo=","rsaPubs":["MIICCgKCAgEAzo2bGRRrwn1/TFlkJ9yqvOcx0BYvmay+rPQFnDFsKxb+WHHLtwn/juxY0Ub+ABwCgJgBUr4k3G9piFYwtL0R3ton6UulwYMgNQ6cnn8/zmAx/STP/WGYKXRtTR80csC+u8g/kzUK/lX2pGz77BLNxflKf/yfnm3wkCcJecnv2PLW84J3/s6b3TUkS+ygUQL3SB+IN7dI/i1pls7my6pCTOJxIu7TJ+PPahyDkRhE0OapjH0OQIbHXNeCqe71uQwALdf1dwTDl2JeIL7jhGWB8xb2PfeLX+VZsOWUR0NPfs83viS+Pjtz6ndYX1+3+BQxOIutnkUC6IwSBqsG+M2cqElETIgUHpxqRl0QtReq18+GTX9CfFB5hmWgLlGICij9Lnz0zpwtyIQJXn2Cny8XeWi8E9uKpi+4MNkDqwPd2U+wIXBPVBgqPjTByLeish+VaxKV2bHzqManB5WHa0g7WDK9p8OdZ7To8miJF+hdqOZMHnxThY/hr0102ffOq8XCDIfm873Ie2Cn/+KBHwCc6e7XO5ohWKm9WQbsxpmpn3+ru1ekWTkqC8YC7FFpljMCpl9NiGz4edVzSnnL8OU12M1pofEwpbMtlNCzaVJkMzfo9jDRoWxDyKffRYbdp93V1Oio0ab2ou9uZ0Jx0mXIpLyvznRNmDEsj5nrWmbW5jcCAwEAAQ==","MIICCgKCAgEA83UMSAbKSL4/W9VAzn+XjqCmhl8og6BoZvukS1pQI0JFrox63hJYavHTQB0DO2iXomfpm9d+J8NHsBsWf7DD/9aNaByGRJ0k5Lde64FfTj6LP9I5yRoKuGGQ0Heuvuisz9DMWRyhkO9hJiyedX7VdPx3VdUW4AX+FWyJ1pKpj0g/s8eYrUFyzISdoq/pRwkVkzHpXqFh3L5ASUjf9eQXGYsQsDI0UDuzZdYD4nitQ5Q0POM7jCgSQQ8d/b0eaF2hCzbZ1UWKx8LzCU7j4NRqrYJluRqkxeEtBeZsq7QX6Hs13tg+wKKCkOI+wt/1tifLE8IA3es0pXm0UstVduaTMeFtLTvIYE9E/0yFC23aFydz1Fny6HBjpfNo6BgzNCurMziOdpiuLy+7luPM+SBJ3YV0D9TVU8Lo0vawPccj3tcKmozeJdBhuedXWAm00mlCw+LueKBUVxti2kwHiDjBDbLDymZYZHR8HYI0KsrycsvemTotZzYXgDjyRful7mPLGecJhRye7xNX9lVUse81C94gmdZXVL2GKY1PquWJvgazg99gta62GrRj127vDcS2UI+6/4aTJwQFvRqWRLvS/MIJyq5eiq1WyDLOT8dOyBlb7+BV55cB7JUTiO7MsMNaX0h/C3iGrTOnh8rmC/20ygHqZC3E1Lw0SezI2r1NzzcCAwEAAQ==","MIICCgKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQ=="],"id":3} \ No newline at end of file diff --git a/cmd/horcrux/cmd/testdata/cosigner-key-v2.json b/cmd/horcrux/cmd/testdata/cosigner-key-v2.json new file mode 100644 index 00000000..e8a06b1d --- /dev/null +++ b/cmd/horcrux/cmd/testdata/cosigner-key-v2.json @@ -0,0 +1,11 @@ +{ + "rsa_key": "MIIJKAIBAAKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQKCAgAZ/xLkK43QqwBmbRwGnLtrPO4eBW6re24kjjSfBfPuZ4aMOK4n8lTYaerGV4Z4e4AIdM8Hs6yfYWtK1XuoY9Q4Mxu1Qg0ubIuOsP18ctCbVGZpsnVesrFODNUbRi/MbnlSdKMyl+dkAGhVk+7c1/r5YVhK+Va0yr8fUvP2uobtyZRMm64XhDtSAtv+1BN2dLQhwPW+/cQmHXoO/C7cM+CAHpsyJ5MPcIAukqG/8H1Uks1yI9QNJsMMgzkjDtXOIv5oI2Dhex4fvUF6pFCYktHX8YPIBKZbEx0bM6pAcAJecmhNH78I6HKbcqBzGovrpHfdwBqIZgrQMfS7rTeKPDykNZ2aHb3xc2Tv1FOcAx+sVg58eEwyTzhTSriBJVWBlGfHbey09eojRhDMlrMgqTVArT25okKwUF+xl0eKqJlFvspSDN3XV6Yb0D35MXS8qQEFoTyM5b40mUIlgabR7cWu+jPB1sFU3P28NPHRG40fwnr89yYX3gevRPSrigewfVnkb3qMBa/fJ7Khuet7XxWYVCXwLDzRSSgHkEgkY3llc8J0Yg2HHdnNOSLqpRmnRED9li7Ol04Ps6sFB6nVszpEa6D6B7ADg7nVxsHbwrvKZ47Ut8dbGBm9Cpp4hFg9oGcvRdogz2bsjCU4Y90X3nfvR1YIkwoEkj/n5ZbuX1PPKQKCAQEA37ZzyFozo/Y2//heRxWgFPwVzBK27+DYc16WLtZxs+shaiXJ4Zn0DOI5x1VdC50sxHvlERcTQeQhSO+jnXQOkF6YQgylxLuYoFdo2l5jojn2vsHa4Klw9UatpFStCwz+PhD0wK7WhDoEKvwbvQ2wTa/wvDpnAuhhAEh2hMMuDkRpiVulYe40ywQW/NOrfUklAw1D/5NflDGPCYBYreP7pNtYbMRt9zLhOgQSvn9GqbpnnMj88gZDFxNO7jDiUsYO7yFsi01ALJ+T6AfVtKRyOEbjhpOBBpvlpwbUAMHuARjgcTWmvyWPbafIGpiaSX8ThtG8h78n/ITX6+NPF6fIfwKCAQEA6NFO9qLmYJWj8IaJaPKYBB1JdnFXOJvmPiJ4DS8BhLsSoEYsYE83ECXJwTI2F2l6MVyBXq+PPrQKm2vsUEWzzn/Fi+b7jEbjTv379yLylD1fjsMT0NHtN3vq09Yp3kNgTrd1IfCNxZ5A92Vh0f67PjHB3aMXeYd+ydBeIIlLVAgR6nUtkvGmFtuPunUlZiuB6UpMXDjPRN1VrddvQVTgSkPl9WB8wPcShxOz+wL4Hi5DII+ThFdAAh9pnIFaBF1Et/xMl2ss/7hcxqcIItQSBLotU0brPHMvoiSEjHWLuekw/b5noabkrfm8NOhB6Gjrq58oODe9ZrDjiaweh55jAwKCAQEApTqIgW29vlfXf27dkvrx5Q3au4MHAly7AVrW6XkROaVsZI3McYfXrLxZmFQACNfpfKVVJi441u27d7cmzOAu8YosQnw84vT7YVGt67rTM7pD99gN5OjAuSeekETKGeNa1FSJsNZxMe/3rBfQFO3LTVWpJByugINJQYBDqQLPPVJh8EVz/MSG0XsPz2Q2wK4JXBusIVOjwDxqPMZCuQwtjDFFOfBKl81IdCUWAwTWF/3JEQ+RYuAlJSHpphsMzb3iwdOZ67j+sPabs0A2ItliUxZobbj8DvmNwLNWWcjiFIVfH75UjdEcAg1tydbz/VyR+31lFY2l5ufm4h5dCEev2QKCAQAX543NAxLWbebkRlwLe4UiPwOQ9rg25sLwNEfRSrdEMpUKAcqCpP+JV+fsP0SQiNL0CIR7/Vie3ouMQ7uCznVUyYe2AqRnVcv3C1r4mA0CLX8HQH5jXXqWzNFiqMWpvY9A5dNQBcv4s3QGMtGlZxtAmolGQX2ii8f33r4bZx1l5mI4iYmBYfBkvmx2f5q0b9kp4+gNPAQEFRm7/Le+pIFW/ru4wwxsH7I2Tk6XgkmJh8R6rmM+HltDHIiSejGM6yqoHW6byXRYWUylVPcf5FhpRdhriYeTsFv+sPMvHM6Y6xmNpCQt0939AvxRDlveCg/Qkknl48s9pQHn29VSpW+TAoIBAE862157emwegrRYu66ENMMNLbF6YxBpL47iWC1NQ4/8aM5i58edv1VWUw5R441uvAGXk7uzsYkrxmp0nj6UANkjq6n06mayN90kbg4FvjAzEtbbAu8byap+B5xLSuetIc+dVqACKihQ09zwm3d/LFtbrgZ2KGiw/RfvMxxT0HYp9A7NdYT4nf2Aqa3UR4SuxWm4bWWLHHMGeS4N9MuwFP64jLgLrXzyB851Avuz9LJCpNAflE9SQUvTqGwpFvsgEwQcGH+5vcvcBENCYbAwq5hmMnzrAXsA1NnJNqn+oqXG8GIogG7DOa4965QL60TwDu9s/opzV2bMVhVtxDqKSfo=", + "pub_key": "CiBRLhKCqDU6Wufhj0TZK6jNO8LreArc+CKHKYitdTyYUg==", + "rsa_pubs": [ + "MIICCgKCAgEAzo2bGRRrwn1/TFlkJ9yqvOcx0BYvmay+rPQFnDFsKxb+WHHLtwn/juxY0Ub+ABwCgJgBUr4k3G9piFYwtL0R3ton6UulwYMgNQ6cnn8/zmAx/STP/WGYKXRtTR80csC+u8g/kzUK/lX2pGz77BLNxflKf/yfnm3wkCcJecnv2PLW84J3/s6b3TUkS+ygUQL3SB+IN7dI/i1pls7my6pCTOJxIu7TJ+PPahyDkRhE0OapjH0OQIbHXNeCqe71uQwALdf1dwTDl2JeIL7jhGWB8xb2PfeLX+VZsOWUR0NPfs83viS+Pjtz6ndYX1+3+BQxOIutnkUC6IwSBqsG+M2cqElETIgUHpxqRl0QtReq18+GTX9CfFB5hmWgLlGICij9Lnz0zpwtyIQJXn2Cny8XeWi8E9uKpi+4MNkDqwPd2U+wIXBPVBgqPjTByLeish+VaxKV2bHzqManB5WHa0g7WDK9p8OdZ7To8miJF+hdqOZMHnxThY/hr0102ffOq8XCDIfm873Ie2Cn/+KBHwCc6e7XO5ohWKm9WQbsxpmpn3+ru1ekWTkqC8YC7FFpljMCpl9NiGz4edVzSnnL8OU12M1pofEwpbMtlNCzaVJkMzfo9jDRoWxDyKffRYbdp93V1Oio0ab2ou9uZ0Jx0mXIpLyvznRNmDEsj5nrWmbW5jcCAwEAAQ==", + "MIICCgKCAgEA83UMSAbKSL4/W9VAzn+XjqCmhl8og6BoZvukS1pQI0JFrox63hJYavHTQB0DO2iXomfpm9d+J8NHsBsWf7DD/9aNaByGRJ0k5Lde64FfTj6LP9I5yRoKuGGQ0Heuvuisz9DMWRyhkO9hJiyedX7VdPx3VdUW4AX+FWyJ1pKpj0g/s8eYrUFyzISdoq/pRwkVkzHpXqFh3L5ASUjf9eQXGYsQsDI0UDuzZdYD4nitQ5Q0POM7jCgSQQ8d/b0eaF2hCzbZ1UWKx8LzCU7j4NRqrYJluRqkxeEtBeZsq7QX6Hs13tg+wKKCkOI+wt/1tifLE8IA3es0pXm0UstVduaTMeFtLTvIYE9E/0yFC23aFydz1Fny6HBjpfNo6BgzNCurMziOdpiuLy+7luPM+SBJ3YV0D9TVU8Lo0vawPccj3tcKmozeJdBhuedXWAm00mlCw+LueKBUVxti2kwHiDjBDbLDymZYZHR8HYI0KsrycsvemTotZzYXgDjyRful7mPLGecJhRye7xNX9lVUse81C94gmdZXVL2GKY1PquWJvgazg99gta62GrRj127vDcS2UI+6/4aTJwQFvRqWRLvS/MIJyq5eiq1WyDLOT8dOyBlb7+BV55cB7JUTiO7MsMNaX0h/C3iGrTOnh8rmC/20ygHqZC3E1Lw0SezI2r1NzzcCAwEAAQ==", + "MIICCgKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQ==" + ], + "secret_share": "q0zuUQpplAfBJxoA2e2O1H7BdBgopzhPmL8mIWP8AQw=", + "id": 3 +} \ No newline at end of file diff --git a/cmd/horcrux/cmd/testdata/testdata.go b/cmd/horcrux/cmd/testdata/testdata.go new file mode 100644 index 00000000..2b7a0449 --- /dev/null +++ b/cmd/horcrux/cmd/testdata/testdata.go @@ -0,0 +1,20 @@ +package testdata + +import ( + _ "embed" // required to embed files +) + +//go:embed config-migrated.yaml +var ConfigMigrated string + +//go:embed config-v2.yaml +var ConfigV2 []byte + +//go:embed cosigner-key-migrated-ed25519.json +var CosignerEd25519KeyMigrated string + +//go:embed cosigner-key-migrated-rsa.json +var CosignerRSAKeyMigrated string + +//go:embed cosigner-key-v2.json +var CosignerKeyV2 []byte diff --git a/cmd/horcrux/cmd/threshold.go b/cmd/horcrux/cmd/threshold.go new file mode 100644 index 00000000..e0a57863 --- /dev/null +++ b/cmd/horcrux/cmd/threshold.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + cometservice "github.com/cometbft/cometbft/libs/service" + "github.com/strangelove-ventures/horcrux/v3/signer" +) + +const maxWaitForSameBlockAttempts = 3 + +func NewThresholdValidator( + ctx context.Context, + logger cometlog.Logger, +) ([]cometservice.Service, *signer.ThresholdValidator, error) { + if err := config.Config.ValidateThresholdModeConfig(); err != nil { + return nil, nil, err + } + + thresholdCfg := config.Config.ThresholdModeConfig + + remoteCosigners := make([]signer.Cosigner, 0, len(thresholdCfg.Cosigners)-1) + + var p2pListen string + + var security signer.CosignerSecurity + var eciesErr error + security, eciesErr = config.CosignerSecurityECIES() + if eciesErr != nil { + var rsaErr error + security, rsaErr = config.CosignerSecurityRSA() + if rsaErr != nil { + return nil, nil, fmt.Errorf("failed to initialize cosigner ECIES / RSA security : %w / %w", eciesErr, rsaErr) + } + } + + for _, c := range thresholdCfg.Cosigners { + if c.ShardID != security.GetID() { + rc, err := signer.NewRemoteCosigner(c.ShardID, c.P2PAddr) + if err != nil { + return nil, nil, fmt.Errorf("failed to initialize remote cosigner: %w", err) + } + remoteCosigners = append( + remoteCosigners, + rc, + ) + } else { + p2pListen = c.P2PAddr + } + } + + if p2pListen == "" { + return nil, nil, fmt.Errorf("cosigner config does not exist for our shard ID %d", security.GetID()) + } + + localCosigner := signer.NewLocalCosigner( + logger, + &config, + security, + p2pListen, + ) + + // Validated prior in ValidateThresholdModeConfig + grpcTimeout, _ := time.ParseDuration(thresholdCfg.GRPCTimeout) + raftTimeout, _ := time.ParseDuration(thresholdCfg.RaftTimeout) + + raftDir := filepath.Join(config.HomeDir, "raft") + if err := os.MkdirAll(raftDir, 0700); err != nil { + return nil, nil, fmt.Errorf("error creating raft directory: %w", err) + } + + // RAFT node ID is the cosigner ID + nodeID := fmt.Sprint(security.GetID()) + + // Start RAFT store listener + raftStore := signer.NewRaftStore(nodeID, + raftDir, p2pListen, raftTimeout, logger, localCosigner, remoteCosigners) + if err := raftStore.Start(); err != nil { + return nil, nil, fmt.Errorf("error starting raft store: %w", err) + } + services := []cometservice.Service{raftStore} + + val := signer.NewThresholdValidator( + logger, + &config, + thresholdCfg.Threshold, + grpcTimeout, + maxWaitForSameBlockAttempts, + localCosigner, + remoteCosigners, + raftStore, + ) + + raftStore.SetThresholdValidator(val) + + if err := val.Start(ctx); err != nil { + return nil, nil, fmt.Errorf("failed to start threshold validator: %w", err) + } + + return services, val, nil +} diff --git a/cmd/horcrux/cmd/version.go b/cmd/horcrux/cmd/version.go index a99b05d1..ecead366 100644 --- a/cmd/horcrux/cmd/version.go +++ b/cmd/horcrux/cmd/version.go @@ -19,7 +19,7 @@ import ( "encoding/json" "fmt" "runtime" - dbg "runtime/debug" + "runtime/debug" "github.com/spf13/cobra" ) @@ -32,24 +32,20 @@ var ( // sdk version SDKVersion = "" // tendermint version - TMVersion = "" + CBFTVersion = "" ) -func init() { - rootCmd.AddCommand(versionCmd) -} - // Info defines the application version information. type Info struct { - Version string `json:"version" yaml:"version"` - GitCommit string `json:"commit" yaml:"commit"` - GoVersion string `json:"go_version" yaml:"go_version"` - CosmosSdkVersion string `json:"cosmos_sdk_version" yaml:"cosmos_sdk_version"` - TendermintVersion string `json:"tendermint_version" yaml:"tendermint_version"` + Version string `json:"version" yaml:"version"` + GitCommit string `json:"commit" yaml:"commit"` + GoVersion string `json:"go_version" yaml:"go_version"` + CosmosSdkVersion string `json:"cosmos_sdk_version" yaml:"cosmos_sdk_version"` + CometBFTVersion string `json:"cometbft_version" yaml:"cometbft_version"` } func NewInfo() Info { - bi, _ := dbg.ReadBuildInfo() + bi, _ := debug.ReadBuildInfo() dependencyVersions := map[string]string{} @@ -58,25 +54,27 @@ func NewInfo() Info { } return Info{ - Version: Version, - GitCommit: Commit, - GoVersion: fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH), - CosmosSdkVersion: dependencyVersions["github.com/cosmos/cosmos-sdk"], - TendermintVersion: dependencyVersions["github.com/tendermint/tendermint"], + Version: Version, + GitCommit: Commit, + GoVersion: fmt.Sprintf("%s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH), + CosmosSdkVersion: dependencyVersions["github.com/cosmos/cosmos-sdk"], + CometBFTVersion: dependencyVersions["github.com/cometbft/cometbft"], } } // versionCmd represents the version command -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Version information for horcrux", - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) error { - bz, err := json.MarshalIndent(NewInfo(), "", " ") - if err != nil { - return err - } - cmd.Println(string(bz)) - return nil - }, +func versionCmd() *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Version information for horcrux", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + bz, err := json.MarshalIndent(NewInfo(), "", " ") + if err != nil { + return err + } + cmd.Println(string(bz)) + return nil + }, + } } diff --git a/cmd/horcrux/main.go b/cmd/horcrux/main.go index a7ba661f..fe2b2a2f 100644 --- a/cmd/horcrux/main.go +++ b/cmd/horcrux/main.go @@ -15,7 +15,7 @@ limitations under the License. */ package main -import "github.com/strangelove-ventures/horcrux/cmd/horcrux/cmd" +import "github.com/strangelove-ventures/horcrux/v3/cmd/horcrux/cmd" func main() { cmd.Execute() diff --git a/docker/horcrux/Dockerfile b/docker/horcrux/Dockerfile index 5c173655..5db8717d 100644 --- a/docker/horcrux/Dockerfile +++ b/docker/horcrux/Dockerfile @@ -1,24 +1,72 @@ -FROM --platform=$BUILDPLATFORM golang:1.19-alpine AS build-env +FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS build-env -ENV PACKAGES make git +RUN apk add --update --no-cache curl make git libc-dev bash gcc linux-headers eudev-dev -RUN apk add --no-cache $PACKAGES +ARG TARGETARCH +ARG BUILDARCH + +RUN if [ "${TARGETARCH}" = "arm64" ] && [ "${BUILDARCH}" != "arm64" ]; then \ + wget -c https://musl.cc/aarch64-linux-musl-cross.tgz -O - | tar -xzvv --strip-components 1 -C /usr; \ + elif [ "${TARGETARCH}" = "amd64" ] && [ "${BUILDARCH}" != "amd64" ]; then \ + wget -c https://musl.cc/x86_64-linux-musl-cross.tgz -O - | tar -xzvv --strip-components 1 -C /usr; \ + fi -WORKDIR /go/src/github.com/strangelove-ventures/horcrux +WORKDIR /horcrux ADD . . -ARG TARGETARCH -ARG TARGETOS +RUN if [ "${TARGETARCH}" = "arm64" ] && [ "${BUILDARCH}" != "arm64" ]; then \ + export CC=aarch64-linux-musl-gcc CXX=aarch64-linux-musl-g++;\ + elif [ "${TARGETARCH}" = "amd64" ] && [ "${BUILDARCH}" != "amd64" ]; then \ + export CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++; \ + fi; \ + GOOS=linux GOARCH=$TARGETARCH CGO_ENABLED=1 LDFLAGS='-linkmode external -extldflags "-static"' make install + +RUN if [ -d "/go/bin/linux_${TARGETARCH}" ]; then mv /go/bin/linux_${TARGETARCH}/* /go/bin/; fi + +# Use minimal busybox from infra-toolkit image for final scratch image +FROM ghcr.io/strangelove-ventures/infra-toolkit:v0.0.6 AS busybox-min +RUN addgroup --gid 2345 -S horcrux && adduser --uid 2345 -S horcrux -G horcrux + +# Use ln and rm from full featured busybox for assembling final image +FROM busybox:1.34.1-musl AS busybox-full + +# Build final image from scratch +FROM scratch + +LABEL org.opencontainers.image.source="https://github.com/cosmos/horcrux" + +WORKDIR /bin + +# Install ln (for making hard links) and rm (for cleanup) from full busybox image (will be deleted, only needed for image assembly) +COPY --from=busybox-full /bin/ln /bin/rm ./ -RUN export GOOS=${TARGETOS} GOARCH=${TARGETARCH} && make build +# Install minimal busybox image as shell binary (will create hardlinks for the rest of the binaries to this data) +COPY --from=busybox-min /busybox/busybox /bin/sh -FROM alpine:edge +# Add hard links for read-only utils, then remove ln and rm +# Will then only have one copy of the busybox minimal binary file with all utils pointing to the same underlying inode +RUN ln sh pwd && \ + ln sh ls && \ + ln sh cat && \ + ln sh less && \ + ln sh grep && \ + ln sh sleep && \ + ln sh env && \ + ln sh tar && \ + ln sh tee && \ + ln sh du && \ + rm ln rm -RUN apk add --no-cache ca-certificates +# Install chain binaries +COPY --from=build-env /go/bin/horcrux /bin -WORKDIR /root +# Install trusted CA certificates +COPY --from=busybox-min /etc/ssl/cert.pem /etc/ssl/cert.pem -COPY --from=build-env /go/src/github.com/strangelove-ventures/horcrux/build/horcrux /usr/bin/horcrux +# Install horcrux user +COPY --from=busybox-min /etc/passwd /etc/passwd +COPY --from=busybox-min --chown=2345:2345 /home/horcrux /home/horcrux -CMD ["horcrux"] +WORKDIR /home/horcrux +USER horcrux \ No newline at end of file diff --git a/docker/horcrux/native.Dockerfile b/docker/horcrux/native.Dockerfile index e54a6343..843dca44 100644 --- a/docker/horcrux/native.Dockerfile +++ b/docker/horcrux/native.Dockerfile @@ -1,21 +1,60 @@ -FROM golang:1.19-alpine AS build-env +FROM golang:1.21-alpine AS build-env -ENV PACKAGES make git +RUN apk add --update --no-cache curl make git libc-dev bash gcc linux-headers eudev-dev -RUN apk add --no-cache $PACKAGES +WORKDIR /horcrux -WORKDIR /go/src/github.com/strangelove-ventures/horcrux +ADD go.mod go.sum ./ + +RUN go mod download ADD . . -RUN make build +RUN CGO_ENABLED=1 LDFLAGS='-linkmode external -extldflags "-static"' make install + +# Use minimal busybox from infra-toolkit image for final scratch image +FROM ghcr.io/strangelove-ventures/infra-toolkit:v0.0.6 AS busybox-min +RUN addgroup --gid 2345 -S horcrux && adduser --uid 2345 -S horcrux -G horcrux + +# Use ln and rm from full featured busybox for assembling final image +FROM busybox:1.34.1-musl AS busybox-full + +# Build final image from scratch +FROM scratch + +LABEL org.opencontainers.image.source="https://github.com/strangelove-ventures/horcrux" + +WORKDIR /bin + +# Install ln (for making hard links) and rm (for cleanup) from full busybox image (will be deleted, only needed for image assembly) +COPY --from=busybox-full /bin/ln /bin/rm ./ + +# Install minimal busybox image as shell binary (will create hardlinks for the rest of the binaries to this data) +COPY --from=busybox-min /busybox/busybox /bin/sh -FROM alpine:edge +# Add hard links for read-only utils, then remove ln and rm +# Will then only have one copy of the busybox minimal binary file with all utils pointing to the same underlying inode +RUN ln sh pwd && \ + ln sh ls && \ + ln sh cat && \ + ln sh less && \ + ln sh grep && \ + ln sh sleep && \ + ln sh env && \ + ln sh tar && \ + ln sh tee && \ + ln sh du && \ + rm ln rm -RUN apk add --no-cache ca-certificates +# Install chain binaries +COPY --from=build-env /go/bin/horcrux /bin -WORKDIR /root +# Install trusted CA certificates +COPY --from=busybox-min /etc/ssl/cert.pem /etc/ssl/cert.pem -COPY --from=build-env /go/src/github.com/strangelove-ventures/horcrux/build/horcrux /usr/bin/horcrux +# Install horcrux user +COPY --from=busybox-min /etc/passwd /etc/passwd +COPY --from=busybox-min --chown=2345:2345 /home/horcrux /home/horcrux -CMD ["horcrux"] +WORKDIR /home/horcrux +USER horcrux \ No newline at end of file diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..b5fc1364 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,43 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at . diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..0301027e --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,86 @@ +# Contributing to Horcrux + +Welcome to the Horcrux project! We're thrilled that you're interested in contributing. Horcrux a multi-party-computation signing service for CometBFT nodes. This document outlines the process and guidelines for contributing to Horcrux. Please read it carefully to ensure that you understand our workflow and the standards we expect for our community. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Contributing Guidelines](#contributing-guidelines) +- [Issues](#issues) +- [Pull Requests](#pull-requests) +- [Responsibilities of a PR Reviewer](#responsibilities-of-a-pr-reviewer) + +## Code of Conduct + +Please review our [Code of Conduct](./CODE_OF_CONDUCT.md) to understand the standards and expectations for participating in our community. We are committed to fostering a welcoming and inclusive environment. + +## Getting Started + +Before you start contributing, make sure you have the following prerequisites installed: + +- [Go](https://golang.org/dl/) +- [Docker](https://www.docker.com/get-started) +- [VSCode (recommended editor)](https://code.visualstudio.com/) +- [Make](https://www.gnu.org/software/make/) + +To get started, follow these steps: + +1. Fork the Horcrux repository to your own GitHub account. + +2. Clone your forked repository to your local machine: + + ```sh + git clone https://github.com//horcrux.git + ``` + +3. Crate a new branch on your fork + + ```sh + git checkout -b name/broad-description-of-feature + ``` + +4. Make your changes and commit them with descriptive commit messages. +5. Test your changes locally with `make test`, or by running the specific test affecting your feature or fix. +6. You can validate your changes with `make build` or `make install`. +7. Push your changes to your github forked repository + + ```sh + git push origin name/broad-description-of-feature + ``` + +8. Create a pull request (PR) against the main branch of the Horcrux repository. If the PR is still a work-in-progress, please mark the PR as draft. + +## Contributing Guidelines + +- Adhere to the project's coding style and conventions. +- Write clear and concise commit messages and PR descriptions. +- Be responsive to feedback and collaborate with others. +- Document code and include appropriate tests. +- For documentation or typo fixes, submit separate PRs. +- Keep PRs focused on a single issue or feature. + +## Issues + +We welcome bug reports, feature requests, and other contributions to our project. To open an issue, please follow these guidelines: + +1) Search existing issues: Before opening a new issue, please search existing issues to ensure that is not a duplicates. +2) Provide a clear and descriptive title: This helps others understand the nature of the issue at a glance. +3) Provide detailed information: In the issue description, clearly state the purpose of the issue and follow the guidelines of the issue template +4) A maintainer will take care of assigning the appropriate labels to your issue, if applicable. + +## Pull requests + +In almost all cases, you should target branch `main` with your work. +For internal branches, branch names should be prefixed with the author's name followed by a short description of the feature, eg. `name/feature-x`. +Pull requests are made against `main` and are squash-merged into main after approval. All CI pipeline test must pass for this to be the case. + +## Responsibilities of a PR Reviewer + +As a PR reviewer, your primary responsibility is to guide the PR through to completion. This entails not only identifying and addressing issues within the PR but also taking a leadership role in resolving any decisions necessary for the PR to be merged successfully. + +In cases where you are assigned as a reviewer for a PR that pertains to an unfamiliar part of the codebase, it's perfectly acceptable to delegate the review to a more knowledgeable colleague, provided they are willing to assume that responsibility. This ensures that the PR receives expert attention and increases the likelihood of its successful merging. Collaboration and teamwork are key in maintaining code quality and project progress. + +--- + +We appreciate your contributions and look forward to working with you to make Horcrux a continued valuable resource. If you have any questions or need assistance, feel free to reach out to the maintainers, community members, or . diff --git a/docs/horcrux.service b/docs/horcrux.service index f5428390..43ffd830 100644 --- a/docs/horcrux.service +++ b/docs/horcrux.service @@ -6,7 +6,7 @@ After=network.target Type=simple User=ubuntu WorkingDirectory=/home/ubuntu -ExecStart=/usr/bin/horcrux cosigner start +ExecStart=/usr/bin/horcrux start Restart=on-failure RestartSec=3 LimitNOFILE=4096 diff --git a/docs/metrics.md b/docs/metrics.md index a7090fc3..24afd6f9 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -13,20 +13,20 @@ debug-addr: 0.0.0.0:6001 Resulting in a configuration like the following: ``` -chain-id: testnet-1 -cosigner: +thresholdMode: threshold: 2 - shares: 3 - p2p-listen: tcp://localhost:5001 - peers: - - share-id: 2 - p2p-addr: tcp://localhost:5002 - - share-id: 3 - p2p-addr: tcp://localhost:5003 - rpc-timeout: 1500ms -chain-nodes: -- priv-val-addr: tcp://localhost:2300 -debug-addr: 0.0.0.0:6001 + cosigners: + - shardID: 1 + p2pAddr: tcp://localhost:5001 + - shardID: 2 + p2pAddr: tcp://localhost:5002 + - shardID: 3 + p2pAddr: tcp://localhost:5003 + grpcTimeout: 500ms + raftTimeout: 500ms +chainNodes: +- privValAddr: tcp://localhost:2300 +debugAddr: 0.0.0.0:6001 ``` ## Prometheus Cautions @@ -63,7 +63,7 @@ If 'signer_total_sentry_connect_tries' is significant, it can indicate network o ## Watching Cosigner With Grafana -A sample Grafana configration is available. See [`horcrux.json`](https://github.com/chillyvee/horcrux-info/blob/master/grafana/horcrux.json) +A sample Grafana configuration is available. See [`horcrux.json`](https://github.com/chillyvee/horcrux-info/blob/master/grafana/horcrux.json) ## Watching For Cosigner Trouble @@ -71,7 +71,7 @@ Metrics may vary between Cosigner processes since there is only one leader. Watch 'signer_missed_ephemeral_shares' which will note when the leader is not able to get a signature from the peer. If 'signer_total_missed_ephemeral_shares' increases to a high number, this may indicate a larger issue. -Each block, Ephemeral Secrets are shared between Cosigners. Monitoring 'signer_seconds_since_last_local_ephemeral_share_time' and ensuring it does not exceed the block time will allow you to know when a Cosigner was not contacted for a block. +Each block, Nonce Secrets are shared between Cosigners. Monitoring 'signer_seconds_since_last_local_ephemeral_share_time' and ensuring it does not exceed the block time will allow you to know when a Cosigner was not contacted for a block. ## Metrics that don't always correspond to block time There is no guarantee that a Cosigner will sign a block if the threshold is reached early. You may watch 'signer_seconds_since_last_local_sign_start_time' but there is no guarantee that 'signer_seconds_since_last_local_sign_finish_time' will be reached since there are multiple sanity checks that may cause an early exit in some circumstances (rather rare) diff --git a/docs/migrating.md b/docs/migrating.md index c57b0018..b3046a41 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -19,20 +19,20 @@ This document will describe a migration from a "starting system" to a 2-of-3 mul - Sentries: 3x VM w/ 4 CPU, 16GB RAM, 500GB SSD storage running fully synced chain daemon - These chain daemons should only expose the `:26656` (p2p) port to the open internet - The daemons will need to expose `:1234` (priv validator port) to the `horcrux` nodes, but not to the open internet -- Signers: 3x VM w/ 1 CPU, 1 GB RAM, 20 GB SSD storage running `horcux` +- Signers: 3x VM w/ 1 CPU, 1 GB RAM, 20 GB SSD storage running `horcrux` - These nodes should not expose any ports to the open internet and should only connect with the sentries ## Migration Steps -### 1. Setup Full Nodes +### 1. Setup Chain Nodes -The first step to the migration is to sync the full nodes you will be using as sentries. To follow this guide, ensure that you have 3 nodes from the chain you are validating on which are synced. Follow the instructions for the individual chain for spinning up those nodes. This is the part of setting up `horcrux` that takes the longest. +The first step to the migration is to sync the chain nodes (also known as full nodes) that you will be using as sentries. To follow this guide, ensure that you have nodes from the chain you are validating on that are in sync with the latest height of the chain. You can validate with a minimum of one sentry node, but more are recommended for redundancy/availability. We will use three chain nodes for this example. Follow the instructions for the individual chain for spinning up those nodes. This is the part of setting up `horcrux` that takes the longest. > **NOTE:** This is also a great usecase for [state sync](https://blog.cosmos.network/cosmos-sdk-state-sync-guide-99e4cf43be2f). Or one of the [quick sync services](https://quicksync.io/) that exist. ### 2. Setup Signer Nodes -To setup the signer nodes, start by recording the private IPs for each of the signer and sentry nodes. Order matters, and you will need these values to configure the signers. Make a table like so: +To setup the signer nodes, start by recording the private IPs or DNS hostnames for each of the signer and sentry (chain) nodes. Order matters, and you will need these values to configure the signers. Make a table like so: ```bash # EXAMPLE @@ -45,79 +45,109 @@ signer-2: 10.168.1.2 signer-3: 10.168.1.3 ``` -When installing `horcrux` we recommend using the prebuilt binaries from the [releases page](https://github.com/strangelove-ventures/horcrux/releases). Pick the release corresponding to the `tendermint` dependency for the `go.mod` of your chain binary. You should be able to get this with `{binary} version --long`. Install like so: +When installing `horcrux` we recommend using either the [container image](https://github.com/strangelove-ventures/horcrux/pkgs/container/horcrux) or the [prebuilt binary](https://github.com/strangelove-ventures/horcrux/releases) for the latest stable release. +The image or binary will be used on each cosigner (bare virtual machine, docker container, kubernetes pod, etc.) +The binary should also be installed on your local machine for working with the config and key files before distributing to the cosigner nodes. + +Run the following on your local machine. If you are using the binary on the cosigners rather than container image, run this on each cosigner node VM also. ```bash -# On each signer VM -$ wget https://github.com/strangelove-ventures/horcrux/releases/download/v2.0.0/horcrux_2.0.0_linux_amd64.tar.gz -$ tar -xzf horcrux_2.0.0_linux_amd64.tar.gz -$ sudo mv horcrux /usr/bin/horcrux && rm horcrux_2.0.0_linux_amd64.tar.gz README.md LICENSE.md +TAG=v3.0.0 +$ wget https://github.com/strangelove-ventures/horcrux/releases/download/${TAG}/horcrux_${TAG}_linux_amd64.tar.gz +$ tar -xzf horcrux_${TAG}_linux_amd64.tar.gz +$ sudo mv horcrux /usr/bin/horcrux && rm horcrux_${TAG}_linux_amd64.tar.gz README.md LICENSE.md ``` -Once the binary is installed in `/usr/bin`, install the `systemd` unit file. You can find an [example here](./horcrux.service): +For each cosigner node (not required on local machine): once the binary is installed in `/usr/bin`, install the `systemd` unit file. You can find an [example here](./horcrux.service): ```bash -# On each signer VM +# On each horcrux cosigner $ sudo nano /etc/systemd/system/horcrux.service # copy file contents and modify to fit your environment $ sudo systemctl daemon-reload ``` -After that is done, initialize the configuration for each node using the `horcrux` cli. Each node will require a slightly different command. Below are the commands for each of the 3 signer nodes given the private IPs above. Input your own data here: +After that is done, initialize the shared configuration for the cosigners on your local machine using the `horcrux` cli. If you would like different cosigners to connect to different sentry node(s): repeat this command and modify the `--node` flag values for each cosigner, or modify the config after the initial generation. ```bash -# Run this command on the signer-1 VM -# signer-1 connects to sentry-1 -$ horcrux config init {my_chain_id} "tcp://10.168.0.1:1234" -c -p "tcp://10.168.1.2:2222|2,tcp://10.168.1.3:2222|3" -l "tcp://10.168.1.1:2222" -t 2 --timeout 1500ms +$ horcrux config init --node "tcp://10.168.0.1:1234" --node "tcp://10.168.0.2:1234" --node "tcp://10.168.0.3:1234" --cosigner "tcp://10.168.1.1:2222" --cosigner "tcp://10.168.1.2:2222" --cosigner "tcp://10.168.1.3:2222" --threshold 2 --grpc-timeout 1000ms --raft-timeout 1000ms +``` -# Run this command on the signer-2 VM -# signer-2 connects to sentry-2 -$ horcrux config init {my_chain_id} "tcp://10.168.0.2:1234" -c -p "tcp://10.168.1.1:2222|1,tcp://10.168.1.3:2222|3" -l "tcp://10.168.1.2:2222" -t 2 --timeout 1500ms +> **Note** +> Note the use of multiple `--node` and `--cosigner` flags. In this example, there are 3 sentry (chain) nodes that each horcrux cosigner will connect to. There are 3 horcrux cosigners, with a threshold of 2 cosigners required to sign a valid block signature. -# Run this command on the signer-3 VM -# signer-3 connects to sentry-3 -$ horcrux config init {my_chain_id} "tcp://10.168.0.3:1234" -c -p "tcp://10.168.1.1:2222|1,tcp://10.168.1.2:2222|2" -l "tcp://10.168.1.3:2222" -t 2 --timeout 1500ms -``` +#### Flags -> **NOTE:** Note the node address (e.g. "tcp://10.168.0.1:1234") of each command. In this example, each horcrux node is communicating with a corresponding sentry. It is also possible to include a comma separated list of node addresses (e.g. "tcp://chain-node-1:1234,tcp://chain-node-2:1234", etc), allowing all horcrux nodes to communicate with all sentries. +- `-c`/`--cosigner`: configures the P2P address and shard ID for cosigner nodes. Keeping the node names and the IDs the same helps avoid errors. The DNS/IP used for all of these must be reachable by the other cosigners, i.e. do not use 0.0.0.0 for the hostname. +- `-n`/`--node`: configures the priv-val interface listen address for the chain sentry nodes. +- `-k`/`--key-dir`: configures the directory for the RSA and Ed25519 private key files if you would like to use a different path than the default, `~/.horcrux`. +- `--grpc-timeout`: configures the timeout for cosigner-to-cosigner GRPC communication. This value defaults to `1000ms`. +- `--raft-timeout`: configures the timeout for cosigner-to-cosigner Raft consensus. This value defaults to `1000ms`. +- `-m`/`--mode`: this flag allows changing the sign mode. By default, horcrux uses `threshold` mode for MPC cosigner operations. This is the officially-supported configuration. The signer can also be run in single signer configuration for experimental, non-mainnet deployments. To enable single-signer mode, use `single` for this flag, exclude the `-c`, `-t`, `--grpc-timeout`, and `--raft-timeout` flags, and pass the `--accept-risk` flag to accept the elevated risk of running in single signer mode. -> **NOTE:** The `-c` or `--cosigner` flag here says to configure the signer for cosigner operations. The signer can also be run in single signer configuration, if you want to do that don't pass `-c`, `-p` or `-t` or `--timeout`. +> **Warning** +> SINGLE-SIGNER MODE SHOULD NOT BE USED FOR MAINNET! Horcrux single-signer mode does not give the level of improved key security and fault tolerance that Horcrux MPC/cosigner mode provides. While it is a simpler deployment configuration, single-signer should only be used for experimentation as it is not officially supported by Strangelove. -> **NOTE:** The `-p` or `--peers` flag lets you set the addresses of the other signer nodes in the config. Two ports are required, the P2P port for RCP traffic, and the Raft port for key-value sharing. Note that each signer also has an index. This index corresponds to the shard of the private key it will sign with. Keeping the node names and the indexes the same helps avoid errors and allows you to work more quickly -> **NOTE:** The `-l` or `--listen` flag lets you set the listen address for the cosigner, which is used for communication between cosigners, Raft and GRPC. The DNS/IP used for this must be reachable by the other peers, i.e. do not use 0.0.0.0 for the hostname. +### 3. Generate cosigner communication encryption keys -> **NOTE:** The `-k` or `--keyfile` flag lets you set the file path for the private key share file if you would like to use a different path than `~/.horcrux/share.json`. +Horcrux uses secp256k1 keys to encrypt (ECIES) and sign (ECDSA) cosigner-to-cosigner p2p communication. This is done by encrypting the payloads that are sent over GRPC between cosigners. Open your shell to a working directory and generate the ECIES keys that will be used on each cosigner using the `horcrux` CLI on your local machine. + +```bash +$ horcrux create-ecies-shards --shards 3 +Created ECIES Shard cosigner_1/ecies_keys.json +Created ECIES Shard cosigner_2/ecies_keys.json +Created ECIES Shard cosigner_3/ecies_keys.json -> **NOTE:** The `--timeout` value defaults to `1000ms`. If you are running in disconnected data centers (i.e. across amazon AZs or gcp zones) increasing the timeout slightly helps to avoid missed blocks especially around proposals. +$ ls -R +.: +cosigner_1 cosigner_2 cosigner_3 + +./cosigner_1: +ecies_keys.json + +./cosigner_2: +ecies_keys.json + +./cosigner_3: +ecies_keys.json +``` -### 3. Split `priv_validator_key.json` and distribute key material +### 4. Shard `priv_validator_key.json` for each chain. -> **CAUTION:** **The security of any key material is outside the scope of this guide. The suggested procedure here is not necessarily the one you will use. We aim to make this guide easy to understand, not necessarily the most secure. The tooling here is all written in go and can be compiled and used in an airgapped setup if needed. Please open issues if you have questions about how to fit `horcrux` into your infra.** +> **CAUTION:** **The security of any key material is outside the scope of this guide. The suggested procedure here is not necessarily the one you will use. We aim to make this guide easy to understand, not necessarily the most secure. This guide assumes that your local machine is a trusted computer. The tooling here is all written in go and can be compiled and used in an airgapped setup if needed. Please open issues if you have questions about how to fit `horcrux` into your infra.** -On some computer that contains your `priv_validator_key.json` create a folder to split the key through the following command. This may take a moment o complete: +Horcrux uses threshold Ed25519 cryptography to sign a block payload on the cosigners and combine the resulting signatures to produce a signature that can be validated against your validator's Ed25519 public key. On your local machine which contains your full `priv_validator_key.json` key file(s), shard the key using the `horcrux` CLI in the same working directory as the previous command. ```bash -$ ls -priv_validator_key.json - -$ horcrux create-shares priv_validator_key.json 2 3 -Created Share 1 -Created Share 2 -Created Share 3 - -$ ls -priv_validator_key.json -private_share_1.json -private_share_2.json -private_share_3.json +$ horcrux create-ed25519-shards --chain-id cosmoshub-4 --key-file /path/to/cosmoshub/priv_validator_key.json --threshold 2 --shards 3 +Created Ed25519 Shard cosigner_1/cosmoshub-4_shard.json +Created Ed25519 Shard cosigner_2/cosmoshub-4_shard.json +Created Ed25519 Shard cosigner_3/cosmoshub-4_shard.json + +$ ls -R +.: +cosigner_1 cosigner_2 cosigner_3 + +./cosigner_1: +cosmoshub-4_shard.json ecies_keys.json + +./cosigner_2: +cosmoshub-4_shard.json ecies_keys.json + +./cosigner_3: +cosmoshub-4_shard.json ecies_keys.json ``` -The shares need to be moved their co-responding signer nodes at `~/.horcrux/share.json`. It is very important to make sure the share id (in `private_share_.json`) is on the corresponding cosigner node otherwise your signer cluster won't communicate properly and will not sign blocks. If you have named your nodes with their index as the signer index, as in this guide, this operation should be easy to check. +If you will be signing for multiple chains with this single horcrux cluster, repeat this step with the `priv_validator_key.json` for each additional chain ID. + +### 5. Distribute config file and key shards to each cosigner. -At the end of this step, each of your horcrux nodes will have a `~/.horcrux/share.json` file with the contents matching the appropriate `private_share_.json` file corresponding to the node number. +The files need to be moved their corresponding signer nodes in the `~/.horcrux/` directory. It is important to make sure the files for the cosigner `{id}` (in `cosigner_{id}`) are placed on the corresponding cosigner node. If not, the cluster will not produce valid signatures. If you have named your nodes with their index as the signer index, as in this guide, this operation should be easy to check. -### 4. Halt your validator node and supply signer state data `horcrux` nodes +At the end of this step, each of your horcrux nodes should have a `~/.horcrux/{chain-id}_shard.json` file for each `chain-id` with the contents matching the appropriate `cosigner_{id}/{chain-id}_shard.json` file corresponding to the node number. Additionally, each of your horcrux nodes should have a `~/.horcrux/ecies_keys.json` file with the contents matching the appropriate `cosigner_{id}/ecies_keys.json` file corresponding to the node number. + +### 6. Halt your validator node and supply signer state data `horcrux` nodes Now is the moment of truth. There will be a few minutes of downtime for this step, so ensure you have read the following directions completely before moving forward. @@ -137,7 +167,7 @@ Once the validator has been stopped, you will need the contents of the `$NODE_HO } ``` -You will need to replace the contents of the `~/.horcrux/state/{chain-id}_priv_validator_state.json` and `~/.horcrux/state/{chain-id}_share_sign_state.json` on each signer node with a truncated and slightly modified version of the file. Note the `""` especially on the `"round"` value: +You will need to replace the contents of the `~/.horcrux/state/{chain-id}_priv_validator_state.json` on each signer node with a truncated and slightly modified version of the file. Note the `""` especially on the `"round"` value: ```json { @@ -147,11 +177,11 @@ You will need to replace the contents of the `~/.horcrux/state/{chain-id}_priv_v } ``` -> **NOTE:** This step can be error prone. We will be [adding a feature](https://github.com/strangelove-ventures/horcrux/issues/18) to allow using the CLI to set these values but for now `nano`/`vi`, `cat` and [`jq`](https://stedolan.github.io/jq/) are your friends. +`horcrux state import` can be used to import an existing `priv_validator_state.json` -### 5. Start the signer cluster +### 7. Start the cosigner cluster -Once you have all of the signer nodes fully configured its time to start them. Start all of them at roughly the same time: +Once you have all of the cosigner nodes fully configured its time to start them. Start all of them at roughly the same time: ```bash sudo systemctl start horcrux && journalctl -u horcrux -f @@ -160,23 +190,25 @@ sudo systemctl start horcrux && journalctl -u horcrux -f The following logs should be flowing on each signer node: ```log -I[2021-09-24|02:10:09.022] Tendermint Validator module=validator mode=mpc priv_key=... -I[2021-09-24|02:10:09.023] Starting CosignerRPCServer service module=validator impl=CosignerRPCServer -I[2021-09-24|02:10:09.025] Signer module=validator pubkey=PubKeyEd25519{9A66109B69C... -I[2021-09-24|02:10:09.025] Starting RemoteSigner service module=validator impl=RemoteSigner -E[2021-09-24|02:10:09.027] Dialing module=validator err="dial tcp 10.180.0.16:1234... -I[2021-09-24|02:10:09.027] Retrying module=validator sleep(s)=3 address=tcp://10.180... +I[2023-05-15|20:09:22.988] Horcrux Validator module=validator mode=threshold priv-state-dir=/root/.horcrux/state +I[2023-05-15|20:09:22.990] service start module=validator msg="Starting CosignerRaftStore service" impl=CosignerRaftStore +I[2023-05-15|20:09:22.991] Local Raft Listening module=validator port=2222 +I[2023-05-15|20:09:22.993] service start module=validator msg="Starting RemoteSigner service" impl=RemoteSigner +I[2023-05-15|20:09:22.993] service start module=validator msg="Starting RemoteSigner service" impl=RemoteSigner +I[2023-05-15|20:09:22.993] service start module=validator msg="Starting RemoteSigner service" impl=RemoteSigner +E[2023-05-15|20:09:22.994] Dialing module=validator err="dial tcp 10.180.0.16:1234... +I[2023-05-15|20:09:22.995] Retrying module=validator sleep(s)=3 address=tcp://10.180... ... ``` The signer will continue retrying attempts to reach the sentries until we turn the sentry `priv_validator` listener on in the next step. Any panic causing errors are likely due to one of the two following issues: - Misnaming or incorrect structure of the files in `~/.horcrux/state`. Double check these if you see errors -- Misnaming or misplacement of the `~/.horcrux/share.json` file +- Misnaming or misplacement of the `~/.horcrux/{chain-id}_shard.json` file > **NOTE:** leaving these logs streaming in seperate terminal windows will enable you to watch the cluster connect to the sentries. -### 6. Configure and start your full nodes +### 8. Configure and start your full nodes Once the signer cluster has started successfully its time to reconfigure and restart your sentry nodes. On each node enable the priv validator listener and verify config changes with the following commands: @@ -194,15 +226,24 @@ $ sudo systemctl restart {node_service} && journalctl -u {node_service} -f Common failure modes: -- Ports on your cloud service aren't properly configured and prevent signers/sentries from communicating +- Ports on the firewall (cosigner VM, cloud service, LAN port-forwards, etc.) aren't properly opened and prevent signers/sentries from communicating - Node crashes because the signer didn't retry in time, can be fixed by trying again and/or restarting signer. May take some fiddling -### 7. CONGRATS! +### 9. CONGRATS! You now can sleep much better at night because you are much less likely to have a down validator wake you up in the middle of the night. You have also completed a stressful migration on a production system. Go run around outside screaming, pet your dog, eat a nice meal, hug your kids/significant other, etc... and enjoy the rest of your day! -### 8. Administration Commands +### 10. Administration Commands + +`horcrux elect` - Elect a new cluster leader. Pass an optional argument with the intended leader ID to elect that cosigner as the new leader, e.g. `horcrux elect 3` to elect cosigner with `shardID: 3` as leader. This is an optimistic leader election, it is not guaranteed that the exact requested leader will be elected. + +`horcrux address` - Get the public key address as both hex and optionally the validator consensus bech32 address. To retrieve the valcons bech32 address, pass an optional argument with the chain's bech32 prefix, e.g. `horcrux address cosmos` + +## Steps to Migrate a Peer on a New IP -`horcrux elect` - Elect a new cluster leader. Pass an optional argument with the intended leader ID to elect that cosigner as the new leader, e.g. `horcrux elect 3` to elect cosigner with `ID: 3` as leader +To change the DNS/IP of a cosigner: -`horcrux cosigner address` - Get the public key address as both hex and optionally the validator consensus bech32 address. To retrieve the valcons bech32 address, pass an optional argument with the chain's bech32 valcons prefix, e.g. `horcrux cosigner address cosmosvalcons` +- update config files on each cosigner +- bring all cosigners down +- remove the .horcrux/raft directory on all cosigners +- restart all cosigners diff --git a/docs/signing.md b/docs/signing.md index f8595875..547d3442 100644 --- a/docs/signing.md +++ b/docs/signing.md @@ -9,7 +9,7 @@ To provide private key security, The key is sharded into multiple pieces using [ Each piece of the key is added to a relative signer node that will run the horcrux application. These signer nodes are part of an overall Horcrux cluster that will communicate with each other to sign each block, as requested by the configured sentry node(s). -A higher threshold _`t`_ equates to more security, since more pieces of the key will be required to assemble a full signature. A higher threshold also means the fault tolerance is lower since you cannot tolerate as many missing participants, or in Horcrux' case, failed nodes. It is important to think about your specific use case to determine what the balance of _`n`_ and _`t`_. +A higher threshold _`t`_ equates to more security, since more pieces of the key will be required to assemble a full signature. A higher threshold also means the fault tolerance is lower since you cannot tolerate as many missing participants, or in Horcrux's case, failed nodes. It is important to think about your specific use case to determine what the balance of _`n`_ and _`t`_. > IMPORTANT: For security and to ensure that there cannot be a situation in which there are two separate clusters of signer nodes that could combine a full signature, it is required that _`t > n/2`_ @@ -34,11 +34,11 @@ Each block sign request (votes and proposals) from any connected sentry node(s), The signer node that is the current elected raft leader will act upon the sign requests by managing the threshold validation process: - Check the requested block against the high watermark file (kept in consensus between the signer nodes) to avoid double signing. -- Request ephemeral nonces for the block signature from each signer node peer. +- Request ephemeral nonces for the block signature from each cosigner node. - Each signer will act upon the request by generating the ephemeral nonce shares for all other signers (encrypted with the destination signer's RSA public key). These shares will be the response to the leader. - The leader will wait until it has received _`t - 1`_ responses. The signer nodes which responded in time, _`blockSigners`_ are the signers that will be included with the leader for signing the block. - The leader will then make a request to each of the _`blockSigners`_ to set the ephemeral nonces for the other signers that are participating in the block signing (_`blockSigners`_ and leader), and produce the signature part from the block data. -- The participant in _`blockSigners`_ will handle this request by decrypting the ephemeral shares with its RSA private key, verify the signatures of the ephemeral share to verify the identity of the source signers, and then save it in memory. After all of the nonces are saved (consensus with the leader and _`blockSigners`_), it will produce its signature piece for the block data and respond to the leader with it. +- The participant in _`blockSigners`_ will handle this request by decrypting the nonce shares with its RSA private key, verify the signatures of the nonce share to verify the identity of the source signers, and then save it in memory. After all of the nonces are saved (consensus with the leader and _`blockSigners`_), it will sign the block data with it's Ed25519 key shard, and respond with to the leader with its signature piece. - Once the leader receives the signature parts from all of the _`blockSigners`_, it will make a combined signature including its own signature part and those from the _`blockSigners`_ - The leader will verify the combined signature is valid, then update its own high watermark file and also emit the block metadata (height, round, and step), to the rest of the signers through raft in order to update their high watermark files. This gives the cluster consensus on what the last successfully signed block was. - The leader will finally respond with the combined signature for the block, either directly to the requesting sentry if the raft leader was the one who handled the sentry request, or the signer that proxied the request to the leader, which would then respond to the requesting sentry. diff --git a/go.mod b/go.mod index 2c87f935..d0d48e3c 100644 --- a/go.mod +++ b/go.mod @@ -1,139 +1,132 @@ -module github.com/strangelove-ventures/horcrux +module github.com/strangelove-ventures/horcrux/v3 -go 1.19 +go 1.21 require ( - github.com/Jille/grpc-multi-resolver v1.1.0 github.com/Jille/raft-grpc-leader-rpc v1.1.0 - github.com/Jille/raft-grpc-transport v1.2.1-0.20220914172309-2f253856eefc - github.com/Jille/raftadmin v1.2.0 - github.com/avast/retry-go v3.0.0+incompatible - github.com/cosmos/cosmos-sdk v0.44.5 - github.com/gogo/protobuf v1.3.3 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 - github.com/hashicorp/raft v1.3.10 + github.com/Jille/raft-grpc-transport v1.4.0 + github.com/Jille/raftadmin v1.2.1 + github.com/armon/go-metrics v0.4.1 + github.com/cometbft/cometbft v0.38.2 + github.com/cosmos/cosmos-sdk v0.50.1 + github.com/cosmos/gogoproto v1.4.11 + github.com/ethereum/go-ethereum v1.13.5 + github.com/gogo/protobuf v1.3.2 + github.com/google/uuid v1.3.1 + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 + github.com/hashicorp/raft v1.6.0 github.com/hashicorp/raft-boltdb/v2 v2.2.2 + github.com/kraken-hpc/go-fork v0.1.1 github.com/mitchellh/go-homedir v1.1.0 - github.com/ory/dockertest v3.3.5+incompatible - github.com/spf13/cobra v1.2.1 - github.com/spf13/viper v1.8.1 - github.com/stretchr/testify v1.7.0 + github.com/prometheus/client_golang v1.17.0 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.16.0 + github.com/stretchr/testify v1.8.4 github.com/tendermint/go-amino v0.16.0 - github.com/tendermint/tendermint v0.34.14 gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e - gitlab.com/unit410/threshold-ed25519 v0.0.0-20220725172740-6ee731f539ac - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/grpc v1.44.0 - google.golang.org/protobuf v1.27.1 + gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc + golang.org/x/sync v0.3.0 + google.golang.org/grpc v1.59.0 gopkg.in/yaml.v2 v2.4.0 ) require ( - filippo.io/edwards25519 v1.0.0-beta.2 // indirect - github.com/99designs/keyring v1.1.6 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect - github.com/DataDog/zstd v1.4.5 // indirect - github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/armon/go-metrics v0.3.9 // indirect + cosmossdk.io/api v0.7.2 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/errors v1.0.0 // indirect + cosmossdk.io/log v1.2.1 // indirect + cosmossdk.io/math v1.2.0 // indirect + cosmossdk.io/store v1.0.0 // indirect + cosmossdk.io/x/tx v0.12.0 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect github.com/boltdb/bolt v1.3.1 // indirect - github.com/btcsuite/btcd v0.22.0-beta // indirect - github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/confio/ics23/go v0.6.6 // indirect - github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 // indirect - github.com/cosmos/btcutil v1.0.4 // indirect - github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/iavl v0.17.3 // indirect - github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect - github.com/cosmos/ledger-go v0.9.2 // indirect - github.com/danieljoos/wincred v1.0.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v0.0.0-20231116191551-bfb2e6a97fdd // 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.7.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.0 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 // indirect - github.com/dgraph-io/ristretto v0.0.3 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // 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/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect - github.com/fatih/color v1.12.0 // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/go-kit/kit v0.10.0 // indirect - github.com/go-logfmt/logfmt v0.5.0 // indirect - github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect - github.com/gogo/gateway v1.1.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 // indirect - github.com/google/btree v1.0.0 // indirect - github.com/gorilla/handlers v1.5.1 // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect - github.com/gtank/merlin v0.1.1 // indirect - github.com/gtank/ristretto255 v0.1.2 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-hclog v0.16.2 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/getsentry/sentry-go v0.25.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/golang/glog v1.1.2 // indirect + github.com/golang/protobuf v1.5.3 // 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/hashicorp/errwrap v1.1.0 // 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.2 // indirect github.com/hashicorp/go-msgpack v1.1.5 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/holiman/uint256 v1.2.3 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect - github.com/libp2p/go-buffer-pool v0.0.2 // indirect - github.com/magiconair/properties v1.8.5 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect - github.com/mtibben/percent v0.2.1 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/opencontainers/runc v1.0.2 // indirect - github.com/pelletier/go-toml v1.9.3 // indirect - github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect + github.com/klauspost/compress v1.17.3 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/linxGnu/grocksdb v1.8.5 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect + github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.11.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.29.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect - github.com/rakyll/statik v0.1.7 // indirect - github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect - github.com/regen-network/cosmos-proto v0.3.1 // indirect - github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - github.com/spf13/afero v1.6.0 // indirect - github.com/spf13/cast v1.3.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/zerolog v1.31.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.2.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect - github.com/tendermint/btcd v0.1.1 // indirect - github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect - github.com/tendermint/tm-db v0.6.4 // indirect - github.com/zondax/hid v0.9.0 // indirect - go.etcd.io/bbolt v1.3.5 // indirect - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect - golang.org/x/net v0.0.0-20210907225631-ff17edfbf26d // indirect - golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 // indirect - golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect - golang.org/x/text v0.3.7 // indirect - google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + go.etcd.io/bbolt v1.3.8 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) - -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - -replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 diff --git a/go.sum b/go.sum index db3822a8..48645992 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= 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,9 +16,7 @@ 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.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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 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= @@ -27,7 +25,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 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= @@ -37,300 +34,225 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo 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= +cosmossdk.io/api v0.7.2 h1:BO3i5fvKMKvfaUiMkCznxViuBEfyWA/k6w2eAF6q1C4= +cosmossdk.io/api v0.7.2/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= +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.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= +cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= +cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= +cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= +cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= +cosmossdk.io/store v1.0.0 h1:6tnPgTpTSIskaTmw/4s5C9FARdgFflycIc9OX8i1tOI= +cosmossdk.io/store v1.0.0/go.mod h1:ABMprwjvx6IpMp8l06TwuMrj6694/QP5NIW+X6jaTYc= +cosmossdk.io/x/tx v0.12.0 h1:Ry2btjQdrfrje9qZ3iZeZSmDArjgxUJMMcLMrX4wj5U= +cosmossdk.io/x/tx v0.12.0/go.mod h1:qTth2coAGkwCwOCjqQ8EAQg+9udXNRzcnSbMgGKGEI0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0-beta.2 h1:/BZRNzm8N4K4eWfK28dL4yescorxtO7YG1yun8fy+pI= -filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= -github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM= -github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= 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/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= -github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Jille/grpc-multi-resolver v1.1.0 h1:+SfnPGnoMjJVkURNfzpR8D+0Oy4w7IGAZ1azX1AQScY= -github.com/Jille/grpc-multi-resolver v1.1.0/go.mod h1:mvSEHOdOT1ju0ySQXhWMNCI4QsAGhuUqy6+wHGVJS+8= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Jille/raft-grpc-leader-rpc v1.1.0 h1:u36rmA4tjp+4FSdZ17jg/1sfSCYNQIe5bzzwvW0iVTM= github.com/Jille/raft-grpc-leader-rpc v1.1.0/go.mod h1:l+pK+uPuqpFDFcPmyUPSng4257UXrST0Vc3Lo4XwVB0= -github.com/Jille/raft-grpc-transport v1.2.0 h1:W/YSPz8IsirEyomjKmDog5Xk71o9+l4KhyMEX2TsgSs= -github.com/Jille/raft-grpc-transport v1.2.0/go.mod h1:GQGUXJfjlzwA390Ox1AyVYpjCLhtGd6yqY9Sb5hpQfc= -github.com/Jille/raft-grpc-transport v1.2.1-0.20220914172309-2f253856eefc h1:xF58NlLrijxTgZ/sfwUEVFJj/y0v2SxdIPoyHlLEjxI= -github.com/Jille/raft-grpc-transport v1.2.1-0.20220914172309-2f253856eefc/go.mod h1:77bQXfQSgLTAn1Iwi9MJDNE7KwPmdeW42Pd4HUHdl9E= -github.com/Jille/raftadmin v1.2.0 h1:hMLFUK7iKpeXP+CoIhNMWj+F53XOLSjMDSia0C60cps= -github.com/Jille/raftadmin v1.2.0/go.mod h1:vtVEpToPGTUPVwwunypWDpi69JpdnHMhWRUlc/65U+Y= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -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/Jille/raft-grpc-transport v1.4.0 h1:Kwk+IceQD8MpLKOulBu2ignX+aZAEjOhffEhN44sdzQ= +github.com/Jille/raft-grpc-transport v1.4.0/go.mod h1:afVUd8LQKUUo3V/ToLBH3mbSyvivRlMYCDK0eJRGTfQ= +github.com/Jille/raftadmin v1.2.1 h1:P35JYYJmyHnsJ7sEjdXVRRvaqXUQUuNjIqPFf5n3CEM= +github.com/Jille/raftadmin v1.2.1/go.mod h1:CocBqmOFhfk8oIgbzZKYACdm5PizUXfzupqTQV17Pgk= 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 v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= 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/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM= -github.com/adlio/schema v1.1.13/go.mod h1:L5Z7tw+7lRK1Fnpi/LT/ooCP1elkXn0krMWBQHUhEDE= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= -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-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -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/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= -github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +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/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +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/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= -github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/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/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +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.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 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.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 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/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-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-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coinbase/rosetta-sdk-go v0.6.10 h1:rgHD/nHjxLh0lMEdfGDqpTtlvtSBwULqrrZ2qPdNaCM= -github.com/coinbase/rosetta-sdk-go v0.6.10/go.mod h1:J/JFMsfcePrjJZkwQFLh+hJErkAmdm9Iyy3D5Y0LfXo= -github.com/confio/ics23/go v0.6.6 h1:pkOy18YxxJ/r0XFDCnrl4Bjv6h4LkBSpLS6F38mrKL8= -github.com/confio/ics23/go v0.6.6/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 h1:6ejg6Lkk8dskcM7wQ28gONkukbQkM4qpj4RnYbpFzrI= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +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 v0.0.0-20231116191551-bfb2e6a97fdd h1:qJuMKww8C5kQx0A0zgQUaswdcWlzgzTGAA5YdFAin3Y= +github.com/cockroachdb/pebble v0.0.0-20231116191551-bfb2e6a97fdd/go.mod h1:acMRUGd/BK8AUmQNK3spUCCGzFLZU2bSST3NMXSq2Kc= +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/cometbft/cometbft v0.38.2 h1:io0JCh5EPxINKN5ZMI5hCdpW3QVZRy+o8qWe3mlJa/8= +github.com/cometbft/cometbft v0.38.2/go.mod h1:PIi48BpzwlHqtV3mzwPyQgOyOnU94BNBimLS2ebBHOg= +github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= +github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+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/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 v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44= -github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU= -github.com/cosmos/cosmos-sdk v0.44.5 h1:t5h+KPzZb0Zsag1RP1DCMQlyJyIQqJcqSPJrbUCDGHY= -github.com/cosmos/cosmos-sdk v0.44.5/go.mod h1:maUA6m2TBxOJZkbwl0eRtEBgTX37kcaiOWU5t1HEGaY= -github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= +github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= +github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= +github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= +github.com/cosmos/cosmos-sdk v0.50.1 h1:2SYwAYqd7ZwtrWxu/J8PwbQV/cDcu90bCr/a78g3lVw= +github.com/cosmos/cosmos-sdk v0.50.1/go.mod h1:fsLSPGstCwn6MMsFDMAQWGJj8E4sYsN9Gnu1bGE5imA= 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/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y= -github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w= -github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= -github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= -github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= -github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +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.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= +github.com/cosmos/iavl v1.0.0 h1:bw6t0Mv/mVCJvlMTOPHWLs5uUE3BRBfVWCRelOzl+so= +github.com/cosmos/iavl v1.0.0/go.mod h1:CmTGqMnRnucjxbjduneZXT+0vPgNElYvdefjX2q9tYc= +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/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.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= -github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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 v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +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.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 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.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= -github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +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.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= -github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= 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/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ= -github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -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 v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +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.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/emicklei/dot v1.6.0 h1:vUzuoVE8ipzS7QkES4UfxdpCwdU2U97m2Pb2tQCoYRY= +github.com/emicklei/dot v1.6.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= 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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +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.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +github.com/getsentry/sentry-go v0.25.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/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw 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 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= -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-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 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +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-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/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= -github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +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.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= 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= @@ -341,7 +263,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt 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= @@ -360,16 +281,17 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3 h1:ur2rms48b3Ep1dxh7aUV2FZEQ8jEVO2F6ILKx8ofkAg= -github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 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/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= @@ -378,13 +300,12 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -401,548 +322,324 @@ 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-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-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/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.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -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.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= -github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -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.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= -github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= -github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.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-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +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-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-metrics v0.5.2 h1:ErEYO2f//CjKsUDw4SmLzelsK6L3ZmOAR/4P9iS7ruY= +github.com/hashicorp/go-metrics v0.5.2/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-msgpack/v2 v2.1.1 h1:xQEY9yB2wnHitoSzk/B9UjXWRQ67QKu5AOm8aFp8N3I= +github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +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-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 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.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 v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +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/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/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft v1.3.1/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= -github.com/hashicorp/raft v1.3.3 h1:Xr6DSHC5cIM8kzxu+IgoT/+MeNeUNeWin3ie6nlSrMg= -github.com/hashicorp/raft v1.3.3/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= -github.com/hashicorp/raft v1.3.10 h1:LR5QZX1VQd0DFWZfeCwWawyeKfpS/Tm1yjnJIY5X4Tw= -github.com/hashicorp/raft v1.3.10/go.mod h1:J8naEwc6XaaCfts7+28whSeRvCqTd6e20BlCU3LtEO4= +github.com/hashicorp/raft v1.3.7/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= +github.com/hashicorp/raft v1.6.0 h1:tkIAORZy2GbJ2Trp5eUSggLXDPOJLXC+JJLNMMqtgtM= +github.com/hashicorp/raft v1.6.0/go.mod h1:Xil5pDgeGwRWuX4uPUmwa+7Vagg4N804dz6mhNi6S7o= github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea h1:RxcPJuutPRM8PUOyiweMmkuNO+RJyfy2jds2gfvgNmU= github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea/go.mod h1:qRd6nFJYYS6Iqnc/8HcUmko2/2Gw8qTFEmxDLii6W5I= github.com/hashicorp/raft-boltdb/v2 v2.2.2 h1:rlkPtOllgIcKLxVT4nutqlTH2NRFn+tO1wwZk/4Dxqw= github.com/hashicorp/raft-boltdb/v2 v2.2.2/go.mod h1:N8YgaZgNJLpZC+h+by7vDu5rzsRgONThTEeUS3zWbfY= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 h1:uUjLpLt6bVvZ72SQc/B4dXcPBw4Vgd7soowdRl52qEM= -github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87/go.mod h1:XGsKKeXxeRr95aEOgipvluMPlgjr7dGlk9ZTWOjcUcg= -github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +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/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +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.14.1 h1:NrN4PY71A6tAz2sKDvC5JCauENWp0ykG8Oq1H3cpFvw= -github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +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/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -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.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 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/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= -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.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= +github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 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.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= -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/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/kraken-hpc/go-fork v0.1.1 h1:O3X/ynoNy/eS7UIcZYef8ndFq2RXEIOue9kZqyzF0Sk= +github.com/kraken-hpc/go-fork v0.1.1/go.mod h1:uu0e5h+V4ONH5Qk/xuVlyNXJXy/swhqGIEMK7w+9dNc= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/linxGnu/grocksdb v1.8.5 h1:Okfk5B1h0ikCYdDM7Tc5yJUS8LTwAmMBq5IPWTmOLPs= +github.com/linxGnu/grocksdb v1.8.5/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -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.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= -github.com/minio/highwayhash v1.0.1/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/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +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/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +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/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/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= 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/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -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/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= 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/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/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 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.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -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/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= 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.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -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.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -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/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= +github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= 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/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= -github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -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 v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= -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 v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -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/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA= +github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/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.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 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.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= 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 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.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.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE= -github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= -github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg= -github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid 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/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g= -github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +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.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= -github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= -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/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.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= -github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= -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/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= -github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= -github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= 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/tendermint/tendermint v0.34.14 h1:GCXmlS8Bqd2Ix3TQCpwYLUNHe+Y+QyJsm5YE+S/FkPo= -github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0= -github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ= -github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= -github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -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/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/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vmihailenco/msgpack/v5 v5.1.4/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= -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/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= 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/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= -github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +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= gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e h1:/QfokHt2yG9PcjnFSdpIQhJwrz2Q1bmKA718vw4/He8= gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e/go.mod h1:lTSPILLBMt6qQOJgsiarbW85JhpkhoOfW2EBkxkcuSI= -gitlab.com/unit410/threshold-ed25519 v0.0.0-20220725172740-6ee731f539ac h1:SUxQNWwBUVFdvRP72Wi1wZ8K2iD7+SXFfMUMJyMCJjc= -gitlab.com/unit410/threshold-ed25519 v0.0.0-20220725172740-6ee731f539ac/go.mod h1:2TURxKmaBFbTDxXQa+B2nGuKSRWD0Jlcc3LETTAnSS0= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= +gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc h1:ESmaQH1+uOLW0wOIPT4EVLh+gwKdmKovr+eZg4jWUNs= +gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc/go.mod h1:1CIpwbV+rSyzoBVr6Ok9vvEP7UHrW/d0zEB+jU6+E3o= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 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.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -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/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.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= 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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -950,7 +647,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -967,28 +665,21 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPI 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/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 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.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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/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-20181011144130-49bb7cea24b1/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= @@ -999,8 +690,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL 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-20190923162816-aa69164e4478/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= @@ -1008,7 +697,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL 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= @@ -1016,23 +704,20 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ 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-20200904194848-62affa334b73/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-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210907225631-ff17edfbf26d h1:kuk8nKPQ25KCDODLCDXt99tnTVeOyOM8HGvtJ0NzAvw= golang.org/x/net v0.0.0-20210907225631-ff17edfbf26d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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= @@ -1042,10 +727,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ 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-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= @@ -1056,18 +737,14 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ 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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 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= @@ -1077,24 +754,16 @@ 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-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/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-20190712062909-fae7ac547cb7/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-20190924154521-2837fb4f24fe/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-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/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= @@ -1104,46 +773,43 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w 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-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/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-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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-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-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20220811171246-fbc7d0a398ab/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 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= @@ -1152,20 +818,18 @@ 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-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-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 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= @@ -1176,9 +840,7 @@ 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-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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= @@ -1186,8 +848,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn 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-20200117012304-6edc0a871e69/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= @@ -1202,10 +862,8 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc 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-20200522201501-cb1345f3a375/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-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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= @@ -1216,16 +874,14 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f 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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= @@ -1245,11 +901,7 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 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.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1262,7 +914,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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= @@ -1278,7 +929,6 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG 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-20200324203455-a04cca1dde73/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= @@ -1292,30 +942,22 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D 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-20201119123407-9b1e624d6bc4/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-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-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-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 h1:3V2dxSZpz4zozWWUq36vUxXEKnSYitEH2LdsAx+RUmg= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/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-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/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= @@ -1325,19 +967,14 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji 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.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 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= @@ -1348,32 +985,23 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 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.25.1-0.20200805231151-a709e31e5d12/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 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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 h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +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/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.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +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/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/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= -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= @@ -1384,11 +1012,12 @@ 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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 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 v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= @@ -1398,8 +1027,10 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 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/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= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/go.work b/go.work new file mode 100644 index 00000000..1519011f --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.21 + +use ( + . + ./test +) diff --git a/goreleaser.yaml b/goreleaser.yaml deleted file mode 100644 index 09f4beba..00000000 --- a/goreleaser.yaml +++ /dev/null @@ -1,30 +0,0 @@ -project_name: horcrux - -env: - # Require use of Go modules. - - GO111MODULE=on - -builds: - - id: "horcrux" - main: ./cmd/horcrux/main.go - ldflags: - - -X "github.com/strangelove-ventures/horcrux/cmd/horcrux/cmd.Version={{ .Version }}" - - -X "github.com/strangelove-ventures/horcrux/cmd/horcrux/cmd.Commit={{ .Commit }}" - goos: - - darwin - - linux - goarch: - - amd64 - - arm64 - -checksum: - name_template: SHA256SUMS-{{.Version}}.txt - algorithm: sha256 - -release: - name_template: "Release {{.Version}}" - -archives: - - files: - - LICENSE.md - - README.md \ No newline at end of file diff --git a/proto/buf.gen.yaml b/proto/buf.gen.yaml new file mode 100644 index 00000000..2d44f0f5 --- /dev/null +++ b/proto/buf.gen.yaml @@ -0,0 +1,5 @@ +version: v1 +plugins: + - name: gocosmos + out: . + opt: plugins=grpc diff --git a/proto/buf.lock b/proto/buf.lock new file mode 100755 index 00000000..c91b5810 --- /dev/null +++ b/proto/buf.lock @@ -0,0 +1,2 @@ +# Generated by buf. DO NOT EDIT. +version: v1 diff --git a/proto/buf.yaml b/proto/buf.yaml new file mode 100644 index 00000000..c126332f --- /dev/null +++ b/proto/buf.yaml @@ -0,0 +1 @@ +version: v1 diff --git a/proto/strangelove/horcrux/cosigner.proto b/proto/strangelove/horcrux/cosigner.proto new file mode 100644 index 00000000..5e0c0cef --- /dev/null +++ b/proto/strangelove/horcrux/cosigner.proto @@ -0,0 +1,98 @@ +syntax = "proto3"; +package strangelove.horcrux; + +option go_package = "github.com/strangelove-ventures/horcrux/v3/signer/proto"; + +service Cosigner { + rpc SignBlock (SignBlockRequest) returns (SignBlockResponse) {} + rpc SetNoncesAndSign (SetNoncesAndSignRequest) returns (SetNoncesAndSignResponse) {} + rpc GetNonces (GetNoncesRequest) returns (GetNoncesResponse) {} + rpc TransferLeadership (TransferLeadershipRequest) returns (TransferLeadershipResponse) {} + rpc GetLeader (GetLeaderRequest) returns (GetLeaderResponse) {} + rpc Ping(PingRequest) returns (PingResponse) {} +} + +message Block { + int64 height = 1; + int64 round = 2; + int32 step = 3; + bytes signBytes = 4; + bytes voteExtSignBytes = 5; + int64 timestamp = 6; +} + +message SignBlockRequest { + string chainID = 1; + Block block = 2; +} + +message SignBlockResponse { + bytes signature = 1; + bytes vote_ext_signature = 2; + int64 timestamp = 3; +} + +message Nonce { + int32 sourceID = 1; + int32 destinationID = 2; + bytes pubKey = 3; + bytes share = 4; + bytes signature = 5; +} + +message UUIDNonce { + bytes uuid = 1; + repeated Nonce nonces = 2; +} + +message HRST { + int64 height = 1; + int64 round = 2; + int32 step = 3; + int64 timestamp = 4; +} + +message SetNoncesAndSignRequest { + bytes uuid = 1; + repeated Nonce nonces = 2; + HRST hrst = 3; + bytes signBytes = 4; + bytes voteExtUuid = 5; + repeated Nonce voteExtNonces = 6; + bytes voteExtSignBytes = 7; + string chainID = 8; +} + +message SetNoncesAndSignResponse { + int64 timestamp = 1; + bytes noncePublic = 2; + bytes signature = 3; + bytes voteExtNoncePublic = 4; + bytes voteExtSignature = 5; +} + +message GetNoncesRequest { + repeated bytes uuids = 1; +} + +message GetNoncesResponse { + repeated UUIDNonce nonces = 1; +} + +message TransferLeadershipRequest { + string leaderID = 1; +} + +message TransferLeadershipResponse { + string leaderID = 1; + string leaderAddress = 2; +} + +message GetLeaderRequest {} + +message GetLeaderResponse { + int32 leader = 1; +} + +message PingRequest {} +message PingResponse {} diff --git a/proto/strangelove/horcrux/remote_signer.proto b/proto/strangelove/horcrux/remote_signer.proto new file mode 100644 index 00000000..2e1a6c2d --- /dev/null +++ b/proto/strangelove/horcrux/remote_signer.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package strangelove.horcrux; + +import "strangelove/horcrux/cosigner.proto"; + +option go_package = "github.com/strangelove-ventures/horcrux/v3/signer/proto"; + +service RemoteSigner { + rpc PubKey (PubKeyRequest) returns (PubKeyResponse) {} + rpc Sign(strangelove.horcrux.SignBlockRequest) returns (strangelove.horcrux.SignBlockResponse) {} +} + +message PubKeyRequest { + string chain_id = 1; +} + +message PubKeyResponse { + bytes pub_key = 1; +} diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh new file mode 100644 index 00000000..6e0428d0 --- /dev/null +++ b/scripts/protocgen.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eox pipefail + +echo "Generating proto code" + +proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +for dir in $proto_dirs; do + for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do + buf generate $file --template proto/buf.gen.yaml + done +done + +cp -r github.com/strangelove-ventures/horcrux/v3/signer ./ +rm -rf github.com diff --git a/scripts/spin-up.bash b/scripts/spin-up.bash deleted file mode 100755 index f168f615..00000000 --- a/scripts/spin-up.bash +++ /dev/null @@ -1,200 +0,0 @@ -#!/bin/sh -# USAGE: ./one-chain - -CHAINID=test-chain-id -CHAINDIR=./data -HORCRUX=../build/horcrux - -function setConfigHorcrux() { - cfgdir=$1 - rpc=$2 - p2p=$3 - grpc=$4 - grpcweb=$5 - profile=$6 - privval=$7 - sed -i '' "s#127.0.0.1:26657#0.0.0.0:$rpc#g" $cfgdir/config.toml - sed -i '' "s#0.0.0.0:26656#0.0.0.0:$p2p#g" $cfgdir/config.toml - sed -i '' "s#0.0.0.0:9090#0.0.0.0:$grpc#g" $cfgdir/app.toml - sed -i '' "s#0.0.0.0:9091#0.0.0.0:$grpcweb#g" $cfgdir/app.toml - sed -i '' "s#localhost:6060#localhost:$profile#g" $cfgdir/config.toml - sed -i '' 's/timeout_commit = "5s"/timeout_commit = "3s"/g' $cfgdir/config.toml - sed -i '' 's/timeout_propose = "3s"/timeout_propose = "3s"/g' $cfgdir/config.toml - sed -i '' "s#priv_validator_laddr = \"\"#priv_validator_laddr = \"tcp://0.0.0.0:$privval\"#g" $cfgdir/config.toml - sed -i '' 's#allow_duplicate_ip = false#allow_duplicate_ip = true#g' $cfgdir/config.toml - sed -i '' 's#log_level = "main:info,state:info,statesync:info,*:error"#log_level = "info"#g' $cfgdir/config.toml - sed -i '' 's#addr_book_strict = true#addr_book_strict = false#g' $cfgdir/config.toml - sed -i '' "s#external_address = \"\"#external_address = \"tcp://127.0.0.1:$p2p\"#g" $cfgdir/config.toml -} - -function setConfigValidator() { - cfgdir=$1 - rpc=$2 - p2p=$3 - grpc=$4 - grpcweb=$5 - profile=$6 - sed -i '' "s#127.0.0.1:26657#0.0.0.0:$rpc#g" $cfgdir/config.toml - sed -i '' "s#0.0.0.0:26656#0.0.0.0:$p2p#g" $cfgdir/config.toml - sed -i '' "s#0.0.0.0:9090#0.0.0.0:$grpc#g" $cfgdir/app.toml - sed -i '' "s#0.0.0.0:9091#0.0.0.0:$grpcweb#g" $cfgdir/app.toml - sed -i '' "s#localhost:6060#localhost:$profile#g" $cfgdir/config.toml - sed -i '' 's/timeout_commit = "5s"/timeout_commit = "3s"/g' $cfgdir/config.toml - sed -i '' 's/timeout_propose = "3s"/timeout_propose = "3s"/g' $cfgdir/config.toml - sed -i '' 's#allow_duplicate_ip = false#allow_duplicate_ip = true#g' $cfgdir/config.toml - sed -i '' 's#log_level = "main:info,state:info,statesync:info,*:error"#log_level = "info"#g' $cfgdir/config.toml - sed -i '' 's#addr_book_strict = true#addr_book_strict = false#g' $cfgdir/config.toml - sed -i '' "s#external_address = \"\"#external_address = \"tcp://127.0.0.1:$p2p\"#g" $cfgdir/config.toml -} - -# echo "Creating gaiad instance with home=$CHAINDIR chain-id=$CHAINID..." -# Build genesis file incl account for passed address -coins="100000000000stake,100000000000samoleans" - -# Have different home folders for each node -# Nodes for horcrux -n0dir="$CHAINDIR/$CHAINID/n0" -n1dir="$CHAINDIR/$CHAINID/n1" -n2dir="$CHAINDIR/$CHAINID/n2" -# Other validators -n3dir="$CHAINDIR/$CHAINID/n3" -n4dir="$CHAINDIR/$CHAINID/n4" -n5dir="$CHAINDIR/$CHAINID/n5" - -# Nodes for horcrux -home0="--home $n0dir" -home1="--home $n1dir" -home2="--home $n2dir" -# Other validators -home3="--home $n3dir" -home4="--home $n4dir" -home5="--home $n5dir" - -# Nodes for horcrux -n0cfgDir="$n0dir/config" -n1cfgDir="$n1dir/config" -n2cfgDir="$n2dir/config" -# Other validators -n3cfgDir="$n3dir/config" -n4cfgDir="$n4dir/config" -n5cfgDir="$n5dir/config" - -# Nodes for horcrux -n0cfg="$n0cfgDir/config.toml" -n1cfg="$n1cfgDir/config.toml" -n2cfg="$n2cfgDir/config.toml" -# Other validators -n3cfg="$n3cfgDir/config.toml" -n4cfg="$n4cfgDir/config.toml" -n5cfg="$n5cfgDir/config.toml" -kbt="--keyring-backend="test"" - -# Initialize the 2 home directories -gaiad $home0 --chain-id $CHAINID init n0 &>/dev/null -gaiad $home1 --chain-id $CHAINID init n1 &>/dev/null -gaiad $home2 --chain-id $CHAINID init n2 &>/dev/null -gaiad $home3 --chain-id $CHAINID init n3 &>/dev/null -gaiad $home4 --chain-id $CHAINID init n4 &>/dev/null -gaiad $home5 --chain-id $CHAINID init n5 &>/dev/null - -# Add some keys for funds -# Add horcrux validator key -gaiad $home0 keys add validator $kbt &>/dev/null -# Add other validator keys -gaiad $home3 keys add validator $kbt &>/dev/null -gaiad $home4 keys add validator $kbt &>/dev/null -gaiad $home5 keys add validator $kbt &>/dev/null -# have a key with some extra funds -gaiad $home0 keys add extra $kbt &>/dev/null - -# Add addresses to genesis -# Add all validator addresses keys to node0 genesis -gaiad $home0 add-genesis-account $(gaiad $home0 keys $kbt show validator -a) $coins &>/dev/null -gaiad $home0 add-genesis-account $(gaiad $home3 keys $kbt show validator -a) $coins &>/dev/null -gaiad $home0 add-genesis-account $(gaiad $home4 keys $kbt show validator -a) $coins &>/dev/null -gaiad $home0 add-genesis-account $(gaiad $home5 keys $kbt show validator -a) $coins &>/dev/null -# Add extra funds key to node0 genesis -gaiad $home0 add-genesis-account $(gaiad $home0 keys $kbt show extra -a) $coins &>/dev/null -# Add validator addresses for gentxs to other validators home folders -gaiad $home3 add-genesis-account $(gaiad $home3 keys $kbt show validator -a) $coins &>/dev/null -gaiad $home4 add-genesis-account $(gaiad $home4 keys $kbt show validator -a) $coins &>/dev/null -gaiad $home5 add-genesis-account $(gaiad $home5 keys $kbt show validator -a) $coins &>/dev/null - -# Gentxs for each node -gaiad $home0 gentx validator 100000000000stake $kbt --chain-id $CHAINID &>/dev/null -gaiad $home3 gentx validator 100000000000stake $kbt --chain-id $CHAINID &>/dev/null -gaiad $home4 gentx validator 100000000000stake $kbt --chain-id $CHAINID &>/dev/null -gaiad $home5 gentx validator 100000000000stake $kbt --chain-id $CHAINID &>/dev/null -# Move gentxs to node0 config dir -mv $n3cfgDir/gentx/* $n0cfgDir/gentx &>/dev/null -mv $n4cfgDir/gentx/* $n0cfgDir/gentx &>/dev/null -mv $n5cfgDir/gentx/* $n0cfgDir/gentx &>/dev/null - -# finalize genesis -gaiad $home0 collect-gentxs &>/dev/null - -# Copy genesis over to other nodes -cp $n0cfgDir/genesis.json $n1cfgDir/genesis.json -cp $n0cfgDir/genesis.json $n2cfgDir/genesis.json -cp $n0cfgDir/genesis.json $n3cfgDir/genesis.json -cp $n0cfgDir/genesis.json $n4cfgDir/genesis.json -cp $n0cfgDir/genesis.json $n5cfgDir/genesis.json - -# Set proper defaults and change ports on horcrux nodes -setConfigHorcrux $n0cfgDir 26657 26656 9090 9091 6060 1234 -setConfigHorcrux $n1cfgDir 26658 26655 9092 9093 6061 1235 -setConfigHorcrux $n2cfgDir 26659 26654 9094 9095 6062 1236 -# Set proper defaults and change ports on validator nodes -setConfigValidator $n3cfgDir 26660 26653 9096 9097 6062 -setConfigValidator $n4cfgDir 26661 26652 9098 9099 6063 -setConfigValidator $n5cfgDir 26662 26651 9100 9101 6064 - -# Set peers for all nodes -peer0="$(gaiad $home0 tendermint show-node-id)@127.0.0.1:26656" -peer1="$(gaiad $home1 tendermint show-node-id)@127.0.0.1:26655" -peer2="$(gaiad $home2 tendermint show-node-id)@127.0.0.1:26654" -peer3="$(gaiad $home3 tendermint show-node-id)@127.0.0.1:26653" -peer4="$(gaiad $home4 tendermint show-node-id)@127.0.0.1:26652" -peer5="$(gaiad $home5 tendermint show-node-id)@127.0.0.1:26651" -sed -i '' 's#persistent_peers = ""#persistent_peers = "'$peer1','$peer2','$peer3','$peer4','$peer5'"#g' $n0cfg -sed -i '' 's#persistent_peers = ""#persistent_peers = "'$peer0','$peer2','$peer3','$peer4','$peer5'"#g' $n1cfg -sed -i '' 's#persistent_peers = ""#persistent_peers = "'$peer0','$peer1','$peer3','$peer4','$peer5'"#g' $n2cfg -sed -i '' 's#persistent_peers = ""#persistent_peers = "'$peer0','$peer1','$peer2','$peer4','$peer5'"#g' $n3cfg -sed -i '' 's#persistent_peers = ""#persistent_peers = "'$peer0','$peer1','$peer2','$peer3','$peer5'"#g' $n4cfg -sed -i '' 's#persistent_peers = ""#persistent_peers = "'$peer0','$peer1','$peer2','$peer3','$peer4'"#g' $n5cfg - -# Copy priv validator over from node that signed gentx to the signer -cp $n0cfgDir/priv_validator_key.json $CHAINDIR/priv_validator_key.json -cd $CHAINDIR -$HORCRUX create-shares ./priv_validator_key.json 2 3 -$HORCRUX config init $CHAINID localhost:1234 --home $(pwd)/signer1 --cosigner --peers "tcp://localhost:2223|2,tcp://localhost:2224|3" --threshold 2 --listen "tcp://0.0.0.0:2222" -$HORCRUX config init $CHAINID localhost:1235 --home $(pwd)/signer2 --cosigner --peers "tcp://localhost:2222|1,tcp://localhost:2224|3" --threshold 2 --listen "tcp://0.0.0.0:2223" -$HORCRUX config init $CHAINID localhost:1236 --home $(pwd)/signer3 --cosigner --peers "tcp://localhost:2222|1,tcp://localhost:2223|2" --threshold 2 --listen "tcp://0.0.0.0:2224" -cp ./private_share_1.json ./signer1/share.json -cp ./private_share_2.json ./signer2/share.json -cp ./private_share_3.json ./signer3/share.json -cd .. - -# Start the gaia instances -./build/horcrux --home ./data/signer1 cosigner start > $CHAINDIR/signer1.log 2>&1 & -./build/horcrux --home ./data/signer2 cosigner start > $CHAINDIR/signer2.log 2>&1 & -./build/horcrux --home ./data/signer3 cosigner start > $CHAINDIR/signer3.log 2>&1 & -sleep 5 -gaiad $home0 start --pruning=nothing > $CHAINDIR/$CHAINID.n0.log 2>&1 & -gaiad $home1 start --pruning=nothing > $CHAINDIR/$CHAINID.n1.log 2>&1 & -gaiad $home2 start --pruning=nothing > $CHAINDIR/$CHAINID.n2.log 2>&1 & -gaiad $home3 start --pruning=nothing > $CHAINDIR/$CHAINID.n3.log 2>&1 & -gaiad $home4 start --pruning=nothing > $CHAINDIR/$CHAINID.n4.log 2>&1 & -gaiad $home5 start --pruning=nothing > $CHAINDIR/$CHAINID.n5.log 2>&1 & - -echo -echo "Logs:" -echo " - n0 'tail -f ./data/signer1.log'" -echo " - n1 'tail -f ./data/signer2.log'" -echo " - n2 'tail -f ./data/signer3.log'" -echo " - f0 'tail -f ./data/test-chain-id.n0.log'" -echo " - f1 'tail -f ./data/test-chain-id.n1.log'" -echo " - f1 'tail -f ./data/test-chain-id.n2.log'" -echo " - f1 'tail -f ./data/test-chain-id.n3.log'" -echo " - f1 'tail -f ./data/test-chain-id.n4.log'" -echo " - f1 'tail -f ./data/test-chain-id.n5.log'" diff --git a/signer/Config.go b/signer/Config.go deleted file mode 100644 index 9afd6137..00000000 --- a/signer/Config.go +++ /dev/null @@ -1,72 +0,0 @@ -package signer - -import ( - "fmt" - "os" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/codec/legacy" - "github.com/cosmos/cosmos-sdk/codec/types" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/bech32" - "github.com/tendermint/tendermint/crypto" -) - -type NodeConfig struct { - Address string -} - -type CosignerConfig struct { - ID int - Address string -} - -type Config struct { - Mode string - PrivValKeyFile string - PrivValStateDir string - ChainID string - CosignerThreshold int - ListenAddress string - Nodes []NodeConfig - Cosigners []CosignerConfig -} - -func (cfg *Config) KeyFileExists() error { - if _, err := os.Stat(cfg.PrivValKeyFile); os.IsNotExist(err) { - return fmt.Errorf("private key share doesn't exist at path(%s)", cfg.PrivValKeyFile) - } - return nil -} - -func PubKey(bech32BasePrefix string, pubKey crypto.PubKey) (string, error) { - if bech32BasePrefix != "" { - pubkey, err := cryptocodec.FromTmPubKeyInterface(pubKey) - if err != nil { - return "", err - } - consPubPrefix := bech32BasePrefix + "valconspub" - pubKeyBech32, err := bech32.ConvertAndEncode(consPubPrefix, legacy.Cdc.Amino.MustMarshalBinaryBare(pubkey)) - if err != nil { - return "", err - } - return pubKeyBech32, nil - } - - registry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(registry) - var pk *cryptotypes.PubKey - registry.RegisterInterface("cosmos.crypto.PubKey", pk) - registry.RegisterImplementations(pk, &ed25519.PubKey{}) - sdkPK, err := cryptocodec.FromTmPubKeyInterface(pubKey) - if err != nil { - return "", err - } - pubKeyJSON, err := marshaler.MarshalInterfaceJSON(sdkPK) - if err != nil { - return "", err - } - return string(pubKeyJSON), nil -} diff --git a/signer/Cosigner.go b/signer/Cosigner.go deleted file mode 100644 index 5a86ded8..00000000 --- a/signer/Cosigner.go +++ /dev/null @@ -1,142 +0,0 @@ -package signer - -import ( - "time" - - proto "github.com/strangelove-ventures/horcrux/signer/proto" -) - -type HRSKey struct { - Height int64 - Round int64 - Step int8 -} - -type HRSTKey struct { - Height int64 - Round int64 - Step int8 - Timestamp int64 -} - -func HRSTKeyFromProto(hrs *proto.HRST) HRSTKey { - return HRSTKey{ - Height: hrs.GetHeight(), - Round: hrs.GetRound(), - Step: int8(hrs.GetStep()), - Timestamp: hrs.GetTimestamp(), - } -} - -func (hrst HRSTKey) toProto() *proto.HRST { - return &proto.HRST{ - Height: hrst.Height, - Round: hrst.Round, - Step: int32(hrst.Step), - Timestamp: hrst.Timestamp, - } -} - -// CosignerSignRequest is sent to a co-signer to obtain their signature for the SignBytes -// The SignBytes should be a serialized block -type CosignerSignRequest struct { - SignBytes []byte -} - -type CosignerSignResponse struct { - EphemeralPublic []byte - Timestamp time.Time - Signature []byte -} - -type CosignerEphemeralSecretPart struct { - SourceID int - DestinationID int - SourceEphemeralSecretPublicKey []byte - EncryptedSharePart []byte - SourceSig []byte -} - -func (secretPart *CosignerEphemeralSecretPart) toProto() *proto.EphemeralSecretPart { - return &proto.EphemeralSecretPart{ - SourceID: int32(secretPart.SourceID), - DestinationID: int32(secretPart.DestinationID), - SourceEphemeralSecretPublicKey: secretPart.SourceEphemeralSecretPublicKey, - EncryptedSharePart: secretPart.EncryptedSharePart, - SourceSig: secretPart.SourceSig, - } -} - -type CosignerEphemeralSecretParts []CosignerEphemeralSecretPart - -func (secretParts CosignerEphemeralSecretParts) toProto() (out []*proto.EphemeralSecretPart) { - for _, secretPart := range secretParts { - out = append(out, secretPart.toProto()) - } - return -} - -func CosignerEphemeralSecretPartFromProto(secretPart *proto.EphemeralSecretPart) CosignerEphemeralSecretPart { - return CosignerEphemeralSecretPart{ - SourceID: int(secretPart.SourceID), - DestinationID: int(secretPart.DestinationID), - SourceEphemeralSecretPublicKey: secretPart.SourceEphemeralSecretPublicKey, - EncryptedSharePart: secretPart.EncryptedSharePart, - SourceSig: secretPart.SourceSig, - } -} - -func CosignerEphemeralSecretPartsFromProto( - secretParts []*proto.EphemeralSecretPart) (out []CosignerEphemeralSecretPart) { - for _, secretPart := range secretParts { - out = append(out, CosignerEphemeralSecretPartFromProto(secretPart)) - } - return -} - -type CosignerSetEphemeralSecretPartRequest struct { - SourceID int - SourceEphemeralSecretPublicKey []byte - EncryptedSharePart []byte - SourceSig []byte - Height int64 - Round int64 - Step int8 - Timestamp time.Time -} - -type CosignerSignBlockRequest struct { - ChainID string - Block *Block -} - -type CosignerSignBlockResponse struct { - Signature []byte -} - -type CosignerEphemeralSecretPartsResponse struct { - EncryptedSecrets []CosignerEphemeralSecretPart -} - -type CosignerSetEphemeralSecretPartsAndSignRequest struct { - EncryptedSecrets []CosignerEphemeralSecretPart - HRST HRSTKey - SignBytes []byte -} - -// Cosigner interface is a set of methods for an m-of-n threshold signature. -// This interface abstracts the underlying key storage and management -type Cosigner interface { - // Get the ID of the cosigner - // The ID is the shamir index: 1, 2, etc... - GetID() int - - // Get the P2P URL (GRPC and Raft) - GetAddress() string - - // Get ephemeral secret part for all peers - GetEphemeralSecretParts(hrst HRSTKey) (*CosignerEphemeralSecretPartsResponse, error) - - // Sign the requested bytes - SetEphemeralSecretPartsAndSign(req CosignerSetEphemeralSecretPartsAndSignRequest) (*CosignerSignResponse, error) -} diff --git a/signer/PvGuard.go b/signer/PvGuard.go deleted file mode 100644 index 2a3ed207..00000000 --- a/signer/PvGuard.go +++ /dev/null @@ -1,37 +0,0 @@ -package signer - -import ( - "sync" - - "github.com/tendermint/tendermint/crypto" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" - tm "github.com/tendermint/tendermint/types" -) - -// PvGuard guards access to an underlying PrivValidator by using mutexes -// for each of the PrivValidator interface functions -type PvGuard struct { - PrivValidator tm.PrivValidator - pvMutex sync.Mutex -} - -// GetPubKey implements types.PrivValidator -func (pv *PvGuard) GetPubKey() (crypto.PubKey, error) { - pv.pvMutex.Lock() - defer pv.pvMutex.Unlock() - return pv.PrivValidator.GetPubKey() -} - -// SignVote implements types.PrivValidator -func (pv *PvGuard) SignVote(chainID string, vote *tmProto.Vote) error { - pv.pvMutex.Lock() - defer pv.pvMutex.Unlock() - return pv.PrivValidator.SignVote(chainID, vote) -} - -// SignProposal implements types.PrivValidator -func (pv *PvGuard) SignProposal(chainID string, proposal *tmProto.Proposal) error { - pv.pvMutex.Lock() - defer pv.pvMutex.Unlock() - return pv.PrivValidator.SignProposal(chainID, proposal) -} diff --git a/signer/Serialization.go b/signer/Serialization.go deleted file mode 100644 index 7cb039f7..00000000 --- a/signer/Serialization.go +++ /dev/null @@ -1,44 +0,0 @@ -package signer - -import ( - "errors" - "io" - - "github.com/tendermint/tendermint/libs/protoio" - tmProtoPrivval "github.com/tendermint/tendermint/proto/tendermint/privval" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" -) - -// ReadMsg reads a message from an io.Reader -func ReadMsg(reader io.Reader) (msg tmProtoPrivval.Message, err error) { - const maxRemoteSignerMsgSize = 1024 * 10 - protoReader := protoio.NewDelimitedReader(reader, maxRemoteSignerMsgSize) - _, err = protoReader.ReadMsg(&msg) - return msg, err -} - -// WriteMsg writes a message to an io.Writer -func WriteMsg(writer io.Writer, msg tmProtoPrivval.Message) (err error) { - protoWriter := protoio.NewDelimitedWriter(writer) - _, err = protoWriter.WriteMsg(&msg) - return err -} - -// UnpackHRS deserializes sign bytes and gets the height, round, and step -func UnpackHRST(signBytes []byte) (HRSTKey, error) { - { - var proposal tmProto.CanonicalProposal - if err := protoio.UnmarshalDelimited(signBytes, &proposal); err == nil { - return HRSTKey{proposal.Height, proposal.Round, stepPropose, proposal.Timestamp.UnixNano()}, nil - } - } - - { - var vote tmProto.CanonicalVote - if err := protoio.UnmarshalDelimited(signBytes, &vote); err == nil { - return HRSTKey{vote.Height, vote.Round, CanonicalVoteToStep(&vote), vote.Timestamp.UnixNano()}, nil - } - } - - return HRSTKey{0, 0, 0, 0}, errors.New("could not UnpackHRS from sign bytes") -} diff --git a/signer/Serialization_test.go b/signer/Serialization_test.go deleted file mode 100644 index b510de85..00000000 --- a/signer/Serialization_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package signer - -import ( - "testing" - - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tm "github.com/tendermint/tendermint/types" -) - -func TestUnpackHRSPrevote(t *testing.T) { - vote := tmproto.Vote{ - Height: 1, - Round: 2, - Type: tmproto.PrevoteType, - } - - signBytes := tm.VoteSignBytes("chain-id", &vote) - - hrs, err := UnpackHRST(signBytes) - require.NoError(t, err) - require.Equal(t, int64(1), hrs.Height) - require.Equal(t, int64(2), hrs.Round) - require.Equal(t, int8(2), hrs.Step) -} - -func TestUnpackHRSPrecommit(t *testing.T) { - vote := tmproto.Vote{ - Height: 3, - Round: 2, - Type: tmproto.PrecommitType, - } - - signBytes := tm.VoteSignBytes("chain-id", &vote) - - hrs, err := UnpackHRST(signBytes) - require.NoError(t, err) - require.Equal(t, int64(3), hrs.Height) - require.Equal(t, int64(2), hrs.Round) - require.Equal(t, int8(3), hrs.Step) -} - -func TestUnpackHRSProposal(t *testing.T) { - proposal := tmproto.Proposal{ - Height: 1, - Round: 2, - Type: tmproto.ProposalType, - } - - signBytes := tm.ProposalSignBytes("chain-id", &proposal) - - hrs, err := UnpackHRST(signBytes) - require.NoError(t, err) - require.Equal(t, int64(1), hrs.Height) - require.Equal(t, int64(2), hrs.Round) - require.Equal(t, int8(1), hrs.Step) -} diff --git a/signer/cond/cond.go b/signer/cond/cond.go new file mode 100644 index 00000000..74b0b3bd --- /dev/null +++ b/signer/cond/cond.go @@ -0,0 +1,63 @@ +package cond + +import ( + "sync" + "sync/atomic" + "time" +) + +// Inspired by https://gist.github.com/zviadm/c234426882bfc8acba88f3503edaaa36#file-cond2-go +// Similar to sync.Cond, but supports timeout based Wait() calls. + +// Cond is a conditional variable implementation that uses channels for notifications. +// It only supports .Broadcast() method, however supports timeout based Wait() calls +// unlike regular sync.Cond. +type Cond struct { + L sync.Locker + p atomic.Pointer[chan struct{}] +} + +func New(l sync.Locker) *Cond { + c := &Cond{ + L: l, + p: atomic.Pointer[chan struct{}]{}, + } + n := make(chan struct{}) + c.p.Store(&n) + return c +} + +// Wait waits for Broadcast calls. Similar to regular sync.Cond, this unlocks the underlying +// locker first, waits on changes and re-locks it before returning. +func (c *Cond) Wait() { + n := c.NotifyChan() + c.L.Unlock() + <-n + c.L.Lock() +} + +// WaitWithTimeout is same as Wait() call, but will only wait up to a given timeout. +func (c *Cond) WaitWithTimeout(t time.Duration) { + tm := time.NewTimer(t) + defer tm.Stop() + n := c.NotifyChan() + c.L.Unlock() + select { + case <-n: + case <-tm.C: + } + c.L.Lock() +} + +// NotifyChan returns a channel that can be used to wait for next Broadcast() call. +func (c *Cond) NotifyChan() <-chan struct{} { + ptr := c.p.Load() + return *ptr +} + +// Broadcast notifies all waiting goroutines that something has changed. +func (c *Cond) Broadcast() { + n := make(chan struct{}) + p := c.p.Swap(&n) + close(*p) +} diff --git a/signer/cond/cond_test.go b/signer/cond/cond_test.go new file mode 100644 index 00000000..fd64ede4 --- /dev/null +++ b/signer/cond/cond_test.go @@ -0,0 +1,62 @@ +package cond_test + +import ( + "runtime" + "sync" + "testing" + + "github.com/strangelove-ventures/horcrux/v3/signer/cond" + "github.com/stretchr/testify/require" +) + +func TestRace(t *testing.T) { + x := 0 + c := cond.New(&sync.Mutex{}) + done := make(chan bool) + go func() { + c.L.Lock() + x = 1 + c.Wait() + require.Equal(t, 2, x) + x = 3 + c.Broadcast() + c.L.Unlock() + done <- true + }() + go func() { + c.L.Lock() + for { + if x == 1 { + x = 2 + c.Broadcast() + break + } + c.L.Unlock() + runtime.Gosched() + c.L.Lock() + } + c.L.Unlock() + done <- true + }() + go func() { + c.L.Lock() + for { + if x == 2 { + c.Wait() + require.Equal(t, 3, x) + break + } + if x == 3 { + break + } + c.L.Unlock() + runtime.Gosched() + c.L.Lock() + } + c.L.Unlock() + done <- true + }() + <-done + <-done + <-done +} diff --git a/signer/config.go b/signer/config.go new file mode 100644 index 00000000..9ba07403 --- /dev/null +++ b/signer/config.go @@ -0,0 +1,379 @@ +package signer + +import ( + "fmt" + "net" + "net/url" + "os" + "path/filepath" + "time" + + "github.com/cometbft/cometbft/crypto" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/strangelove-ventures/horcrux/v3/client" + "gopkg.in/yaml.v2" +) + +type SignMode string + +const ( + SignModeThreshold SignMode = "threshold" + SignModeSingle SignMode = "single" +) + +// Config maps to the on-disk yaml format +type Config struct { + PrivValKeyDir *string `yaml:"keyDir,omitempty"` + SignMode SignMode `yaml:"signMode"` + ThresholdModeConfig *ThresholdModeConfig `yaml:"thresholdMode,omitempty"` + ChainNodes ChainNodes `yaml:"chainNodes"` + DebugAddr string `yaml:"debugAddr"` + GRPCAddr string `yaml:"grpcAddr"` + MaxReadSize int `yaml:"maxReadSize"` +} + +func (c *Config) Nodes() (out []string) { + for _, n := range c.ChainNodes { + out = append(out, n.PrivValAddr) + } + return out +} + +func (c *Config) MustMarshalYaml() []byte { + out, err := yaml.Marshal(c) + if err != nil { + panic(err) + } + return out +} + +func (c *Config) ValidateSingleSignerConfig() error { + return c.ChainNodes.Validate() +} + +func (c *Config) ValidateThresholdModeConfig() error { + if err := c.ValidateSingleSignerConfig(); err != nil { + return err + } + + if c.ThresholdModeConfig == nil { + return fmt.Errorf("cosigner config can't be empty") + // the rest of the checks depend on non-nil c.ThresholdModeConfig + } + + numShards := len(c.ThresholdModeConfig.Cosigners) + + if c.ThresholdModeConfig.Threshold <= numShards/2 { + return fmt.Errorf("threshold (%d) must be greater than number of shards (%d) / 2", + c.ThresholdModeConfig.Threshold, numShards) + } + + if numShards < c.ThresholdModeConfig.Threshold { + return fmt.Errorf("number of shards (%d) must be greater or equal to threshold (%d)", + numShards, c.ThresholdModeConfig.Threshold) + } + + if _, err := time.ParseDuration(c.ThresholdModeConfig.RaftTimeout); err != nil { + return fmt.Errorf("invalid raftTimeout: %w", err) + } + + if _, err := time.ParseDuration(c.ThresholdModeConfig.GRPCTimeout); err != nil { + return fmt.Errorf("invalid grpcTimeout: %w", err) + } + + if err := c.ThresholdModeConfig.Cosigners.Validate(); err != nil { + return err + } + + return c.ThresholdModeConfig.Cosigners.Validate() +} + +type RuntimeConfig struct { + HomeDir string + ConfigFile string + StateDir string + PidFile string + Config Config +} + +func (c RuntimeConfig) CosignerSecurityECIES() (*CosignerSecurityECIES, error) { + keyFile, err := c.KeyFileExistsCosignerECIES() + if err != nil { + return nil, err + } + + key, err := LoadCosignerECIESKey(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading cosigner key (%s): %w", keyFile, err) + } + + return NewCosignerSecurityECIES(key), nil +} + +func (c RuntimeConfig) CosignerSecurityRSA() (*CosignerSecurityRSA, error) { + keyFile, err := c.KeyFileExistsCosignerRSA() + if err != nil { + return nil, err + } + + key, err := LoadCosignerRSAKey(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading cosigner key (%s): %w", keyFile, err) + } + + return NewCosignerSecurityRSA(key), nil +} + +func (c RuntimeConfig) cachedKeyDirectory() string { + if c.Config.PrivValKeyDir != nil { + return *c.Config.PrivValKeyDir + } + return "" +} + +func (c RuntimeConfig) KeyFilePathSingleSigner(chainID string) string { + keyDir := c.HomeDir + if kd := c.cachedKeyDirectory(); kd != "" { + keyDir = kd + } + return filepath.Join(keyDir, fmt.Sprintf("%s_priv_validator_key.json", chainID)) +} + +func (c RuntimeConfig) KeyFilePathCosigner(chainID string) string { + keyDir := c.HomeDir + if kd := c.cachedKeyDirectory(); kd != "" { + keyDir = kd + } + return filepath.Join(keyDir, fmt.Sprintf("%s_shard.json", chainID)) +} + +func (c RuntimeConfig) KeyFilePathCosignerRSA() string { + keyDir := c.HomeDir + if kd := c.cachedKeyDirectory(); kd != "" { + keyDir = kd + } + return filepath.Join(keyDir, "rsa_keys.json") +} + +func (c RuntimeConfig) KeyFilePathCosignerECIES() string { + keyDir := c.HomeDir + if kd := c.cachedKeyDirectory(); kd != "" { + keyDir = kd + } + return filepath.Join(keyDir, "ecies_keys.json") +} + +func (c RuntimeConfig) PrivValStateFile(chainID string) string { + return filepath.Join(c.StateDir, fmt.Sprintf("%s_priv_validator_state.json", chainID)) +} + +func (c RuntimeConfig) CosignerStateFile(chainID string) string { + return filepath.Join(c.StateDir, fmt.Sprintf("%s_share_sign_state.json", chainID)) +} + +func (c RuntimeConfig) WriteConfigFile() error { + return os.WriteFile(c.ConfigFile, c.Config.MustMarshalYaml(), 0600) +} + +func fileExists(file string) error { + stat, err := os.Stat(file) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("file doesn't exist at path (%s): %w", file, err) + } + return fmt.Errorf("unexpected error checking file existence (%s): %w", file, err) + } + if stat.IsDir() { + return fmt.Errorf("path is not a file (%s)", file) + } + + return nil +} + +func (c RuntimeConfig) KeyFileExistsSingleSigner(chainID string) (string, error) { + keyFile := c.KeyFilePathSingleSigner(chainID) + return keyFile, fileExists(keyFile) +} + +func (c RuntimeConfig) KeyFileExistsCosigner(chainID string) (string, error) { + keyFile := c.KeyFilePathCosigner(chainID) + return keyFile, fileExists(keyFile) +} + +func (c RuntimeConfig) KeyFileExistsCosignerRSA() (string, error) { + keyFile := c.KeyFilePathCosignerRSA() + return keyFile, fileExists(keyFile) +} + +func (c RuntimeConfig) KeyFileExistsCosignerECIES() (string, error) { + keyFile := c.KeyFilePathCosignerECIES() + return keyFile, fileExists(keyFile) +} + +// ThresholdModeConfig is the on disk config format for threshold sign mode. +type ThresholdModeConfig struct { + Threshold int `yaml:"threshold"` + Cosigners CosignersConfig `yaml:"cosigners"` + GRPCTimeout string `yaml:"grpcTimeout"` + RaftTimeout string `yaml:"raftTimeout"` +} + +func (cfg *ThresholdModeConfig) LeaderElectMultiAddress() (string, error) { + addresses := make([]string, len(cfg.Cosigners)) + for i, c := range cfg.Cosigners { + addresses[i] = c.P2PAddr + } + return client.MultiAddress(addresses) +} + +// CosignerConfig is the on disk format representing a cosigner for threshold sign mode. +type CosignerConfig struct { + ShardID int `yaml:"shardID"` + P2PAddr string `yaml:"p2pAddr"` +} + +type CosignersConfig []CosignerConfig + +func (cosigners CosignersConfig) Validate() error { + // Check IDs to make sure none are duplicated + if dupl := duplicateCosigners(cosigners); len(dupl) != 0 { + return fmt.Errorf("found duplicate cosigner shard ID(s) in args: %v", dupl) + } + + shards := len(cosigners) + + // Make sure that the cosigner IDs match the number of cosigners. + for _, cosigner := range cosigners { + if cosigner.ShardID < 1 || cosigner.ShardID > shards { + return fmt.Errorf("cosigner shard ID %d in args is out of range, must be between 1 and %d, inclusive", + cosigner.ShardID, shards) + } + + url, err := url.Parse(cosigner.P2PAddr) + if err != nil { + return fmt.Errorf("failed to parse cosigner (shard ID: %d) p2p address: %w", cosigner.ShardID, err) + } + + host, _, err := net.SplitHostPort(url.Host) + if err != nil { + return fmt.Errorf("failed to parse cosigner (shard ID: %d) host port: %w", cosigner.ShardID, err) + } + + if host == "0.0.0.0" { + return fmt.Errorf("host cannot be 0.0.0.0, must be reachable from other cosigners") + } + } + + // Check that exactly {num-shards} cosigners are in the list + if len(cosigners) != shards { + return fmt.Errorf("incorrect number of cosigners. expected (%d shards = %d cosigners)", + shards, shards) + } + + return nil +} + +func duplicateCosigners(cosigners []CosignerConfig) (duplicates map[int][]string) { + idAddrs := make(map[int][]string) + for _, cosigner := range cosigners { + // Collect all addresses assigned to each cosigner. + idAddrs[cosigner.ShardID] = append(idAddrs[cosigner.ShardID], cosigner.P2PAddr) + } + + for shardID, cosigners := range idAddrs { + if len(cosigners) == 1 { + // One address per ID is correct. + delete(idAddrs, shardID) + } + } + + if len(idAddrs) == 0 { + // No duplicates, return nil for simple check by caller. + return nil + } + + // Non-nil result: there were duplicates. + return idAddrs +} + +func CosignersFromFlag(cosigners []string) (out []CosignerConfig, err error) { + var errs []error + for i, c := range cosigners { + out = append(out, CosignerConfig{ShardID: i + 1, P2PAddr: c}) + } + if len(errs) > 0 { + return nil, nil + } + return out, nil +} + +type ChainNode struct { + PrivValAddr string `json:"privValAddr" yaml:"privValAddr"` +} + +func (cn ChainNode) Validate() error { + _, err := url.Parse(cn.PrivValAddr) + return err +} + +type ChainNodes []ChainNode + +func (cns ChainNodes) Validate() error { + if cns == nil { + return nil + } + for _, cn := range cns { + if err := cn.Validate(); err != nil { + return err + } + } + return nil +} + +func ChainNodesFromFlag(nodes []string) (ChainNodes, error) { + out := make(ChainNodes, len(nodes)) + for i, n := range nodes { + cn := ChainNode{PrivValAddr: n} + out[i] = cn + } + if err := out.Validate(); err != nil { + return nil, err + } + return out, nil +} + +func PubKey(bech32BasePrefix string, pubKey crypto.PubKey) (string, error) { + if bech32BasePrefix != "" { + pubkey, err := cryptocodec.FromCmtPubKeyInterface(pubKey) + if err != nil { + return "", err + } + consPubPrefix := bech32BasePrefix + "valconspub" + pubKeyBech32, err := bech32.ConvertAndEncode(consPubPrefix, legacy.Cdc.Amino.MustMarshalBinaryBare(pubkey)) + if err != nil { + return "", err + } + return pubKeyBech32, nil + } + + registry := types.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(registry) + var pk *cryptotypes.PubKey + registry.RegisterInterface("cosmos.crypto.PubKey", pk) + registry.RegisterImplementations(pk, &ed25519.PubKey{}) + sdkPK, err := cryptocodec.FromCmtPubKeyInterface(pubKey) + if err != nil { + return "", err + } + pubKeyJSON, err := marshaler.MarshalInterfaceJSON(sdkPK) + if err != nil { + return "", err + } + return string(pubKeyJSON), nil +} diff --git a/signer/config_test.go b/signer/config_test.go new file mode 100644 index 00000000..19d48fdd --- /dev/null +++ b/signer/config_test.go @@ -0,0 +1,577 @@ +package signer_test + +import ( + "fmt" + "io/fs" + "net/url" + "os" + "path/filepath" + "testing" + + "github.com/strangelove-ventures/horcrux/v3/signer" + "github.com/stretchr/testify/require" +) + +const testChainID = "test" + +func TestNodes(t *testing.T) { + c := signer.Config{ + ChainNodes: signer.ChainNodes{ + { + PrivValAddr: "tcp://0.0.0.0:1234", + }, + { + PrivValAddr: "tcp://0.0.0.0:5678", + }, + }, + } + + require.Equal(t, []string{"tcp://0.0.0.0:1234", "tcp://0.0.0.0:5678"}, c.Nodes()) +} + +func TestValidateSingleSignerConfig(t *testing.T) { + type testCase struct { + name string + config signer.Config + expectErr error + } + + testCases := []testCase{ + { + name: "valid config", + config: signer.Config{ + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + }, + }, + expectErr: nil, + }, + { + name: "invalid node address", + config: signer.Config{ + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "abc://\\invalid_addr", + }, + }, + }, + expectErr: &url.Error{Op: "parse", URL: "abc://\\invalid_addr", Err: url.InvalidHostError("\\")}, + }, + } + + for _, tc := range testCases { + err := tc.config.ValidateSingleSignerConfig() + if tc.expectErr == nil { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + require.EqualError(t, err, tc.expectErr.Error(), tc.name) + } + } +} + +func TestValidateThresholdModeConfig(t *testing.T) { + type testCase struct { + name string + config signer.Config + expectErr error + } + + testCases := []testCase{ + { + name: "valid config", + config: signer.Config{ + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 2, + RaftTimeout: "1000ms", + GRPCTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + }, + expectErr: nil, + }, + { + name: "no cosigner config", + config: signer.Config{ + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + }, + expectErr: fmt.Errorf("cosigner config can't be empty"), + }, + { + name: "invalid p2p listen", + config: signer.Config{ + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 2, + RaftTimeout: "1000ms", + GRPCTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: ":2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + }, + expectErr: fmt.Errorf("failed to parse cosigner (shard ID: 1) p2p address: %w", &url.Error{ + Op: "parse", + URL: ":2222", + Err: fmt.Errorf("missing protocol scheme"), + }), + }, + { + name: "not enough cosigners", + config: signer.Config{ + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 3, + RaftTimeout: "1000ms", + GRPCTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + }, + expectErr: fmt.Errorf("number of shards (2) must be greater or equal to threshold (3)"), + }, + { + name: "invalid raft timeout", + config: signer.Config{ + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 2, + GRPCTimeout: "1000ms", + RaftTimeout: "1000", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + }, + expectErr: fmt.Errorf("invalid raftTimeout: %w", fmt.Errorf("time: missing unit in duration \"1000\"")), + }, + { + name: "invalid grpc timeout", + config: signer.Config{ + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 2, + GRPCTimeout: "1000", + RaftTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + }, + expectErr: fmt.Errorf("invalid grpcTimeout: %w", fmt.Errorf("time: missing unit in duration \"1000\"")), + }, + { + name: "invalid node address", + config: signer.Config{ + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 2, + RaftTimeout: "1000ms", + GRPCTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "abc://\\invalid_addr", + }, + }, + }, + expectErr: &url.Error{Op: "parse", URL: "abc://\\invalid_addr", Err: url.InvalidHostError("\\")}, + }, + } + + for _, tc := range testCases { + err := tc.config.ValidateThresholdModeConfig() + if tc.expectErr == nil { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + require.EqualError(t, err, tc.expectErr.Error(), tc.name) + } + } +} + +func TestRuntimeConfigKeyFilePath(t *testing.T) { + dir := t.TempDir() + c := signer.RuntimeConfig{ + HomeDir: dir, + } + + require.Equal(t, filepath.Join(dir, fmt.Sprintf("%s_shard.json", testChainID)), c.KeyFilePathCosigner(testChainID)) + require.Equal( + t, + filepath.Join(dir, fmt.Sprintf("%s_priv_validator_key.json", testChainID)), + c.KeyFilePathSingleSigner(testChainID), + ) +} + +func TestRuntimeConfigPrivValStateFile(t *testing.T) { + dir := t.TempDir() + c := signer.RuntimeConfig{ + StateDir: dir, + } + + require.Equal(t, filepath.Join(dir, "chain-1_priv_validator_state.json"), c.PrivValStateFile("chain-1")) +} + +func TestRuntimeConfigWriteConfigFile(t *testing.T) { + dir := t.TempDir() + configFile := filepath.Join(dir, "config.yaml") + c := signer.RuntimeConfig{ + ConfigFile: configFile, + Config: signer.Config{ + SignMode: signer.SignModeThreshold, + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: 2, + RaftTimeout: "1000ms", + GRPCTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + }, + ChainNodes: []signer.ChainNode{ + { + PrivValAddr: "tcp://127.0.0.1:1234", + }, + { + PrivValAddr: "tcp://127.0.0.1:2345", + }, + { + PrivValAddr: "tcp://127.0.0.1:3456", + }, + }, + MaxReadSize: 1024 * 1024, + }, + } + + require.NoError(t, c.WriteConfigFile()) + configYamlBz, err := os.ReadFile(configFile) + require.NoError(t, err) + require.Equal(t, `signMode: threshold +thresholdMode: + threshold: 2 + cosigners: + - shardID: 1 + p2pAddr: tcp://127.0.0.1:2222 + - shardID: 2 + p2pAddr: tcp://127.0.0.1:2223 + - shardID: 3 + p2pAddr: tcp://127.0.0.1:2224 + grpcTimeout: 1000ms + raftTimeout: 1000ms +chainNodes: +- privValAddr: tcp://127.0.0.1:1234 +- privValAddr: tcp://127.0.0.1:2345 +- privValAddr: tcp://127.0.0.1:3456 +debugAddr: "" +grpcAddr: "" +maxReadSize: 1048576 +`, string(configYamlBz)) +} + +func TestRuntimeConfigKeyFileExists(t *testing.T) { + dir := t.TempDir() + c := signer.RuntimeConfig{ + HomeDir: dir, + } + + // Test cosigner + keyFile, err := c.KeyFileExistsCosigner(testChainID) + require.Error(t, err) + + require.Equal(t, fmt.Errorf( + "file doesn't exist at path (%s): %w", + keyFile, + &fs.PathError{ + Op: "stat", + Path: keyFile, + Err: fmt.Errorf("no such file or directory"), + }, + ).Error(), err.Error()) + + err = os.WriteFile(keyFile, []byte{}, 0600) + require.NoError(t, err) + + _, err = c.KeyFileExistsCosigner(testChainID) + require.NoError(t, err) + + // Test single signer + keyFile, err = c.KeyFileExistsSingleSigner(testChainID) + require.Error(t, err) + + require.Equal(t, fmt.Errorf( + "file doesn't exist at path (%s): %w", + keyFile, + &fs.PathError{ + Op: "stat", + Path: keyFile, + Err: fmt.Errorf("no such file or directory"), + }, + ).Error(), err.Error()) + + err = os.WriteFile(keyFile, []byte{}, 0600) + require.NoError(t, err) + + _, err = c.KeyFileExistsSingleSigner(testChainID) + require.NoError(t, err) +} + +func TestThresholdModeConfigLeaderElectMultiAddress(t *testing.T) { + c := &signer.ThresholdModeConfig{ + Threshold: 2, + RaftTimeout: "1000ms", + GRPCTimeout: "1000ms", + Cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + } + + multiAddr, err := c.LeaderElectMultiAddress() + require.NoError(t, err) + require.Equal(t, "multi:///127.0.0.1:2222,127.0.0.1:2223,127.0.0.1:2224", multiAddr) +} + +func TestCosignerRSAPubKeysConfigValidate(t *testing.T) { + type testCase struct { + name string + cosigners signer.CosignersConfig + expectErr error + } + testCases := []testCase{ + { + name: "valid config", + cosigners: signer.CosignersConfig{ + { + ShardID: 1, + P2PAddr: "tcp://127.0.0.1:2222", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + expectErr: nil, + }, + { + name: "too many cosigners", + cosigners: signer.CosignersConfig{ + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 3, + P2PAddr: "tcp://127.0.0.1:2224", + }, + }, + expectErr: fmt.Errorf("cosigner shard ID 3 in args is out of range, must be between 1 and 2, inclusive"), + }, + { + name: "duplicate cosigner", + cosigners: signer.CosignersConfig{ + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + { + ShardID: 2, + P2PAddr: "tcp://127.0.0.1:2223", + }, + }, + expectErr: fmt.Errorf( + "found duplicate cosigner shard ID(s) in args: map[2:[tcp://127.0.0.1:2223 tcp://127.0.0.1:2223]]", + ), + }, + } + + for _, tc := range testCases { + err := tc.cosigners.Validate() + if tc.expectErr == nil { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + require.EqualError(t, err, tc.expectErr.Error(), tc.name) + } + } +} + +func TestCosignersFromFlag(t *testing.T) { + type testCase struct { + name string + cosigners []string + expectErr error + } + + testCases := []testCase{ + { + name: "valid cosigners flag", + cosigners: []string{"tcp://127.0.0.1:2222", "tcp://127.0.0.1:2223"}, + expectErr: nil, + }, + } + + for _, tc := range testCases { + _, err := signer.CosignersFromFlag(tc.cosigners) + if tc.expectErr == nil { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + require.EqualError(t, err, tc.expectErr.Error(), tc.name) + } + } +} diff --git a/signer/cosigner.go b/signer/cosigner.go new file mode 100644 index 00000000..216e7e59 --- /dev/null +++ b/signer/cosigner.go @@ -0,0 +1,206 @@ +package signer + +import ( + "context" + "errors" + "fmt" + "time" + + cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/libs/protoio" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/google/uuid" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" +) + +// Cosigner interface is a set of methods for an m-of-n threshold signature. +// This interface abstracts the underlying key storage and management +type Cosigner interface { + // Get the ID of the cosigner + // The ID is the shamir index: 1, 2, etc... + GetID() int + + // Get the P2P URL (GRPC and Raft) + GetAddress() string + + // Get the combined public key + GetPubKey(chainID string) (cometcrypto.PubKey, error) + + VerifySignature(chainID string, payload, signature []byte) bool + + // Get nonces for all cosigner shards + GetNonces(ctx context.Context, uuids []uuid.UUID) (CosignerUUIDNoncesMultiple, error) + + // Sign the requested bytes + SetNoncesAndSign(ctx context.Context, req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) +} + +type Cosigners []Cosigner + +func (cosigners Cosigners) GetByID(id int) Cosigner { + for _, cosigner := range cosigners { + if cosigner.GetID() == id { + return cosigner + } + } + return nil +} + +// CosignerSignRequest is sent to a co-signer to obtain their signature for the SignBytes +// The SignBytes should be a serialized block +type CosignerSignRequest struct { + ChainID string + SignBytes []byte + UUID uuid.UUID + VoteExtensionSignBytes []byte + VoteExtUUID uuid.UUID +} + +type CosignerSignResponse struct { + Timestamp time.Time + NoncePublic []byte + Signature []byte + VoteExtensionNoncePublic []byte + VoteExtensionSignature []byte +} + +type CosignerNonce struct { + SourceID int + DestinationID int + PubKey []byte + Share []byte + Signature []byte +} + +func (secretPart *CosignerNonce) toProto() *proto.Nonce { + return &proto.Nonce{ + SourceID: int32(secretPart.SourceID), + DestinationID: int32(secretPart.DestinationID), + PubKey: secretPart.PubKey, + Share: secretPart.Share, + Signature: secretPart.Signature, + } +} + +type CosignerNonces []CosignerNonce + +func (secretParts CosignerNonces) toProto() (out []*proto.Nonce) { + for _, secretPart := range secretParts { + out = append(out, secretPart.toProto()) + } + return +} + +func CosignerNonceFromProto(secretPart *proto.Nonce) CosignerNonce { + return CosignerNonce{ + SourceID: int(secretPart.SourceID), + DestinationID: int(secretPart.DestinationID), + PubKey: secretPart.PubKey, + Share: secretPart.Share, + Signature: secretPart.Signature, + } +} + +func CosignerNoncesFromProto(secretParts []*proto.Nonce) []CosignerNonce { + out := make([]CosignerNonce, len(secretParts)) + for i, secretPart := range secretParts { + out[i] = CosignerNonceFromProto(secretPart) + } + return out +} + +type CosignerSignBlockRequest struct { + ChainID string + Block *Block +} + +type CosignerSignBlockResponse struct { + Signature []byte + VoteExtensionSignature []byte +} +type CosignerUUIDNonces struct { + UUID uuid.UUID + Nonces CosignerNonces +} + +func (n *CosignerUUIDNonces) For(id int) *CosignerUUIDNonces { + res := &CosignerUUIDNonces{UUID: n.UUID} + for _, nonce := range n.Nonces { + if nonce.DestinationID == id { + res.Nonces = append(res.Nonces, nonce) + } + } + return res +} + +type CosignerUUIDNoncesMultiple []*CosignerUUIDNonces + +func (n CosignerUUIDNoncesMultiple) toProto() []*proto.UUIDNonce { + out := make([]*proto.UUIDNonce, len(n)) + for i, nonces := range n { + out[i] = &proto.UUIDNonce{ + Uuid: nonces.UUID[:], + Nonces: nonces.Nonces.toProto(), + } + } + return out +} + +type CosignerSetNoncesAndSignRequest struct { + ChainID string + HRST HRSTKey + + Nonces *CosignerUUIDNonces + SignBytes []byte + + VoteExtensionNonces *CosignerUUIDNonces + VoteExtensionSignBytes []byte +} + +func verifySignPayload(chainID string, signBytes, voteExtensionSignBytes []byte) (HRSTKey, bool, error) { + var vote cometproto.CanonicalVote + voteErr := protoio.UnmarshalDelimited(signBytes, &vote) + if voteErr == nil && (vote.Type == cometproto.PrevoteType || vote.Type == cometproto.PrecommitType) { + hrstKey := HRSTKey{ + Height: vote.Height, + Round: vote.Round, + Step: CanonicalVoteToStep(&vote), + Timestamp: vote.Timestamp.UnixNano(), + } + + if hrstKey.Step == stepPrecommit && len(voteExtensionSignBytes) > 0 && vote.BlockID != nil { + var voteExt cometproto.CanonicalVoteExtension + if err := protoio.UnmarshalDelimited(voteExtensionSignBytes, &voteExt); err != nil { + return hrstKey, false, fmt.Errorf("failed to unmarshal vote extension: %w", err) + } + if voteExt.ChainId != chainID { + return hrstKey, false, fmt.Errorf("vote extension chain ID %s does not match chain ID %s", voteExt.ChainId, chainID) + } + if voteExt.Height != hrstKey.Height { + return hrstKey, false, + fmt.Errorf("vote extension height %d does not match block height %d", voteExt.Height, hrstKey.Height) + } + if voteExt.Round != hrstKey.Round { + return hrstKey, false, + fmt.Errorf("vote extension round %d does not match block round %d", voteExt.Round, hrstKey.Round) + } + return hrstKey, true, nil + } + + return hrstKey, false, nil + } + + var proposal cometproto.CanonicalProposal + proposalErr := protoio.UnmarshalDelimited(signBytes, &proposal) + if proposalErr == nil { + return HRSTKey{ + Height: proposal.Height, + Round: proposal.Round, + Step: stepPropose, + Timestamp: proposal.Timestamp.UnixNano(), + }, false, nil + } + + return HRSTKey{}, false, + fmt.Errorf("failed to unmarshal sign bytes into vote or proposal: %w", errors.Join(voteErr, proposalErr)) +} diff --git a/signer/cosigner_grpc_server.go b/signer/cosigner_grpc_server.go index 8cd5c112..1522ae15 100644 --- a/signer/cosigner_grpc_server.go +++ b/signer/cosigner_grpc_server.go @@ -3,86 +3,152 @@ package signer import ( "context" "fmt" - "time" + "github.com/google/uuid" "github.com/hashicorp/raft" - proto "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" ) +var _ proto.CosignerServer = &CosignerGRPCServer{} + type CosignerGRPCServer struct { cosigner *LocalCosigner thresholdValidator *ThresholdValidator raftStore *RaftStore - proto.UnimplementedCosignerGRPCServer + proto.UnimplementedCosignerServer } -func (rpc *CosignerGRPCServer) SignBlock( - ctx context.Context, req *proto.CosignerGRPCSignBlockRequest) (*proto.CosignerGRPCSignBlockResponse, error) { - block := &Block{ - Height: req.Block.GetHeight(), - Round: req.Block.GetRound(), - Step: int8(req.Block.GetStep()), - SignBytes: req.Block.GetSignBytes(), - Timestamp: time.Unix(0, req.Block.GetTimestamp()), +func NewCosignerGRPCServer( + cosigner *LocalCosigner, + thresholdValidator *ThresholdValidator, + raftStore *RaftStore, +) *CosignerGRPCServer { + return &CosignerGRPCServer{ + cosigner: cosigner, + thresholdValidator: thresholdValidator, + raftStore: raftStore, } - res, _, err := rpc.thresholdValidator.SignBlock(req.ChainID, block) +} + +func (rpc *CosignerGRPCServer) SignBlock( + ctx context.Context, + req *proto.SignBlockRequest, +) (*proto.SignBlockResponse, error) { + sig, voteExtSig, _, err := rpc.thresholdValidator.Sign(ctx, req.ChainID, BlockFromProto(req.Block)) if err != nil { return nil, err } - return &proto.CosignerGRPCSignBlockResponse{ - Signature: res, + return &proto.SignBlockResponse{ + Signature: sig, + VoteExtSignature: voteExtSig, }, nil } -func (rpc *CosignerGRPCServer) SetEphemeralSecretPartsAndSign( +func (rpc *CosignerGRPCServer) SetNoncesAndSign( ctx context.Context, - req *proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest, -) (*proto.CosignerGRPCSetEphemeralSecretPartsAndSignResponse, error) { - res, err := rpc.cosigner.SetEphemeralSecretPartsAndSign(CosignerSetEphemeralSecretPartsAndSignRequest{ - EncryptedSecrets: CosignerEphemeralSecretPartsFromProto(req.GetEncryptedSecrets()), - HRST: HRSTKeyFromProto(req.GetHrst()), - SignBytes: req.GetSignBytes(), - }) + req *proto.SetNoncesAndSignRequest, +) (*proto.SetNoncesAndSignResponse, error) { + cosignerReq := CosignerSetNoncesAndSignRequest{ + ChainID: req.ChainID, + + HRST: HRSTKeyFromProto(req.Hrst), + + Nonces: &CosignerUUIDNonces{ + UUID: uuid.UUID(req.Uuid), + Nonces: CosignerNoncesFromProto(req.Nonces), + }, + SignBytes: req.SignBytes, + } + + if len(req.VoteExtSignBytes) > 0 && len(req.VoteExtUuid) == 16 { + cosignerReq.VoteExtensionNonces = &CosignerUUIDNonces{ + UUID: uuid.UUID(req.VoteExtUuid), + Nonces: CosignerNoncesFromProto(req.VoteExtNonces), + } + cosignerReq.VoteExtensionSignBytes = req.VoteExtSignBytes + } + + res, err := rpc.cosigner.SetNoncesAndSign(ctx, cosignerReq) if err != nil { + rpc.raftStore.logger.Error( + "Failed to sign with shard", + "chain_id", req.ChainID, + "height", req.Hrst.Height, + "round", req.Hrst.Round, + "step", req.Hrst.Step, + "error", err, + ) return nil, err } - return &proto.CosignerGRPCSetEphemeralSecretPartsAndSignResponse{ - EphemeralPublic: res.EphemeralPublic, - Timestamp: res.Timestamp.UnixNano(), - Signature: res.Signature, + rpc.raftStore.logger.Info( + "Signed with shard", + "chain_id", req.ChainID, + "height", req.Hrst.Height, + "round", req.Hrst.Round, + "step", req.Hrst.Step, + ) + return &proto.SetNoncesAndSignResponse{ + NoncePublic: res.NoncePublic, + Timestamp: res.Timestamp.UnixNano(), + Signature: res.Signature, + VoteExtNoncePublic: res.VoteExtensionNoncePublic, + VoteExtSignature: res.VoteExtensionSignature, }, nil } -func (rpc *CosignerGRPCServer) GetEphemeralSecretParts( +func (rpc *CosignerGRPCServer) GetNonces( ctx context.Context, - req *proto.CosignerGRPCGetEphemeralSecretPartsRequest, -) (*proto.CosignerGRPCGetEphemeralSecretPartsResponse, error) { - res, err := rpc.cosigner.GetEphemeralSecretParts(HRSTKeyFromProto(req.GetHrst())) + req *proto.GetNoncesRequest, +) (*proto.GetNoncesResponse, error) { + uuids := make([]uuid.UUID, len(req.Uuids)) + for i, uuidBytes := range req.Uuids { + uuids[i] = uuid.UUID(uuidBytes) + } + res, err := rpc.cosigner.GetNonces( + ctx, + uuids, + ) if err != nil { return nil, err } - return &proto.CosignerGRPCGetEphemeralSecretPartsResponse{ - EncryptedSecrets: CosignerEphemeralSecretParts(res.EncryptedSecrets).toProto(), + + return &proto.GetNoncesResponse{ + Nonces: res.toProto(), }, nil } func (rpc *CosignerGRPCServer) TransferLeadership( - ctx context.Context, - req *proto.CosignerGRPCTransferLeadershipRequest, -) (*proto.CosignerGRPCTransferLeadershipResponse, error) { + _ context.Context, + req *proto.TransferLeadershipRequest, +) (*proto.TransferLeadershipResponse, error) { + if rpc.raftStore.raft.State() != raft.Leader { + return &proto.TransferLeadershipResponse{}, nil + } leaderID := req.GetLeaderID() if leaderID != "" { - for _, peer := range rpc.raftStore.Peers { - thisPeerID := fmt.Sprint(peer.GetID()) - if thisPeerID == leaderID { - peerRaftAddress := p2pURLToRaftAddress(peer.GetAddress()) - fmt.Printf("Transferring leadership to ID: %s - Address: %s\n", thisPeerID, peerRaftAddress) - rpc.raftStore.raft.LeadershipTransferToServer(raft.ServerID(thisPeerID), raft.ServerAddress(peerRaftAddress)) - return &proto.CosignerGRPCTransferLeadershipResponse{LeaderID: thisPeerID, LeaderAddress: peerRaftAddress}, nil + for _, c := range rpc.raftStore.Cosigners { + shardID := fmt.Sprint(c.GetID()) + if shardID == leaderID { + raftAddress := p2pURLToRaftAddress(c.GetAddress()) + fmt.Printf("Transferring leadership to ID: %s - Address: %s\n", shardID, raftAddress) + rpc.raftStore.raft.LeadershipTransferToServer(raft.ServerID(shardID), raft.ServerAddress(raftAddress)) + return &proto.TransferLeadershipResponse{LeaderID: shardID, LeaderAddress: raftAddress}, nil } } } fmt.Printf("Transferring leadership to next candidate\n") rpc.raftStore.raft.LeadershipTransfer() - return &proto.CosignerGRPCTransferLeadershipResponse{}, nil + return &proto.TransferLeadershipResponse{}, nil +} + +func (rpc *CosignerGRPCServer) GetLeader( + context.Context, + *proto.GetLeaderRequest, +) (*proto.GetLeaderResponse, error) { + leader := rpc.raftStore.GetLeader() + return &proto.GetLeaderResponse{Leader: int32(leader)}, nil +} + +func (rpc *CosignerGRPCServer) Ping(context.Context, *proto.PingRequest) (*proto.PingResponse, error) { + return &proto.PingResponse{}, nil } diff --git a/signer/cosigner_health.go b/signer/cosigner_health.go new file mode 100644 index 00000000..94697ce1 --- /dev/null +++ b/signer/cosigner_health.go @@ -0,0 +1,109 @@ +package signer + +import ( + "context" + "sort" + "sync" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" +) + +const ( + pingInterval = 1 * time.Second +) + +type CosignerHealth struct { + logger cometlog.Logger + cosigners []Cosigner + rtt map[int]int64 + mu sync.RWMutex + + leader Leader +} + +func NewCosignerHealth(logger cometlog.Logger, cosigners []Cosigner, leader Leader) *CosignerHealth { + return &CosignerHealth{ + logger: logger, + cosigners: cosigners, + rtt: make(map[int]int64), + leader: leader, + } +} + +func (ch *CosignerHealth) Reconcile(ctx context.Context) { + if !ch.leader.IsLeader() { + return + } + var wg sync.WaitGroup + wg.Add(len(ch.cosigners)) + for _, cosigner := range ch.cosigners { + if rc, ok := cosigner.(*RemoteCosigner); ok { + go ch.updateRTT(ctx, rc, &wg) + } + } + wg.Wait() +} + +func (ch *CosignerHealth) Start(ctx context.Context) { + ticker := time.NewTicker(pingInterval) + for { + ch.Reconcile(ctx) + select { + case <-ctx.Done(): + return + case <-ticker.C: + // continue + } + } +} + +func (ch *CosignerHealth) MarkUnhealthy(cosigner Cosigner) { + ch.mu.Lock() + defer ch.mu.Unlock() + ch.rtt[cosigner.GetID()] = -1 +} + +func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigner, wg *sync.WaitGroup) { + defer wg.Done() + + rtt := int64(-1) + defer func() { + ch.mu.Lock() + defer ch.mu.Unlock() + ch.rtt[cosigner.GetID()] = rtt + }() + start := time.Now() + ctx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + + _, err := cosigner.client.Ping(ctx, &proto.PingRequest{}) + if err != nil { + ch.logger.Error("Failed to ping", "cosigner", cosigner.GetID(), "error", err) + return + } + rtt = time.Since(start).Nanoseconds() +} + +func (ch *CosignerHealth) GetFastest() []Cosigner { + ch.mu.RLock() + defer ch.mu.RUnlock() + + fastest := make([]Cosigner, len(ch.cosigners)) + copy(fastest, ch.cosigners) + + sort.Slice(fastest, func(i, j int) bool { + rtt1, ok1 := ch.rtt[fastest[i].GetID()] + rtt2, ok2 := ch.rtt[fastest[j].GetID()] + if rtt1 == -1 || !ok1 { + return false + } + if rtt2 == -1 || !ok2 { + return true + } + return rtt1 < rtt2 + }) + + return fastest +} diff --git a/signer/cosigner_health_test.go b/signer/cosigner_health_test.go new file mode 100644 index 00000000..4f7398c2 --- /dev/null +++ b/signer/cosigner_health_test.go @@ -0,0 +1,36 @@ +package signer + +import ( + "os" + "testing" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/stretchr/testify/require" +) + +func TestCosignerHealth(t *testing.T) { + ch := NewCosignerHealth( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + []Cosigner{ + &RemoteCosigner{id: 2}, + &RemoteCosigner{id: 3}, + &RemoteCosigner{id: 4}, + &RemoteCosigner{id: 5}, + }, + &MockLeader{id: 1}, + ) + + ch.rtt = map[int]int64{ + 2: 200, + 3: -1, + 4: 100, + 5: 300, + } + + fastest := ch.GetFastest() + + require.Len(t, fastest, 4) + + require.Equal(t, 4, fastest[0].GetID()) + require.Equal(t, 2, fastest[1].GetID()) +} diff --git a/signer/cosigner_key.go b/signer/cosigner_key.go index 8f91c63d..8aaa396d 100644 --- a/signer/cosigner_key.go +++ b/signer/cosigner_key.go @@ -1,39 +1,27 @@ package signer import ( - "crypto/rsa" - "crypto/x509" "encoding/json" "os" + cometcrypto "github.com/cometbft/cometbft/crypto" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" + cometprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" amino "github.com/tendermint/go-amino" - tmCrypto "github.com/tendermint/tendermint/crypto" - tmEd25519 "github.com/tendermint/tendermint/crypto/ed25519" - tmCryptoEncoding "github.com/tendermint/tendermint/crypto/encoding" - tmProtoCrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) -// CosignerKey is a single key for an m-of-n threshold signer. -type CosignerKey struct { - PubKey tmCrypto.PubKey `json:"pub_key"` - ShareKey []byte `json:"secret_share"` - RSAKey rsa.PrivateKey `json:"rsa_key"` - ID int `json:"id"` - CosignerKeys []*rsa.PublicKey `json:"rsa_pubs"` +// CosignerEd25519Key is a single Ed255219 key shard for an m-of-n threshold signer. +type CosignerEd25519Key struct { + PubKey cometcrypto.PubKey `json:"pubKey"` + PrivateShard []byte `json:"privateShard"` + ID int `json:"id"` } -func (cosignerKey *CosignerKey) MarshalJSON() ([]byte, error) { - type Alias CosignerKey +func (key *CosignerEd25519Key) MarshalJSON() ([]byte, error) { + type Alias CosignerEd25519Key - // marshal our private key and all public keys - privateBytes := x509.MarshalPKCS1PrivateKey(&cosignerKey.RSAKey) - rsaPubKeysBytes := make([][]byte, 0) - for _, pubKey := range cosignerKey.CosignerKeys { - publicBytes := x509.MarshalPKCS1PublicKey(pubKey) - rsaPubKeysBytes = append(rsaPubKeysBytes, publicBytes) - } - - protoPubkey, err := tmCryptoEncoding.PubKeyToProto(cosignerKey.PubKey) + protoPubkey, err := cometcryptoencoding.PubKeyToProto(key.PubKey) if err != nil { return nil, err } @@ -44,82 +32,61 @@ func (cosignerKey *CosignerKey) MarshalJSON() ([]byte, error) { } return json.Marshal(&struct { - RSAKey []byte `json:"rsa_key"` - Pubkey []byte `json:"pub_key"` - CosignerKeys [][]byte `json:"rsa_pubs"` + PubKey []byte `json:"pubKey"` *Alias }{ - Pubkey: protoBytes, - RSAKey: privateBytes, - CosignerKeys: rsaPubKeysBytes, - Alias: (*Alias)(cosignerKey), + PubKey: protoBytes, + Alias: (*Alias)(key), }) } -func (cosignerKey *CosignerKey) UnmarshalJSON(data []byte) error { - type Alias CosignerKey +func (key *CosignerEd25519Key) UnmarshalJSON(data []byte) error { + type Alias CosignerEd25519Key aux := &struct { - RSAKey []byte `json:"rsa_key"` - PubkeyBytes []byte `json:"pub_key"` - CosignerKeys [][]byte `json:"rsa_pubs"` + PubkeyBytes []byte `json:"pubKey"` *Alias }{ - Alias: (*Alias)(cosignerKey), + Alias: (*Alias)(key), } if err := json.Unmarshal(data, &aux); err != nil { return err } - privateKey, err := x509.ParsePKCS1PrivateKey(aux.RSAKey) - if err != nil { - return err - } - var pubkey tmCrypto.PubKey - var protoPubkey tmProtoCrypto.PublicKey - err = protoPubkey.Unmarshal(aux.PubkeyBytes) + var pubkey cometcrypto.PubKey + var protoPubkey cometprotocrypto.PublicKey + err := protoPubkey.Unmarshal(aux.PubkeyBytes) // Prior to the tendermint protobuf migration, the public key bytes in key files // were encoded using the go-amino libraries via - // cdc.MarshalBinaryBare(cosignerKey.PubKey) + // cdc.MarshalBinaryBare(key.PubKey) // // To support reading the public key bytes from these key files, we fallback to // amino unmarshalling if the protobuf unmarshalling fails if err != nil { - var pub tmEd25519.PubKey + var pub cometcryptoed25519.PubKey codec := amino.NewCodec() - codec.RegisterInterface((*tmCrypto.PubKey)(nil), nil) - codec.RegisterConcrete(tmEd25519.PubKey{}, "tendermint/PubKeyEd25519", nil) + codec.RegisterInterface((*cometcrypto.PubKey)(nil), nil) + codec.RegisterConcrete(cometcryptoed25519.PubKey{}, "tendermint/PubKeyEd25519", nil) errInner := codec.UnmarshalBinaryBare(aux.PubkeyBytes, &pub) if errInner != nil { return err } pubkey = pub } else { - pubkey, err = tmCryptoEncoding.PubKeyFromProto(protoPubkey) - if err != nil { - return err - } - } - - // unmarshal the public key bytes for each cosigner - cosignerKey.CosignerKeys = make([]*rsa.PublicKey, 0) - for _, bytes := range aux.CosignerKeys { - cosignerRsaPubkey, err := x509.ParsePKCS1PublicKey(bytes) + pubkey, err = cometcryptoencoding.PubKeyFromProto(protoPubkey) if err != nil { return err } - cosignerKey.CosignerKeys = append(cosignerKey.CosignerKeys, cosignerRsaPubkey) } - cosignerKey.RSAKey = *privateKey - cosignerKey.PubKey = pubkey + key.PubKey = pubkey return nil } -// LoadCosignerKey loads a CosignerKey from file. -func LoadCosignerKey(file string) (CosignerKey, error) { - pvKey := CosignerKey{} +// LoadCosignerEd25519Key loads a CosignerEd25519Key from file. +func LoadCosignerEd25519Key(file string) (CosignerEd25519Key, error) { + pvKey := CosignerEd25519Key{} keyJSONBytes, err := os.ReadFile(file) if err != nil { return pvKey, err diff --git a/signer/cosigner_key_shares.go b/signer/cosigner_key_shares.go index 0f030c06..0aee38a5 100644 --- a/signer/cosigner_key_shares.go +++ b/signer/cosigner_key_shares.go @@ -6,71 +6,146 @@ import ( "encoding/json" "os" - tmjson "github.com/tendermint/tendermint/libs/json" - "github.com/tendermint/tendermint/privval" + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" + "golang.org/x/sync/errgroup" ) -// CreateCosignerSharesFromFile creates cosigner key objects from a priv_validator_key.json file -func CreateCosignerSharesFromFile(priv string, threshold, shares int64) ([]CosignerKey, error) { +// CreateCosignerEd25519ShardsFromFile creates CosignerEd25519Key objects from a priv_validator_key.json file +func CreateCosignerEd25519ShardsFromFile(priv string, threshold, shards uint8) ([]CosignerEd25519Key, error) { pv, err := ReadPrivValidatorFile(priv) if err != nil { return nil, err } - return CreateCosignerShares(pv, threshold, shares) + return CreateCosignerEd25519Shards(pv, threshold, shards), nil } -// CreateCosignerShares creates cosigner key objects from a privval.FilePVKey -func CreateCosignerShares(pv privval.FilePVKey, threshold, shares int64) (out []CosignerKey, err error) { - privshares := tsed25519.DealShares(tsed25519.ExpandSecret(pv.PrivKey.Bytes()[:32]), uint8(threshold), uint8(shares)) - rsaKeys, pubKeys, err := makeRSAKeys(len(privshares)) +// CreateCosignerEd25519Shards creates CosignerEd25519Key objects from a privval.FilePVKey +func CreateCosignerEd25519Shards(pv privval.FilePVKey, threshold, shards uint8) []CosignerEd25519Key { + privShards := tsed25519.DealShares(tsed25519.ExpandSecret(pv.PrivKey.Bytes()[:32]), threshold, shards) + out := make([]CosignerEd25519Key, shards) + for i, shard := range privShards { + out[i] = CosignerEd25519Key{ + PubKey: pv.PubKey, + PrivateShard: shard, + ID: i + 1, + } + } + return out +} + +// CreateCosignerRSAShards generate CosignerRSAKey objects. +func CreateCosignerRSAShards(shards int) ([]CosignerRSAKey, error) { + rsaKeys, pubKeys, err := makeRSAKeys(shards) if err != nil { return nil, err } - for idx, share := range privshares { - out = append(out, CosignerKey{ - PubKey: pv.PubKey, - ShareKey: share, - ID: idx + 1, - RSAKey: *rsaKeys[idx], - CosignerKeys: pubKeys, - }) + out := make([]CosignerRSAKey, shards) + for i, key := range rsaKeys { + out[i] = CosignerRSAKey{ + ID: i + 1, + RSAKey: *key, + RSAPubs: pubKeys, + } } - return + return out, nil } -// ReadPrivValidatorFile reads in a privval.FilePVKey from a given file +// ReadPrivValidatorFile reads in a privval.FilePVKey from a given file. func ReadPrivValidatorFile(priv string) (out privval.FilePVKey, err error) { var bz []byte if bz, err = os.ReadFile(priv); err != nil { return } - if err = tmjson.Unmarshal(bz, &out); err != nil { + if err = cometjson.Unmarshal(bz, &out); err != nil { return } return } -// WriteCosignerShareFile writes a cosigner key to a given file name -func WriteCosignerShareFile(cosigner CosignerKey, file string) error { +// WriteCosignerEd25519ShardFile writes a cosigner Ed25519 key to a given file name. +func WriteCosignerEd25519ShardFile(cosigner CosignerEd25519Key, file string) error { jsonBytes, err := json.Marshal(&cosigner) if err != nil { return err } - return os.WriteFile(file, jsonBytes, 0644) //nolint + return os.WriteFile(file, jsonBytes, 0600) +} + +// WriteCosignerRSAShardFile writes a cosigner RSA key to a given file name. +func WriteCosignerRSAShardFile(cosigner CosignerRSAKey, file string) error { + jsonBytes, err := json.Marshal(&cosigner) + if err != nil { + return err + } + return os.WriteFile(file, jsonBytes, 0600) +} + +// CreateCosignerECIESShards generates CosignerECIESKey objects. +func CreateCosignerECIESShards(shards int) ([]CosignerECIESKey, error) { + eciesKeys, pubKeys, err := makeECIESKeys(shards) + if err != nil { + return nil, err + } + out := make([]CosignerECIESKey, shards) + for i, key := range eciesKeys { + out[i] = CosignerECIESKey{ + ID: i + 1, + ECIESKey: key, + ECIESPubs: pubKeys, + } + } + return out, nil +} + +// WriteCosignerECIESShardFile writes a cosigner ECIES key to a given file name. +func WriteCosignerECIESShardFile(cosigner CosignerECIESKey, file string) error { + jsonBytes, err := json.Marshal(&cosigner) + if err != nil { + return err + } + return os.WriteFile(file, jsonBytes, 0600) } func makeRSAKeys(num int) (rsaKeys []*rsa.PrivateKey, pubKeys []*rsa.PublicKey, err error) { rsaKeys = make([]*rsa.PrivateKey, num) pubKeys = make([]*rsa.PublicKey, num) + var eg errgroup.Group + bitSize := 4096 for i := 0; i < num; i++ { - bitSize := 4096 - rsaKey, err := rsa.GenerateKey(rand.Reader, bitSize) - if err != nil { - return rsaKeys, pubKeys, err - } - rsaKeys[i] = rsaKey - pubKeys[i] = &rsaKey.PublicKey + i := i + eg.Go(func() error { + rsaKey, err := rsa.GenerateKey(rand.Reader, bitSize) + if err != nil { + return err + } + rsaKeys[i] = rsaKey + pubKeys[i] = &rsaKey.PublicKey + + return nil + }) } - return + return rsaKeys, pubKeys, eg.Wait() +} + +func makeECIESKeys(num int) ([]*ecies.PrivateKey, []*ecies.PublicKey, error) { + eciesKeys := make([]*ecies.PrivateKey, num) + pubKeys := make([]*ecies.PublicKey, num) + var eg errgroup.Group + for i := 0; i < num; i++ { + i := i + eg.Go(func() error { + eciesKey, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + if err != nil { + return err + } + eciesKeys[i] = eciesKey + pubKeys[i] = &eciesKey.PublicKey + return nil + }) + } + return eciesKeys, pubKeys, eg.Wait() } diff --git a/signer/cosigner_key_test.go b/signer/cosigner_key_test.go deleted file mode 100644 index aaf472a3..00000000 --- a/signer/cosigner_key_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package signer - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestLoadCosignerKey(t *testing.T) { - key, err := LoadCosignerKey("./fixtures/cosigner-key.json") - require.NoError(t, err) - require.Equal(t, key.ID, 3) - - // public key from cosigner pubs array should match public key from our private key - require.Equal(t, &key.RSAKey.PublicKey, key.CosignerKeys[key.ID-1]) -} diff --git a/signer/cosigner_nonce_cache.go b/signer/cosigner_nonce_cache.go new file mode 100644 index 00000000..327bd00b --- /dev/null +++ b/signer/cosigner_nonce_cache.go @@ -0,0 +1,425 @@ +package signer + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/google/uuid" +) + +const ( + defaultGetNoncesInterval = 3 * time.Second + defaultGetNoncesTimeout = 4 * time.Second + defaultNonceExpiration = 10 * time.Second // half of the local cosigner cache expiration + nonceOverallocation = 1.5 +) + +type CosignerNonceCache struct { + logger cometlog.Logger + cosigners []Cosigner + + leader Leader + + lastReconcileNonces atomic.Uint64 + lastReconcileTime time.Time + + getNoncesInterval time.Duration + getNoncesTimeout time.Duration + nonceExpiration time.Duration + + threshold uint8 + + cache *NonceCache + + pruner NonceCachePruner + + movingAverage *movingAverage + + empty chan struct{} +} + +type movingAverageItem struct { + timeSinceLastReconcile time.Duration + noncesPerMinute float64 +} + +type movingAverage struct { + items []movingAverageItem + period time.Duration +} + +func newMovingAverage(period time.Duration) *movingAverage { + return &movingAverage{period: period} +} + +func (m *movingAverage) add( + timeSinceLastReconcile time.Duration, + noncesPerMinute float64, +) { + duration := timeSinceLastReconcile + keep := len(m.items) - 1 + for i, e := range m.items { + duration += e.timeSinceLastReconcile + if duration >= m.period { + keep = i + break + } + } + m.items = append( + []movingAverageItem{{timeSinceLastReconcile: timeSinceLastReconcile, noncesPerMinute: noncesPerMinute}}, + m.items[:keep+1]..., + ) +} + +func (m *movingAverage) average() float64 { + weightedSum := float64(0) + duration := float64(0) + + for _, e := range m.items { + d := float64(e.timeSinceLastReconcile) + weightedSum += e.noncesPerMinute * d + duration += d + } + + return weightedSum / duration +} + +type NonceCachePruner interface { + PruneNonces() int +} + +type NonceCache struct { + cache []*CachedNonce + mu sync.RWMutex +} + +func (nc *NonceCache) Size() int { + nc.mu.RLock() + defer nc.mu.RUnlock() + return len(nc.cache) +} + +func (nc *NonceCache) Add(cn *CachedNonce) { + nc.mu.Lock() + defer nc.mu.Unlock() + nc.cache = append(nc.cache, cn) +} + +func (nc *NonceCache) Delete(index int) { + nc.cache = append(nc.cache[:index], nc.cache[index+1:]...) +} + +func (nc *NonceCache) PruneNonces() int { + nc.mu.Lock() + defer nc.mu.Unlock() + nonExpiredIndex := -1 + for i := 0; i < len(nc.cache); i++ { + if time.Now().Before(nc.cache[i].Expiration) { + nonExpiredIndex = i + break + } + } + + var deleteCount int + if nonExpiredIndex == -1 { + // No non-expired nonces, delete everything + deleteCount = len(nc.cache) + nc.cache = nil + } else { + // Prune everything up to the non-expired nonce + deleteCount = nonExpiredIndex + nc.cache = nc.cache[nonExpiredIndex:] + } + return deleteCount +} + +type CosignerNoncesRel struct { + Cosigner Cosigner + Nonces CosignerNonces +} + +type CachedNonceSingle struct { + Cosigner Cosigner + Nonces CosignerUUIDNoncesMultiple +} + +type CachedNonce struct { + // UUID identifying this collection of nonces + UUID uuid.UUID + + // Expiration time of this nonce + Expiration time.Time + + // Cached nonces, cosigners which have this nonce in their metadata, ready to sign + Nonces []CosignerNoncesRel +} + +func NewCosignerNonceCache( + logger cometlog.Logger, + cosigners []Cosigner, + leader Leader, + getNoncesInterval time.Duration, + getNoncesTimeout time.Duration, + nonceExpiration time.Duration, + threshold uint8, + pruner NonceCachePruner, +) *CosignerNonceCache { + cnc := &CosignerNonceCache{ + logger: logger, + cosigners: cosigners, + leader: leader, + getNoncesInterval: getNoncesInterval, + getNoncesTimeout: getNoncesTimeout, + nonceExpiration: nonceExpiration, + threshold: threshold, + pruner: pruner, + cache: new(NonceCache), + // buffer up to 1000 empty events so that we don't ever block + empty: make(chan struct{}, 1000), + movingAverage: newMovingAverage(4 * getNoncesInterval), // weighted average over 4 intervals + } + // the only time pruner is expected to be non-nil is during tests, otherwise we use the cache logic. + if pruner == nil { + cnc.pruner = cnc.cache + } + + return cnc +} + +func (cnc *CosignerNonceCache) getUuids(n int) []uuid.UUID { + uuids := make([]uuid.UUID, n) + for i := 0; i < n; i++ { + uuids[i] = uuid.New() + } + return uuids +} + +func (cnc *CosignerNonceCache) target(noncesPerMinute float64) int { + t := int((noncesPerMinute / 60) * + ((cnc.getNoncesInterval.Seconds() * nonceOverallocation) + cnc.getNoncesTimeout.Seconds())) + if t <= 0 { + return 1 // always target at least one nonce ready + } + return t +} + +func (cnc *CosignerNonceCache) reconcile(ctx context.Context) { + // prune expired nonces + pruned := cnc.pruner.PruneNonces() + + if !cnc.leader.IsLeader() { + return + } + remainingNonces := cnc.cache.Size() + timeSinceLastReconcile := time.Since(cnc.lastReconcileTime) + + lastReconcileNonces := cnc.lastReconcileNonces.Load() + // calculate nonces per minute + noncesPerMin := float64(int(lastReconcileNonces)-remainingNonces-pruned) / timeSinceLastReconcile.Minutes() + if noncesPerMin < 0 { + noncesPerMin = 0 + } + + cnc.movingAverage.add(timeSinceLastReconcile, noncesPerMin) + + // calculate how many nonces we need to load to keep up with demand + // load 120% the number of nonces we need to keep up with demand, + // plus a couple seconds worth of nonces to account for nonce consumption during LoadN + // plus 10 for padding + + avgNoncesPerMin := cnc.movingAverage.average() + t := cnc.target(avgNoncesPerMin) + additional := t - remainingNonces + + defer func() { + cnc.lastReconcileNonces.Store(uint64(remainingNonces + additional)) + cnc.lastReconcileTime = time.Now() + }() + + if additional <= 0 { + additional = 0 + // we're ahead of demand, don't load any more + cnc.logger.Debug( + "Cosigner nonce cache ahead of demand", + "target", t, + "remaining", remainingNonces, + "nonces_per_min", noncesPerMin, + "avg_nonces_per_min", avgNoncesPerMin, + ) + return + } + + cnc.logger.Debug( + "Loading additional nonces to meet demand", + "target", t, + "remaining", remainingNonces, + "additional", additional, + "nonces_per_min", noncesPerMin, + "avg_nonces_per_min", avgNoncesPerMin, + ) + + cnc.LoadN(ctx, additional) +} + +func (cnc *CosignerNonceCache) LoadN(ctx context.Context, n int) { + if n == 0 { + return + } + uuids := cnc.getUuids(n) + nonces := make([]*CachedNonceSingle, len(cnc.cosigners)) + var wg sync.WaitGroup + wg.Add(len(cnc.cosigners)) + + expiration := time.Now().Add(cnc.nonceExpiration) + + for i, p := range cnc.cosigners { + i := i + p := p + go func() { + defer wg.Done() + ctx, cancel := context.WithTimeout(ctx, cnc.getNoncesTimeout) + defer cancel() + + peerStartTime := time.Now() + n, err := p.GetNonces(ctx, uuids) + if err != nil { + // Significant missing shares may lead to signature failure + missedNonces.WithLabelValues(p.GetAddress()).Add(float64(1)) + totalMissedNonces.WithLabelValues(p.GetAddress()).Inc() + + cnc.logger.Error("Failed to get nonces from peer", "peer", p.GetID(), "error", err) + return + } + + missedNonces.WithLabelValues(p.GetAddress()).Set(0) + timedCosignerNonceLag.WithLabelValues(p.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + + nonces[i] = &CachedNonceSingle{ + Cosigner: p, + Nonces: n, + } + }() + } + wg.Wait() + added := 0 + for i, u := range uuids { + nonce := CachedNonce{ + UUID: u, + Expiration: expiration, + } + num := uint8(0) + for _, n := range nonces { + if n == nil { + continue + } + num++ + nonce.Nonces = append(nonce.Nonces, CosignerNoncesRel{ + Cosigner: n.Cosigner, + Nonces: n.Nonces[i].Nonces, + }) + } + if num >= cnc.threshold { + cnc.cache.Add(&nonce) + added++ + } + } + cnc.logger.Debug("Loaded nonces", "desired", n, "added", added) +} + +func (cnc *CosignerNonceCache) Start(ctx context.Context) { + cnc.lastReconcileNonces.Store(uint64(cnc.cache.Size())) + cnc.lastReconcileTime = time.Now() + + timer := time.NewTimer(cnc.getNoncesInterval) + for { + select { + case <-ctx.Done(): + return + case <-timer.C: + case <-cnc.empty: + // clear out channel + for len(cnc.empty) > 0 { + <-cnc.empty + } + } + cnc.reconcile(ctx) + timer.Reset(cnc.getNoncesInterval) + } +} + +func (cnc *CosignerNonceCache) GetNonces(fastestPeers []Cosigner) (*CosignerUUIDNonces, error) { + cnc.cache.mu.Lock() + defer cnc.cache.mu.Unlock() +CheckNoncesLoop: + for i, cn := range cnc.cache.cache { + var nonces CosignerNonces + for _, p := range fastestPeers { + found := false + for _, n := range cn.Nonces { + if n.Cosigner.GetID() == p.GetID() { + found = true + nonces = append(nonces, n.Nonces...) + break + } + } + if !found { + // this set of nonces doesn't have the peer we need + continue CheckNoncesLoop + } + } + + // remove this set of nonces from the cache + cnc.cache.Delete(i) + + if len(cnc.cache.cache) == 0 && len(cnc.empty) == 0 { + cnc.logger.Debug("Nonce cache is empty, triggering reload") + cnc.empty <- struct{}{} + } + + // all peers found + return &CosignerUUIDNonces{ + UUID: cn.UUID, + Nonces: nonces, + }, nil + } + + // increment so it's taken into account in the nonce burn rate in the next reconciliation + cnc.lastReconcileNonces.Add(1) + + // no nonces found + cosignerInts := make([]int, len(fastestPeers)) + for i, p := range fastestPeers { + cosignerInts[i] = p.GetID() + } + return nil, fmt.Errorf("no nonces found involving cosigners %+v", cosignerInts) +} + +func (cnc *CosignerNonceCache) ClearNonces(cosigner Cosigner) { + cnc.cache.mu.Lock() + defer cnc.cache.mu.Unlock() + for i := 0; i < len(cnc.cache.cache); i++ { + cn := cnc.cache.cache[i] + + deleteID := -1 + for j, n := range cn.Nonces { + if n.Cosigner.GetID() == cosigner.GetID() { + // remove cosigner from this nonce. + deleteID = j + break + } + } + if deleteID >= 0 { + if len(cn.Nonces)-1 < int(cnc.threshold) { + // If cosigners on this nonce drops below threshold, delete it as it's no longer usable + cnc.cache.Delete(i) + i-- + } else { + cn.Nonces = append(cn.Nonces[:deleteID], cn.Nonces[deleteID+1:]...) + } + } + } +} diff --git a/signer/cosigner_nonce_cache_test.go b/signer/cosigner_nonce_cache_test.go new file mode 100644 index 00000000..2c65f175 --- /dev/null +++ b/signer/cosigner_nonce_cache_test.go @@ -0,0 +1,472 @@ +package signer + +import ( + "context" + "os" + "sync" + "testing" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +func TestNonceCache(_ *testing.T) { + nc := NonceCache{} + for i := 0; i < 10; i++ { + nc.Add(&CachedNonce{UUID: uuid.New(), Expiration: time.Now().Add(1 * time.Second)}) + } + + nc.Delete(nc.Size() - 1) + nc.Delete(0) +} + +func TestMovingAverage(t *testing.T) { + ma := newMovingAverage(12 * time.Second) + + ma.add(3*time.Second, 500) + require.Len(t, ma.items, 1) + require.Equal(t, float64(500), ma.average()) + + ma.add(3*time.Second, 100) + require.Len(t, ma.items, 2) + require.Equal(t, float64(300), ma.average()) + + ma.add(6*time.Second, 600) + require.Len(t, ma.items, 3) + require.Equal(t, float64(450), ma.average()) + + // should kick out the first one + ma.add(3*time.Second, 500) + require.Len(t, ma.items, 3) + require.Equal(t, float64(450), ma.average()) + + // should kick out the second one + ma.add(6*time.Second, 500) + require.Len(t, ma.items, 3) + require.Equal(t, float64(540), ma.average()) + + for i := 0; i < 5; i++ { + ma.add(2500*time.Millisecond, 1000) + } + + require.Len(t, ma.items, 5) + require.Equal(t, float64(1000), ma.average()) +} + +func TestClearNonces(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + cnc := CosignerNonceCache{ + threshold: 2, + cache: new(NonceCache), + } + + for i := 0; i < 10; i++ { + // When deleting nonce for cosigner 1 ([0]), + // these nonce will drop below threshold and be deleted. + cnc.cache.Add(&CachedNonce{ + UUID: uuid.New(), + Expiration: time.Now().Add(1 * time.Second), + Nonces: []CosignerNoncesRel{ + {Cosigner: cosigners[0]}, + {Cosigner: cosigners[1]}, + }, + }) + // When deleting nonce for cosigner 1 ([0]), these nonces will still be above threshold, + // so they will remain without cosigner 1. + cnc.cache.Add(&CachedNonce{ + UUID: uuid.New(), + Expiration: time.Now().Add(1 * time.Second), + Nonces: []CosignerNoncesRel{ + {Cosigner: cosigners[0]}, + {Cosigner: cosigners[1]}, + {Cosigner: cosigners[2]}, + }, + }) + } + + require.Equal(t, 20, cnc.cache.Size()) + + cnc.ClearNonces(cosigners[0]) + + require.Equal(t, 10, cnc.cache.Size()) + + for _, n := range cnc.cache.cache { + require.Len(t, n.Nonces, 2) + oneFound := false + twoFound := false + for _, cnr := range n.Nonces { + if cnr.Cosigner == cosigners[1] { + oneFound = true + } + if cnr.Cosigner == cosigners[2] { + twoFound = true + } + } + require.True(t, oneFound) + require.True(t, twoFound) + } + + cnc.ClearNonces(cosigners[1]) + + require.Equal(t, 0, cnc.cache.Size()) +} + +type mockPruner struct { + cache *NonceCache + count int + pruned int + mu sync.Mutex +} + +func (mp *mockPruner) PruneNonces() int { + pruned := mp.cache.PruneNonces() + mp.mu.Lock() + defer mp.mu.Unlock() + mp.count++ + mp.pruned += pruned + return pruned +} + +func (mp *mockPruner) Result() (int, int) { + mp.mu.Lock() + defer mp.mu.Unlock() + return mp.count, mp.pruned +} + +func TestNonceCacheDemand(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + mp := &mockPruner{} + + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, + 500*time.Millisecond, + 100*time.Millisecond, + defaultNonceExpiration, + 2, + mp, + ) + + mp.cache = nonceCache.cache + + ctx, cancel := context.WithCancel(context.Background()) + + nonceCache.LoadN(ctx, 500) + + go nonceCache.Start(ctx) + + for i := 0; i < 3000; i++ { + _, err := nonceCache.GetNonces([]Cosigner{cosigners[0], cosigners[1]}) + require.NoError(t, err) + time.Sleep(10 * time.Millisecond) + require.Greater(t, nonceCache.cache.Size(), 0) + } + + size := nonceCache.cache.Size() + + require.Greater(t, size, 0) + + cancel() + + require.LessOrEqual(t, size, nonceCache.target(nonceCache.movingAverage.average())) + + count, pruned := mp.Result() + + require.Greater(t, count, 0, "count of pruning calls must be greater than 0") + require.Equal(t, 0, pruned, "no nonces should have been pruned") +} + +func TestNonceCacheExpiration(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + mp := &mockPruner{} + + noncesExpiration := 1000 * time.Millisecond + getNoncesInterval := noncesExpiration / 5 + getNoncesTimeout := 10 * time.Millisecond + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, + getNoncesInterval, + getNoncesTimeout, + noncesExpiration, + 2, + mp, + ) + + mp.cache = nonceCache.cache + + ctx, cancel := context.WithCancel(context.Background()) + + const loadN = 100 + // Load first set of 100 nonces + nonceCache.LoadN(ctx, loadN) + + go nonceCache.Start(ctx) + + // Sleep for 1/2 nonceExpiration, no nonces should have expired yet + time.Sleep(noncesExpiration / 2) + + // Load second set of 100 nonces + nonceCache.LoadN(ctx, loadN) + + // Wait for first set of nonces to expire + wait for the interval to have run + time.Sleep((noncesExpiration / 2) + getNoncesInterval) + + count, pruned := mp.Result() + + // we should have pruned at least 5 times after + // waiting for 1200ms with a reconcile interval of 200ms + require.GreaterOrEqual(t, count, 5) + + // we should have pruned only the first set of nonces + // The second set of nonces should not have expired yet and we should not have load any more + require.Equal(t, pruned, loadN) + + cancel() + + // the cache should be 100 (loadN) as the second set should not have expired. + require.LessOrEqual(t, nonceCache.cache.Size(), loadN) +} + +func TestNonceCachePrune(t *testing.T) { + type testCase struct { + name string + nonces []*CachedNonce + expected []*CachedNonce + } + + now := time.Now() + + testCases := []testCase{ + { + name: "no nonces", + nonces: nil, + expected: nil, + }, + { + name: "no expired nonces", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + expected: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + }, + { + name: "first nonce is expired", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + expected: []*CachedNonce{ + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + }, + { + name: "all but last nonce expired", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + expected: []*CachedNonce{ + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + }, + { + name: "all nonces expired", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(-1 * time.Second), + }, + }, + expected: nil, + }, + } + + for _, tc := range testCases { + nc := NonceCache{ + cache: tc.nonces, + } + + pruned := nc.PruneNonces() + + require.Equal(t, len(tc.nonces)-len(tc.expected), pruned, tc.name) + + require.Equal(t, tc.expected, nc.cache, tc.name) + } +} + +func TestNonceCacheDemandSlow(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, + 90*time.Millisecond, + 100*time.Millisecond, + 500*time.Millisecond, + 2, + nil, + ) + + ctx, cancel := context.WithCancel(context.Background()) + + go nonceCache.Start(ctx) + + for i := 0; i < 10; i++ { + time.Sleep(200 * time.Millisecond) + require.Greater(t, nonceCache.cache.Size(), 0) + _, err := nonceCache.GetNonces([]Cosigner{cosigners[0], cosigners[1]}) + require.NoError(t, err) + } + + cancel() + + require.LessOrEqual(t, nonceCache.cache.Size(), nonceCache.target(300)) +} + +func TestNonceCacheDemandSlowDefault(t *testing.T) { + if testing.Short() { + t.Skip() + } + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, + defaultGetNoncesInterval, + defaultGetNoncesTimeout, + defaultNonceExpiration, + 2, + nil, + ) + + ctx, cancel := context.WithCancel(context.Background()) + + go nonceCache.Start(ctx) + + for i := 0; i < 10; i++ { + time.Sleep(7 * time.Second) + require.Greater(t, nonceCache.cache.Size(), 0) + _, err := nonceCache.GetNonces([]Cosigner{cosigners[0], cosigners[1]}) + require.NoError(t, err) + } + + cancel() + + require.LessOrEqual(t, nonceCache.cache.Size(), nonceCache.target(60/7)) +} diff --git a/signer/cosigner_security.go b/signer/cosigner_security.go new file mode 100644 index 00000000..dc22254a --- /dev/null +++ b/signer/cosigner_security.go @@ -0,0 +1,22 @@ +package signer + +// CosignerSecurity is an interface for the security layer of the cosigner. +type CosignerSecurity interface { + // GetID returns the ID of the cosigner. + GetID() int + + // EncryptAndSign encrypts the nonce and signs it for authentication. + EncryptAndSign( + id int, + noncePub []byte, + nonceShare []byte, + ) (CosignerNonce, error) + + // DecryptAndVerify decrypts the nonce and verifies the signature to authenticate the source cosigner. + DecryptAndVerify( + id int, + encryptedNoncePub []byte, + encryptedNonceShare []byte, + signature []byte, + ) (noncePub []byte, nonceShare []byte, err error) +} diff --git a/signer/cosigner_security_ecies.go b/signer/cosigner_security_ecies.go new file mode 100644 index 00000000..cde12d12 --- /dev/null +++ b/signer/cosigner_security_ecies.go @@ -0,0 +1,253 @@ +package signer + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/sha256" + "encoding/json" + "fmt" + "math/big" + "os" + + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "golang.org/x/sync/errgroup" +) + +var _ CosignerSecurity = &CosignerSecurityECIES{} + +// CosignerSecurityECIES is an implementation of CosignerSecurity +// using ECIES for encryption and ECDSA for digital signature. +type CosignerSecurityECIES struct { + key CosignerECIESKey + eciesPubKeys map[int]CosignerECIESPubKey +} + +// CosignerECIESKey is a cosigner's ECIES public key. +type CosignerECIESPubKey struct { + ID int + PublicKey *ecies.PublicKey +} + +// CosignerECIESKey is an ECIES key for an m-of-n threshold signer, composed of a private key and n public keys. +type CosignerECIESKey struct { + ECIESKey *ecies.PrivateKey `json:"eciesKey"` + ID int `json:"id"` + ECIESPubs []*ecies.PublicKey `json:"eciesPubs"` +} + +func (key *CosignerECIESKey) MarshalJSON() ([]byte, error) { + type Alias CosignerECIESKey + + // marshal our private key and all public keys + privateBytes := key.ECIESKey.D.Bytes() + pubKeysBytes := make([][]byte, len(key.ECIESPubs)) + for i, pubKey := range key.ECIESPubs { + pubBz := make([]byte, 65) + pubBz[0] = 0x04 + copy(pubBz[1:33], pubKey.X.Bytes()) + copy(pubBz[33:65], pubKey.Y.Bytes()) + pubKeysBytes[i] = pubBz + } + + return json.Marshal(&struct { + ECIESKey []byte `json:"eciesKey"` + ECIESPubs [][]byte `json:"eciesPubs"` + *Alias + }{ + ECIESKey: privateBytes, + ECIESPubs: pubKeysBytes, + Alias: (*Alias)(key), + }) +} + +func (key *CosignerECIESKey) UnmarshalJSON(data []byte) error { + type Alias CosignerECIESKey + + aux := &struct { + ECIESKey []byte `json:"eciesKey"` + ECIESPubs [][]byte `json:"eciesPubs"` + *Alias + }{ + Alias: (*Alias)(key), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // unmarshal the public key bytes for each cosigner + key.ECIESPubs = make([]*ecies.PublicKey, len(aux.ECIESPubs)) + for i, bytes := range aux.ECIESPubs { + pub := &ecies.PublicKey{ + X: new(big.Int).SetBytes(bytes[1:33]), + Y: new(big.Int).SetBytes(bytes[33:]), + Curve: secp256k1.S256(), + Params: ecies.ECIES_AES128_SHA256, + } + + key.ECIESPubs[i] = pub + } + + key.ECIESKey = &ecies.PrivateKey{ + PublicKey: *key.ECIESPubs[aux.ID-1], + D: new(big.Int).SetBytes(aux.ECIESKey), + } + + return nil +} + +// LoadCosignerECIESKey loads a CosignerECIESKey from file. +func LoadCosignerECIESKey(file string) (CosignerECIESKey, error) { + pvKey := CosignerECIESKey{} + keyJSONBytes, err := os.ReadFile(file) + if err != nil { + return pvKey, err + } + + err = json.Unmarshal(keyJSONBytes, &pvKey) + if err != nil { + return pvKey, err + } + + return pvKey, nil +} + +// NewCosignerSecurityECIES creates a new CosignerSecurityECIES. +func NewCosignerSecurityECIES(key CosignerECIESKey) *CosignerSecurityECIES { + c := &CosignerSecurityECIES{ + key: key, + eciesPubKeys: make(map[int]CosignerECIESPubKey, len(key.ECIESPubs)), + } + + for i, pubKey := range key.ECIESPubs { + c.eciesPubKeys[i+1] = CosignerECIESPubKey{ + ID: i + 1, + PublicKey: pubKey, + } + } + + return c +} + +// GetID returns the ID of the cosigner. +func (c *CosignerSecurityECIES) GetID() int { + return c.key.ID +} + +// EncryptAndSign encrypts the nonce and signs it for authentication. +func (c *CosignerSecurityECIES) EncryptAndSign(id int, noncePub []byte, nonceShare []byte) (CosignerNonce, error) { + nonce := CosignerNonce{ + SourceID: c.key.ID, + } + + // grab the cosigner info for the ID being requested + pubKey, ok := c.eciesPubKeys[id] + if !ok { + return nonce, fmt.Errorf("unknown cosigner ID: %d", id) + } + + var encryptedPub []byte + var encryptedShare []byte + var eg errgroup.Group + + eg.Go(func() (err error) { + encryptedShare, err = ecies.Encrypt(rand.Reader, pubKey.PublicKey, nonceShare, nil, nil) + return err + }) + + eg.Go(func() (err error) { + encryptedPub, err = ecies.Encrypt(rand.Reader, pubKey.PublicKey, noncePub, nil, nil) + return err + }) + + if err := eg.Wait(); err != nil { + return nonce, err + } + + nonce.PubKey = encryptedPub + nonce.Share = encryptedShare + + // sign the response payload with our private key + // cosigners can verify the signature to confirm sender validity + + jsonBytes, err := cometjson.Marshal(nonce) + + if err != nil { + return nonce, err + } + + hash := sha256.Sum256(jsonBytes) + signature, err := ecdsa.SignASN1( + rand.Reader, + c.key.ECIESKey.ExportECDSA(), + hash[:], + ) + if err != nil { + return nonce, err + } + + nonce.DestinationID = id + nonce.Signature = signature + + return nonce, nil +} + +// DecryptAndVerify decrypts the nonce and verifies +// the signature to authenticate the source cosigner. +func (c *CosignerSecurityECIES) DecryptAndVerify( + id int, + encryptedNoncePub []byte, + encryptedNonceShare []byte, + signature []byte, +) ([]byte, []byte, error) { + pubKey, ok := c.eciesPubKeys[id] + if !ok { + return nil, nil, fmt.Errorf("unknown cosigner: %d", id) + } + + digestMsg := CosignerNonce{ + SourceID: id, + PubKey: encryptedNoncePub, + Share: encryptedNonceShare, + } + + digestBytes, err := cometjson.Marshal(digestMsg) + if err != nil { + return nil, nil, err + } + + digest := sha256.Sum256(digestBytes) + + validSignature := ecdsa.VerifyASN1(pubKey.PublicKey.ExportECDSA(), digest[:], signature) + if !validSignature { + return nil, nil, fmt.Errorf("signature is invalid") + } + + var eg errgroup.Group + + var noncePub []byte + var nonceShare []byte + + eg.Go(func() (err error) { + noncePub, err = c.key.ECIESKey.Decrypt(encryptedNoncePub, nil, nil) + if err != nil { + return fmt.Errorf("failed to decrypt nonce pub: %w", err) + } + return nil + }) + + eg.Go(func() (err error) { + nonceShare, err = c.key.ECIESKey.Decrypt(encryptedNonceShare, nil, nil) + if err != nil { + return fmt.Errorf("failed to decrypt nonce share: %w", err) + } + return nil + }) + + if err := eg.Wait(); err != nil { + return nil, nil, err + } + + return noncePub, nonceShare, nil +} diff --git a/signer/cosigner_security_ecies_test.go b/signer/cosigner_security_ecies_test.go new file mode 100644 index 00000000..aa7d04d8 --- /dev/null +++ b/signer/cosigner_security_ecies_test.go @@ -0,0 +1,135 @@ +package signer + +import ( + "crypto/rand" + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" +) + +func TestCosignerECIES(t *testing.T) { + t.Parallel() + + keys := make([]*ecies.PrivateKey, 3) + pubs := make([]*ecies.PublicKey, 3) + + for i := 0; i < 3; i++ { + key, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + require.NoError(t, err) + + keys[i] = key + pubs[i] = &key.PublicKey + } + + securities := make([]CosignerSecurity, 3) + + for i := 0; i < 3; i++ { + key := CosignerECIESKey{ + ID: i + 1, + ECIESKey: keys[i], + ECIESPubs: pubs, + } + securities[i] = NewCosignerSecurityECIES(key) + + bz, err := json.Marshal(&key) + require.NoError(t, err) + + var key2 CosignerECIESKey + require.NoError(t, json.Unmarshal(bz, &key2)) + require.Equal(t, key, key2) + + require.Equal(t, key.ECIESKey.D.Bytes(), key2.ECIESKey.D.Bytes()) + + for i := 0; i < 3; i++ { + require.Equal(t, key.ECIESPubs[i].X.Bytes(), key2.ECIESPubs[i].X.Bytes()) + require.Equal(t, key.ECIESPubs[i].Y.Bytes(), key2.ECIESPubs[i].Y.Bytes()) + } + } + + err := testCosignerSecurity(t, securities) + require.ErrorContains(t, err, "ecies: invalid message") + require.ErrorContains(t, err, "failed to decrypt") +} + +func testCosignerSecurity(t *testing.T, securities []CosignerSecurity) error { + var ( + mockPub = []byte("mock_pub") + mockShare = []byte("mock_share") + ) + + nonce, err := securities[0].EncryptAndSign(2, mockPub, mockShare) + require.NoError(t, err) + + decryptedPub, decryptedShare, err := securities[1].DecryptAndVerify(1, nonce.PubKey, nonce.Share, nonce.Signature) + require.NoError(t, err) + + require.Equal(t, mockPub, decryptedPub) + require.Equal(t, mockShare, decryptedShare) + + _, _, err = securities[2].DecryptAndVerify(1, nonce.PubKey, nonce.Share, nonce.Signature) + + return err +} + +func TestConcurrentIterateCosignerECIES(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + keys := make([]*ecies.PrivateKey, 3) + pubs := make([]*ecies.PublicKey, 3) + + for i := 0; i < 3; i++ { + key, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + require.NoError(t, err) + + keys[i] = key + pubs[i] = &key.PublicKey + } + + securities := make([]CosignerSecurity, 3) + + for i := 0; i < 3; i++ { + securities[i] = NewCosignerSecurityECIES(CosignerECIESKey{ + ID: i + 1, + ECIESKey: keys[i], + ECIESPubs: pubs, + }) + } + + for i := 0; i < 5000; i++ { + var eg errgroup.Group + for i, security := range securities { + security := security + i := i + eg.Go(func() error { + var nestedEg errgroup.Group + for j, security2 := range securities { + if i == j { + continue + } + security2 := security2 + j := j + nestedEg.Go(func() error { + n, err := security.EncryptAndSign(j+1, []byte("mock_pub"), []byte("mock_share")) + if err != nil { + return err + } + + _, _, err = security2.DecryptAndVerify(i+1, n.PubKey, n.Share, n.Signature) + if err != nil { + return err + } + return nil + }) + } + return nestedEg.Wait() + }) + } + require.NoErrorf(t, eg.Wait(), "success count: %d", i) + } +} diff --git a/signer/cosigner_security_rsa.go b/signer/cosigner_security_rsa.go new file mode 100644 index 00000000..eb46b442 --- /dev/null +++ b/signer/cosigner_security_rsa.go @@ -0,0 +1,235 @@ +package signer + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/json" + "fmt" + "os" + + cometjson "github.com/cometbft/cometbft/libs/json" + "golang.org/x/sync/errgroup" +) + +var _ CosignerSecurity = &CosignerSecurityRSA{} + +// CosignerSecurityRSA is an implementation of CosignerSecurity using RSA for encryption and P5S for digital signature. +type CosignerSecurityRSA struct { + key CosignerRSAKey + rsaPubKeys map[int]CosignerRSAPubKey +} + +// CosignerRSAKey is a cosigner's RSA public key. +type CosignerRSAPubKey struct { + ID int + PublicKey rsa.PublicKey +} + +// CosignerRSAKey is an RSA key for an m-of-n threshold signer, composed of a private key and n public keys. +type CosignerRSAKey struct { + RSAKey rsa.PrivateKey `json:"rsaKey"` + ID int `json:"id"` + RSAPubs []*rsa.PublicKey `json:"rsaPubs"` +} + +func (key *CosignerRSAKey) MarshalJSON() ([]byte, error) { + type Alias CosignerRSAKey + + // marshal our private key and all public keys + privateBytes := x509.MarshalPKCS1PrivateKey(&key.RSAKey) + rsaPubKeysBytes := make([][]byte, len(key.RSAPubs)) + for i, pubKey := range key.RSAPubs { + publicBytes := x509.MarshalPKCS1PublicKey(pubKey) + rsaPubKeysBytes[i] = publicBytes + } + + return json.Marshal(&struct { + RSAKey []byte `json:"rsaKey"` + RSAPubs [][]byte `json:"rsaPubs"` + *Alias + }{ + RSAKey: privateBytes, + RSAPubs: rsaPubKeysBytes, + Alias: (*Alias)(key), + }) +} + +func (key *CosignerRSAKey) UnmarshalJSON(data []byte) error { + type Alias CosignerRSAKey + + aux := &struct { + RSAKey []byte `json:"rsaKey"` + RSAPubs [][]byte `json:"rsaPubs"` + *Alias + }{ + Alias: (*Alias)(key), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + privateKey, err := x509.ParsePKCS1PrivateKey(aux.RSAKey) + if err != nil { + return err + } + + // unmarshal the public key bytes for each cosigner + key.RSAPubs = make([]*rsa.PublicKey, len(aux.RSAPubs)) + for i, bytes := range aux.RSAPubs { + cosignerRsaPubkey, err := x509.ParsePKCS1PublicKey(bytes) + if err != nil { + return err + } + key.RSAPubs[i] = cosignerRsaPubkey + } + + key.RSAKey = *privateKey + return nil +} + +// LoadCosignerRSAKey loads a CosignerRSAKey from file. +func LoadCosignerRSAKey(file string) (CosignerRSAKey, error) { + pvKey := CosignerRSAKey{} + keyJSONBytes, err := os.ReadFile(file) + if err != nil { + return pvKey, err + } + + err = json.Unmarshal(keyJSONBytes, &pvKey) + if err != nil { + return pvKey, err + } + + return pvKey, nil +} + +// NewCosignerSecurityRSA creates a new CosignerSecurityRSA. +func NewCosignerSecurityRSA(key CosignerRSAKey) *CosignerSecurityRSA { + c := &CosignerSecurityRSA{ + key: key, + rsaPubKeys: make(map[int]CosignerRSAPubKey), + } + + for i, pubKey := range key.RSAPubs { + c.rsaPubKeys[i+1] = CosignerRSAPubKey{ + ID: i + 1, + PublicKey: *pubKey, + } + } + + return c +} + +// GetID returns the ID of the cosigner. +func (c *CosignerSecurityRSA) GetID() int { + return c.key.ID +} + +// EncryptAndSign encrypts the nonce and signs it for authentication. +func (c *CosignerSecurityRSA) EncryptAndSign(id int, noncePub []byte, nonceShare []byte) (CosignerNonce, error) { + nonce := CosignerNonce{ + SourceID: c.key.ID, + } + + // grab the cosigner info for the ID being requested + pubKey, ok := c.rsaPubKeys[id] + if !ok { + return nonce, fmt.Errorf("unknown cosigner ID: %d", id) + } + + var encryptedPub []byte + var encryptedShare []byte + var eg errgroup.Group + + eg.Go(func() (err error) { + encryptedShare, err = rsa.EncryptOAEP(sha256.New(), rand.Reader, &pubKey.PublicKey, nonceShare, nil) + return err + }) + + eg.Go(func() (err error) { + encryptedPub, err = rsa.EncryptOAEP(sha256.New(), rand.Reader, &pubKey.PublicKey, noncePub, nil) + return err + }) + + if err := eg.Wait(); err != nil { + return nonce, err + } + + nonce.PubKey = encryptedPub + nonce.Share = encryptedShare + + // sign the response payload with our private key + // cosigners can verify the signature to confirm sender validity + + jsonBytes, err := cometjson.Marshal(nonce) + + if err != nil { + return nonce, err + } + + hash := sha256.Sum256(jsonBytes) + signature, err := rsa.SignPSS(rand.Reader, &c.key.RSAKey, crypto.SHA256, hash[:], nil) + if err != nil { + return nonce, err + } + + nonce.DestinationID = id + nonce.Signature = signature + + return nonce, nil +} + +// DecryptAndVerify decrypts the nonce and verifies +// the signature to authenticate the source cosigner. +func (c *CosignerSecurityRSA) DecryptAndVerify( + id int, + encryptedNoncePub []byte, + encryptedNonceShare []byte, + signature []byte, +) ([]byte, []byte, error) { + pubKey, ok := c.rsaPubKeys[id] + if !ok { + return nil, nil, fmt.Errorf("unknown cosigner: %d", id) + } + + digestMsg := CosignerNonce{ + SourceID: id, + PubKey: encryptedNoncePub, + Share: encryptedNonceShare, + } + + digestBytes, err := cometjson.Marshal(digestMsg) + if err != nil { + return nil, nil, err + } + + digest := sha256.Sum256(digestBytes) + + err = rsa.VerifyPSS(&pubKey.PublicKey, crypto.SHA256, digest[:], signature, nil) + if err != nil { + return nil, nil, err + } + + var eg errgroup.Group + + var noncePub []byte + var nonceShare []byte + + eg.Go(func() (err error) { + noncePub, err = rsa.DecryptOAEP(sha256.New(), rand.Reader, &c.key.RSAKey, encryptedNoncePub, nil) + return err + }) + + eg.Go(func() (err error) { + nonceShare, err = rsa.DecryptOAEP(sha256.New(), rand.Reader, &c.key.RSAKey, encryptedNonceShare, nil) + return err + }) + + if err := eg.Wait(); err != nil { + return nil, nil, err + } + + return noncePub, nonceShare, nil +} diff --git a/signer/cosigner_security_rsa_test.go b/signer/cosigner_security_rsa_test.go new file mode 100644 index 00000000..7830d9af --- /dev/null +++ b/signer/cosigner_security_rsa_test.go @@ -0,0 +1,111 @@ +package signer + +import ( + "crypto/rand" + "crypto/rsa" + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" +) + +func TestCosignerRSA(t *testing.T) { + t.Parallel() + + keys := make([]*rsa.PrivateKey, 3) + pubKeys := make([]*rsa.PublicKey, 3) + + for i := 0; i < 3; i++ { + key, err := rsa.GenerateKey(rand.Reader, bitSize) + require.NoError(t, err) + + keys[i] = key + pubKeys[i] = &key.PublicKey + } + + securities := make([]CosignerSecurity, 3) + + for i := 0; i < 3; i++ { + key := CosignerRSAKey{ + ID: i + 1, + RSAKey: *keys[i], + RSAPubs: pubKeys, + } + securities[i] = NewCosignerSecurityRSA(key) + + bz, err := json.Marshal(&key) + require.NoError(t, err) + + var key2 CosignerRSAKey + require.NoError(t, json.Unmarshal(bz, &key2)) + require.Equal(t, key, key2) + + require.Equal(t, key.RSAKey.N.Bytes(), key2.RSAKey.N.Bytes()) + require.Equal(t, key.RSAKey.D.Bytes(), key2.RSAKey.D.Bytes()) + require.Equal(t, key.RSAKey.E, key2.RSAKey.E) + + for i := 0; i < 3; i++ { + require.Equal(t, key.RSAPubs[i].N.Bytes(), key2.RSAPubs[i].N.Bytes()) + require.Equal(t, key.RSAPubs[i].E, key2.RSAPubs[i].E) + } + } + + err := testCosignerSecurity(t, securities) + require.ErrorIs(t, rsa.ErrDecryption, err) +} + +func TestConcurrentIterateCosignerRSA(t *testing.T) { + keys := make([]*rsa.PrivateKey, 3) + pubKeys := make([]*rsa.PublicKey, 3) + + for i := 0; i < 3; i++ { + key, err := rsa.GenerateKey(rand.Reader, bitSize) + require.NoError(t, err) + + keys[i] = key + pubKeys[i] = &key.PublicKey + } + + securities := make([]CosignerSecurity, 3) + + for i := 0; i < 3; i++ { + securities[i] = NewCosignerSecurityRSA(CosignerRSAKey{ + ID: i + 1, + RSAKey: *keys[i], + RSAPubs: pubKeys, + }) + } + + for i := 0; i < 100; i++ { + var eg errgroup.Group + for i, security := range securities { + security := security + i := i + eg.Go(func() error { + var nestedEg errgroup.Group + for j, security2 := range securities { + if i == j { + continue + } + security2 := security2 + j := j + nestedEg.Go(func() error { + n, err := security.EncryptAndSign(j+1, []byte("mock_pub"), []byte("mock_share")) + if err != nil { + return err + } + + _, _, err = security2.DecryptAndVerify(i+1, n.PubKey, n.Share, n.Signature) + if err != nil { + return err + } + return nil + }) + } + return nestedEg.Wait() + }) + } + require.NoErrorf(t, eg.Wait(), "success count: %d", i) + } +} diff --git a/signer/file.go b/signer/file.go new file mode 100644 index 00000000..d4379dbc --- /dev/null +++ b/signer/file.go @@ -0,0 +1,345 @@ +package signer + +import ( + "bytes" + "errors" + "fmt" + "os" + "time" + + "github.com/cosmos/gogoproto/proto" + + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/ed25519" + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/libs/protoio" + "github.com/cometbft/cometbft/libs/tempfile" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cometbft/cometbft/types" +) + +//------------------------------------------------------------------------------- + +// FilePVKey stores the immutable part of PrivValidator. +type FilePVKey struct { + Address types.Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + PrivKey crypto.PrivKey `json:"priv_key"` + + filePath string +} + +// Save persists the FilePVKey to its filePath. +func (pvKey FilePVKey) Save() { + outFile := pvKey.filePath + if outFile == "" { + panic("cannot save PrivValidator key: filePath not set") + } + + jsonBytes, err := cometjson.MarshalIndent(pvKey, "", " ") + if err != nil { + panic(err) + } + + if err := tempfile.WriteFileAtomic(outFile, jsonBytes, 0600); err != nil { + panic(err) + } +} + +//------------------------------------------------------------------------------- + +// FilePVLastSignState stores the mutable part of PrivValidator. +type FilePVLastSignState struct { + Height int64 `json:"height"` + Round int32 `json:"round"` + Step int8 `json:"step"` + Signature []byte `json:"signature,omitempty"` + SignBytes []byte `json:"signbytes,omitempty"` + + filePath string +} + +// CheckHRS checks the given height, round, step (HRS) against that of the +// FilePVLastSignState. It returns an error if the arguments constitute a regression, +// or if they match but the SignBytes are empty. +// The returned boolean indicates whether the last Signature should be reused - +// it returns true if the HRS matches the arguments and the SignBytes are not empty (indicating +// we have already signed for this HRS, and can reuse the existing signature). +// It panics if the HRS matches the arguments, there's a SignBytes, but no Signature. +func (lss *FilePVLastSignState) CheckHRS(height int64, round int32, step int8) (bool, error) { + + if lss.Height > height { + return false, fmt.Errorf("height regression. Got %v, last height %v", height, lss.Height) + } + + if lss.Height == height { + if lss.Round > round { + return false, fmt.Errorf("round regression at height %v. Got %v, last round %v", height, round, lss.Round) + } + + if lss.Round == round { + if lss.Step > step { + return false, fmt.Errorf( + "step regression at height %v round %v. Got %v, last step %v", + height, + round, + step, + lss.Step, + ) + } else if lss.Step == step { + if lss.SignBytes != nil { + if lss.Signature == nil { + panic("pv: Signature is nil but SignBytes is not!") + } + return true, nil + } + return false, errors.New("no SignBytes found") + } + } + } + return false, nil +} + +// Save persists the FilePvLastSignState to its filePath. +func (lss *FilePVLastSignState) Save() { + outFile := lss.filePath + if outFile == "" { + panic("cannot save FilePVLastSignState: filePath not set") + } + jsonBytes, err := cometjson.MarshalIndent(lss, "", " ") + if err != nil { + panic(err) + } + err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) + if err != nil { + panic(err) + } +} + +//------------------------------------------------------------------------------- + +// FilePV implements PrivValidator using data persisted to disk +// to prevent double signing. +// NOTE: the directories containing pv.Key.filePath and pv.LastSignState.filePath must already exist. +// It includes the LastSignature and LastSignBytes so we don't lose the signature +// if the process crashes after signing but before the resulting consensus message is processed. +type FilePV struct { + Key FilePVKey + LastSignState FilePVLastSignState +} + +// NewFilePV generates a new validator from the given key and paths. +func NewFilePV(privKey crypto.PrivKey, keyFilePath, stateFilePath string) *FilePV { + return &FilePV{ + Key: FilePVKey{ + Address: privKey.PubKey().Address(), + PubKey: privKey.PubKey(), + PrivKey: privKey, + filePath: keyFilePath, + }, + LastSignState: FilePVLastSignState{ + Step: 0, + filePath: stateFilePath, + }, + } +} + +// GenFilePV generates a new validator with randomly generated private key +// and sets the filePaths, but does not call Save(). +func GenFilePV(keyFilePath, stateFilePath string) *FilePV { + return NewFilePV(ed25519.GenPrivKey(), keyFilePath, stateFilePath) +} + +// If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. +func LoadFilePV(keyFilePath, stateFilePath string, loadState bool) (*FilePV, error) { + keyJSONBytes, err := os.ReadFile(keyFilePath) + if err != nil { + return nil, err + } + pvKey := FilePVKey{} + err = cometjson.Unmarshal(keyJSONBytes, &pvKey) + if err != nil { + return nil, fmt.Errorf("error reading PrivValidator key from %s: %w", keyFilePath, err) + } + + // overwrite pubkey and address for convenience + pvKey.PubKey = pvKey.PrivKey.PubKey() + pvKey.Address = pvKey.PubKey.Address() + pvKey.filePath = keyFilePath + + pvState := FilePVLastSignState{} + + if loadState { + stateJSONBytes, err := os.ReadFile(stateFilePath) + if err != nil { + return nil, err + } + err = cometjson.Unmarshal(stateJSONBytes, &pvState) + if err != nil { + return nil, fmt.Errorf("error reading PrivValidator state from %s: %w", stateFilePath, err) + } + } + + pvState.filePath = stateFilePath + + return &FilePV{ + Key: pvKey, + LastSignState: pvState, + }, nil +} + +// GetAddress returns the address of the validator. +// Implements PrivValidator. +func (pv *FilePV) GetAddress() types.Address { + return pv.Key.Address +} + +// GetPubKey returns the public key of the validator. +// Implements PrivValidator. +func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { + return pv.Key.PubKey, nil +} + +func (pv *FilePV) Sign(chainID string, block Block) ([]byte, []byte, time.Time, error) { + height, round, step := block.Height, int32(block.Round), block.Step + signBytes, voteExtensionSignBytes := block.SignBytes, block.VoteExtensionSignBytes + + lss := pv.LastSignState + + sameHRS, err := lss.CheckHRS(height, round, step) + if err != nil { + return nil, nil, block.Timestamp, err + } + + _, hasVoteExtensions, err := verifySignPayload(chainID, signBytes, voteExtensionSignBytes) + if err != nil { + return nil, nil, block.Timestamp, err + } + + // Vote extensions are non-deterministic, so it is possible that an + // application may have created a different extension. We therefore always + // re-sign the vote extensions of precommits. For prevotes and nil + // precommits, the extension signature will always be empty. + // Even if the signed over data is empty, we still add the signature + var extSig []byte + if hasVoteExtensions { + extSig, err = pv.Key.PrivKey.Sign(voteExtensionSignBytes) + if err != nil { + return nil, nil, block.Timestamp, err + } + } + + // We might crash before writing to the wal, + // causing us to try to re-sign for the same HRS. + // If signbytes are the same, use the last signature. + // If they only differ by timestamp, use last timestamp and signature + // Otherwise, return error + if sameHRS { + switch { + case bytes.Equal(signBytes, lss.SignBytes): + return lss.Signature, nil, block.Timestamp, nil + case block.Step == stepPropose: + if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { + return lss.Signature, nil, timestamp, nil + } + case block.Step == stepPrevote || block.Step == stepPrecommit: + if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { + return lss.Signature, extSig, timestamp, nil + } + } + + return nil, extSig, block.Timestamp, fmt.Errorf("conflicting data") + } + + // It passed the checks. Sign the vote + sig, err := pv.Key.PrivKey.Sign(signBytes) + if err != nil { + return nil, nil, block.Timestamp, err + } + pv.saveSigned(height, round, step, signBytes, sig) + + return sig, extSig, block.Timestamp, nil +} + +// Save persists the FilePV to disk. +func (pv *FilePV) Save() { + pv.Key.Save() + pv.LastSignState.Save() +} + +// Reset resets all fields in the FilePV. +// NOTE: Unsafe! +func (pv *FilePV) Reset() { + var sig []byte + pv.LastSignState.Height = 0 + pv.LastSignState.Round = 0 + pv.LastSignState.Step = 0 + pv.LastSignState.Signature = sig + pv.LastSignState.SignBytes = nil + pv.Save() +} + +// String returns a string representation of the FilePV. +func (pv *FilePV) String() string { + return fmt.Sprintf( + "PrivValidator{%v LH:%v, LR:%v, LS:%v}", + pv.GetAddress(), + pv.LastSignState.Height, + pv.LastSignState.Round, + pv.LastSignState.Step, + ) +} + +// Persist height/round/step and signature +func (pv *FilePV) saveSigned(height int64, round int32, step int8, + signBytes []byte, sig []byte) { + + pv.LastSignState.Height = height + pv.LastSignState.Round = round + pv.LastSignState.Step = step + pv.LastSignState.Signature = sig + pv.LastSignState.SignBytes = signBytes + pv.LastSignState.Save() +} + +//----------------------------------------------------------------------------------------- + +// returns the timestamp from the lastSignBytes. +// returns true if the only difference in the votes is their timestamp. +func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { + var lastVote, newVote cometproto.CanonicalVote + if err := protoio.UnmarshalDelimited(lastSignBytes, &lastVote); err != nil { + panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err)) + } + if err := protoio.UnmarshalDelimited(newSignBytes, &newVote); err != nil { + panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err)) + } + + lastTime := lastVote.Timestamp + // set the times to the same value and check equality + now := time.Now() + lastVote.Timestamp = now + newVote.Timestamp = now + + return lastTime, proto.Equal(&newVote, &lastVote) +} + +// returns the timestamp from the lastSignBytes. +// returns true if the only difference in the proposals is their timestamp +func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { + var lastProposal, newProposal cometproto.CanonicalProposal + if err := protoio.UnmarshalDelimited(lastSignBytes, &lastProposal); err != nil { + panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err)) + } + if err := protoio.UnmarshalDelimited(newSignBytes, &newProposal); err != nil { + panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err)) + } + + lastTime := lastProposal.Timestamp + // set the times to the same value and check equality + now := time.Now() + lastProposal.Timestamp = now + newProposal.Timestamp = now + + return lastTime, proto.Equal(&newProposal, &lastProposal) +} diff --git a/signer/fixtures/cosigner-key.json b/signer/fixtures/cosigner-key.json deleted file mode 100644 index af4d83f4..00000000 --- a/signer/fixtures/cosigner-key.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "rsa_key": "MIIJKAIBAAKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQKCAgAZ/xLkK43QqwBmbRwGnLtrPO4eBW6re24kjjSfBfPuZ4aMOK4n8lTYaerGV4Z4e4AIdM8Hs6yfYWtK1XuoY9Q4Mxu1Qg0ubIuOsP18ctCbVGZpsnVesrFODNUbRi/MbnlSdKMyl+dkAGhVk+7c1/r5YVhK+Va0yr8fUvP2uobtyZRMm64XhDtSAtv+1BN2dLQhwPW+/cQmHXoO/C7cM+CAHpsyJ5MPcIAukqG/8H1Uks1yI9QNJsMMgzkjDtXOIv5oI2Dhex4fvUF6pFCYktHX8YPIBKZbEx0bM6pAcAJecmhNH78I6HKbcqBzGovrpHfdwBqIZgrQMfS7rTeKPDykNZ2aHb3xc2Tv1FOcAx+sVg58eEwyTzhTSriBJVWBlGfHbey09eojRhDMlrMgqTVArT25okKwUF+xl0eKqJlFvspSDN3XV6Yb0D35MXS8qQEFoTyM5b40mUIlgabR7cWu+jPB1sFU3P28NPHRG40fwnr89yYX3gevRPSrigewfVnkb3qMBa/fJ7Khuet7XxWYVCXwLDzRSSgHkEgkY3llc8J0Yg2HHdnNOSLqpRmnRED9li7Ol04Ps6sFB6nVszpEa6D6B7ADg7nVxsHbwrvKZ47Ut8dbGBm9Cpp4hFg9oGcvRdogz2bsjCU4Y90X3nfvR1YIkwoEkj/n5ZbuX1PPKQKCAQEA37ZzyFozo/Y2//heRxWgFPwVzBK27+DYc16WLtZxs+shaiXJ4Zn0DOI5x1VdC50sxHvlERcTQeQhSO+jnXQOkF6YQgylxLuYoFdo2l5jojn2vsHa4Klw9UatpFStCwz+PhD0wK7WhDoEKvwbvQ2wTa/wvDpnAuhhAEh2hMMuDkRpiVulYe40ywQW/NOrfUklAw1D/5NflDGPCYBYreP7pNtYbMRt9zLhOgQSvn9GqbpnnMj88gZDFxNO7jDiUsYO7yFsi01ALJ+T6AfVtKRyOEbjhpOBBpvlpwbUAMHuARjgcTWmvyWPbafIGpiaSX8ThtG8h78n/ITX6+NPF6fIfwKCAQEA6NFO9qLmYJWj8IaJaPKYBB1JdnFXOJvmPiJ4DS8BhLsSoEYsYE83ECXJwTI2F2l6MVyBXq+PPrQKm2vsUEWzzn/Fi+b7jEbjTv379yLylD1fjsMT0NHtN3vq09Yp3kNgTrd1IfCNxZ5A92Vh0f67PjHB3aMXeYd+ydBeIIlLVAgR6nUtkvGmFtuPunUlZiuB6UpMXDjPRN1VrddvQVTgSkPl9WB8wPcShxOz+wL4Hi5DII+ThFdAAh9pnIFaBF1Et/xMl2ss/7hcxqcIItQSBLotU0brPHMvoiSEjHWLuekw/b5noabkrfm8NOhB6Gjrq58oODe9ZrDjiaweh55jAwKCAQEApTqIgW29vlfXf27dkvrx5Q3au4MHAly7AVrW6XkROaVsZI3McYfXrLxZmFQACNfpfKVVJi441u27d7cmzOAu8YosQnw84vT7YVGt67rTM7pD99gN5OjAuSeekETKGeNa1FSJsNZxMe/3rBfQFO3LTVWpJByugINJQYBDqQLPPVJh8EVz/MSG0XsPz2Q2wK4JXBusIVOjwDxqPMZCuQwtjDFFOfBKl81IdCUWAwTWF/3JEQ+RYuAlJSHpphsMzb3iwdOZ67j+sPabs0A2ItliUxZobbj8DvmNwLNWWcjiFIVfH75UjdEcAg1tydbz/VyR+31lFY2l5ufm4h5dCEev2QKCAQAX543NAxLWbebkRlwLe4UiPwOQ9rg25sLwNEfRSrdEMpUKAcqCpP+JV+fsP0SQiNL0CIR7/Vie3ouMQ7uCznVUyYe2AqRnVcv3C1r4mA0CLX8HQH5jXXqWzNFiqMWpvY9A5dNQBcv4s3QGMtGlZxtAmolGQX2ii8f33r4bZx1l5mI4iYmBYfBkvmx2f5q0b9kp4+gNPAQEFRm7/Le+pIFW/ru4wwxsH7I2Tk6XgkmJh8R6rmM+HltDHIiSejGM6yqoHW6byXRYWUylVPcf5FhpRdhriYeTsFv+sPMvHM6Y6xmNpCQt0939AvxRDlveCg/Qkknl48s9pQHn29VSpW+TAoIBAE862157emwegrRYu66ENMMNLbF6YxBpL47iWC1NQ4/8aM5i58edv1VWUw5R441uvAGXk7uzsYkrxmp0nj6UANkjq6n06mayN90kbg4FvjAzEtbbAu8byap+B5xLSuetIc+dVqACKihQ09zwm3d/LFtbrgZ2KGiw/RfvMxxT0HYp9A7NdYT4nf2Aqa3UR4SuxWm4bWWLHHMGeS4N9MuwFP64jLgLrXzyB851Avuz9LJCpNAflE9SQUvTqGwpFvsgEwQcGH+5vcvcBENCYbAwq5hmMnzrAXsA1NnJNqn+oqXG8GIogG7DOa4965QL60TwDu9s/opzV2bMVhVtxDqKSfo=", - "pub_key": "CiBRLhKCqDU6Wufhj0TZK6jNO8LreArc+CKHKYitdTyYUg==", - "rsa_pubs": [ - "MIICCgKCAgEAzo2bGRRrwn1/TFlkJ9yqvOcx0BYvmay+rPQFnDFsKxb+WHHLtwn/juxY0Ub+ABwCgJgBUr4k3G9piFYwtL0R3ton6UulwYMgNQ6cnn8/zmAx/STP/WGYKXRtTR80csC+u8g/kzUK/lX2pGz77BLNxflKf/yfnm3wkCcJecnv2PLW84J3/s6b3TUkS+ygUQL3SB+IN7dI/i1pls7my6pCTOJxIu7TJ+PPahyDkRhE0OapjH0OQIbHXNeCqe71uQwALdf1dwTDl2JeIL7jhGWB8xb2PfeLX+VZsOWUR0NPfs83viS+Pjtz6ndYX1+3+BQxOIutnkUC6IwSBqsG+M2cqElETIgUHpxqRl0QtReq18+GTX9CfFB5hmWgLlGICij9Lnz0zpwtyIQJXn2Cny8XeWi8E9uKpi+4MNkDqwPd2U+wIXBPVBgqPjTByLeish+VaxKV2bHzqManB5WHa0g7WDK9p8OdZ7To8miJF+hdqOZMHnxThY/hr0102ffOq8XCDIfm873Ie2Cn/+KBHwCc6e7XO5ohWKm9WQbsxpmpn3+ru1ekWTkqC8YC7FFpljMCpl9NiGz4edVzSnnL8OU12M1pofEwpbMtlNCzaVJkMzfo9jDRoWxDyKffRYbdp93V1Oio0ab2ou9uZ0Jx0mXIpLyvznRNmDEsj5nrWmbW5jcCAwEAAQ==", - "MIICCgKCAgEA83UMSAbKSL4/W9VAzn+XjqCmhl8og6BoZvukS1pQI0JFrox63hJYavHTQB0DO2iXomfpm9d+J8NHsBsWf7DD/9aNaByGRJ0k5Lde64FfTj6LP9I5yRoKuGGQ0Heuvuisz9DMWRyhkO9hJiyedX7VdPx3VdUW4AX+FWyJ1pKpj0g/s8eYrUFyzISdoq/pRwkVkzHpXqFh3L5ASUjf9eQXGYsQsDI0UDuzZdYD4nitQ5Q0POM7jCgSQQ8d/b0eaF2hCzbZ1UWKx8LzCU7j4NRqrYJluRqkxeEtBeZsq7QX6Hs13tg+wKKCkOI+wt/1tifLE8IA3es0pXm0UstVduaTMeFtLTvIYE9E/0yFC23aFydz1Fny6HBjpfNo6BgzNCurMziOdpiuLy+7luPM+SBJ3YV0D9TVU8Lo0vawPccj3tcKmozeJdBhuedXWAm00mlCw+LueKBUVxti2kwHiDjBDbLDymZYZHR8HYI0KsrycsvemTotZzYXgDjyRful7mPLGecJhRye7xNX9lVUse81C94gmdZXVL2GKY1PquWJvgazg99gta62GrRj127vDcS2UI+6/4aTJwQFvRqWRLvS/MIJyq5eiq1WyDLOT8dOyBlb7+BV55cB7JUTiO7MsMNaX0h/C3iGrTOnh8rmC/20ygHqZC3E1Lw0SezI2r1NzzcCAwEAAQ==", - "MIICCgKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQ==" - ], - "secret_share": "q0zuUQpplAfBJxoA2e2O1H7BdBgopzhPmL8mIWP8AQw=", - "id": 3 -} \ No newline at end of file diff --git a/signer/grpc_server.go b/signer/grpc_server.go deleted file mode 100644 index 1ab9b3bb..00000000 --- a/signer/grpc_server.go +++ /dev/null @@ -1,102 +0,0 @@ -package signer - -import ( - "context" - "fmt" - "time" - - "github.com/hashicorp/raft" - proto "github.com/strangelove-ventures/horcrux/signer/proto" -) - -type GRPCServer struct { - cosigner *LocalCosigner - thresholdValidator *ThresholdValidator - raftStore *RaftStore - proto.UnimplementedCosignerGRPCServer -} - -func (rpc *GRPCServer) SignBlock( - ctx context.Context, req *proto.CosignerGRPCSignBlockRequest) (*proto.CosignerGRPCSignBlockResponse, error) { - block := &Block{ - Height: req.Block.GetHeight(), - Round: req.Block.GetRound(), - Step: int8(req.Block.GetStep()), - SignBytes: req.Block.GetSignBytes(), - Timestamp: time.Unix(0, req.Block.GetTimestamp()), - } - res, _, err := rpc.thresholdValidator.SignBlock(req.ChainID, block) - if err != nil { - return nil, err - } - return &proto.CosignerGRPCSignBlockResponse{ - Signature: res, - }, nil -} - -func (rpc *GRPCServer) SetEphemeralSecretPartsAndSign( - ctx context.Context, - req *proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest, -) (*proto.CosignerGRPCSetEphemeralSecretPartsAndSignResponse, error) { - res, err := rpc.cosigner.SetEphemeralSecretPartsAndSign(CosignerSetEphemeralSecretPartsAndSignRequest{ - EncryptedSecrets: CosignerEphemeralSecretPartsFromProto(req.GetEncryptedSecrets()), - HRST: HRSTKeyFromProto(req.GetHrst()), - SignBytes: req.GetSignBytes(), - }) - if err != nil { - rpc.raftStore.logger.Error("Failed to sign with share", "error", err) - return nil, err - } - rpc.raftStore.logger.Info("Signed with share", - "height", req.Hrst.Height, - "round", req.Hrst.Round, - "step", req.Hrst.Step, - ) - return &proto.CosignerGRPCSetEphemeralSecretPartsAndSignResponse{ - EphemeralPublic: res.EphemeralPublic, - Timestamp: res.Timestamp.UnixNano(), - Signature: res.Signature, - }, nil -} - -func (rpc *GRPCServer) GetEphemeralSecretParts( - ctx context.Context, - req *proto.CosignerGRPCGetEphemeralSecretPartsRequest, -) (*proto.CosignerGRPCGetEphemeralSecretPartsResponse, error) { - res, err := rpc.cosigner.GetEphemeralSecretParts(HRSTKeyFromProto(req.GetHrst())) - if err != nil { - return nil, err - } - return &proto.CosignerGRPCGetEphemeralSecretPartsResponse{ - EncryptedSecrets: CosignerEphemeralSecretParts(res.EncryptedSecrets).toProto(), - }, nil -} - -func (rpc *GRPCServer) TransferLeadership( - ctx context.Context, - req *proto.CosignerGRPCTransferLeadershipRequest, -) (*proto.CosignerGRPCTransferLeadershipResponse, error) { - leaderID := req.GetLeaderID() - if leaderID != "" { - for _, peer := range rpc.raftStore.Peers { - thisPeerID := fmt.Sprint(peer.GetID()) - if thisPeerID == leaderID { - peerRaftAddress := p2pURLToRaftAddress(peer.GetAddress()) - fmt.Printf("Transferring leadership to ID: %s - Address: %s\n", thisPeerID, peerRaftAddress) - rpc.raftStore.raft.LeadershipTransferToServer(raft.ServerID(thisPeerID), raft.ServerAddress(peerRaftAddress)) - return &proto.CosignerGRPCTransferLeadershipResponse{LeaderID: thisPeerID, LeaderAddress: peerRaftAddress}, nil - } - } - } - fmt.Printf("Transferring leadership to next candidate\n") - rpc.raftStore.raft.LeadershipTransfer() - return &proto.CosignerGRPCTransferLeadershipResponse{}, nil -} - -func (rpc *GRPCServer) GetLeader( - ctx context.Context, - req *proto.CosignerGRPCGetLeaderRequest, -) (*proto.CosignerGRPCGetLeaderResponse, error) { - leader := rpc.raftStore.GetLeader() - return &proto.CosignerGRPCGetLeaderResponse{Leader: string(leader)}, nil -} diff --git a/signer/hrs.go b/signer/hrs.go new file mode 100644 index 00000000..19155f66 --- /dev/null +++ b/signer/hrs.go @@ -0,0 +1,70 @@ +package signer + +import ( + "github.com/strangelove-ventures/horcrux/v3/signer/proto" +) + +// HRSKey represents the key for the HRS metadata map. +type HRSKey struct { + Height int64 + Round int64 + Step int8 +} + +// GreaterThan returns true if the HRSKey is greater than the other HRSKey. +func (hrs HRSKey) GreaterThan(other HRSKey) bool { + if hrs.Height > other.Height { + return true + } + if hrs.Height < other.Height { + return false + } + if hrs.Round > other.Round { + return true + } + if hrs.Round < other.Round { + return false + } + return hrs.Step > other.Step +} + +// LessThan returns true if the HRSKey is less than the other HRSKey. +func (hrs HRSKey) LessThan(other HRSKey) bool { + return hrs != other && !hrs.GreaterThan(other) +} + +// HRSTKey represents the HRS metadata key with a timestamp. +type HRSTKey struct { + Height int64 + Round int64 + Step int8 + Timestamp int64 +} + +// HRSKey returns the HRSKey portion of the HRSTKey. +func (hrst HRSTKey) HRSKey() HRSKey { + return HRSKey{ + Height: hrst.Height, + Round: hrst.Round, + Step: hrst.Step, + } +} + +// HRSTKeyFromProto returns a HRSTKey from a proto.HRST. +func HRSTKeyFromProto(hrs *proto.HRST) HRSTKey { + return HRSTKey{ + Height: hrs.GetHeight(), + Round: hrs.GetRound(), + Step: int8(hrs.GetStep()), + Timestamp: hrs.GetTimestamp(), + } +} + +func (hrst HRSTKey) toProto() *proto.HRST { + return &proto.HRST{ + Height: hrst.Height, + Round: hrst.Round, + Step: int32(hrst.Step), + Timestamp: hrst.Timestamp, + } +} diff --git a/signer/io.go b/signer/io.go new file mode 100644 index 00000000..690ba2af --- /dev/null +++ b/signer/io.go @@ -0,0 +1,25 @@ +package signer + +import ( + "io" + + "github.com/cometbft/cometbft/libs/protoio" + cometprotoprivval "github.com/cometbft/cometbft/proto/tendermint/privval" +) + +// ReadMsg reads a message from an io.Reader +func ReadMsg(reader io.Reader, maxReadSize int) (msg cometprotoprivval.Message, err error) { + if maxReadSize <= 0 { + maxReadSize = 1024 * 1024 // 1MB + } + protoReader := protoio.NewDelimitedReader(reader, maxReadSize) + _, err = protoReader.ReadMsg(&msg) + return msg, err +} + +// WriteMsg writes a message to an io.Writer +func WriteMsg(writer io.Writer, msg cometprotoprivval.Message) (err error) { + protoWriter := protoio.NewDelimitedWriter(writer) + _, err = protoWriter.WriteMsg(&msg) + return err +} diff --git a/signer/leader.go b/signer/leader.go new file mode 100644 index 00000000..beafb3ee --- /dev/null +++ b/signer/leader.go @@ -0,0 +1,13 @@ +package signer + +// Leader is an interface for the detecting if the current cosigner is the leader and performing leader actions. +type Leader interface { + // IsLeader returns true if the cosigner is the leader. + IsLeader() bool + + // ShareSigned shares the last signed state with the other cosigners. + ShareSigned(lss ChainSignStateConsensus) error + + // Get current leader + GetLeader() int +} diff --git a/signer/leader_mock.go b/signer/leader_mock.go new file mode 100644 index 00000000..bd1c7c09 --- /dev/null +++ b/signer/leader_mock.go @@ -0,0 +1,34 @@ +package signer + +import ( + "sync" +) + +var _ Leader = (*MockLeader)(nil) + +type MockLeader struct { + id int + + mu sync.Mutex + leader *ThresholdValidator +} + +func (m *MockLeader) IsLeader() bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.leader != nil && m.leader.myCosigner.GetID() == m.id +} + +func (m *MockLeader) SetLeader(tv *ThresholdValidator) { + m.mu.Lock() + defer m.mu.Unlock() + m.leader = tv +} + +func (m *MockLeader) GetLeader() int { + return m.id +} + +func (m *MockLeader) ShareSigned(_ ChainSignStateConsensus) error { + return nil +} diff --git a/signer/local_cosigner.go b/signer/local_cosigner.go index 0fbb7c62..4559974f 100644 --- a/signer/local_cosigner.go +++ b/signer/local_cosigner.go @@ -1,235 +1,293 @@ package signer import ( - "bytes" - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" + "context" "errors" "fmt" "sync" "time" - tmcryptoed25519 "github.com/tendermint/tendermint/crypto/ed25519" - tmjson "github.com/tendermint/tendermint/libs/json" - "gitlab.com/unit410/edwards25519" - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" + cometcrypto "github.com/cometbft/cometbft/crypto" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/google/uuid" + "golang.org/x/sync/errgroup" ) -type LastSignStateWrapper struct { - // Signing is thread safe - lastSignStateMutex is used for putting locks so only one goroutine can r/w to the function - lastSignStateMutex sync.Mutex +var _ Cosigner = &LocalCosigner{} - // lastSignState stores the last sign state for a share we have fully signed - // incremented whenever we are asked to sign a share - LastSignState *SignState +// double the CosignerNonceCache expiration so that sign requests from the leader +// never reference nonces which have expired here in the LocalCosigner. +const nonceExpiration = 20 * time.Second + +// LocalCosigner responds to sign requests. +// It maintains a high watermark to avoid double-signing. +// Signing is thread safe. +type LocalCosigner struct { + logger cometlog.Logger + config *RuntimeConfig + security CosignerSecurity + chainState sync.Map + address string + pendingDiskWG sync.WaitGroup + + nonces map[uuid.UUID]*NoncesWithExpiration + // protects the nonces map + noncesMu sync.RWMutex } -// return true if we are less than the other key -func (hrst *HRSTKey) Less(other HRSTKey) bool { - if hrst.Height < other.Height { - return true +func NewLocalCosigner( + logger cometlog.Logger, + config *RuntimeConfig, + security CosignerSecurity, + address string, +) *LocalCosigner { + return &LocalCosigner{ + logger: logger, + config: config, + security: security, + address: address, + nonces: make(map[uuid.UUID]*NoncesWithExpiration), } +} - if hrst.Height > other.Height { - return false - } +type ChainState struct { + // lastSignState stores the last sign state for an HRS we have fully signed + // incremented whenever we are asked to sign an HRS + lastSignState *SignState + // signer generates nonces, combines nonces, signs, and verifies signatures. + signer ThresholdSigner +} - // height is equal, check round +// StartNoncePruner periodically prunes nonces that have expired. +func (cosigner *LocalCosigner) StartNoncePruner(ctx context.Context) { + ticker := time.NewTicker(nonceExpiration / 4) + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + cosigner.pruneNonces() + } + } +} - if hrst.Round < other.Round { - return true +// pruneNonces removes nonces that have expired. +func (cosigner *LocalCosigner) pruneNonces() { + cosigner.noncesMu.Lock() + defer cosigner.noncesMu.Unlock() + now := time.Now() + for uuid, nonces := range cosigner.nonces { + if now.After(nonces.Expiration) { + delete(cosigner.nonces, uuid) + } } +} - if hrst.Round > other.Round { - return false +func (cosigner *LocalCosigner) combinedNonces(myID int, threshold uint8, uuid uuid.UUID) ([]Nonce, error) { + cosigner.noncesMu.RLock() + defer cosigner.noncesMu.RUnlock() + + nonces, ok := cosigner.nonces[uuid] + if !ok { + return nil, errors.New("no metadata at HRS") } - // round is equal, check step + combinedNonces := make([]Nonce, 0, threshold) + + // calculate secret and public keys + for _, c := range nonces.Nonces { + if len(c.Shares) == 0 || len(c.Shares[myID-1]) == 0 { + continue + } - if hrst.Step < other.Step { - return true + combinedNonces = append(combinedNonces, Nonce{ + Share: c.Shares[myID-1], + PubKey: c.PubKey, + }) } - // HRS is greater or equal - return false + return combinedNonces, nil } -type CosignerPeer struct { - ID int - PublicKey rsa.PublicKey -} +// Save updates the high watermark height/round/step (HRS) if it is greater +// than the current high watermark. A mutex is used to avoid concurrent state updates. +// The disk write is scheduled in a separate goroutine which will perform an atomic write. +// pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. +func (cosigner *LocalCosigner) SaveLastSignedState(chainID string, signState SignStateConsensus) error { + ccs, err := cosigner.getChainState(chainID) + if err != nil { + return err + } -type CosignerGetEphemeralSecretPartRequest struct { - ID int - Height int64 - Round int64 - Step int8 - Timestamp time.Time + return ccs.lastSignState.Save( + signState, + &cosigner.pendingDiskWG, + ) } -type LocalCosignerConfig struct { - CosignerKey CosignerKey - SignState *SignState - RsaKey rsa.PrivateKey - Peers []CosignerPeer - Address string - RaftAddress string - Total uint8 - Threshold uint8 +// waitForSignStatesToFlushToDisk waits for all state file writes queued +// in SaveLastSignedState to complete before termination. +func (cosigner *LocalCosigner) waitForSignStatesToFlushToDisk() { + cosigner.pendingDiskWG.Wait() } -// LocalCosigner responds to sign requests using their share key -// The cosigner maintains a watermark to avoid double-signing -// -// LocalCosigner signing is thread saafe -type LocalCosigner struct { - pubKeyBytes []byte - key CosignerKey - rsaKey rsa.PrivateKey - total uint8 - threshold uint8 - - // stores the last sign state for a share we have fully signed - // incremented whenever we are asked to sign a share - lastSignState *SignState +// GetID returns the id of the cosigner +// Implements Cosigner interface +func (cosigner *LocalCosigner) GetID() int { + return cosigner.security.GetID() +} - // signing is thread safe - lastSignStateMutex sync.Mutex +// GetAddress returns the RPC URL of the cosigner +// Implements Cosigner interface +func (cosigner *LocalCosigner) GetAddress() string { + return cosigner.address +} - // Height, Round, Step -> metadata - hrsMeta map[HRSTKey]HrsMetadata - peers map[int]CosignerPeer +func (cosigner *LocalCosigner) getChainState(chainID string) (*ChainState, error) { + cs, ok := cosigner.chainState.Load(chainID) + if !ok { + return nil, fmt.Errorf("failed to load chain state for %s", chainID) + } - address string -} + ccs, ok := cs.(*ChainState) + if !ok { + return nil, fmt.Errorf("expected: (*ChainState), actual: (%T)", cs) + } -func (cosigner *LocalCosigner) SaveLastSignedState(signState SignStateConsensus) error { - return cosigner.lastSignState.Save(signState, &cosigner.lastSignStateMutex, true) + return ccs, nil } -func NewLocalCosigner(cfg LocalCosignerConfig) *LocalCosigner { - cosigner := &LocalCosigner{ - key: cfg.CosignerKey, - lastSignState: cfg.SignState, - rsaKey: cfg.RsaKey, - hrsMeta: make(map[HRSTKey]HrsMetadata), - peers: make(map[int]CosignerPeer), - total: cfg.Total, - threshold: cfg.Threshold, - address: cfg.Address, +// GetPubKey returns public key of the validator. +// Implements Cosigner interface +func (cosigner *LocalCosigner) GetPubKey(chainID string) (cometcrypto.PubKey, error) { + if err := cosigner.LoadSignStateIfNecessary(chainID); err != nil { + return nil, err } - for _, peer := range cfg.Peers { - cosigner.peers[peer.ID] = peer + ccs, err := cosigner.getChainState(chainID) + if err != nil { + return nil, err } - // cache the public key bytes for signing operations - switch ed25519Key := cosigner.key.PubKey.(type) { - case tmcryptoed25519.PubKey: - cosigner.pubKeyBytes = make([]byte, len(ed25519Key)) - copy(cosigner.pubKeyBytes, ed25519Key[:]) - default: - panic("Not an ed25519 public key") + return cometcryptoed25519.PubKey(ccs.signer.PubKey()), nil +} + +// CombineSignatures combines partial signatures into a full signature. +func (cosigner *LocalCosigner) CombineSignatures(chainID string, signatures []PartialSignature) ([]byte, error) { + ccs, err := cosigner.getChainState(chainID) + if err != nil { + return nil, err } - return cosigner + return ccs.signer.CombineSignatures(signatures) } -// GetID returns the id of the cosigner +// VerifySignature validates a signed payload against the public key. // Implements Cosigner interface -func (cosigner *LocalCosigner) GetID() int { - return cosigner.key.ID -} +func (cosigner *LocalCosigner) VerifySignature(chainID string, payload, signature []byte) bool { + if err := cosigner.LoadSignStateIfNecessary(chainID); err != nil { + return false + } -// GetAddress returns the RPC URL of the cosigner -// Implements Cosigner interface -func (cosigner *LocalCosigner) GetAddress() string { - return cosigner.address + ccs, err := cosigner.getChainState(chainID) + if err != nil { + return false + } + + sig := make([]byte, len(signature)) + copy(sig, signature) + + return cometcryptoed25519.PubKey(ccs.signer.PubKey()).VerifySignature(payload, sig) } -// Sign the sign request using the cosigner's share +// Sign the sign request using the cosigner's shard // Return the signed bytes or an error // Implements Cosigner interface func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignResponse, error) { - // This function has multiple exit points. Only start time can be guaranteed - metricsTimeKeeper.SetPreviousLocalSignStart(time.Now()) - - cosigner.lastSignStateMutex.Lock() - defer cosigner.lastSignStateMutex.Unlock() + chainID := req.ChainID res := CosignerSignResponse{} - lss := cosigner.lastSignState - hrst, err := UnpackHRST(req.SignBytes) + ccs, err := cosigner.getChainState(chainID) if err != nil { return res, err } - sameHRS, err := lss.CheckHRS(hrst) + hrst, hasVoteExtensions, err := verifySignPayload(chainID, req.SignBytes, req.VoteExtensionSignBytes) if err != nil { return res, err } - // If the HRS is the same the sign bytes may still differ by timestamp - // It is ok to re-sign a different timestamp if that is the only difference in the sign bytes - if sameHRS { - if bytes.Equal(req.SignBytes, lss.SignBytes) { - res.EphemeralPublic = lss.EphemeralPublic - res.Signature = lss.Signature - return res, nil - } else if err := lss.OnlyDifferByTimestamp(req.SignBytes); err != nil { - return res, err - } + // This function has multiple exit points. Only start time can be guaranteed + metricsTimeKeeper.SetPreviousLocalSignStart(time.Now()) - // same HRS, and only differ by timestamp - ok to sign again + existingSignature, err := ccs.lastSignState.existingSignatureOrErrorIfRegression(hrst, req.SignBytes) + if err != nil { + return res, err } - meta, ok := cosigner.hrsMeta[hrst] - if !ok { - return res, errors.New("no metadata at HRS") + if existingSignature != nil { + res.Signature = existingSignature + return res, nil } - shareParts := make([]tsed25519.Scalar, 0) - publicKeys := make([]tsed25519.Element, 0) + defer func() { + cosigner.noncesMu.Lock() + delete(cosigner.nonces, req.UUID) + delete(cosigner.nonces, req.VoteExtUUID) + cosigner.noncesMu.Unlock() + }() - // calculate secret and public keys - for _, peer := range meta.Peers { - if len(peer.Share) == 0 { - continue - } - shareParts = append(shareParts, peer.Share) - publicKeys = append(publicKeys, peer.EphemeralSecretPublicKey) + nonces, err := cosigner.combinedNonces( + cosigner.GetID(), + uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), + req.UUID, + ) + if err != nil { + return res, err } - ephemeralShare := tsed25519.AddScalars(shareParts) - ephemeralPublic := tsed25519.AddElements(publicKeys) - - // check bounds for ephemeral share to avoid passing out of bounds valids to SignWithShare - { - if len(ephemeralShare) != 32 { - return res, errors.New("ephemeral share is out of bounds") + var voteExtNonces []Nonce + if hasVoteExtensions { + voteExtNonces, err = cosigner.combinedNonces( + cosigner.GetID(), + uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), + req.VoteExtUUID, + ) + if err != nil { + return res, err } + } - var scalarBytes [32]byte - copy(scalarBytes[:], ephemeralShare) - if !edwards25519.ScMinimal(&scalarBytes) { - return res, errors.New("ephemeral share is out of bounds") - } + var eg errgroup.Group + + var sig, voteExtSig []byte + eg.Go(func() error { + var err error + sig, err = ccs.signer.Sign(nonces, req.SignBytes) + return err + }) + if hasVoteExtensions { + eg.Go(func() error { + var err error + voteExtSig, err = ccs.signer.Sign(voteExtNonces, req.VoteExtensionSignBytes) + return err + }) } - sig := tsed25519.SignWithShare( - req.SignBytes, cosigner.key.ShareKey, ephemeralShare, cosigner.pubKeyBytes, ephemeralPublic) + if err := eg.Wait(); err != nil { + return res, err + } - cosigner.lastSignState.EphemeralPublic = ephemeralPublic - err = cosigner.lastSignState.Save(SignStateConsensus{ - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - Signature: sig, - SignBytes: req.SignBytes, - }, nil, true) + err = ccs.lastSignState.Save(SignStateConsensus{ + Height: hrst.Height, + Round: hrst.Round, + Step: hrst.Step, + Signature: sig, + SignBytes: req.SignBytes, + VoteExtensionSignature: res.VoteExtensionSignature, + }, &cosigner.pendingDiskWG) if err != nil { if _, isSameHRSError := err.(*SameHRSError); !isSameHRSError { @@ -237,16 +295,8 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon } } - for existingKey := range cosigner.hrsMeta { - // delete any HRS lower than our signed level - // we will not be providing parts for any lower HRS - if existingKey.Less(hrst) { - delete(cosigner.hrsMeta, existingKey) - } - } - - res.EphemeralPublic = ephemeralPublic res.Signature = sig + res.VoteExtensionSignature = voteExtSig // Note - Function may return before this line so elapsed time for Finish may be multiple block times metricsTimeKeeper.SetPreviousLocalSignFinish(time.Now()) @@ -254,240 +304,255 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, nil } -func (cosigner *LocalCosigner) dealShares(req CosignerGetEphemeralSecretPartRequest) (HrsMetadata, error) { - hrsKey := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), +func (cosigner *LocalCosigner) generateNonces() ([]Nonces, error) { + total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) + meta := make([]Nonces, total) + + nonces, err := GenerateNonces( + uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), + uint8(total), + ) + if err != nil { + return nil, err } - meta, ok := cosigner.hrsMeta[hrsKey] + meta[cosigner.GetID()-1] = nonces - if ok { - return meta, nil - } + return meta, nil +} - secret := make([]byte, 32) - if _, err := rand.Read(secret); err != nil { - return HrsMetadata{}, err +func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { + if chainID == "" { + return fmt.Errorf("chain id cannot be empty") } - meta = HrsMetadata{ - Secret: secret, - Peers: make([]PeerMetadata, cosigner.total), + if _, ok := cosigner.chainState.Load(chainID); ok { + return nil } - // split this secret with shamirs - // !! dealt shares need to be saved because dealing produces different shares each time! - meta.DealtShares = tsed25519.DealShares(meta.Secret, cosigner.threshold, cosigner.total) - - cosigner.hrsMeta[hrsKey] = meta - - return meta, nil - -} + signState, err := LoadOrCreateSignState(cosigner.config.CosignerStateFile(chainID)) + if err != nil { + return err + } -func (cosigner *LocalCosigner) GetEphemeralSecretParts( - hrst HRSTKey) (*CosignerEphemeralSecretPartsResponse, error) { - metricsTimeKeeper.SetPreviousLocalEphemeralShare(time.Now()) + var signer ThresholdSigner - res := &CosignerEphemeralSecretPartsResponse{ - EncryptedSecrets: make([]CosignerEphemeralSecretPart, 0, len(cosigner.peers)-1), + signer, err = NewThresholdSignerSoft(cosigner.config, cosigner.GetID(), chainID) + if err != nil { + return err } - for _, peer := range cosigner.peers { - if peer.ID == cosigner.GetID() { - continue - } - secretPart, err := cosigner.getEphemeralSecretPart(CosignerGetEphemeralSecretPartRequest{ - ID: peer.ID, - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - Timestamp: time.Unix(0, hrst.Timestamp), - }) - if err != nil { - return nil, err - } + cosigner.chainState.Store(chainID, &ChainState{ + lastSignState: signState, + signer: signer, + }) - res.EncryptedSecrets = append(res.EncryptedSecrets, secretPart) - } - return res, nil + return nil } -// Get the ephemeral secret part for an ephemeral share -// The ephemeral secret part is encrypted for the receiver -func (cosigner *LocalCosigner) getEphemeralSecretPart( - req CosignerGetEphemeralSecretPartRequest) (CosignerEphemeralSecretPart, error) { - res := CosignerEphemeralSecretPart{} +// GetNonces returns the nonces for the given UUIDs, generating if necessary. +func (cosigner *LocalCosigner) GetNonces( + _ context.Context, + uuids []uuid.UUID, +) (CosignerUUIDNoncesMultiple, error) { + metricsTimeKeeper.SetPreviousLocalNonce(time.Now()) - // protects the meta map - cosigner.lastSignStateMutex.Lock() - defer cosigner.lastSignStateMutex.Unlock() + total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) - hrst := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), - } + res := make(CosignerUUIDNoncesMultiple, len(uuids)) - meta, ok := cosigner.hrsMeta[hrst] - // generate metadata placeholder - if !ok { - newMeta, err := cosigner.dealShares(CosignerGetEphemeralSecretPartRequest{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp, - }) + id := cosigner.GetID() - if err != nil { - return res, err - } + var outerEg errgroup.Group + // getting nonces requires encrypting and signing for each cosigner, + // so we perform these operations in parallel. - meta = newMeta - cosigner.hrsMeta[hrst] = meta - } + for j, u := range uuids { + j := j + u := u - ourEphPublicKey := tsed25519.ScalarMultiplyBase(meta.Secret) + outerEg.Go(func() error { + meta, err := cosigner.generateNoncesIfNecessary(u) + if err != nil { + return err + } - // set our values - meta.Peers[cosigner.key.ID-1].Share = meta.DealtShares[cosigner.key.ID-1] - meta.Peers[cosigner.key.ID-1].EphemeralSecretPublicKey = ourEphPublicKey + var eg errgroup.Group - // grab the peer info for the ID being requested - peer, ok := cosigner.peers[req.ID] - if !ok { - return res, errors.New("unknown peer ID") - } + nonces := make([]CosignerNonce, total-1) - sharePart := meta.DealtShares[req.ID-1] + for i := 0; i < total; i++ { + peerID := i + 1 + if peerID == id { + continue + } - // use RSA public to encrypt user's share part - encrypted, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &peer.PublicKey, sharePart, nil) - if err != nil { - return res, err - } + i := i - res.SourceID = cosigner.key.ID - res.SourceEphemeralSecretPublicKey = ourEphPublicKey - res.EncryptedSharePart = encrypted + eg.Go(func() error { + secretPart, err := cosigner.getNonce(meta, peerID) - // sign the response payload with our private key - // cosigners can verify the signature to confirm sender validity - { - jsonBytes, err := tmjson.Marshal(res) + if i >= id { + nonces[i-1] = secretPart + } else { + nonces[i] = secretPart + } - if err != nil { - return res, err - } + return err + }) + } - digest := sha256.Sum256(jsonBytes) - signature, err := rsa.SignPSS(rand.Reader, &cosigner.rsaKey, crypto.SHA256, digest[:], nil) - if err != nil { - return res, err - } + if err := eg.Wait(); err != nil { + return err + } - res.SourceSig = signature + res[j] = &CosignerUUIDNonces{ + UUID: u, + Nonces: nonces, + } + + return nil + }) } - res.DestinationID = req.ID + if err := outerEg.Wait(); err != nil { + return nil, err + } return res, nil } -// Store an ephemeral secret share part provided by another cosigner -func (cosigner *LocalCosigner) setEphemeralSecretPart(req CosignerSetEphemeralSecretPartRequest) error { - // Verify the source signature - { - if req.SourceSig == nil { - return errors.New("SourceSig field is required") - } +func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) (*NoncesWithExpiration, error) { + // protects the meta map + cosigner.noncesMu.RLock() + nonces, ok := cosigner.nonces[uuid] + cosigner.noncesMu.RUnlock() + if ok { + return nonces, nil + } - digestMsg := CosignerEphemeralSecretPart{} - digestMsg.SourceID = req.SourceID - digestMsg.SourceEphemeralSecretPublicKey = req.SourceEphemeralSecretPublicKey - digestMsg.EncryptedSharePart = req.EncryptedSharePart + newNonces, err := cosigner.generateNonces() + if err != nil { + return nil, err + } - digestBytes, err := tmjson.Marshal(digestMsg) - if err != nil { - return err - } + res := NoncesWithExpiration{ + Nonces: newNonces, + Expiration: time.Now().Add(nonceExpiration), + } - digest := sha256.Sum256(digestBytes) - peer, ok := cosigner.peers[req.SourceID] + cosigner.noncesMu.Lock() + cosigner.nonces[uuid] = &res + cosigner.noncesMu.Unlock() - if !ok { - return fmt.Errorf("unknown cosigner: %d", req.SourceID) - } + return &res, nil +} - peerPub := peer.PublicKey - err = rsa.VerifyPSS(&peerPub, crypto.SHA256, digest[:], req.SourceSig, nil) - if err != nil { - return err - } - } +// Get the ephemeral secret part for an ephemeral share +// The ephemeral secret part is encrypted for the receiver +func (cosigner *LocalCosigner) getNonce( + meta *NoncesWithExpiration, + peerID int, +) (CosignerNonce, error) { + zero := CosignerNonce{} - // protects the meta map - cosigner.lastSignStateMutex.Lock() - defer cosigner.lastSignStateMutex.Unlock() + id := cosigner.GetID() - hrst := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), + ourCosignerMeta := meta.Nonces[id-1] + nonce, err := cosigner.security.EncryptAndSign(peerID, ourCosignerMeta.PubKey, ourCosignerMeta.Shares[peerID-1]) + if err != nil { + return zero, err } - meta, ok := cosigner.hrsMeta[hrst] - // generate metadata placeholder - if !ok { - newMeta, err := cosigner.dealShares(CosignerGetEphemeralSecretPartRequest{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - }) + return nonce, nil +} - if err != nil { - return err - } +const errUnexpectedState = "unexpected state, metadata does not exist for U:" - meta = newMeta - cosigner.hrsMeta[hrst] = meta +// setNonce stores a nonce provided by another cosigner +func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce CosignerNonce) error { + // Verify the source signature + if nonce.Signature == nil { + return errors.New("signature field is required") } - // decrypt share - sharePart, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, &cosigner.rsaKey, req.EncryptedSharePart, nil) + noncePub, nonceShare, err := cosigner.security.DecryptAndVerify( + nonce.SourceID, nonce.PubKey, nonce.Share, nonce.Signature) if err != nil { return err } + // protects the meta map + cosigner.noncesMu.Lock() + defer cosigner.noncesMu.Unlock() + + n, ok := cosigner.nonces[uuid] + // generate metadata placeholder + if !ok { + return fmt.Errorf( + "%s %s", + errUnexpectedState, + uuid, + ) + } + // set slot - meta.Peers[req.SourceID-1].Share = sharePart - meta.Peers[req.SourceID-1].EphemeralSecretPublicKey = req.SourceEphemeralSecretPublicKey + if n.Nonces[nonce.SourceID-1].Shares == nil { + n.Nonces[nonce.SourceID-1].Shares = make([][]byte, len(cosigner.config.Config.ThresholdModeConfig.Cosigners)) + } + n.Nonces[nonce.SourceID-1].Shares[cosigner.GetID()-1] = nonceShare + n.Nonces[nonce.SourceID-1].PubKey = noncePub + return nil } -func (cosigner *LocalCosigner) SetEphemeralSecretPartsAndSign( - req CosignerSetEphemeralSecretPartsAndSignRequest) (*CosignerSignResponse, error) { - for _, secretPart := range req.EncryptedSecrets { - err := cosigner.setEphemeralSecretPart(CosignerSetEphemeralSecretPartRequest{ - SourceID: secretPart.SourceID, - SourceEphemeralSecretPublicKey: secretPart.SourceEphemeralSecretPublicKey, - EncryptedSharePart: secretPart.EncryptedSharePart, - SourceSig: secretPart.SourceSig, - Height: req.HRST.Height, - Round: req.HRST.Round, - Step: req.HRST.Step, - Timestamp: time.Unix(0, req.HRST.Timestamp), +func (cosigner *LocalCosigner) SetNoncesAndSign( + _ context.Context, + req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { + chainID := req.ChainID + + if err := cosigner.LoadSignStateIfNecessary(chainID); err != nil { + return nil, err + } + + var eg errgroup.Group + + // setting nonces requires decrypting and verifying signature from each cosigner, + // so we perform these operations in parallel. + + for _, secretPart := range req.Nonces.Nonces { + secretPart := secretPart + + eg.Go(func() error { + return cosigner.setNonce(req.Nonces.UUID, secretPart) }) - if err != nil { - return nil, err + } + + if req.VoteExtensionNonces != nil { + for _, secretPart := range req.VoteExtensionNonces.Nonces { + secretPart := secretPart + + eg.Go(func() error { + return cosigner.setNonce(req.VoteExtensionNonces.UUID, secretPart) + }) } } - res, err := cosigner.sign(CosignerSignRequest{req.SignBytes}) + if err := eg.Wait(); err != nil { + return nil, err + } + + cosignerReq := CosignerSignRequest{ + UUID: req.Nonces.UUID, + ChainID: chainID, + SignBytes: req.SignBytes, + } + + if len(req.VoteExtensionSignBytes) > 0 { + cosignerReq.VoteExtensionSignBytes = req.VoteExtensionSignBytes + cosignerReq.VoteExtUUID = req.VoteExtensionNonces.UUID + } + + res, err := cosigner.sign(cosignerReq) return &res, err } diff --git a/signer/local_cosigner_test.go b/signer/local_cosigner_test.go index aac054c1..462da002 100644 --- a/signer/local_cosigner_test.go +++ b/signer/local_cosigner_test.go @@ -1,131 +1,125 @@ package signer import ( + "context" "crypto/rand" "crypto/rsa" + "fmt" "os" + "path/filepath" "testing" "time" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/libs/log" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + comet "github.com/cometbft/cometbft/types" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/google/uuid" "github.com/stretchr/testify/require" - tmCryptoEd25519 "github.com/tendermint/tendermint/crypto/ed25519" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" - tm "github.com/tendermint/tendermint/types" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" ) -func TestLocalCosignerGetID(t *testing.T) { - dummyPub := tmCryptoEd25519.PubKey{} +const ( + testChainID = "chain-1" + testChainID2 = "chain-2" + bitSize = 4096 +) - bitSize := 4096 - rsaKey, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) +func TestLocalCosignerSignRSA2of3(t *testing.T) { + testLocalCosignerSignRSA(t, 2, 3) +} - key := CosignerKey{ - PubKey: dummyPub, - ShareKey: []byte{}, - ID: 1, - } - signState := SignState{ - Height: 0, - Round: 0, - Step: 0, - } +func TestLocalCosignerSignRSA3of5(t *testing.T) { + testLocalCosignerSignRSA(t, 3, 5) +} - config := LocalCosignerConfig{ - CosignerKey: key, - SignState: &signState, - RsaKey: *rsaKey, - Peers: []CosignerPeer{{ - ID: 1, - PublicKey: rsaKey.PublicKey, - }}, - } +func testLocalCosignerSignRSA(t *testing.T, threshold, total uint8) { + t.Parallel() - cosigner := NewLocalCosigner(config) - require.Equal(t, cosigner.GetID(), 1) -} + security := make([]CosignerSecurity, total) -func TestLocalCosignerSign2of2(t *testing.T) { - // Test signing with a 2 of 2 + keys := make([]*rsa.PrivateKey, total) + pubKeys := make([]*rsa.PublicKey, total) + for i := 0; i < int(total); i++ { + var err error + keys[i], err = rsa.GenerateKey(rand.Reader, bitSize) + require.NoError(t, err) - total := uint8(2) - threshold := uint8(2) + pubKeys[i] = &keys[i].PublicKey + } - bitSize := 4096 - rsaKey1, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) + for i, k := range keys { + security[i] = NewCosignerSecurityRSA( + CosignerRSAKey{ + ID: i + 1, + RSAKey: *k, + RSAPubs: pubKeys, + }, + ) + } - rsaKey2, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) + testLocalCosignerSign(t, threshold, total, security) +} - peers := []CosignerPeer{{ - ID: 1, - PublicKey: rsaKey1.PublicKey, - }, { - ID: 2, - PublicKey: rsaKey2.PublicKey, - }} +func TestLocalCosignerSignECIES2of3(t *testing.T) { + testLocalCosignerSignECIES(t, 2, 3) +} - privateKey := tmCryptoEd25519.GenPrivKey() +func TestLocalCosignerSignECIES3of5(t *testing.T) { + testLocalCosignerSignECIES(t, 3, 5) +} - privKeyBytes := [64]byte{} - copy(privKeyBytes[:], privateKey[:]) - secretShares := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) +func testLocalCosignerSignECIES(t *testing.T, threshold, total uint8) { + t.Parallel() - key1 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[0], - ID: 1, - } + security := make([]CosignerSecurity, total) - stateFile1, err := os.CreateTemp("", "state1.json") - require.NoError(t, err) - defer os.Remove(stateFile1.Name()) + keys := make([]*ecies.PrivateKey, total) + pubKeys := make([]*ecies.PublicKey, total) + for i := 0; i < int(total); i++ { + var err error + keys[i], err = ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + require.NoError(t, err) - signState1, err := LoadOrCreateSignState(stateFile1.Name()) - require.NoError(t, err) + pubKeys[i] = &keys[i].PublicKey + } - key2 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[1], - ID: 2, + for i, k := range keys { + security[i] = NewCosignerSecurityECIES( + CosignerECIESKey{ + ID: i + 1, + ECIESKey: k, + ECIESPubs: pubKeys, + }, + ) } - stateFile2, err := os.CreateTemp("", "state2.json") - require.NoError(t, err) - defer os.Remove(stateFile2.Name()) - signState2, err := LoadOrCreateSignState(stateFile2.Name()) - require.NoError(t, err) + testLocalCosignerSign(t, threshold, total, security) +} - config1 := LocalCosignerConfig{ - CosignerKey: key1, - SignState: &signState1, - RsaKey: *rsaKey1, - Peers: peers, - Total: total, - Threshold: threshold, - } +func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []CosignerSecurity) { + privateKey := cometcryptoed25519.GenPrivKey() - config2 := LocalCosignerConfig{ - CosignerKey: key2, - SignState: &signState2, - RsaKey: *rsaKey2, - Peers: peers, - Total: total, - Threshold: threshold, + privKeyBytes := [64]byte{} + copy(privKeyBytes[:], privateKey[:]) + privShards := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) + pubKey := privateKey.PubKey() + + cfg := Config{ + ThresholdModeConfig: &ThresholdModeConfig{ + Threshold: int(threshold), + Cosigners: make(CosignersConfig, total), + }, } - var cosigner1 Cosigner - var cosigner2 Cosigner - - cosigner1 = NewLocalCosigner(config1) - cosigner2 = NewLocalCosigner(config2) + ctx := context.Background() - require.Equal(t, cosigner1.GetID(), 1) - require.Equal(t, cosigner2.GetID(), 2) + tmpDir := t.TempDir() - publicKeys := make([]tsed25519.Element, 0) + thresholdCosigners := make([]*LocalCosigner, threshold) + nonces := make([][]CosignerNonce, threshold) now := time.Now() @@ -136,103 +130,104 @@ func TestLocalCosignerSign2of2(t *testing.T) { Timestamp: now.UnixNano(), } - ephemeralSharesFor2, err := cosigner1.GetEphemeralSecretParts(hrst) + u, err := uuid.NewRandom() require.NoError(t, err) - publicKeys = append(publicKeys, ephemeralSharesFor2.EncryptedSecrets[0].SourceEphemeralSecretPublicKey) + for i := 0; i < int(total); i++ { + id := i + 1 - ephemeralSharesFor1, err := cosigner2.GetEphemeralSecretParts(hrst) - require.NoError(t, err) - - t.Logf("Shares from 2: %d", len(ephemeralSharesFor1.EncryptedSecrets)) - - publicKeys = append(publicKeys, ephemeralSharesFor1.EncryptedSecrets[0].SourceEphemeralSecretPublicKey) - - ephemeralPublic := tsed25519.AddElements(publicKeys) - - t.Logf("public keys: %x", publicKeys) - t.Logf("eph pub: %x", ephemeralPublic) - // pack a vote into sign bytes - var vote tmProto.Vote - vote.Height = 1 - vote.Round = 0 - vote.Type = tmProto.PrevoteType - vote.Timestamp = now - - signBytes := tm.VoteSignBytes("chain-id", &vote) + key := CosignerEd25519Key{ + PubKey: pubKey, + PrivateShard: privShards[i], + ID: id, + } - sigRes1, err := cosigner1.SetEphemeralSecretPartsAndSign(CosignerSetEphemeralSecretPartsAndSignRequest{ - EncryptedSecrets: ephemeralSharesFor1.EncryptedSecrets, - HRST: hrst, - SignBytes: signBytes, - }) - require.NoError(t, err) + cfg.ThresholdModeConfig.Cosigners[i] = CosignerConfig{ + ShardID: id, + } - sigRes2, err := cosigner2.SetEphemeralSecretPartsAndSign(CosignerSetEphemeralSecretPartsAndSignRequest{ - EncryptedSecrets: ephemeralSharesFor2.EncryptedSecrets, - HRST: hrst, - SignBytes: signBytes, - }) - require.NoError(t, err) + cosignerDir := filepath.Join(tmpDir, fmt.Sprintf("cosigner%d", id)) + err := os.Mkdir(cosignerDir, 0700) + require.NoError(t, err) - sigIds := []int{1, 2} - sigArr := [][]byte{sigRes1.Signature, sigRes2.Signature} + cosigner := NewLocalCosigner( + log.NewNopLogger(), + &RuntimeConfig{ + HomeDir: cosignerDir, + StateDir: cosignerDir, + Config: cfg, + }, + security[i], + "", + ) + + keyBz, err := key.MarshalJSON() + require.NoError(t, err) + err = os.WriteFile(cosigner.config.KeyFilePathCosigner(testChainID), keyBz, 0600) + require.NoError(t, err) - t.Logf("sig arr: %x", sigArr) + defer cosigner.waitForSignStatesToFlushToDisk() - combinedSig := tsed25519.CombineShares(total, sigIds, sigArr) - signature := ephemeralPublic - signature = append(signature, combinedSig...) + err = cosigner.LoadSignStateIfNecessary(testChainID) + require.NoError(t, err) - t.Logf("signature: %x", signature) - require.True(t, privateKey.PubKey().VerifySignature(signBytes, signature)) -} + require.Equal(t, cosigner.GetID(), id) -func TestLocalCosignerWatermark(t *testing.T) { - /* - privateKey := tm_ed25519.GenPrivKey() + if i < int(threshold) { + thresholdCosigners[i] = cosigner - privKeyBytes := [64]byte{} - copy(privKeyBytes[:], privateKey[:]) - secretShares := tsed25519.DealShares(privKeyBytes[:32], 2, 2) + res, err := cosigner.GetNonces(ctx, []uuid.UUID{u}) + require.NoError(t, err) - key1 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[0], - ID: 1, + nonces[i] = res[0].Nonces } + } - stateFile1, err := os.CreateTemp("", "state1.json") - require.NoError(t, err) - defer os.Remove(stateFile1.Name()) + // pack a vote into sign bytes + var vote cometproto.Vote + vote.Height = 1 + vote.Round = 0 + vote.Type = cometproto.PrevoteType + vote.Timestamp = now - signState1, err := LoadOrCreateSignState(stateFile1.Name()) + signBytes := comet.VoteSignBytes("chain-id", &vote) - cosigner1 := NewLocalCosigner(key1, &signState1) + sigs := make([]PartialSignature, threshold) - ephPublicKey, ephPrivateKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) + for i, cosigner := range thresholdCosigners { + cosignerNonces := make([]CosignerNonce, 0, threshold-1) - ephShares := tsed25519.DealShares(ephPrivateKey.Seed(), 2, 2) + for j, nonce := range nonces { + if i == j { + continue + } - signReq1 := CosignerSignRequest{ - EphemeralPublic: ephPublicKey, - EphemeralShareSecret: ephShares[0], - Height: 2, - Round: 0, - Step: 0, - SignBytes: []byte("Hello World!"), + for _, n := range nonce { + if n.DestinationID == cosigner.GetID() { + cosignerNonces = append(cosignerNonces, n) + } + } } - _, err = cosigner1.Sign(signReq1) + sigRes, err := cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ + Nonces: &CosignerUUIDNonces{ + UUID: u, + Nonces: cosignerNonces, + }, + ChainID: testChainID, + HRST: hrst, + SignBytes: signBytes, + }) require.NoError(t, err) - // watermark should have increased after signing - require.Equal(t, signState1.Height, int64(2)) + sigs[i] = PartialSignature{ + ID: cosigner.GetID(), + Signature: sigRes.Signature, + } + } + + combinedSig, err := thresholdCosigners[0].CombineSignatures(testChainID, sigs) + require.NoError(t, err) - // revert the height to a lower number and check if signing is rejected - signReq1.Height = 1 - _, err = cosigner1.Sign(signReq1) - require.Error(t, err, "height regression. Got 1, last height 2") - */ + require.True(t, pubKey.VerifySignature(signBytes, combinedSig)) } diff --git a/signer/metrics.go b/signer/metrics.go index 5709a06a..2145ba77 100644 --- a/signer/metrics.go +++ b/signer/metrics.go @@ -12,7 +12,7 @@ type metricsTimer struct { mu sync.Mutex previousPrecommit, previousPrevote time.Time previousLocalSignStart, previousLocalSignFinish time.Time - previousLocalEphemeralShare time.Time + previousLocalNonce time.Time } func newMetricsTimer() *metricsTimer { @@ -21,7 +21,7 @@ func newMetricsTimer() *metricsTimer { mu: sync.Mutex{}, previousPrecommit: now, previousPrevote: now, previousLocalSignStart: now, previousLocalSignFinish: now, - previousLocalEphemeralShare: now, + previousLocalNonce: now, } } @@ -49,13 +49,13 @@ func (mt *metricsTimer) SetPreviousLocalSignFinish(t time.Time) { mt.previousLocalSignFinish = t } -func (mt *metricsTimer) SetPreviousLocalEphemeralShare(t time.Time) { +func (mt *metricsTimer) SetPreviousLocalNonce(t time.Time) { mt.mu.Lock() defer mt.mu.Unlock() - mt.previousLocalEphemeralShare = t + mt.previousLocalNonce = t } -func (mt *metricsTimer) UpdatePrometheusMetrics(t time.Time) { +func (mt *metricsTimer) UpdatePrometheusMetrics() { mt.mu.Lock() defer mt.mu.Unlock() @@ -64,7 +64,7 @@ func (mt *metricsTimer) UpdatePrometheusMetrics(t time.Time) { secondsSinceLastPrevote.Set(time.Since(mt.previousPrevote).Seconds()) secondsSinceLastLocalSignStart.Set(time.Since(mt.previousLocalSignStart).Seconds()) secondsSinceLastLocalSignFinish.Set(time.Since(mt.previousLocalSignFinish).Seconds()) - secondsSinceLastLocalEphemeralShareTime.Set(time.Since(mt.previousLocalEphemeralShare).Seconds()) + secondsSinceLastLocalNonceTime.Set(time.Since(mt.previousLocalNonce).Seconds()) } var ( @@ -74,48 +74,79 @@ var ( metricsTimeKeeper = newMetricsTimer() // Prometheus Metrics - totalPubKeyRequests = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_pubkey_requests", - Help: "Total times public key requested (High count may indicate validator restarts)", - }) - lastPrecommitHeight = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_last_precommit_height", - Help: "Last Height Precommit Signed", - }) - lastPrevoteHeight = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_last_prevote_height", - Help: "Last Height Prevote Signed", - }) + totalPubKeyRequests = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_pubkey_requests", + Help: "Total times public key requested (High count may indicate validator restarts)", + }, + []string{"chain_id"}, + ) + lastPrecommitHeight = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_last_precommit_height", + Help: "Last Height Precommit Signed", + }, + []string{"chain_id"}, + ) - lastProposalHeight = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_last_proposal_height", - Help: "Last Height Proposal Signed", - }) - lastPrecommitRound = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_last_precommit_round", - Help: "Last Round Precommit Signed", - }) - lastPrevoteRound = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_last_prevote_round", - Help: "Last Round Prevote Signed", - }) - lastProposalRound = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_last_proposal_round", - Help: "Last Round Proposal Signed", - }) + lastPrevoteHeight = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_last_prevote_height", + Help: "Last Height Prevote Signed", + }, + []string{"chain_id"}, + ) - totalPrecommitsSigned = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_precommits_signed", - Help: "Total Precommit Signed", - }) - totalPrevotesSigned = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_prevotes_signed", - Help: "Total Prevote Signed", - }) - totalProposalsSigned = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_proposals_signed", - Help: "Total Proposal Signed", - }) + lastProposalHeight = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_last_proposal_height", + Help: "Last Height Proposal Signed", + }, + []string{"chain_id"}, + ) + lastPrecommitRound = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_last_precommit_round", + Help: "Last Round Precommit Signed", + }, + []string{"chain_id"}, + ) + lastPrevoteRound = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_last_prevote_round", + Help: "Last Round Prevote Signed", + }, + []string{"chain_id"}, + ) + lastProposalRound = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_last_proposal_round", + Help: "Last Round Proposal Signed", + }, + []string{"chain_id"}, + ) + + totalPrecommitsSigned = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_precommits_signed", + Help: "Total Precommit Signed", + }, + []string{"chain_id"}, + ) + totalPrevotesSigned = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_prevotes_signed", + Help: "Total Prevote Signed", + }, + []string{"chain_id"}, + ) + totalProposalsSigned = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_proposals_signed", + Help: "Total Proposal Signed", + }, + []string{"chain_id"}, + ) secondsSinceLastPrecommit = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_precommit", @@ -134,61 +165,97 @@ var ( Help: "Seconds Since Last Local Finish Sign (Should stay below 2 * Block Time)", }) - secondsSinceLastLocalEphemeralShareTime = promauto.NewGauge(prometheus.GaugeOpts{ + secondsSinceLastLocalNonceTime = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_local_ephemeral_share_time", Help: "Seconds Since Last Local Ephemeral Share Sign " + "(Should not increase beyond block time; If high, may indicate raft joining issue for CoSigner) ", }) - missedPrecommits = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_missed_precommits", - Help: "Consecutive Precommit Missed", - }) - missedPrevotes = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_missed_prevotes", - Help: "Consecutive Prevote Missed", - }) - totalMissedPrecommits = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_missed_precommits", - Help: "Total Precommit Missed", - }) - totalMissedPrevotes = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_missed_prevotes", - Help: "Total Prevote Missed", - }) + missedPrecommits = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_missed_precommits", + Help: "Consecutive Precommit Missed", + }, + []string{"chain_id"}, + ) + missedPrevotes = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_missed_prevotes", + Help: "Consecutive Prevote Missed", + }, + []string{"chain_id"}, + ) + totalMissedPrecommits = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_missed_precommits", + Help: "Total Precommit Missed", + }, + []string{"chain_id"}, + ) + totalMissedPrevotes = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_missed_prevotes", + Help: "Total Prevote Missed", + }, + []string{"chain_id"}, + ) - missedEphemeralShares = promauto.NewGaugeVec( + missedNonces = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_missed_ephemeral_shares", Help: "Consecutive Threshold Signature Parts Missed", }, []string{"peerid"}, ) - totalMissedEphemeralShares = promauto.NewCounterVec( + totalMissedNonces = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_missed_ephemeral_shares", Help: "Total Threshold Signature Parts Missed", }, []string{"peerid"}, ) + drainedNonceCache = promauto.NewGauge( + prometheus.GaugeOpts{ + Name: "signer_drained_nonce_cache", + Help: "Consecutive Nonces Requested When Cache is Drained", + }, + ) + totalDrainedNonceCache = promauto.NewCounter( + prometheus.CounterOpts{ + Name: "signer_total_drained_nonce_cache", + Help: "Total Nonces Requested When Cache is Drained", + }, + ) - sentryConnectTries = promauto.NewGauge(prometheus.GaugeOpts{ - Name: "signer_sentry_connect_tries", - Help: "Consecutive Number of times sentry TCP connect has been tried (High count may indicate validator restarts)", - }) - totalSentryConnectTries = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_sentry_connect_tries", - Help: "Total Number of times sentry TCP connect has been tried (High count may indicate validator restarts)", - }) + sentryConnectTries = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "signer_sentry_connect_tries", + Help: "Consecutive Number of times sentry TCP connect has been tried (High count may indicate validator restarts)", + }, + []string{"node"}, + ) + totalSentryConnectTries = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_sentry_connect_tries", + Help: "Total Number of times sentry TCP connect has been tried (High count may indicate validator restarts)", + }, + []string{"node"}, + ) - beyondBlockErrors = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_beyond_block_errors", - Help: "Total Times Signing Started but duplicate height/round request arrives", - }) - failedSignVote = promauto.NewCounter(prometheus.CounterOpts{ - Name: "signer_total_failed_sign_vote", - Help: "Total Times Signer Failed to sign block - Unstarted and Unexepcted Height", - }) + beyondBlockErrors = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_beyond_block_errors", + Help: "Total Times Signing Started but duplicate height/round request arrives", + }, + []string{"chain_id"}, + ) + failedSignVote = promauto.NewCounterVec( + prometheus.CounterOpts{ + Name: "signer_total_failed_sign_vote", + Help: "Total Times Signer Failed to sign block - Unstarted and Unexepcted Height", + }, + []string{"chain_id"}, + ) totalRaftLeader = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_total_raft_leader", @@ -198,11 +265,10 @@ var ( Name: "signer_total_raft_not_leader", Help: "Total Times Signer is NOT Raft Leader (Proxy signing to Raft Leader)", }) - totalRaftLeaderElectiontimeout = promauto.NewCounter(prometheus.CounterOpts{ + totalRaftLeaderElectionTimeout = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_total_raft_leader_election_timeout", Help: "Total Times Raft Leader Failed Election (Lacking Peers)", }) - totalInvalidSignature = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_error_total_invalid_signatures", Help: "Total Times Combined Signature is Invalid", @@ -231,7 +297,7 @@ var ( Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - timedCosignerEphemeralShareLag = promauto.NewSummaryVec( + timedCosignerNonceLag = promauto.NewSummaryVec( prometheus.SummaryOpts{ Name: "signer_cosigner_ephemeral_share_lag_seconds", Help: "Time taken to get cosigner ephemeral share", @@ -252,7 +318,7 @@ var ( func StartMetrics() { // Update elapsed times on an interval basis for { - metricsTimeKeeper.UpdatePrometheusMetrics(time.Now()) + metricsTimeKeeper.UpdatePrometheusMetrics() // Prometheus often only polls every 1 to every few seconds // Frequent updates minimize reporting error. diff --git a/signer/multiresolver/multi.go b/signer/multiresolver/multi.go new file mode 100644 index 00000000..82e5bc6e --- /dev/null +++ b/signer/multiresolver/multi.go @@ -0,0 +1,197 @@ +/*multiresolver package allows you to Dial to multiple hosts/IPs as a single ClientConn. + * + * This was originally sourced from https://github.com/Jille/grpc-multi-resolver + * + * Usage: multi:///127.0.0.1:1234,dns://example.org:1234 + * Note the triple slash at the beginning. + */ +package multiresolver + +import ( + "fmt" + "net/url" + "strings" + "sync" + + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/serviceconfig" +) + +var _ resolver.Builder = builder{} +var _ resolver.ClientConn = &partialClientConn{} +var _ resolver.Resolver = &multiResolver{} + +// Register registers the multiresolver builder +func Register() { + resolver.Register(builder{}) +} + +// builder is a resolver.Builder to build a multiresolver. +type builder struct{} + +// Scheme declares the scheme that should be resolved by this resolver.Builder. +func (builder) Scheme() string { + return "multi" +} + +// Build builds a resolver that will resolve to multiple targets. +func (builder) Build( + target resolver.Target, + cc resolver.ClientConn, + opts resolver.BuildOptions, +) (resolver.Resolver, error) { + mr := &multiResolver{ + pccg: &partialClientConnGroup{ + cc: cc, + }, + } + + rawTargets := strings.Split(target.Endpoint(), ",") + + for _, t := range rawTargets { + if err := mr.resolverBuilder(t, opts); err != nil { + mr.Close() + return nil, err + } + } + + return mr, nil +} + +type partialClientConnGroup struct { + cc resolver.ClientConn + parts []*partialClientConn +} + +func (pccg *partialClientConnGroup) updateState() error { + s := resolver.State{} + pccg.parts[0].mtx.Lock() + s.ServiceConfig = pccg.parts[0].state.ServiceConfig + s.Attributes = pccg.parts[0].state.Attributes + pccg.parts[0].mtx.Unlock() + for _, p := range pccg.parts { + p.mtx.Lock() + s.Addresses = append(s.Addresses, p.state.Addresses...) + p.mtx.Unlock() + } + return pccg.cc.UpdateState(s) +} + +type partialClientConn struct { + parent *partialClientConnGroup + + mtx sync.Mutex + state resolver.State +} + +// UpdateState updates the state of the ClientConn appropriately. +func (cc *partialClientConn) UpdateState(s resolver.State) error { + cc.mtx.Lock() + cc.state = s + cc.mtx.Unlock() + return cc.parent.updateState() +} + +// ReportError notifies the ClientConn that the Resolver encountered an +// error. The ClientConn will notify the load balancer and begin calling +// ResolveNow on the Resolver with exponential backoff. +func (cc *partialClientConn) ReportError(err error) { + cc.parent.cc.ReportError(err) +} + +// NewAddress is called by resolver to notify ClientConn a new list +// of resolved addresses. +// The address list should be the complete list of resolved addresses. +// +// Deprecated: Use UpdateState instead. +func (cc *partialClientConn) NewAddress(addresses []resolver.Address) { + cc.mtx.Lock() + cc.state.Addresses = addresses + cc.mtx.Unlock() + _ = cc.parent.updateState() +} + +// NewServiceConfig is called by resolver to notify ClientConn a new +// service config. The service config should be provided as a json string. +// +// Deprecated: Use UpdateState instead. +func (cc *partialClientConn) NewServiceConfig(serviceConfig string) { + cc.mtx.Lock() + cc.state.ServiceConfig = cc.ParseServiceConfig(serviceConfig) + cc.mtx.Unlock() + _ = cc.parent.updateState() +} + +// ParseServiceConfig parses the provided service config and returns an +// object that provides the parsed config. +func (cc *partialClientConn) ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult { + return cc.parent.cc.ParseServiceConfig(serviceConfigJSON) +} + +type multiResolver struct { + pccg *partialClientConnGroup + children []resolver.Resolver +} + +// resolverBuilder gets the resolver builder for the specific target. +func (m *multiResolver) resolverBuilder( + rawTarget string, + opts resolver.BuildOptions, +) error { + parsable := rawTarget + if !strings.Contains(rawTarget, "://") { + parsable = "tcp://" + rawTarget + } + + u, err := url.Parse(parsable) + if err != nil { + return err + } + + parsedTarget := resolver.Target{URL: *u} + + resolverBuilder := resolver.Get(u.Scheme) + + if resolverBuilder == nil { + // no scheme provided for this member of the multi address, try default scheme + u = &url.URL{ + Scheme: resolver.GetDefaultScheme(), + Path: rawTarget, + } + parsedTarget = resolver.Target{URL: *u} + + resolverBuilder = resolver.Get(u.Scheme) + if resolverBuilder == nil { + return fmt.Errorf("could not get resolver for default scheme: %q", u.Scheme) + } + } + + pcc := &partialClientConn{parent: m.pccg} + m.pccg.parts = append(m.pccg.parts, pcc) + + resolver, err := resolverBuilder.Build(parsedTarget, pcc, opts) + if err != nil { + return err + } + + m.children = append(m.children, resolver) + + return nil +} + +// ResolveNow will be called by gRPC to try to resolve the target name +// again. It's just a hint, resolver can ignore this if it's not necessary. +// +// It could be called multiple times concurrently. +func (m *multiResolver) ResolveNow(opts resolver.ResolveNowOptions) { + for _, r := range m.children { + r.ResolveNow(opts) + } +} + +// Close closes all children resolvers within the multiResolver. +func (m *multiResolver) Close() { + for _, r := range m.children { + r.Close() + } +} diff --git a/signer/multiresolver/multi_test.go b/signer/multiresolver/multi_test.go new file mode 100644 index 00000000..236c07df --- /dev/null +++ b/signer/multiresolver/multi_test.go @@ -0,0 +1,109 @@ +package multiresolver_test + +import ( + "context" + "fmt" + "net" + "path/filepath" + "strconv" + "testing" + "time" + + grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" + "github.com/strangelove-ventures/horcrux/v3/signer" + "github.com/strangelove-ventures/horcrux/v3/signer/multiresolver" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func createListener(nodeID string, homedir string) (string, func(), error) { + sock, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return "", nil, err + } + + port := strconv.Itoa(sock.Addr().(*net.TCPAddr).Port) + + s := signer.NewRaftStore( + nodeID, + homedir, + "127.0.0.1:"+port, + 500*time.Millisecond, + nil, nil, nil) + + transportManager, err := s.Open() + if err != nil { + return "", nil, err + } + + grpcServer := grpc.NewServer() + proto.RegisterCosignerServer(grpcServer, signer.NewCosignerGRPCServer(nil, nil, s)) + transportManager.Register(grpcServer) + + go func() { + _ = grpcServer.Serve(sock) + }() + + return port, func() { + grpcServer.Stop() + }, nil +} + +func TestMultiResolver(t *testing.T) { + targetIP, targetDNS := "multi:///", "multi:///" + + tmp := t.TempDir() + + for i := 0; i < 3; i++ { + port, c, err := createListener(strconv.Itoa(i+1), filepath.Join(tmp, fmt.Sprintf("cosigner%d", i+1))) + require.NoError(t, err) + defer c() + + if i != 0 { + targetIP += "," + targetDNS += "," + } + + targetIP += "127.0.0.1:" + port + targetDNS += "localhost:" + port + } + + multiresolver.Register() + + serviceConfig := `{"loadBalancingConfig": [ { "round_robin": {} } ]}` + retryOpts := []grpcretry.CallOption{ + grpcretry.WithBackoff(grpcretry.BackoffExponential(100 * time.Millisecond)), + grpcretry.WithMax(5), + } + + connDNS, err := grpc.Dial(targetDNS, + grpc.WithDefaultServiceConfig(serviceConfig), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + grpc.WithUnaryInterceptor(grpcretry.UnaryClientInterceptor(retryOpts...)), + ) + require.NoError(t, err) + defer connDNS.Close() + + ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) + defer cancelFunc() + + grpcClient := proto.NewCosignerClient(connDNS) + _, err = grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) + require.NoError(t, err) + + connIP, err := grpc.Dial(targetIP, + grpc.WithDefaultServiceConfig(serviceConfig), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + grpc.WithUnaryInterceptor(grpcretry.UnaryClientInterceptor(retryOpts...)), + ) + require.NoError(t, err) + defer connIP.Close() + + grpcClient = proto.NewCosignerClient(connIP) + _, err = grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) + require.NoError(t, err) +} diff --git a/signer/proto/cosigner.pb.go b/signer/proto/cosigner.pb.go new file mode 100644 index 00000000..1aed51a3 --- /dev/null +++ b/signer/proto/cosigner.pb.go @@ -0,0 +1,4339 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: strangelove/horcrux/cosigner.proto + +package proto + +import ( + context "context" + fmt "fmt" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Block struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` + SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` + VoteExtSignBytes []byte `protobuf:"bytes,5,opt,name=voteExtSignBytes,proto3" json:"voteExtSignBytes,omitempty"` + Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *Block) Reset() { *m = Block{} } +func (m *Block) String() string { return proto.CompactTextString(m) } +func (*Block) ProtoMessage() {} +func (*Block) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{0} +} +func (m *Block) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Block) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Block.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Block) XXX_Merge(src proto.Message) { + xxx_messageInfo_Block.Merge(m, src) +} +func (m *Block) XXX_Size() int { + return m.Size() +} +func (m *Block) XXX_DiscardUnknown() { + xxx_messageInfo_Block.DiscardUnknown(m) +} + +var xxx_messageInfo_Block proto.InternalMessageInfo + +func (m *Block) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *Block) GetRound() int64 { + if m != nil { + return m.Round + } + return 0 +} + +func (m *Block) GetStep() int32 { + if m != nil { + return m.Step + } + return 0 +} + +func (m *Block) GetSignBytes() []byte { + if m != nil { + return m.SignBytes + } + return nil +} + +func (m *Block) GetVoteExtSignBytes() []byte { + if m != nil { + return m.VoteExtSignBytes + } + return nil +} + +func (m *Block) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type SignBlockRequest struct { + ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"` + Block *Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (m *SignBlockRequest) Reset() { *m = SignBlockRequest{} } +func (m *SignBlockRequest) String() string { return proto.CompactTextString(m) } +func (*SignBlockRequest) ProtoMessage() {} +func (*SignBlockRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{1} +} +func (m *SignBlockRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBlockRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBlockRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignBlockRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBlockRequest.Merge(m, src) +} +func (m *SignBlockRequest) XXX_Size() int { + return m.Size() +} +func (m *SignBlockRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignBlockRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBlockRequest proto.InternalMessageInfo + +func (m *SignBlockRequest) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *SignBlockRequest) GetBlock() *Block { + if m != nil { + return m.Block + } + return nil +} + +type SignBlockResponse struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + VoteExtSignature []byte `protobuf:"bytes,2,opt,name=vote_ext_signature,json=voteExtSignature,proto3" json:"vote_ext_signature,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *SignBlockResponse) Reset() { *m = SignBlockResponse{} } +func (m *SignBlockResponse) String() string { return proto.CompactTextString(m) } +func (*SignBlockResponse) ProtoMessage() {} +func (*SignBlockResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{2} +} +func (m *SignBlockResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBlockResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBlockResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignBlockResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBlockResponse.Merge(m, src) +} +func (m *SignBlockResponse) XXX_Size() int { + return m.Size() +} +func (m *SignBlockResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignBlockResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBlockResponse proto.InternalMessageInfo + +func (m *SignBlockResponse) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *SignBlockResponse) GetVoteExtSignature() []byte { + if m != nil { + return m.VoteExtSignature + } + return nil +} + +func (m *SignBlockResponse) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type Nonce struct { + SourceID int32 `protobuf:"varint,1,opt,name=sourceID,proto3" json:"sourceID,omitempty"` + DestinationID int32 `protobuf:"varint,2,opt,name=destinationID,proto3" json:"destinationID,omitempty"` + PubKey []byte `protobuf:"bytes,3,opt,name=pubKey,proto3" json:"pubKey,omitempty"` + Share []byte `protobuf:"bytes,4,opt,name=share,proto3" json:"share,omitempty"` + Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (m *Nonce) Reset() { *m = Nonce{} } +func (m *Nonce) String() string { return proto.CompactTextString(m) } +func (*Nonce) ProtoMessage() {} +func (*Nonce) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{3} +} +func (m *Nonce) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Nonce) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Nonce.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Nonce) XXX_Merge(src proto.Message) { + xxx_messageInfo_Nonce.Merge(m, src) +} +func (m *Nonce) XXX_Size() int { + return m.Size() +} +func (m *Nonce) XXX_DiscardUnknown() { + xxx_messageInfo_Nonce.DiscardUnknown(m) +} + +var xxx_messageInfo_Nonce proto.InternalMessageInfo + +func (m *Nonce) GetSourceID() int32 { + if m != nil { + return m.SourceID + } + return 0 +} + +func (m *Nonce) GetDestinationID() int32 { + if m != nil { + return m.DestinationID + } + return 0 +} + +func (m *Nonce) GetPubKey() []byte { + if m != nil { + return m.PubKey + } + return nil +} + +func (m *Nonce) GetShare() []byte { + if m != nil { + return m.Share + } + return nil +} + +func (m *Nonce) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +type UUIDNonce struct { + Uuid []byte `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` + Nonces []*Nonce `protobuf:"bytes,2,rep,name=nonces,proto3" json:"nonces,omitempty"` +} + +func (m *UUIDNonce) Reset() { *m = UUIDNonce{} } +func (m *UUIDNonce) String() string { return proto.CompactTextString(m) } +func (*UUIDNonce) ProtoMessage() {} +func (*UUIDNonce) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{4} +} +func (m *UUIDNonce) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UUIDNonce) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UUIDNonce.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UUIDNonce) XXX_Merge(src proto.Message) { + xxx_messageInfo_UUIDNonce.Merge(m, src) +} +func (m *UUIDNonce) XXX_Size() int { + return m.Size() +} +func (m *UUIDNonce) XXX_DiscardUnknown() { + xxx_messageInfo_UUIDNonce.DiscardUnknown(m) +} + +var xxx_messageInfo_UUIDNonce proto.InternalMessageInfo + +func (m *UUIDNonce) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + +func (m *UUIDNonce) GetNonces() []*Nonce { + if m != nil { + return m.Nonces + } + return nil +} + +type HRST struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (m *HRST) Reset() { *m = HRST{} } +func (m *HRST) String() string { return proto.CompactTextString(m) } +func (*HRST) ProtoMessage() {} +func (*HRST) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{5} +} +func (m *HRST) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HRST) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HRST.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HRST) XXX_Merge(src proto.Message) { + xxx_messageInfo_HRST.Merge(m, src) +} +func (m *HRST) XXX_Size() int { + return m.Size() +} +func (m *HRST) XXX_DiscardUnknown() { + xxx_messageInfo_HRST.DiscardUnknown(m) +} + +var xxx_messageInfo_HRST proto.InternalMessageInfo + +func (m *HRST) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *HRST) GetRound() int64 { + if m != nil { + return m.Round + } + return 0 +} + +func (m *HRST) GetStep() int32 { + if m != nil { + return m.Step + } + return 0 +} + +func (m *HRST) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type SetNoncesAndSignRequest struct { + Uuid []byte `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` + Nonces []*Nonce `protobuf:"bytes,2,rep,name=nonces,proto3" json:"nonces,omitempty"` + Hrst *HRST `protobuf:"bytes,3,opt,name=hrst,proto3" json:"hrst,omitempty"` + SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` + VoteExtUuid []byte `protobuf:"bytes,5,opt,name=voteExtUuid,proto3" json:"voteExtUuid,omitempty"` + VoteExtNonces []*Nonce `protobuf:"bytes,6,rep,name=voteExtNonces,proto3" json:"voteExtNonces,omitempty"` + VoteExtSignBytes []byte `protobuf:"bytes,7,opt,name=voteExtSignBytes,proto3" json:"voteExtSignBytes,omitempty"` + ChainID string `protobuf:"bytes,8,opt,name=chainID,proto3" json:"chainID,omitempty"` +} + +func (m *SetNoncesAndSignRequest) Reset() { *m = SetNoncesAndSignRequest{} } +func (m *SetNoncesAndSignRequest) String() string { return proto.CompactTextString(m) } +func (*SetNoncesAndSignRequest) ProtoMessage() {} +func (*SetNoncesAndSignRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{6} +} +func (m *SetNoncesAndSignRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SetNoncesAndSignRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SetNoncesAndSignRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SetNoncesAndSignRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetNoncesAndSignRequest.Merge(m, src) +} +func (m *SetNoncesAndSignRequest) XXX_Size() int { + return m.Size() +} +func (m *SetNoncesAndSignRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SetNoncesAndSignRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SetNoncesAndSignRequest proto.InternalMessageInfo + +func (m *SetNoncesAndSignRequest) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetNonces() []*Nonce { + if m != nil { + return m.Nonces + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetHrst() *HRST { + if m != nil { + return m.Hrst + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetSignBytes() []byte { + if m != nil { + return m.SignBytes + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetVoteExtUuid() []byte { + if m != nil { + return m.VoteExtUuid + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetVoteExtNonces() []*Nonce { + if m != nil { + return m.VoteExtNonces + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetVoteExtSignBytes() []byte { + if m != nil { + return m.VoteExtSignBytes + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +type SetNoncesAndSignResponse struct { + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + NoncePublic []byte `protobuf:"bytes,2,opt,name=noncePublic,proto3" json:"noncePublic,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` + VoteExtNoncePublic []byte `protobuf:"bytes,4,opt,name=voteExtNoncePublic,proto3" json:"voteExtNoncePublic,omitempty"` + VoteExtSignature []byte `protobuf:"bytes,5,opt,name=voteExtSignature,proto3" json:"voteExtSignature,omitempty"` +} + +func (m *SetNoncesAndSignResponse) Reset() { *m = SetNoncesAndSignResponse{} } +func (m *SetNoncesAndSignResponse) String() string { return proto.CompactTextString(m) } +func (*SetNoncesAndSignResponse) ProtoMessage() {} +func (*SetNoncesAndSignResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{7} +} +func (m *SetNoncesAndSignResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SetNoncesAndSignResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SetNoncesAndSignResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SetNoncesAndSignResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetNoncesAndSignResponse.Merge(m, src) +} +func (m *SetNoncesAndSignResponse) XXX_Size() int { + return m.Size() +} +func (m *SetNoncesAndSignResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SetNoncesAndSignResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SetNoncesAndSignResponse proto.InternalMessageInfo + +func (m *SetNoncesAndSignResponse) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *SetNoncesAndSignResponse) GetNoncePublic() []byte { + if m != nil { + return m.NoncePublic + } + return nil +} + +func (m *SetNoncesAndSignResponse) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *SetNoncesAndSignResponse) GetVoteExtNoncePublic() []byte { + if m != nil { + return m.VoteExtNoncePublic + } + return nil +} + +func (m *SetNoncesAndSignResponse) GetVoteExtSignature() []byte { + if m != nil { + return m.VoteExtSignature + } + return nil +} + +type GetNoncesRequest struct { + Uuids [][]byte `protobuf:"bytes,1,rep,name=uuids,proto3" json:"uuids,omitempty"` +} + +func (m *GetNoncesRequest) Reset() { *m = GetNoncesRequest{} } +func (m *GetNoncesRequest) String() string { return proto.CompactTextString(m) } +func (*GetNoncesRequest) ProtoMessage() {} +func (*GetNoncesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{8} +} +func (m *GetNoncesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNoncesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNoncesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNoncesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNoncesRequest.Merge(m, src) +} +func (m *GetNoncesRequest) XXX_Size() int { + return m.Size() +} +func (m *GetNoncesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetNoncesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetNoncesRequest proto.InternalMessageInfo + +func (m *GetNoncesRequest) GetUuids() [][]byte { + if m != nil { + return m.Uuids + } + return nil +} + +type GetNoncesResponse struct { + Nonces []*UUIDNonce `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` +} + +func (m *GetNoncesResponse) Reset() { *m = GetNoncesResponse{} } +func (m *GetNoncesResponse) String() string { return proto.CompactTextString(m) } +func (*GetNoncesResponse) ProtoMessage() {} +func (*GetNoncesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{9} +} +func (m *GetNoncesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNoncesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNoncesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNoncesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNoncesResponse.Merge(m, src) +} +func (m *GetNoncesResponse) XXX_Size() int { + return m.Size() +} +func (m *GetNoncesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetNoncesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetNoncesResponse proto.InternalMessageInfo + +func (m *GetNoncesResponse) GetNonces() []*UUIDNonce { + if m != nil { + return m.Nonces + } + return nil +} + +type TransferLeadershipRequest struct { + LeaderID string `protobuf:"bytes,1,opt,name=leaderID,proto3" json:"leaderID,omitempty"` +} + +func (m *TransferLeadershipRequest) Reset() { *m = TransferLeadershipRequest{} } +func (m *TransferLeadershipRequest) String() string { return proto.CompactTextString(m) } +func (*TransferLeadershipRequest) ProtoMessage() {} +func (*TransferLeadershipRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{10} +} +func (m *TransferLeadershipRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TransferLeadershipRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TransferLeadershipRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TransferLeadershipRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferLeadershipRequest.Merge(m, src) +} +func (m *TransferLeadershipRequest) XXX_Size() int { + return m.Size() +} +func (m *TransferLeadershipRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TransferLeadershipRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferLeadershipRequest proto.InternalMessageInfo + +func (m *TransferLeadershipRequest) GetLeaderID() string { + if m != nil { + return m.LeaderID + } + return "" +} + +type TransferLeadershipResponse struct { + LeaderID string `protobuf:"bytes,1,opt,name=leaderID,proto3" json:"leaderID,omitempty"` + LeaderAddress string `protobuf:"bytes,2,opt,name=leaderAddress,proto3" json:"leaderAddress,omitempty"` +} + +func (m *TransferLeadershipResponse) Reset() { *m = TransferLeadershipResponse{} } +func (m *TransferLeadershipResponse) String() string { return proto.CompactTextString(m) } +func (*TransferLeadershipResponse) ProtoMessage() {} +func (*TransferLeadershipResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{11} +} +func (m *TransferLeadershipResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TransferLeadershipResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TransferLeadershipResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TransferLeadershipResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TransferLeadershipResponse.Merge(m, src) +} +func (m *TransferLeadershipResponse) XXX_Size() int { + return m.Size() +} +func (m *TransferLeadershipResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TransferLeadershipResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TransferLeadershipResponse proto.InternalMessageInfo + +func (m *TransferLeadershipResponse) GetLeaderID() string { + if m != nil { + return m.LeaderID + } + return "" +} + +func (m *TransferLeadershipResponse) GetLeaderAddress() string { + if m != nil { + return m.LeaderAddress + } + return "" +} + +type GetLeaderRequest struct { +} + +func (m *GetLeaderRequest) Reset() { *m = GetLeaderRequest{} } +func (m *GetLeaderRequest) String() string { return proto.CompactTextString(m) } +func (*GetLeaderRequest) ProtoMessage() {} +func (*GetLeaderRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{12} +} +func (m *GetLeaderRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetLeaderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetLeaderRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetLeaderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLeaderRequest.Merge(m, src) +} +func (m *GetLeaderRequest) XXX_Size() int { + return m.Size() +} +func (m *GetLeaderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetLeaderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLeaderRequest proto.InternalMessageInfo + +type GetLeaderResponse struct { + Leader int32 `protobuf:"varint,1,opt,name=leader,proto3" json:"leader,omitempty"` +} + +func (m *GetLeaderResponse) Reset() { *m = GetLeaderResponse{} } +func (m *GetLeaderResponse) String() string { return proto.CompactTextString(m) } +func (*GetLeaderResponse) ProtoMessage() {} +func (*GetLeaderResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{13} +} +func (m *GetLeaderResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetLeaderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetLeaderResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetLeaderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLeaderResponse.Merge(m, src) +} +func (m *GetLeaderResponse) XXX_Size() int { + return m.Size() +} +func (m *GetLeaderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetLeaderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLeaderResponse proto.InternalMessageInfo + +func (m *GetLeaderResponse) GetLeader() int32 { + if m != nil { + return m.Leader + } + return 0 +} + +type PingRequest struct { +} + +func (m *PingRequest) Reset() { *m = PingRequest{} } +func (m *PingRequest) String() string { return proto.CompactTextString(m) } +func (*PingRequest) ProtoMessage() {} +func (*PingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{14} +} +func (m *PingRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PingRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingRequest.Merge(m, src) +} +func (m *PingRequest) XXX_Size() int { + return m.Size() +} +func (m *PingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PingRequest proto.InternalMessageInfo + +type PingResponse struct { +} + +func (m *PingResponse) Reset() { *m = PingResponse{} } +func (m *PingResponse) String() string { return proto.CompactTextString(m) } +func (*PingResponse) ProtoMessage() {} +func (*PingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{15} +} +func (m *PingResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PingResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingResponse.Merge(m, src) +} +func (m *PingResponse) XXX_Size() int { + return m.Size() +} +func (m *PingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PingResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Block)(nil), "strangelove.horcrux.Block") + proto.RegisterType((*SignBlockRequest)(nil), "strangelove.horcrux.SignBlockRequest") + proto.RegisterType((*SignBlockResponse)(nil), "strangelove.horcrux.SignBlockResponse") + proto.RegisterType((*Nonce)(nil), "strangelove.horcrux.Nonce") + proto.RegisterType((*UUIDNonce)(nil), "strangelove.horcrux.UUIDNonce") + proto.RegisterType((*HRST)(nil), "strangelove.horcrux.HRST") + proto.RegisterType((*SetNoncesAndSignRequest)(nil), "strangelove.horcrux.SetNoncesAndSignRequest") + proto.RegisterType((*SetNoncesAndSignResponse)(nil), "strangelove.horcrux.SetNoncesAndSignResponse") + proto.RegisterType((*GetNoncesRequest)(nil), "strangelove.horcrux.GetNoncesRequest") + proto.RegisterType((*GetNoncesResponse)(nil), "strangelove.horcrux.GetNoncesResponse") + proto.RegisterType((*TransferLeadershipRequest)(nil), "strangelove.horcrux.TransferLeadershipRequest") + proto.RegisterType((*TransferLeadershipResponse)(nil), "strangelove.horcrux.TransferLeadershipResponse") + proto.RegisterType((*GetLeaderRequest)(nil), "strangelove.horcrux.GetLeaderRequest") + proto.RegisterType((*GetLeaderResponse)(nil), "strangelove.horcrux.GetLeaderResponse") + proto.RegisterType((*PingRequest)(nil), "strangelove.horcrux.PingRequest") + proto.RegisterType((*PingResponse)(nil), "strangelove.horcrux.PingResponse") +} + +func init() { + proto.RegisterFile("strangelove/horcrux/cosigner.proto", fileDescriptor_b7a1f695b94b848a) +} + +var fileDescriptor_b7a1f695b94b848a = []byte{ + // 841 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0xe3, 0x54, + 0x10, 0x8f, 0x13, 0x3b, 0x9b, 0x4c, 0x5a, 0x94, 0x7d, 0xac, 0x16, 0xaf, 0x85, 0xa2, 0xf0, 0x04, + 0x28, 0x82, 0x6d, 0x82, 0xb2, 0x12, 0xbd, 0xd2, 0x52, 0x04, 0x55, 0x01, 0x15, 0xa7, 0xbd, 0xa0, + 0xaa, 0x95, 0xe3, 0xbc, 0xc6, 0x16, 0xa9, 0x9d, 0xfa, 0x3d, 0x87, 0xf6, 0xc0, 0x77, 0xe0, 0xc2, + 0x07, 0xe1, 0x5b, 0x70, 0xec, 0x81, 0x43, 0x6f, 0xa0, 0xf6, 0x8b, 0xac, 0xde, 0x1f, 0x3b, 0xb6, + 0xe3, 0xb4, 0x3d, 0xf4, 0x14, 0xcf, 0xf8, 0x37, 0x6f, 0xe6, 0x37, 0xf3, 0x9b, 0x17, 0x03, 0xa6, + 0x2c, 0x72, 0x82, 0x29, 0x99, 0x85, 0x0b, 0x32, 0xf0, 0xc2, 0xc8, 0x8d, 0xe2, 0xab, 0x81, 0x1b, + 0x52, 0x7f, 0x1a, 0x90, 0xa8, 0x3f, 0x8f, 0x42, 0x16, 0xa2, 0x0f, 0x33, 0x98, 0xbe, 0xc2, 0xe0, + 0xbf, 0x35, 0x30, 0x76, 0x67, 0xa1, 0xfb, 0x1b, 0x7a, 0x0d, 0x75, 0x8f, 0xf8, 0x53, 0x8f, 0x99, + 0x5a, 0x57, 0xeb, 0xd5, 0x6c, 0x65, 0xa1, 0x57, 0x60, 0x44, 0x61, 0x1c, 0x4c, 0xcc, 0xaa, 0x70, + 0x4b, 0x03, 0x21, 0xd0, 0x29, 0x23, 0x73, 0xb3, 0xd6, 0xd5, 0x7a, 0x86, 0x2d, 0x9e, 0xd1, 0xc7, + 0xd0, 0xe4, 0x09, 0x77, 0xaf, 0x19, 0xa1, 0xa6, 0xde, 0xd5, 0x7a, 0x1b, 0xf6, 0xd2, 0x81, 0xbe, + 0x80, 0xf6, 0x22, 0x64, 0xe4, 0xbb, 0x2b, 0x36, 0x4a, 0x41, 0x86, 0x00, 0xad, 0xf8, 0xf9, 0x49, + 0xcc, 0xbf, 0x20, 0x94, 0x39, 0x17, 0x73, 0xb3, 0x2e, 0xf2, 0x2e, 0x1d, 0xf8, 0x14, 0xda, 0x02, + 0xca, 0xcb, 0xb6, 0xc9, 0x65, 0x4c, 0x28, 0x43, 0x26, 0xbc, 0x70, 0x3d, 0xc7, 0x0f, 0xf6, 0xf7, + 0x44, 0xf9, 0x4d, 0x3b, 0x31, 0xd1, 0x57, 0x60, 0x8c, 0x39, 0x52, 0xd4, 0xdf, 0x1a, 0x5a, 0xfd, + 0x92, 0x36, 0xf4, 0xe5, 0x59, 0x12, 0x88, 0xff, 0x80, 0x97, 0x99, 0xf3, 0xe9, 0x3c, 0x0c, 0x28, + 0x49, 0xc8, 0x39, 0x2c, 0x8e, 0x88, 0x48, 0xa1, 0xc8, 0x09, 0x07, 0x7a, 0x0b, 0x88, 0x93, 0x38, + 0x23, 0x57, 0xec, 0x6c, 0x09, 0xab, 0xae, 0xd0, 0x93, 0xe8, 0x1c, 0xbd, 0x5a, 0x91, 0xde, 0x5f, + 0x1a, 0x18, 0x3f, 0x87, 0x81, 0x4b, 0x90, 0x05, 0x0d, 0x1a, 0xc6, 0x91, 0x4b, 0x14, 0x2b, 0xc3, + 0x4e, 0x6d, 0xf4, 0x29, 0x6c, 0x4e, 0x08, 0x65, 0x7e, 0xe0, 0x30, 0x3f, 0xe4, 0xb4, 0xab, 0x02, + 0x90, 0x77, 0xf2, 0xa1, 0xce, 0xe3, 0xf1, 0x01, 0xb9, 0x16, 0x69, 0x36, 0x6c, 0x65, 0xf1, 0xa1, + 0x52, 0xcf, 0x89, 0x88, 0x1a, 0x93, 0x34, 0xf2, 0x1c, 0x8d, 0x02, 0x47, 0x3c, 0x82, 0xe6, 0xf1, + 0xf1, 0xfe, 0x9e, 0x2c, 0x0d, 0x81, 0x1e, 0xc7, 0xfe, 0x44, 0x75, 0x42, 0x3c, 0xa3, 0x21, 0xd4, + 0x03, 0xfe, 0x92, 0x9a, 0xd5, 0x6e, 0x6d, 0x6d, 0xab, 0x45, 0xbc, 0xad, 0x90, 0xf8, 0x1c, 0xf4, + 0x1f, 0xec, 0xd1, 0xd1, 0xf3, 0xa8, 0x6f, 0xd9, 0x54, 0xbd, 0xd8, 0xd4, 0xdb, 0x2a, 0x7c, 0x34, + 0x22, 0x4c, 0x24, 0xa7, 0x3b, 0xc1, 0x84, 0x0f, 0x23, 0xd1, 0xce, 0x33, 0x71, 0x41, 0x5b, 0xa0, + 0x7b, 0x11, 0x65, 0xa2, 0xaa, 0xd6, 0xf0, 0x4d, 0x69, 0x04, 0x27, 0x6b, 0x0b, 0xd8, 0x23, 0xeb, + 0xd2, 0x85, 0x96, 0xd2, 0xcd, 0x31, 0xaf, 0x4d, 0x4e, 0x23, 0xeb, 0x42, 0xdf, 0xc0, 0xa6, 0x32, + 0x25, 0x2b, 0xb3, 0xfe, 0x68, 0xa5, 0xf9, 0x80, 0xd2, 0x95, 0x7c, 0xb1, 0x66, 0x25, 0x33, 0x0b, + 0xd6, 0xc8, 0x2d, 0x18, 0xfe, 0x57, 0x03, 0x73, 0xb5, 0xb5, 0xcb, 0xb5, 0x59, 0x4e, 0x45, 0x2b, + 0x4c, 0x85, 0x93, 0x14, 0xbd, 0x3b, 0x8c, 0xc7, 0x33, 0xdf, 0x55, 0xfb, 0x92, 0x75, 0xe5, 0x25, + 0x59, 0x2b, 0xae, 0x5d, 0x5f, 0xae, 0x5d, 0xc2, 0x48, 0x1d, 0x23, 0x7b, 0x59, 0xf2, 0xa6, 0x40, + 0x38, 0xab, 0xf3, 0x15, 0x3f, 0xee, 0x41, 0xfb, 0xfb, 0x84, 0x55, 0xa2, 0x94, 0x57, 0x60, 0x70, + 0x75, 0x50, 0x53, 0xeb, 0xd6, 0xf8, 0xda, 0x08, 0x03, 0x1f, 0xc0, 0xcb, 0x0c, 0x52, 0x11, 0xff, + 0x3a, 0x15, 0x90, 0x26, 0xc6, 0xd2, 0x29, 0x1d, 0x4b, 0xba, 0x50, 0xe9, 0x42, 0x6c, 0xc3, 0x9b, + 0xa3, 0xc8, 0x09, 0xe8, 0x39, 0x89, 0x7e, 0x24, 0xce, 0x84, 0x44, 0xd4, 0xf3, 0xe7, 0x49, 0x7e, + 0x0b, 0x1a, 0x33, 0xe1, 0x4c, 0xaf, 0xb9, 0xd4, 0xc6, 0xa7, 0x60, 0x95, 0x05, 0xaa, 0x72, 0x1e, + 0x88, 0xe4, 0x57, 0x89, 0x7c, 0xde, 0x99, 0x4c, 0x22, 0x42, 0xa9, 0x98, 0x43, 0xd3, 0xce, 0x3b, + 0x31, 0x12, 0xfd, 0x90, 0x47, 0xab, 0x7a, 0xf0, 0x97, 0x82, 0x79, 0xe2, 0x53, 0xa9, 0x5e, 0x43, + 0x5d, 0x46, 0xaa, 0x3b, 0x4b, 0x59, 0x78, 0x13, 0x5a, 0x87, 0x7e, 0x30, 0x4d, 0x62, 0x3f, 0x80, + 0x0d, 0x69, 0xca, 0xb0, 0xe1, 0x7f, 0x3a, 0x34, 0xbe, 0x55, 0xff, 0x58, 0xe8, 0x04, 0x9a, 0xe9, + 0x15, 0x8c, 0x3e, 0x2b, 0x6d, 0x5d, 0xf1, 0x2f, 0xc0, 0xfa, 0xfc, 0x31, 0x98, 0x4c, 0x84, 0x2b, + 0xe8, 0x12, 0xda, 0x45, 0xc1, 0xa2, 0xb7, 0xe5, 0xd1, 0xe5, 0x57, 0x86, 0xb5, 0xf5, 0x44, 0x74, + 0x9a, 0xf2, 0x04, 0x9a, 0xa9, 0x46, 0xd6, 0x10, 0x2a, 0xaa, 0x6d, 0x0d, 0xa1, 0x15, 0xa9, 0xe1, + 0x0a, 0xfa, 0x1d, 0xd0, 0xea, 0xec, 0x51, 0xbf, 0x34, 0x7e, 0xad, 0xba, 0xac, 0xc1, 0x93, 0xf1, + 0x05, 0x5a, 0xf2, 0xd5, 0x7a, 0x5a, 0x39, 0xd1, 0xac, 0xa7, 0x95, 0xd7, 0x11, 0xae, 0xa0, 0x9f, + 0x40, 0xe7, 0x12, 0x41, 0xdd, 0xd2, 0x88, 0x8c, 0x98, 0xac, 0x4f, 0x1e, 0x40, 0x24, 0xc7, 0xed, + 0xfe, 0xf2, 0xcf, 0x5d, 0x47, 0xbb, 0xb9, 0xeb, 0x68, 0xff, 0xdf, 0x75, 0xb4, 0x3f, 0xef, 0x3b, + 0x95, 0x9b, 0xfb, 0x4e, 0xe5, 0xf6, 0xbe, 0x53, 0xf9, 0x75, 0x7b, 0xea, 0x33, 0x2f, 0x1e, 0xf7, + 0xdd, 0xf0, 0x62, 0x90, 0x39, 0x68, 0x6b, 0x41, 0x02, 0x7e, 0x17, 0xd0, 0xf4, 0x93, 0x6a, 0xf1, + 0x6e, 0x20, 0x15, 0x3a, 0x10, 0xdf, 0x54, 0xe3, 0xba, 0xf8, 0x79, 0xf7, 0x3e, 0x00, 0x00, 0xff, + 0xff, 0x61, 0xe1, 0x3e, 0xbf, 0x80, 0x09, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// CosignerClient is the client API for Cosigner service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type CosignerClient interface { + SignBlock(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) + SetNoncesAndSign(ctx context.Context, in *SetNoncesAndSignRequest, opts ...grpc.CallOption) (*SetNoncesAndSignResponse, error) + GetNonces(ctx context.Context, in *GetNoncesRequest, opts ...grpc.CallOption) (*GetNoncesResponse, error) + TransferLeadership(ctx context.Context, in *TransferLeadershipRequest, opts ...grpc.CallOption) (*TransferLeadershipResponse, error) + GetLeader(ctx context.Context, in *GetLeaderRequest, opts ...grpc.CallOption) (*GetLeaderResponse, error) + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) +} + +type cosignerClient struct { + cc grpc1.ClientConn +} + +func NewCosignerClient(cc grpc1.ClientConn) CosignerClient { + return &cosignerClient{cc} +} + +func (c *cosignerClient) SignBlock(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) { + out := new(SignBlockResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.Cosigner/SignBlock", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) SetNoncesAndSign(ctx context.Context, in *SetNoncesAndSignRequest, opts ...grpc.CallOption) (*SetNoncesAndSignResponse, error) { + out := new(SetNoncesAndSignResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.Cosigner/SetNoncesAndSign", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) GetNonces(ctx context.Context, in *GetNoncesRequest, opts ...grpc.CallOption) (*GetNoncesResponse, error) { + out := new(GetNoncesResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.Cosigner/GetNonces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) TransferLeadership(ctx context.Context, in *TransferLeadershipRequest, opts ...grpc.CallOption) (*TransferLeadershipResponse, error) { + out := new(TransferLeadershipResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.Cosigner/TransferLeadership", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) GetLeader(ctx context.Context, in *GetLeaderRequest, opts ...grpc.CallOption) (*GetLeaderResponse, error) { + out := new(GetLeaderResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.Cosigner/GetLeader", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { + out := new(PingResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.Cosigner/Ping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CosignerServer is the server API for Cosigner service. +type CosignerServer interface { + SignBlock(context.Context, *SignBlockRequest) (*SignBlockResponse, error) + SetNoncesAndSign(context.Context, *SetNoncesAndSignRequest) (*SetNoncesAndSignResponse, error) + GetNonces(context.Context, *GetNoncesRequest) (*GetNoncesResponse, error) + TransferLeadership(context.Context, *TransferLeadershipRequest) (*TransferLeadershipResponse, error) + GetLeader(context.Context, *GetLeaderRequest) (*GetLeaderResponse, error) + Ping(context.Context, *PingRequest) (*PingResponse, error) +} + +// UnimplementedCosignerServer can be embedded to have forward compatible implementations. +type UnimplementedCosignerServer struct { +} + +func (*UnimplementedCosignerServer) SignBlock(ctx context.Context, req *SignBlockRequest) (*SignBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignBlock not implemented") +} +func (*UnimplementedCosignerServer) SetNoncesAndSign(ctx context.Context, req *SetNoncesAndSignRequest) (*SetNoncesAndSignResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetNoncesAndSign not implemented") +} +func (*UnimplementedCosignerServer) GetNonces(ctx context.Context, req *GetNoncesRequest) (*GetNoncesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNonces not implemented") +} +func (*UnimplementedCosignerServer) TransferLeadership(ctx context.Context, req *TransferLeadershipRequest) (*TransferLeadershipResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TransferLeadership not implemented") +} +func (*UnimplementedCosignerServer) GetLeader(ctx context.Context, req *GetLeaderRequest) (*GetLeaderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLeader not implemented") +} +func (*UnimplementedCosignerServer) Ping(ctx context.Context, req *PingRequest) (*PingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") +} + +func RegisterCosignerServer(s grpc1.Server, srv CosignerServer) { + s.RegisterService(&_Cosigner_serviceDesc, srv) +} + +func _Cosigner_SignBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).SignBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.Cosigner/SignBlock", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).SignBlock(ctx, req.(*SignBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_SetNoncesAndSign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetNoncesAndSignRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).SetNoncesAndSign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.Cosigner/SetNoncesAndSign", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).SetNoncesAndSign(ctx, req.(*SetNoncesAndSignRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_GetNonces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNoncesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).GetNonces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.Cosigner/GetNonces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).GetNonces(ctx, req.(*GetNoncesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_TransferLeadership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransferLeadershipRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).TransferLeadership(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.Cosigner/TransferLeadership", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).TransferLeadership(ctx, req.(*TransferLeadershipRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_GetLeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLeaderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).GetLeader(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.Cosigner/GetLeader", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).GetLeader(ctx, req.(*GetLeaderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.Cosigner/Ping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Cosigner_serviceDesc = grpc.ServiceDesc{ + ServiceName: "strangelove.horcrux.Cosigner", + HandlerType: (*CosignerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SignBlock", + Handler: _Cosigner_SignBlock_Handler, + }, + { + MethodName: "SetNoncesAndSign", + Handler: _Cosigner_SetNoncesAndSign_Handler, + }, + { + MethodName: "GetNonces", + Handler: _Cosigner_GetNonces_Handler, + }, + { + MethodName: "TransferLeadership", + Handler: _Cosigner_TransferLeadership_Handler, + }, + { + MethodName: "GetLeader", + Handler: _Cosigner_GetLeader_Handler, + }, + { + MethodName: "Ping", + Handler: _Cosigner_Ping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "strangelove/horcrux/cosigner.proto", +} + +func (m *Block) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Block) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Block) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x30 + } + if len(m.VoteExtSignBytes) > 0 { + i -= len(m.VoteExtSignBytes) + copy(dAtA[i:], m.VoteExtSignBytes) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignBytes))) + i-- + dAtA[i] = 0x2a + } + if len(m.SignBytes) > 0 { + i -= len(m.SignBytes) + copy(dAtA[i:], m.SignBytes) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.SignBytes))) + i-- + dAtA[i] = 0x22 + } + if m.Step != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Step)) + i-- + dAtA[i] = 0x18 + } + if m.Round != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Round)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SignBlockRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignBlockRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBlockRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Block != nil { + { + size, err := m.Block.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignBlockResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignBlockResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x18 + } + if len(m.VoteExtSignature) > 0 { + i -= len(m.VoteExtSignature) + copy(dAtA[i:], m.VoteExtSignature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignature))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Nonce) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Nonce) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Nonce) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x2a + } + if len(m.Share) > 0 { + i -= len(m.Share) + copy(dAtA[i:], m.Share) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Share))) + i-- + dAtA[i] = 0x22 + } + if len(m.PubKey) > 0 { + i -= len(m.PubKey) + copy(dAtA[i:], m.PubKey) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.PubKey))) + i-- + dAtA[i] = 0x1a + } + if m.DestinationID != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.DestinationID)) + i-- + dAtA[i] = 0x10 + } + if m.SourceID != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.SourceID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *UUIDNonce) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UUIDNonce) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UUIDNonce) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Nonces) > 0 { + for iNdEx := len(m.Nonces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nonces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Uuid) > 0 { + i -= len(m.Uuid) + copy(dAtA[i:], m.Uuid) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Uuid))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *HRST) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HRST) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HRST) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Timestamp != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x20 + } + if m.Step != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Step)) + i-- + dAtA[i] = 0x18 + } + if m.Round != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Round)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SetNoncesAndSignRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SetNoncesAndSignRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SetNoncesAndSignRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0x42 + } + if len(m.VoteExtSignBytes) > 0 { + i -= len(m.VoteExtSignBytes) + copy(dAtA[i:], m.VoteExtSignBytes) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignBytes))) + i-- + dAtA[i] = 0x3a + } + if len(m.VoteExtNonces) > 0 { + for iNdEx := len(m.VoteExtNonces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.VoteExtNonces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.VoteExtUuid) > 0 { + i -= len(m.VoteExtUuid) + copy(dAtA[i:], m.VoteExtUuid) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtUuid))) + i-- + dAtA[i] = 0x2a + } + if len(m.SignBytes) > 0 { + i -= len(m.SignBytes) + copy(dAtA[i:], m.SignBytes) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.SignBytes))) + i-- + dAtA[i] = 0x22 + } + if m.Hrst != nil { + { + size, err := m.Hrst.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Nonces) > 0 { + for iNdEx := len(m.Nonces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nonces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Uuid) > 0 { + i -= len(m.Uuid) + copy(dAtA[i:], m.Uuid) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Uuid))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SetNoncesAndSignResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SetNoncesAndSignResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SetNoncesAndSignResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.VoteExtSignature) > 0 { + i -= len(m.VoteExtSignature) + copy(dAtA[i:], m.VoteExtSignature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignature))) + i-- + dAtA[i] = 0x2a + } + if len(m.VoteExtNoncePublic) > 0 { + i -= len(m.VoteExtNoncePublic) + copy(dAtA[i:], m.VoteExtNoncePublic) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtNoncePublic))) + i-- + dAtA[i] = 0x22 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0x1a + } + if len(m.NoncePublic) > 0 { + i -= len(m.NoncePublic) + copy(dAtA[i:], m.NoncePublic) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.NoncePublic))) + i-- + dAtA[i] = 0x12 + } + if m.Timestamp != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *GetNoncesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetNoncesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNoncesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Uuids) > 0 { + for iNdEx := len(m.Uuids) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Uuids[iNdEx]) + copy(dAtA[i:], m.Uuids[iNdEx]) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Uuids[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GetNoncesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetNoncesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNoncesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Nonces) > 0 { + for iNdEx := len(m.Nonces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nonces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *TransferLeadershipRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TransferLeadershipRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TransferLeadershipRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.LeaderID) > 0 { + i -= len(m.LeaderID) + copy(dAtA[i:], m.LeaderID) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.LeaderID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TransferLeadershipResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TransferLeadershipResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TransferLeadershipResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.LeaderAddress) > 0 { + i -= len(m.LeaderAddress) + copy(dAtA[i:], m.LeaderAddress) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.LeaderAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.LeaderID) > 0 { + i -= len(m.LeaderID) + copy(dAtA[i:], m.LeaderID) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.LeaderID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetLeaderRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetLeaderRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetLeaderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetLeaderResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetLeaderResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetLeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Leader != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Leader)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *PingRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PingRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *PingResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintCosigner(dAtA []byte, offset int, v uint64) int { + offset -= sovCosigner(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Block) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovCosigner(uint64(m.Height)) + } + if m.Round != 0 { + n += 1 + sovCosigner(uint64(m.Round)) + } + if m.Step != 0 { + n += 1 + sovCosigner(uint64(m.Step)) + } + l = len(m.SignBytes) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.VoteExtSignBytes) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovCosigner(uint64(m.Timestamp)) + } + return n +} + +func (m *SignBlockRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if m.Block != nil { + l = m.Block.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + return n +} + +func (m *SignBlockResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.VoteExtSignature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if m.Timestamp != 0 { + n += 1 + sovCosigner(uint64(m.Timestamp)) + } + return n +} + +func (m *Nonce) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SourceID != 0 { + n += 1 + sovCosigner(uint64(m.SourceID)) + } + if m.DestinationID != 0 { + n += 1 + sovCosigner(uint64(m.DestinationID)) + } + l = len(m.PubKey) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.Share) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + return n +} + +func (m *UUIDNonce) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Uuid) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if len(m.Nonces) > 0 { + for _, e := range m.Nonces { + l = e.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + } + return n +} + +func (m *HRST) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovCosigner(uint64(m.Height)) + } + if m.Round != 0 { + n += 1 + sovCosigner(uint64(m.Round)) + } + if m.Step != 0 { + n += 1 + sovCosigner(uint64(m.Step)) + } + if m.Timestamp != 0 { + n += 1 + sovCosigner(uint64(m.Timestamp)) + } + return n +} + +func (m *SetNoncesAndSignRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Uuid) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if len(m.Nonces) > 0 { + for _, e := range m.Nonces { + l = e.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + } + if m.Hrst != nil { + l = m.Hrst.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.SignBytes) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.VoteExtUuid) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if len(m.VoteExtNonces) > 0 { + for _, e := range m.VoteExtNonces { + l = e.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + } + l = len(m.VoteExtSignBytes) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + return n +} + +func (m *SetNoncesAndSignResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Timestamp != 0 { + n += 1 + sovCosigner(uint64(m.Timestamp)) + } + l = len(m.NoncePublic) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.VoteExtNoncePublic) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.VoteExtSignature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + return n +} + +func (m *GetNoncesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Uuids) > 0 { + for _, b := range m.Uuids { + l = len(b) + n += 1 + l + sovCosigner(uint64(l)) + } + } + return n +} + +func (m *GetNoncesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Nonces) > 0 { + for _, e := range m.Nonces { + l = e.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + } + return n +} + +func (m *TransferLeadershipRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.LeaderID) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + return n +} + +func (m *TransferLeadershipResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.LeaderID) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.LeaderAddress) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + return n +} + +func (m *GetLeaderRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetLeaderResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Leader != 0 { + n += 1 + sovCosigner(uint64(m.Leader)) + } + return n +} + +func (m *PingRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *PingResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovCosigner(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCosigner(x uint64) (n int) { + return sovCosigner(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Block) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Block: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Block: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType) + } + m.Round = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Round |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Step", wireType) + } + m.Step = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Step |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignBytes = append(m.SignBytes[:0], dAtA[iNdEx:postIndex]...) + if m.SignBytes == nil { + m.SignBytes = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtSignBytes = append(m.VoteExtSignBytes[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignBytes == nil { + m.VoteExtSignBytes = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBlockRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignBlockRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBlockRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Block", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Block == nil { + m.Block = &Block{} + } + if err := m.Block.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBlockResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignBlockResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBlockResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtSignature = append(m.VoteExtSignature[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignature == nil { + m.VoteExtSignature = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Nonce) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Nonce: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Nonce: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceID", wireType) + } + m.SourceID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SourceID |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DestinationID", wireType) + } + m.DestinationID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DestinationID |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKey = append(m.PubKey[:0], dAtA[iNdEx:postIndex]...) + if m.PubKey == nil { + m.PubKey = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Share", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Share = append(m.Share[:0], dAtA[iNdEx:postIndex]...) + if m.Share == nil { + m.Share = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UUIDNonce) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UUIDNonce: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UUIDNonce: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuid = append(m.Uuid[:0], dAtA[iNdEx:postIndex]...) + if m.Uuid == nil { + m.Uuid = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonces = append(m.Nonces, &Nonce{}) + if err := m.Nonces[len(m.Nonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HRST) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HRST: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HRST: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType) + } + m.Round = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Round |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Step", wireType) + } + m.Step = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Step |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SetNoncesAndSignRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SetNoncesAndSignRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SetNoncesAndSignRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuid = append(m.Uuid[:0], dAtA[iNdEx:postIndex]...) + if m.Uuid == nil { + m.Uuid = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonces = append(m.Nonces, &Nonce{}) + if err := m.Nonces[len(m.Nonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hrst", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Hrst == nil { + m.Hrst = &HRST{} + } + if err := m.Hrst.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignBytes = append(m.SignBytes[:0], dAtA[iNdEx:postIndex]...) + if m.SignBytes == nil { + m.SignBytes = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtUuid", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtUuid = append(m.VoteExtUuid[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtUuid == nil { + m.VoteExtUuid = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtNonces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtNonces = append(m.VoteExtNonces, &Nonce{}) + if err := m.VoteExtNonces[len(m.VoteExtNonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtSignBytes = append(m.VoteExtSignBytes[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignBytes == nil { + m.VoteExtSignBytes = []byte{} + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SetNoncesAndSignResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SetNoncesAndSignResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SetNoncesAndSignResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NoncePublic", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NoncePublic = append(m.NoncePublic[:0], dAtA[iNdEx:postIndex]...) + if m.NoncePublic == nil { + m.NoncePublic = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtNoncePublic", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtNoncePublic = append(m.VoteExtNoncePublic[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtNoncePublic == nil { + m.VoteExtNoncePublic = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.VoteExtSignature = append(m.VoteExtSignature[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignature == nil { + m.VoteExtSignature = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetNoncesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetNoncesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetNoncesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuids", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuids = append(m.Uuids, make([]byte, postIndex-iNdEx)) + copy(m.Uuids[len(m.Uuids)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetNoncesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetNoncesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetNoncesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonces = append(m.Nonces, &UUIDNonce{}) + if err := m.Nonces[len(m.Nonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TransferLeadershipRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TransferLeadershipRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TransferLeadershipRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LeaderID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LeaderID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TransferLeadershipResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TransferLeadershipResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TransferLeadershipResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LeaderID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LeaderID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LeaderAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LeaderAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetLeaderRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetLeaderRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetLeaderRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetLeaderResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetLeaderResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetLeaderResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType) + } + m.Leader = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Leader |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PingRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PingResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCosigner(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCosigner + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCosigner + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCosigner + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthCosigner + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCosigner + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCosigner + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCosigner = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCosigner = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCosigner = fmt.Errorf("proto: unexpected end of group") +) diff --git a/signer/proto/cosigner_grpc_server.pb.go b/signer/proto/cosigner_grpc_server.pb.go deleted file mode 100644 index f8836e28..00000000 --- a/signer/proto/cosigner_grpc_server.pb.go +++ /dev/null @@ -1,1133 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.6 -// source: signer/proto/cosigner_grpc_server.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) -) - -type Block struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` - Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` - SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` - Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (x *Block) Reset() { - *x = Block{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Block) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Block) ProtoMessage() {} - -func (x *Block) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_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 Block.ProtoReflect.Descriptor instead. -func (*Block) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{0} -} - -func (x *Block) GetHeight() int64 { - if x != nil { - return x.Height - } - return 0 -} - -func (x *Block) GetRound() int64 { - if x != nil { - return x.Round - } - return 0 -} - -func (x *Block) GetStep() int32 { - if x != nil { - return x.Step - } - return 0 -} - -func (x *Block) GetSignBytes() []byte { - if x != nil { - return x.SignBytes - } - return nil -} - -func (x *Block) GetTimestamp() int64 { - if x != nil { - return x.Timestamp - } - return 0 -} - -type CosignerGRPCSignBlockRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"` - Block *Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` -} - -func (x *CosignerGRPCSignBlockRequest) Reset() { - *x = CosignerGRPCSignBlockRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCSignBlockRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCSignBlockRequest) ProtoMessage() {} - -func (x *CosignerGRPCSignBlockRequest) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[1] - 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 CosignerGRPCSignBlockRequest.ProtoReflect.Descriptor instead. -func (*CosignerGRPCSignBlockRequest) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{1} -} - -func (x *CosignerGRPCSignBlockRequest) GetChainID() string { - if x != nil { - return x.ChainID - } - return "" -} - -func (x *CosignerGRPCSignBlockRequest) GetBlock() *Block { - if x != nil { - return x.Block - } - return nil -} - -type CosignerGRPCSignBlockResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` -} - -func (x *CosignerGRPCSignBlockResponse) Reset() { - *x = CosignerGRPCSignBlockResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCSignBlockResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCSignBlockResponse) ProtoMessage() {} - -func (x *CosignerGRPCSignBlockResponse) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[2] - 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 CosignerGRPCSignBlockResponse.ProtoReflect.Descriptor instead. -func (*CosignerGRPCSignBlockResponse) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{2} -} - -func (x *CosignerGRPCSignBlockResponse) GetSignature() []byte { - if x != nil { - return x.Signature - } - return nil -} - -type EphemeralSecretPart struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SourceID int32 `protobuf:"varint,1,opt,name=sourceID,proto3" json:"sourceID,omitempty"` - DestinationID int32 `protobuf:"varint,2,opt,name=destinationID,proto3" json:"destinationID,omitempty"` - SourceEphemeralSecretPublicKey []byte `protobuf:"bytes,3,opt,name=sourceEphemeralSecretPublicKey,proto3" json:"sourceEphemeralSecretPublicKey,omitempty"` - EncryptedSharePart []byte `protobuf:"bytes,4,opt,name=encryptedSharePart,proto3" json:"encryptedSharePart,omitempty"` - SourceSig []byte `protobuf:"bytes,5,opt,name=sourceSig,proto3" json:"sourceSig,omitempty"` -} - -func (x *EphemeralSecretPart) Reset() { - *x = EphemeralSecretPart{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EphemeralSecretPart) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EphemeralSecretPart) ProtoMessage() {} - -func (x *EphemeralSecretPart) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[3] - 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 EphemeralSecretPart.ProtoReflect.Descriptor instead. -func (*EphemeralSecretPart) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{3} -} - -func (x *EphemeralSecretPart) GetSourceID() int32 { - if x != nil { - return x.SourceID - } - return 0 -} - -func (x *EphemeralSecretPart) GetDestinationID() int32 { - if x != nil { - return x.DestinationID - } - return 0 -} - -func (x *EphemeralSecretPart) GetSourceEphemeralSecretPublicKey() []byte { - if x != nil { - return x.SourceEphemeralSecretPublicKey - } - return nil -} - -func (x *EphemeralSecretPart) GetEncryptedSharePart() []byte { - if x != nil { - return x.EncryptedSharePart - } - return nil -} - -func (x *EphemeralSecretPart) GetSourceSig() []byte { - if x != nil { - return x.SourceSig - } - return nil -} - -type HRST struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` - Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` - Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` -} - -func (x *HRST) Reset() { - *x = HRST{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *HRST) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HRST) ProtoMessage() {} - -func (x *HRST) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[4] - 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 HRST.ProtoReflect.Descriptor instead. -func (*HRST) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{4} -} - -func (x *HRST) GetHeight() int64 { - if x != nil { - return x.Height - } - return 0 -} - -func (x *HRST) GetRound() int64 { - if x != nil { - return x.Round - } - return 0 -} - -func (x *HRST) GetStep() int32 { - if x != nil { - return x.Step - } - return 0 -} - -func (x *HRST) GetTimestamp() int64 { - if x != nil { - return x.Timestamp - } - return 0 -} - -type CosignerGRPCSetEphemeralSecretPartsAndSignRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EncryptedSecrets []*EphemeralSecretPart `protobuf:"bytes,1,rep,name=encryptedSecrets,proto3" json:"encryptedSecrets,omitempty"` - Hrst *HRST `protobuf:"bytes,2,opt,name=hrst,proto3" json:"hrst,omitempty"` - SignBytes []byte `protobuf:"bytes,3,opt,name=signBytes,proto3" json:"signBytes,omitempty"` -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) Reset() { - *x = CosignerGRPCSetEphemeralSecretPartsAndSignRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCSetEphemeralSecretPartsAndSignRequest) ProtoMessage() {} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[5] - 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 CosignerGRPCSetEphemeralSecretPartsAndSignRequest.ProtoReflect.Descriptor instead. -func (*CosignerGRPCSetEphemeralSecretPartsAndSignRequest) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{5} -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) GetEncryptedSecrets() []*EphemeralSecretPart { - if x != nil { - return x.EncryptedSecrets - } - return nil -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) GetHrst() *HRST { - if x != nil { - return x.Hrst - } - return nil -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) GetSignBytes() []byte { - if x != nil { - return x.SignBytes - } - return nil -} - -type CosignerGRPCSetEphemeralSecretPartsAndSignResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EphemeralPublic []byte `protobuf:"bytes,1,opt,name=ephemeralPublic,proto3" json:"ephemeralPublic,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignResponse) Reset() { - *x = CosignerGRPCSetEphemeralSecretPartsAndSignResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse) ProtoMessage() {} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignResponse) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[6] - 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 CosignerGRPCSetEphemeralSecretPartsAndSignResponse.ProtoReflect.Descriptor instead. -func (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{6} -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignResponse) GetEphemeralPublic() []byte { - if x != nil { - return x.EphemeralPublic - } - return nil -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignResponse) GetTimestamp() int64 { - if x != nil { - return x.Timestamp - } - return 0 -} - -func (x *CosignerGRPCSetEphemeralSecretPartsAndSignResponse) GetSignature() []byte { - if x != nil { - return x.Signature - } - return nil -} - -type CosignerGRPCGetEphemeralSecretPartsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Hrst *HRST `protobuf:"bytes,1,opt,name=hrst,proto3" json:"hrst,omitempty"` -} - -func (x *CosignerGRPCGetEphemeralSecretPartsRequest) Reset() { - *x = CosignerGRPCGetEphemeralSecretPartsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCGetEphemeralSecretPartsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCGetEphemeralSecretPartsRequest) ProtoMessage() {} - -func (x *CosignerGRPCGetEphemeralSecretPartsRequest) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[7] - 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 CosignerGRPCGetEphemeralSecretPartsRequest.ProtoReflect.Descriptor instead. -func (*CosignerGRPCGetEphemeralSecretPartsRequest) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{7} -} - -func (x *CosignerGRPCGetEphemeralSecretPartsRequest) GetHrst() *HRST { - if x != nil { - return x.Hrst - } - return nil -} - -type CosignerGRPCGetEphemeralSecretPartsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EncryptedSecrets []*EphemeralSecretPart `protobuf:"bytes,1,rep,name=encryptedSecrets,proto3" json:"encryptedSecrets,omitempty"` -} - -func (x *CosignerGRPCGetEphemeralSecretPartsResponse) Reset() { - *x = CosignerGRPCGetEphemeralSecretPartsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCGetEphemeralSecretPartsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCGetEphemeralSecretPartsResponse) ProtoMessage() {} - -func (x *CosignerGRPCGetEphemeralSecretPartsResponse) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[8] - 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 CosignerGRPCGetEphemeralSecretPartsResponse.ProtoReflect.Descriptor instead. -func (*CosignerGRPCGetEphemeralSecretPartsResponse) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{8} -} - -func (x *CosignerGRPCGetEphemeralSecretPartsResponse) GetEncryptedSecrets() []*EphemeralSecretPart { - if x != nil { - return x.EncryptedSecrets - } - return nil -} - -type CosignerGRPCTransferLeadershipRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LeaderID string `protobuf:"bytes,1,opt,name=leaderID,proto3" json:"leaderID,omitempty"` -} - -func (x *CosignerGRPCTransferLeadershipRequest) Reset() { - *x = CosignerGRPCTransferLeadershipRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCTransferLeadershipRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCTransferLeadershipRequest) ProtoMessage() {} - -func (x *CosignerGRPCTransferLeadershipRequest) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[9] - 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 CosignerGRPCTransferLeadershipRequest.ProtoReflect.Descriptor instead. -func (*CosignerGRPCTransferLeadershipRequest) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{9} -} - -func (x *CosignerGRPCTransferLeadershipRequest) GetLeaderID() string { - if x != nil { - return x.LeaderID - } - return "" -} - -type CosignerGRPCTransferLeadershipResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LeaderID string `protobuf:"bytes,1,opt,name=leaderID,proto3" json:"leaderID,omitempty"` - LeaderAddress string `protobuf:"bytes,2,opt,name=leaderAddress,proto3" json:"leaderAddress,omitempty"` -} - -func (x *CosignerGRPCTransferLeadershipResponse) Reset() { - *x = CosignerGRPCTransferLeadershipResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCTransferLeadershipResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCTransferLeadershipResponse) ProtoMessage() {} - -func (x *CosignerGRPCTransferLeadershipResponse) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[10] - 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 CosignerGRPCTransferLeadershipResponse.ProtoReflect.Descriptor instead. -func (*CosignerGRPCTransferLeadershipResponse) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{10} -} - -func (x *CosignerGRPCTransferLeadershipResponse) GetLeaderID() string { - if x != nil { - return x.LeaderID - } - return "" -} - -func (x *CosignerGRPCTransferLeadershipResponse) GetLeaderAddress() string { - if x != nil { - return x.LeaderAddress - } - return "" -} - -type CosignerGRPCGetLeaderRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CosignerGRPCGetLeaderRequest) Reset() { - *x = CosignerGRPCGetLeaderRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCGetLeaderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCGetLeaderRequest) ProtoMessage() {} - -func (x *CosignerGRPCGetLeaderRequest) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[11] - 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 CosignerGRPCGetLeaderRequest.ProtoReflect.Descriptor instead. -func (*CosignerGRPCGetLeaderRequest) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{11} -} - -type CosignerGRPCGetLeaderResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Leader string `protobuf:"bytes,1,opt,name=leader,proto3" json:"leader,omitempty"` -} - -func (x *CosignerGRPCGetLeaderResponse) Reset() { - *x = CosignerGRPCGetLeaderResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_signer_proto_cosigner_grpc_server_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CosignerGRPCGetLeaderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CosignerGRPCGetLeaderResponse) ProtoMessage() {} - -func (x *CosignerGRPCGetLeaderResponse) ProtoReflect() protoreflect.Message { - mi := &file_signer_proto_cosigner_grpc_server_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 CosignerGRPCGetLeaderResponse.ProtoReflect.Descriptor instead. -func (*CosignerGRPCGetLeaderResponse) Descriptor() ([]byte, []int) { - return file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP(), []int{12} -} - -func (x *CosignerGRPCGetLeaderResponse) GetLeader() string { - if x != nil { - return x.Leader - } - return "" -} - -var File_signer_proto_cosigner_grpc_server_proto protoreflect.FileDescriptor - -var file_signer_proto_cosigner_grpc_server_proto_rawDesc = []byte{ - 0x0a, 0x27, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, - 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x85, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x5c, 0x0a, 0x1c, 0x43, 0x6f, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x49, 0x44, 0x12, 0x22, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x3d, 0x0a, 0x1d, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 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, 0xed, 0x01, 0x0a, 0x13, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, - 0x46, 0x0a, 0x1e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, - 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, - 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x12, 0x65, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x53, 0x68, 0x61, 0x72, 0x65, 0x50, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x12, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x53, 0x68, - 0x61, 0x72, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x53, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x69, 0x67, 0x22, 0x66, 0x0a, 0x04, 0x48, 0x52, 0x53, 0x54, 0x12, 0x16, 0x0a, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x74, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xba, 0x01, - 0x0a, 0x31, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, 0x65, - 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x50, 0x61, 0x72, 0x74, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x10, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x52, 0x10, 0x65, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x04, 0x68, - 0x72, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x48, 0x52, 0x53, 0x54, 0x52, 0x04, 0x68, 0x72, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x32, 0x43, - 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, 0x65, 0x74, 0x45, 0x70, - 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, - 0x74, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x70, 0x68, 0x65, - 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x12, 0x1c, 0x0a, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x4d, 0x0a, 0x2a, 0x43, 0x6f, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x04, 0x68, 0x72, 0x73, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x52, 0x53, 0x54, - 0x52, 0x04, 0x68, 0x72, 0x73, 0x74, 0x22, 0x75, 0x0a, 0x2b, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, - 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x10, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x65, 0x64, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, - 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x52, 0x10, 0x65, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x22, 0x43, 0x0a, - 0x25, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x49, 0x44, 0x22, 0x6a, 0x0a, 0x26, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, - 0x50, 0x43, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x1e, - 0x0a, 0x1c, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, - 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x37, - 0x0a, 0x1d, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, - 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x32, 0xd6, 0x04, 0x0a, 0x0c, 0x43, 0x6f, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x12, 0x58, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, - 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x97, 0x01, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x73, 0x41, 0x6e, - 0x64, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x38, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x53, 0x65, 0x74, 0x45, 0x70, 0x68, - 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, - 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x39, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, - 0x47, 0x52, 0x50, 0x43, 0x53, 0x65, 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, - 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x82, 0x01, 0x0a, - 0x17, 0x47, 0x65, 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x73, 0x12, 0x31, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, 0x74, - 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x50, - 0x61, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, - 0x47, 0x65, 0x74, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x50, 0x61, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x73, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, - 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6f, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x47, 0x52, 0x50, 0x43, 0x47, 0x65, 0x74, - 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x72, 0x63, 0x72, 0x75, 0x78, 0x2f, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_signer_proto_cosigner_grpc_server_proto_rawDescOnce sync.Once - file_signer_proto_cosigner_grpc_server_proto_rawDescData = file_signer_proto_cosigner_grpc_server_proto_rawDesc -) - -func file_signer_proto_cosigner_grpc_server_proto_rawDescGZIP() []byte { - file_signer_proto_cosigner_grpc_server_proto_rawDescOnce.Do(func() { - file_signer_proto_cosigner_grpc_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_signer_proto_cosigner_grpc_server_proto_rawDescData) - }) - return file_signer_proto_cosigner_grpc_server_proto_rawDescData -} - -var file_signer_proto_cosigner_grpc_server_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_signer_proto_cosigner_grpc_server_proto_goTypes = []interface{}{ - (*Block)(nil), // 0: proto.Block - (*CosignerGRPCSignBlockRequest)(nil), // 1: proto.CosignerGRPCSignBlockRequest - (*CosignerGRPCSignBlockResponse)(nil), // 2: proto.CosignerGRPCSignBlockResponse - (*EphemeralSecretPart)(nil), // 3: proto.EphemeralSecretPart - (*HRST)(nil), // 4: proto.HRST - (*CosignerGRPCSetEphemeralSecretPartsAndSignRequest)(nil), // 5: proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest - (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse)(nil), // 6: proto.CosignerGRPCSetEphemeralSecretPartsAndSignResponse - (*CosignerGRPCGetEphemeralSecretPartsRequest)(nil), // 7: proto.CosignerGRPCGetEphemeralSecretPartsRequest - (*CosignerGRPCGetEphemeralSecretPartsResponse)(nil), // 8: proto.CosignerGRPCGetEphemeralSecretPartsResponse - (*CosignerGRPCTransferLeadershipRequest)(nil), // 9: proto.CosignerGRPCTransferLeadershipRequest - (*CosignerGRPCTransferLeadershipResponse)(nil), // 10: proto.CosignerGRPCTransferLeadershipResponse - (*CosignerGRPCGetLeaderRequest)(nil), // 11: proto.CosignerGRPCGetLeaderRequest - (*CosignerGRPCGetLeaderResponse)(nil), // 12: proto.CosignerGRPCGetLeaderResponse -} -var file_signer_proto_cosigner_grpc_server_proto_depIdxs = []int32{ - 0, // 0: proto.CosignerGRPCSignBlockRequest.block:type_name -> proto.Block - 3, // 1: proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest.encryptedSecrets:type_name -> proto.EphemeralSecretPart - 4, // 2: proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest.hrst:type_name -> proto.HRST - 4, // 3: proto.CosignerGRPCGetEphemeralSecretPartsRequest.hrst:type_name -> proto.HRST - 3, // 4: proto.CosignerGRPCGetEphemeralSecretPartsResponse.encryptedSecrets:type_name -> proto.EphemeralSecretPart - 1, // 5: proto.CosignerGRPC.SignBlock:input_type -> proto.CosignerGRPCSignBlockRequest - 5, // 6: proto.CosignerGRPC.SetEphemeralSecretPartsAndSign:input_type -> proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest - 7, // 7: proto.CosignerGRPC.GetEphemeralSecretParts:input_type -> proto.CosignerGRPCGetEphemeralSecretPartsRequest - 9, // 8: proto.CosignerGRPC.TransferLeadership:input_type -> proto.CosignerGRPCTransferLeadershipRequest - 11, // 9: proto.CosignerGRPC.GetLeader:input_type -> proto.CosignerGRPCGetLeaderRequest - 2, // 10: proto.CosignerGRPC.SignBlock:output_type -> proto.CosignerGRPCSignBlockResponse - 6, // 11: proto.CosignerGRPC.SetEphemeralSecretPartsAndSign:output_type -> proto.CosignerGRPCSetEphemeralSecretPartsAndSignResponse - 8, // 12: proto.CosignerGRPC.GetEphemeralSecretParts:output_type -> proto.CosignerGRPCGetEphemeralSecretPartsResponse - 10, // 13: proto.CosignerGRPC.TransferLeadership:output_type -> proto.CosignerGRPCTransferLeadershipResponse - 12, // 14: proto.CosignerGRPC.GetLeader:output_type -> proto.CosignerGRPCGetLeaderResponse - 10, // [10:15] is the sub-list for method output_type - 5, // [5:10] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name -} - -func init() { file_signer_proto_cosigner_grpc_server_proto_init() } -func file_signer_proto_cosigner_grpc_server_proto_init() { - if File_signer_proto_cosigner_grpc_server_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_signer_proto_cosigner_grpc_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Block); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCSignBlockRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCSignBlockResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EphemeralSecretPart); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HRST); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCSetEphemeralSecretPartsAndSignRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCSetEphemeralSecretPartsAndSignResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCGetEphemeralSecretPartsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCGetEphemeralSecretPartsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCTransferLeadershipRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCTransferLeadershipResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCGetLeaderRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_signer_proto_cosigner_grpc_server_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CosignerGRPCGetLeaderResponse); 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_signer_proto_cosigner_grpc_server_proto_rawDesc, - NumEnums: 0, - NumMessages: 13, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_signer_proto_cosigner_grpc_server_proto_goTypes, - DependencyIndexes: file_signer_proto_cosigner_grpc_server_proto_depIdxs, - MessageInfos: file_signer_proto_cosigner_grpc_server_proto_msgTypes, - }.Build() - File_signer_proto_cosigner_grpc_server_proto = out.File - file_signer_proto_cosigner_grpc_server_proto_rawDesc = nil - file_signer_proto_cosigner_grpc_server_proto_goTypes = nil - file_signer_proto_cosigner_grpc_server_proto_depIdxs = nil -} diff --git a/signer/proto/cosigner_grpc_server.proto b/signer/proto/cosigner_grpc_server.proto deleted file mode 100644 index c2fe1af3..00000000 --- a/signer/proto/cosigner_grpc_server.proto +++ /dev/null @@ -1,80 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/strangelove-ventures/horcrux/signer/proto"; - -package proto; - -service CosignerGRPC { - rpc SignBlock (CosignerGRPCSignBlockRequest) returns (CosignerGRPCSignBlockResponse) {} - rpc SetEphemeralSecretPartsAndSign (CosignerGRPCSetEphemeralSecretPartsAndSignRequest) returns (CosignerGRPCSetEphemeralSecretPartsAndSignResponse) {} - rpc GetEphemeralSecretParts (CosignerGRPCGetEphemeralSecretPartsRequest) returns (CosignerGRPCGetEphemeralSecretPartsResponse) {} - rpc TransferLeadership (CosignerGRPCTransferLeadershipRequest) returns (CosignerGRPCTransferLeadershipResponse) {} - rpc GetLeader (CosignerGRPCGetLeaderRequest) returns (CosignerGRPCGetLeaderResponse) {} -} - -message Block { - int64 height = 1; - int64 round = 2; - int32 step = 3; - bytes signBytes = 4; - int64 timestamp = 5; -} - -message CosignerGRPCSignBlockRequest { - string chainID = 1; - Block block = 2; -} - -message CosignerGRPCSignBlockResponse { - bytes signature = 1; -} - -message EphemeralSecretPart { - int32 sourceID = 1; - int32 destinationID = 2; - bytes sourceEphemeralSecretPublicKey = 3; - bytes encryptedSharePart = 4; - bytes sourceSig = 5; -} - -message HRST { - int64 height = 1; - int64 round = 2; - int32 step = 3; - int64 timestamp = 4; -} - -message CosignerGRPCSetEphemeralSecretPartsAndSignRequest { - repeated EphemeralSecretPart encryptedSecrets = 1; - HRST hrst = 2; - bytes signBytes = 3; -} - -message CosignerGRPCSetEphemeralSecretPartsAndSignResponse { - bytes ephemeralPublic = 1; - int64 timestamp = 2; - bytes signature = 3; -} - -message CosignerGRPCGetEphemeralSecretPartsRequest { - HRST hrst = 1; -} - -message CosignerGRPCGetEphemeralSecretPartsResponse { - repeated EphemeralSecretPart encryptedSecrets = 1; -} - -message CosignerGRPCTransferLeadershipRequest { - string leaderID = 1; -} - -message CosignerGRPCTransferLeadershipResponse { - string leaderID = 1; - string leaderAddress = 2; -} - -message CosignerGRPCGetLeaderRequest {} - -message CosignerGRPCGetLeaderResponse { - string leader = 1; -} diff --git a/signer/proto/cosigner_grpc_server_grpc.pb.go b/signer/proto/cosigner_grpc_server_grpc.pb.go deleted file mode 100644 index e8889725..00000000 --- a/signer/proto/cosigner_grpc_server_grpc.pb.go +++ /dev/null @@ -1,249 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.21.6 -// source: signer/proto/cosigner_grpc_server.proto - -package proto - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// CosignerGRPCClient is the client API for CosignerGRPC service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type CosignerGRPCClient interface { - SignBlock(ctx context.Context, in *CosignerGRPCSignBlockRequest, opts ...grpc.CallOption) (*CosignerGRPCSignBlockResponse, error) - SetEphemeralSecretPartsAndSign(ctx context.Context, in *CosignerGRPCSetEphemeralSecretPartsAndSignRequest, opts ...grpc.CallOption) (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse, error) - GetEphemeralSecretParts(ctx context.Context, in *CosignerGRPCGetEphemeralSecretPartsRequest, opts ...grpc.CallOption) (*CosignerGRPCGetEphemeralSecretPartsResponse, error) - TransferLeadership(ctx context.Context, in *CosignerGRPCTransferLeadershipRequest, opts ...grpc.CallOption) (*CosignerGRPCTransferLeadershipResponse, error) - GetLeader(ctx context.Context, in *CosignerGRPCGetLeaderRequest, opts ...grpc.CallOption) (*CosignerGRPCGetLeaderResponse, error) -} - -type cosignerGRPCClient struct { - cc grpc.ClientConnInterface -} - -func NewCosignerGRPCClient(cc grpc.ClientConnInterface) CosignerGRPCClient { - return &cosignerGRPCClient{cc} -} - -func (c *cosignerGRPCClient) SignBlock(ctx context.Context, in *CosignerGRPCSignBlockRequest, opts ...grpc.CallOption) (*CosignerGRPCSignBlockResponse, error) { - out := new(CosignerGRPCSignBlockResponse) - err := c.cc.Invoke(ctx, "/proto.CosignerGRPC/SignBlock", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *cosignerGRPCClient) SetEphemeralSecretPartsAndSign(ctx context.Context, in *CosignerGRPCSetEphemeralSecretPartsAndSignRequest, opts ...grpc.CallOption) (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse, error) { - out := new(CosignerGRPCSetEphemeralSecretPartsAndSignResponse) - err := c.cc.Invoke(ctx, "/proto.CosignerGRPC/SetEphemeralSecretPartsAndSign", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *cosignerGRPCClient) GetEphemeralSecretParts(ctx context.Context, in *CosignerGRPCGetEphemeralSecretPartsRequest, opts ...grpc.CallOption) (*CosignerGRPCGetEphemeralSecretPartsResponse, error) { - out := new(CosignerGRPCGetEphemeralSecretPartsResponse) - err := c.cc.Invoke(ctx, "/proto.CosignerGRPC/GetEphemeralSecretParts", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *cosignerGRPCClient) TransferLeadership(ctx context.Context, in *CosignerGRPCTransferLeadershipRequest, opts ...grpc.CallOption) (*CosignerGRPCTransferLeadershipResponse, error) { - out := new(CosignerGRPCTransferLeadershipResponse) - err := c.cc.Invoke(ctx, "/proto.CosignerGRPC/TransferLeadership", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *cosignerGRPCClient) GetLeader(ctx context.Context, in *CosignerGRPCGetLeaderRequest, opts ...grpc.CallOption) (*CosignerGRPCGetLeaderResponse, error) { - out := new(CosignerGRPCGetLeaderResponse) - err := c.cc.Invoke(ctx, "/proto.CosignerGRPC/GetLeader", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// CosignerGRPCServer is the server API for CosignerGRPC service. -// All implementations must embed UnimplementedCosignerGRPCServer -// for forward compatibility -type CosignerGRPCServer interface { - SignBlock(context.Context, *CosignerGRPCSignBlockRequest) (*CosignerGRPCSignBlockResponse, error) - SetEphemeralSecretPartsAndSign(context.Context, *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse, error) - GetEphemeralSecretParts(context.Context, *CosignerGRPCGetEphemeralSecretPartsRequest) (*CosignerGRPCGetEphemeralSecretPartsResponse, error) - TransferLeadership(context.Context, *CosignerGRPCTransferLeadershipRequest) (*CosignerGRPCTransferLeadershipResponse, error) - GetLeader(context.Context, *CosignerGRPCGetLeaderRequest) (*CosignerGRPCGetLeaderResponse, error) - mustEmbedUnimplementedCosignerGRPCServer() -} - -// UnimplementedCosignerGRPCServer must be embedded to have forward compatible implementations. -type UnimplementedCosignerGRPCServer struct { -} - -func (UnimplementedCosignerGRPCServer) SignBlock(context.Context, *CosignerGRPCSignBlockRequest) (*CosignerGRPCSignBlockResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SignBlock not implemented") -} -func (UnimplementedCosignerGRPCServer) SetEphemeralSecretPartsAndSign(context.Context, *CosignerGRPCSetEphemeralSecretPartsAndSignRequest) (*CosignerGRPCSetEphemeralSecretPartsAndSignResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetEphemeralSecretPartsAndSign not implemented") -} -func (UnimplementedCosignerGRPCServer) GetEphemeralSecretParts(context.Context, *CosignerGRPCGetEphemeralSecretPartsRequest) (*CosignerGRPCGetEphemeralSecretPartsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetEphemeralSecretParts not implemented") -} -func (UnimplementedCosignerGRPCServer) TransferLeadership(context.Context, *CosignerGRPCTransferLeadershipRequest) (*CosignerGRPCTransferLeadershipResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method TransferLeadership not implemented") -} -func (UnimplementedCosignerGRPCServer) GetLeader(context.Context, *CosignerGRPCGetLeaderRequest) (*CosignerGRPCGetLeaderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetLeader not implemented") -} -func (UnimplementedCosignerGRPCServer) mustEmbedUnimplementedCosignerGRPCServer() {} - -// UnsafeCosignerGRPCServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to CosignerGRPCServer will -// result in compilation errors. -type UnsafeCosignerGRPCServer interface { - mustEmbedUnimplementedCosignerGRPCServer() -} - -func RegisterCosignerGRPCServer(s grpc.ServiceRegistrar, srv CosignerGRPCServer) { - s.RegisterService(&CosignerGRPC_ServiceDesc, srv) -} - -func _CosignerGRPC_SignBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CosignerGRPCSignBlockRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(CosignerGRPCServer).SignBlock(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.CosignerGRPC/SignBlock", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CosignerGRPCServer).SignBlock(ctx, req.(*CosignerGRPCSignBlockRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _CosignerGRPC_SetEphemeralSecretPartsAndSign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CosignerGRPCSetEphemeralSecretPartsAndSignRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(CosignerGRPCServer).SetEphemeralSecretPartsAndSign(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.CosignerGRPC/SetEphemeralSecretPartsAndSign", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CosignerGRPCServer).SetEphemeralSecretPartsAndSign(ctx, req.(*CosignerGRPCSetEphemeralSecretPartsAndSignRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _CosignerGRPC_GetEphemeralSecretParts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CosignerGRPCGetEphemeralSecretPartsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(CosignerGRPCServer).GetEphemeralSecretParts(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.CosignerGRPC/GetEphemeralSecretParts", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CosignerGRPCServer).GetEphemeralSecretParts(ctx, req.(*CosignerGRPCGetEphemeralSecretPartsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _CosignerGRPC_TransferLeadership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CosignerGRPCTransferLeadershipRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(CosignerGRPCServer).TransferLeadership(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.CosignerGRPC/TransferLeadership", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CosignerGRPCServer).TransferLeadership(ctx, req.(*CosignerGRPCTransferLeadershipRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _CosignerGRPC_GetLeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CosignerGRPCGetLeaderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(CosignerGRPCServer).GetLeader(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/proto.CosignerGRPC/GetLeader", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CosignerGRPCServer).GetLeader(ctx, req.(*CosignerGRPCGetLeaderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// CosignerGRPC_ServiceDesc is the grpc.ServiceDesc for CosignerGRPC service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var CosignerGRPC_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "proto.CosignerGRPC", - HandlerType: (*CosignerGRPCServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "SignBlock", - Handler: _CosignerGRPC_SignBlock_Handler, - }, - { - MethodName: "SetEphemeralSecretPartsAndSign", - Handler: _CosignerGRPC_SetEphemeralSecretPartsAndSign_Handler, - }, - { - MethodName: "GetEphemeralSecretParts", - Handler: _CosignerGRPC_GetEphemeralSecretParts_Handler, - }, - { - MethodName: "TransferLeadership", - Handler: _CosignerGRPC_TransferLeadership_Handler, - }, - { - MethodName: "GetLeader", - Handler: _CosignerGRPC_GetLeader_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "signer/proto/cosigner_grpc_server.proto", -} diff --git a/signer/proto/remote_signer.pb.go b/signer/proto/remote_signer.pb.go new file mode 100644 index 00000000..325f3619 --- /dev/null +++ b/signer/proto/remote_signer.pb.go @@ -0,0 +1,617 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: strangelove/horcrux/remote_signer.proto + +package proto + +import ( + context "context" + fmt "fmt" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type PubKeyRequest struct { + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (m *PubKeyRequest) Reset() { *m = PubKeyRequest{} } +func (m *PubKeyRequest) String() string { return proto.CompactTextString(m) } +func (*PubKeyRequest) ProtoMessage() {} +func (*PubKeyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_afd7664cd19b584a, []int{0} +} +func (m *PubKeyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PubKeyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PubKeyRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PubKeyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PubKeyRequest.Merge(m, src) +} +func (m *PubKeyRequest) XXX_Size() int { + return m.Size() +} +func (m *PubKeyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PubKeyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PubKeyRequest proto.InternalMessageInfo + +func (m *PubKeyRequest) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +type PubKeyResponse struct { + PubKey []byte `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` +} + +func (m *PubKeyResponse) Reset() { *m = PubKeyResponse{} } +func (m *PubKeyResponse) String() string { return proto.CompactTextString(m) } +func (*PubKeyResponse) ProtoMessage() {} +func (*PubKeyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_afd7664cd19b584a, []int{1} +} +func (m *PubKeyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PubKeyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PubKeyResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PubKeyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PubKeyResponse.Merge(m, src) +} +func (m *PubKeyResponse) XXX_Size() int { + return m.Size() +} +func (m *PubKeyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PubKeyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PubKeyResponse proto.InternalMessageInfo + +func (m *PubKeyResponse) GetPubKey() []byte { + if m != nil { + return m.PubKey + } + return nil +} + +func init() { + proto.RegisterType((*PubKeyRequest)(nil), "strangelove.horcrux.PubKeyRequest") + proto.RegisterType((*PubKeyResponse)(nil), "strangelove.horcrux.PubKeyResponse") +} + +func init() { + proto.RegisterFile("strangelove/horcrux/remote_signer.proto", fileDescriptor_afd7664cd19b584a) +} + +var fileDescriptor_afd7664cd19b584a = []byte{ + // 279 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2f, 0x2e, 0x29, 0x4a, + 0xcc, 0x4b, 0x4f, 0xcd, 0xc9, 0x2f, 0x4b, 0xd5, 0xcf, 0xc8, 0x2f, 0x4a, 0x2e, 0x2a, 0xad, 0xd0, + 0x2f, 0x4a, 0xcd, 0xcd, 0x2f, 0x49, 0x8d, 0x2f, 0xce, 0x4c, 0xcf, 0x4b, 0x2d, 0xd2, 0x2b, 0x28, + 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x46, 0x52, 0xa8, 0x07, 0x55, 0x28, 0xa5, 0x84, 0x4d, 0x77, 0x72, + 0x3e, 0xb2, 0x46, 0x25, 0x2d, 0x2e, 0xde, 0x80, 0xd2, 0x24, 0xef, 0xd4, 0xca, 0xa0, 0xd4, 0xc2, + 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x49, 0x2e, 0x8e, 0xe4, 0x8c, 0xc4, 0xcc, 0xbc, 0xf8, 0xcc, 0x14, + 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x76, 0x30, 0xdf, 0x33, 0x45, 0x49, 0x93, 0x8b, 0x0f, + 0xa6, 0xb6, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0x48, 0x9c, 0x8b, 0xbd, 0xa0, 0x34, 0x29, 0x3e, + 0x3b, 0xb5, 0x12, 0xac, 0x96, 0x27, 0x88, 0xad, 0x00, 0xac, 0xc0, 0x68, 0x0f, 0x23, 0x17, 0x4f, + 0x10, 0xd8, 0x9d, 0xc1, 0x60, 0xdb, 0x84, 0x82, 0xb9, 0xd8, 0x20, 0x7a, 0x85, 0x94, 0xf4, 0xb0, + 0xb8, 0x55, 0x0f, 0xc5, 0x11, 0x52, 0xca, 0x78, 0xd5, 0x40, 0x2c, 0x57, 0x62, 0x10, 0x0a, 0xe7, + 0x62, 0x01, 0x19, 0x2f, 0xa4, 0x8a, 0x55, 0x39, 0x48, 0xca, 0x29, 0x27, 0x3f, 0x39, 0x1b, 0x66, + 0xaa, 0x1a, 0x21, 0x65, 0x30, 0x83, 0x9d, 0x02, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, + 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, + 0x8e, 0x21, 0xca, 0x3c, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xc9, + 0x34, 0xdd, 0xb2, 0xd4, 0xbc, 0x92, 0xd2, 0xa2, 0xd4, 0x62, 0x78, 0x38, 0x97, 0x19, 0xeb, 0x43, + 0x02, 0x5a, 0x1f, 0x1c, 0xd0, 0x49, 0x6c, 0x60, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x1e, + 0xa5, 0x90, 0x75, 0xd3, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// RemoteSignerClient is the client API for RemoteSigner service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type RemoteSignerClient interface { + PubKey(ctx context.Context, in *PubKeyRequest, opts ...grpc.CallOption) (*PubKeyResponse, error) + Sign(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) +} + +type remoteSignerClient struct { + cc grpc1.ClientConn +} + +func NewRemoteSignerClient(cc grpc1.ClientConn) RemoteSignerClient { + return &remoteSignerClient{cc} +} + +func (c *remoteSignerClient) PubKey(ctx context.Context, in *PubKeyRequest, opts ...grpc.CallOption) (*PubKeyResponse, error) { + out := new(PubKeyResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.RemoteSigner/PubKey", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *remoteSignerClient) Sign(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) { + out := new(SignBlockResponse) + err := c.cc.Invoke(ctx, "/strangelove.horcrux.RemoteSigner/Sign", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// RemoteSignerServer is the server API for RemoteSigner service. +type RemoteSignerServer interface { + PubKey(context.Context, *PubKeyRequest) (*PubKeyResponse, error) + Sign(context.Context, *SignBlockRequest) (*SignBlockResponse, error) +} + +// UnimplementedRemoteSignerServer can be embedded to have forward compatible implementations. +type UnimplementedRemoteSignerServer struct { +} + +func (*UnimplementedRemoteSignerServer) PubKey(ctx context.Context, req *PubKeyRequest) (*PubKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PubKey not implemented") +} +func (*UnimplementedRemoteSignerServer) Sign(ctx context.Context, req *SignBlockRequest) (*SignBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented") +} + +func RegisterRemoteSignerServer(s grpc1.Server, srv RemoteSignerServer) { + s.RegisterService(&_RemoteSigner_serviceDesc, srv) +} + +func _RemoteSigner_PubKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PubKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteSignerServer).PubKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.RemoteSigner/PubKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteSignerServer).PubKey(ctx, req.(*PubKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _RemoteSigner_Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RemoteSignerServer).Sign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/strangelove.horcrux.RemoteSigner/Sign", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RemoteSignerServer).Sign(ctx, req.(*SignBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _RemoteSigner_serviceDesc = grpc.ServiceDesc{ + ServiceName: "strangelove.horcrux.RemoteSigner", + HandlerType: (*RemoteSignerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PubKey", + Handler: _RemoteSigner_PubKey_Handler, + }, + { + MethodName: "Sign", + Handler: _RemoteSigner_Sign_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "strangelove/horcrux/remote_signer.proto", +} + +func (m *PubKeyRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PubKeyRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PubKeyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintRemoteSigner(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PubKeyResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PubKeyResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PubKeyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PubKey) > 0 { + i -= len(m.PubKey) + copy(dAtA[i:], m.PubKey) + i = encodeVarintRemoteSigner(dAtA, i, uint64(len(m.PubKey))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintRemoteSigner(dAtA []byte, offset int, v uint64) int { + offset -= sovRemoteSigner(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *PubKeyRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovRemoteSigner(uint64(l)) + } + return n +} + +func (m *PubKeyResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.PubKey) + if l > 0 { + n += 1 + l + sovRemoteSigner(uint64(l)) + } + return n +} + +func sovRemoteSigner(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozRemoteSigner(x uint64) (n int) { + return sovRemoteSigner(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *PubKeyRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PubKeyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PubKeyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRemoteSigner + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRemoteSigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRemoteSigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRemoteSigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PubKeyResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PubKeyResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PubKeyResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthRemoteSigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthRemoteSigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKey = append(m.PubKey[:0], dAtA[iNdEx:postIndex]...) + if m.PubKey == nil { + m.PubKey = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRemoteSigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthRemoteSigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipRemoteSigner(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRemoteSigner + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthRemoteSigner + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupRemoteSigner + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthRemoteSigner + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthRemoteSigner = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRemoteSigner = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupRemoteSigner = fmt.Errorf("proto: unexpected end of group") +) diff --git a/signer/raft_events.go b/signer/raft_events.go index f438a5fb..e3c70cd6 100644 --- a/signer/raft_events.go +++ b/signer/raft_events.go @@ -2,12 +2,6 @@ package signer import ( "encoding/json" - "errors" - "time" - - proto "github.com/strangelove-ventures/horcrux/signer/proto" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" ) const ( @@ -26,52 +20,23 @@ func (f *fsm) shouldRetain(key string) bool { } func (f *fsm) handleLSSEvent(value string) { - lss := &SignStateConsensus{} + lss := &ChainSignStateConsensus{} err := json.Unmarshal([]byte(value), lss) if err != nil { - f.logger.Error("LSS Unmarshal Error", err.Error()) + f.logger.Error( + "LastSignState Unmarshal Error", + "error", err, + ) return } - _ = f.thresholdValidator.SaveLastSignedState(*lss) - _ = f.cosigner.SaveLastSignedState(*lss) -} - -func (s *RaftStore) getLeaderGRPCClient() (proto.CosignerGRPCClient, *grpc.ClientConn, error) { - var leader string - for i := 0; i < 30; i++ { - leader = string(s.GetLeader()) - if leader != "" { - break - } - time.Sleep(100 * time.Millisecond) - } - if leader == "" { - totalRaftLeaderElectiontimeout.Inc() - return nil, nil, errors.New("timed out waiting for leader election to complete") - } - conn, err := grpc.Dial(leader, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return nil, nil, err - } - return proto.NewCosignerGRPCClient(conn), conn, nil -} - -func (s *RaftStore) LeaderSignBlock(req CosignerSignBlockRequest) (*CosignerSignBlockResponse, error) { - client, conn, err := s.getLeaderGRPCClient() - if err != nil { - return nil, err - } - defer conn.Close() - context, cancelFunc := getContext() - defer cancelFunc() - res, err := client.SignBlock(context, &proto.CosignerGRPCSignBlockRequest{ - ChainID: req.ChainID, - Block: req.Block.toProto(), - }) - if err != nil { - return nil, err + if err := f.thresholdValidator.LoadSignStateIfNecessary(lss.ChainID); err != nil { + f.logger.Error( + "Error loading sign state during raft replication", + "chain_id", lss.ChainID, + "error", err, + ) + return } - return &CosignerSignBlockResponse{ - Signature: res.GetSignature(), - }, nil + _ = f.thresholdValidator.SaveLastSignedState(lss.ChainID, lss.SignStateConsensus) + _ = f.cosigner.SaveLastSignedState(lss.ChainID, lss.SignStateConsensus) } diff --git a/signer/raft_store.go b/signer/raft_store.go index d71e559d..4cff36b3 100644 --- a/signer/raft_store.go +++ b/signer/raft_store.go @@ -15,22 +15,25 @@ import ( "net/url" "os" "path/filepath" + "strconv" "sync" "time" "github.com/Jille/raft-grpc-leader-rpc/leaderhealth" - gRPCTransport "github.com/Jille/raft-grpc-transport" + raftgrpctransport "github.com/Jille/raft-grpc-transport" "github.com/Jille/raftadmin" + "github.com/cometbft/cometbft/libs/log" + "github.com/cometbft/cometbft/libs/service" "github.com/hashicorp/raft" boltdb "github.com/hashicorp/raft-boltdb/v2" - proto "github.com/strangelove-ventures/horcrux/signer/proto" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/libs/service" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/reflection" ) +var _ Leader = (*RaftStore)(nil) + const ( retainSnapshotCount = 2 ) @@ -49,7 +52,7 @@ type RaftStore struct { RaftDir string RaftBind string RaftTimeout time.Duration - Peers []Cosigner + Cosigners []Cosigner mu sync.Mutex m map[string]string // The key-value store for the system. @@ -64,7 +67,7 @@ type RaftStore struct { // New returns a new Store. func NewRaftStore( nodeID string, directory string, bindAddress string, timeout time.Duration, - logger log.Logger, cosigner *LocalCosigner, raftPeers []Cosigner) *RaftStore { + logger log.Logger, cosigner *LocalCosigner, cosigners []Cosigner) *RaftStore { cosignerRaftStore := &RaftStore{ NodeID: nodeID, RaftDir: directory, @@ -73,7 +76,7 @@ func NewRaftStore( m: make(map[string]string), logger: logger, cosigner: cosigner, - Peers: raftPeers, + Cosigners: cosigners, } cosignerRaftStore.BaseService = *service.NewBaseService(logger, "CosignerRaftStore", cosignerRaftStore) @@ -100,19 +103,12 @@ func (s *RaftStore) init() error { return err } grpcServer := grpc.NewServer() - proto.RegisterCosignerGRPCServer(grpcServer, &GRPCServer{ - cosigner: s.cosigner, - thresholdValidator: s.thresholdValidator, - raftStore: s, - }) + proto.RegisterCosignerServer(grpcServer, NewCosignerGRPCServer(s.cosigner, s.thresholdValidator, s)) transportManager.Register(grpcServer) leaderhealth.Setup(s.raft, grpcServer, []string{"Leader"}) raftadmin.Register(grpcServer, s.raft) reflection.Register(grpcServer) - if err := grpcServer.Serve(sock); err != nil { - return err - } - return nil + return grpcServer.Serve(sock) } // OnStart starts the raft server @@ -138,11 +134,14 @@ func p2pURLToRaftAddress(p2pURL string) string { // Open opens the store. If enableSingle is set, and there are no existing peers, // then this node becomes the first node, and therefore leader, of the cluster. // localID should be the server identifier for this node. -func (s *RaftStore) Open() (*gRPCTransport.Manager, error) { +func (s *RaftStore) Open() (*raftgrpctransport.Manager, error) { // Setup Raft configuration. config := raft.DefaultConfig() config.LocalID = raft.ServerID(s.NodeID) config.LogLevel = "ERROR" + config.ElectionTimeout = s.RaftTimeout + config.HeartbeatTimeout = s.RaftTimeout + config.LeaderLeaseTimeout = s.RaftTimeout / 2 // Create the snapshot store. This allows the Raft to truncate the log. snapshots, err := raft.NewFileSnapshotStore(s.RaftDir, retainSnapshotCount, os.Stderr) @@ -166,7 +165,7 @@ func (s *RaftStore) Open() (*gRPCTransport.Manager, error) { raftAddress := raft.ServerAddress(p2pURLToRaftAddress(s.RaftBind)) // Setup Raft communication. - transportManager := gRPCTransport.New(raftAddress, []grpc.DialOption{ + transportManager := raftgrpctransport.New(raftAddress, []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), }) @@ -185,10 +184,10 @@ func (s *RaftStore) Open() (*gRPCTransport.Manager, error) { }, }, } - for _, peer := range s.Peers { + for _, c := range s.Cosigners { configuration.Servers = append(configuration.Servers, raft.Server{ - ID: raft.ServerID(fmt.Sprint(peer.GetID())), - Address: raft.ServerAddress(p2pURLToRaftAddress(peer.GetAddress())), + ID: raft.ServerID(fmt.Sprint(c.GetID())), + Address: raft.ServerAddress(p2pURLToRaftAddress(c.GetAddress())), }) } s.raft.BootstrapCluster(configuration) @@ -213,7 +212,7 @@ func (s *RaftStore) Emit(key string, value interface{}) error { // Set sets the value for the given key. func (s *RaftStore) Set(key, value string) error { - if s.raft.State() != raft.Leader { + if !s.IsLeader() { return fmt.Errorf("not leader") } @@ -285,8 +284,30 @@ func (s *RaftStore) Join(nodeID, addr string) error { return nil } -func (s *RaftStore) GetLeader() raft.ServerAddress { - return s.raft.Leader() +func (s *RaftStore) IsLeader() bool { + if s == nil || s.raft == nil { + return false + } + return s.raft.State() == raft.Leader +} + +func (s *RaftStore) GetLeader() int { + if s == nil || s.raft == nil { + return -1 + } + _, leaderID := s.raft.LeaderWithID() + if leaderID == "" { + return -1 + } + id, err := strconv.Atoi(string(leaderID)) + if err != nil { + return -1 + } + return id +} + +func (s *RaftStore) ShareSigned(lss ChainSignStateConsensus) error { + return s.Emit(raftEventLSS, lss) } type fsm RaftStore diff --git a/signer/raft_store_test.go b/signer/raft_store_test.go index f4becd99..c7b051e8 100644 --- a/signer/raft_store_test.go +++ b/signer/raft_store_test.go @@ -2,13 +2,15 @@ package signer import ( "crypto/rand" - "crypto/rsa" "os" "testing" "time" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/libs/log" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/stretchr/testify/require" - tmCryptoEd25519 "github.com/tendermint/tendermint/crypto/ed25519" ) // Test_StoreInMemOpenSingleNode tests that a command can be applied to the log @@ -17,34 +19,28 @@ func Test_StoreInMemOpenSingleNode(t *testing.T) { tmpDir, _ := os.MkdirTemp("", "store_test") defer os.RemoveAll(tmpDir) - dummyPub := tmCryptoEd25519.PubKey{} + dummyPub := cometcryptoed25519.PubKey{} - bitSize := 4096 - rsaKey, err := rsa.GenerateKey(rand.Reader, bitSize) + eciesKey, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) require.NoError(t, err) - key := CosignerKey{ - PubKey: dummyPub, - ShareKey: []byte{}, - ID: 1, - } - signState := SignState{ - Height: 0, - Round: 0, - Step: 0, - } - - config := LocalCosignerConfig{ - CosignerKey: key, - SignState: &signState, - RsaKey: *rsaKey, - Peers: []CosignerPeer{{ - ID: 1, - PublicKey: rsaKey.PublicKey, - }}, + key := CosignerEd25519Key{ + PubKey: dummyPub, + PrivateShard: []byte{}, + ID: 1, } - cosigner := NewLocalCosigner(config) + cosigner := NewLocalCosigner( + log.NewNopLogger(), + &RuntimeConfig{}, + NewCosignerSecurityECIES( + CosignerECIESKey{ + ID: key.ID, + ECIESKey: eciesKey, + ECIESPubs: []*ecies.PublicKey{&eciesKey.PublicKey}, + }), + "", + ) s := &RaftStore{ NodeID: "1", @@ -54,7 +50,6 @@ func Test_StoreInMemOpenSingleNode(t *testing.T) { m: make(map[string]string), logger: nil, cosigner: cosigner, - Peers: []Cosigner{}, } if _, err := s.Open(); err != nil { diff --git a/signer/remote_cosigner.go b/signer/remote_cosigner.go index e0c9fd0b..dfb12350 100644 --- a/signer/remote_cosigner.go +++ b/signer/remote_cosigner.go @@ -2,36 +2,41 @@ package signer import ( "context" + "fmt" "net/url" "time" - proto "github.com/strangelove-ventures/horcrux/signer/proto" + cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/google/uuid" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) +var _ Cosigner = &RemoteCosigner{} + // RemoteCosigner uses CosignerGRPC to request signing from a remote cosigner type RemoteCosigner struct { id int address string + + client proto.CosignerClient } // NewRemoteCosigner returns a newly initialized RemoteCosigner -func NewRemoteCosigner(id int, address string) *RemoteCosigner { +func NewRemoteCosigner(id int, address string) (*RemoteCosigner, error) { + client, err := getGRPCClient(address) + if err != nil { + return nil, err + } cosigner := &RemoteCosigner{ id: id, address: address, + client: client, } - return cosigner -} -const ( - rpcTimeout = 4 * time.Second -) - -func getContext() (context.Context, context.CancelFunc) { - return context.WithTimeout(context.Background(), rpcTimeout) + return cosigner, nil } // GetID returns the ID of the remote cosigner @@ -46,63 +51,103 @@ func (cosigner *RemoteCosigner) GetAddress() string { return cosigner.address } -func (cosigner *RemoteCosigner) getGRPCClient() (proto.CosignerGRPCClient, *grpc.ClientConn, error) { +// GetPubKey returns public key of the validator. +// Implements Cosigner interface +func (cosigner *RemoteCosigner) GetPubKey(_ string) (cometcrypto.PubKey, error) { + return nil, fmt.Errorf("unexpected call to RemoteCosigner.GetPubKey") +} + +// VerifySignature validates a signed payload against the public key. +// Implements Cosigner interface +func (cosigner *RemoteCosigner) VerifySignature(_ string, _, _ []byte) bool { + return false +} + +func getGRPCClient(address string) (proto.CosignerClient, error) { var grpcAddress string - url, err := url.Parse(cosigner.address) + url, err := url.Parse(address) if err != nil { - grpcAddress = cosigner.address + grpcAddress = address } else { grpcAddress = url.Host } conn, err := grpc.Dial(grpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - return nil, nil, err + return nil, err } - return proto.NewCosignerGRPCClient(conn), conn, nil + return proto.NewCosignerClient(conn), nil } // Implements the cosigner interface -func (cosigner *RemoteCosigner) GetEphemeralSecretParts( - req HRSTKey) (*CosignerEphemeralSecretPartsResponse, error) { - client, conn, err := cosigner.getGRPCClient() - if err != nil { - return nil, err +func (cosigner *RemoteCosigner) GetNonces( + ctx context.Context, + uuids []uuid.UUID, +) (CosignerUUIDNoncesMultiple, error) { + us := make([][]byte, len(uuids)) + for i, u := range uuids { + us[i] = make([]byte, 16) + copy(us[i], u[:]) } - defer conn.Close() - context, cancelFunc := getContext() - defer cancelFunc() - res, err := client.GetEphemeralSecretParts(context, &proto.CosignerGRPCGetEphemeralSecretPartsRequest{ - Hrst: req.toProto(), + res, err := cosigner.client.GetNonces(ctx, &proto.GetNoncesRequest{ + Uuids: us, }) if err != nil { return nil, err } - return &CosignerEphemeralSecretPartsResponse{ - EncryptedSecrets: CosignerEphemeralSecretPartsFromProto(res.GetEncryptedSecrets()), - }, nil + out := make(CosignerUUIDNoncesMultiple, len(res.Nonces)) + for i, nonces := range res.Nonces { + out[i] = &CosignerUUIDNonces{ + UUID: uuid.UUID(nonces.Uuid), + Nonces: CosignerNoncesFromProto(nonces.Nonces), + } + } + return out, nil } // Implements the cosigner interface -func (cosigner *RemoteCosigner) SetEphemeralSecretPartsAndSign( - req CosignerSetEphemeralSecretPartsAndSignRequest) (*CosignerSignResponse, error) { - client, conn, err := cosigner.getGRPCClient() +func (cosigner *RemoteCosigner) SetNoncesAndSign( + ctx context.Context, + req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { + cosignerReq := &proto.SetNoncesAndSignRequest{ + Uuid: req.Nonces.UUID[:], + ChainID: req.ChainID, + Nonces: req.Nonces.Nonces.toProto(), + Hrst: req.HRST.toProto(), + SignBytes: req.SignBytes, + } + + if req.VoteExtensionNonces != nil && len(req.VoteExtensionSignBytes) > 0 { + cosignerReq.VoteExtUuid = req.VoteExtensionNonces.UUID[:] + cosignerReq.VoteExtNonces = req.VoteExtensionNonces.Nonces.toProto() + cosignerReq.VoteExtSignBytes = req.VoteExtensionSignBytes + } + + res, err := cosigner.client.SetNoncesAndSign(ctx, cosignerReq) if err != nil { return nil, err } - defer conn.Close() - context, cancelFunc := getContext() - defer cancelFunc() - res, err := client.SetEphemeralSecretPartsAndSign(context, &proto.CosignerGRPCSetEphemeralSecretPartsAndSignRequest{ - EncryptedSecrets: CosignerEphemeralSecretParts(req.EncryptedSecrets).toProto(), - Hrst: req.HRST.toProto(), - SignBytes: req.SignBytes, + return &CosignerSignResponse{ + NoncePublic: res.NoncePublic, + Timestamp: time.Unix(0, res.Timestamp), + Signature: res.Signature, + VoteExtensionSignature: res.VoteExtSignature, + VoteExtensionNoncePublic: res.VoteExtNoncePublic, + }, nil +} + +func (cosigner *RemoteCosigner) Sign( + ctx context.Context, + req CosignerSignBlockRequest, +) (*CosignerSignBlockResponse, error) { + res, err := cosigner.client.SignBlock(ctx, &proto.SignBlockRequest{ + ChainID: req.ChainID, + Block: req.Block.ToProto(), }) if err != nil { return nil, err } - return &CosignerSignResponse{ - EphemeralPublic: res.GetEphemeralPublic(), - Timestamp: time.Unix(0, res.GetTimestamp()), - Signature: res.GetSignature(), + return &CosignerSignBlockResponse{ + Signature: res.Signature, + VoteExtensionSignature: res.VoteExtSignature, }, nil } diff --git a/signer/remote_signer.go b/signer/remote_signer.go index edc99455..53dfd9a8 100644 --- a/signer/remote_signer.go +++ b/signer/remote_signer.go @@ -1,33 +1,44 @@ package signer import ( + "context" "fmt" "net" "time" - tmCryptoEd2219 "github.com/tendermint/tendermint/crypto/ed25519" - tmCryptoEncoding "github.com/tendermint/tendermint/crypto/encoding" - tmLog "github.com/tendermint/tendermint/libs/log" - tmNet "github.com/tendermint/tendermint/libs/net" - tmService "github.com/tendermint/tendermint/libs/service" - tmP2pConn "github.com/tendermint/tendermint/p2p/conn" - tmProtoCrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - tmProtoPrivval "github.com/tendermint/tendermint/proto/tendermint/privval" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" - tm "github.com/tendermint/tendermint/types" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" + cometlog "github.com/cometbft/cometbft/libs/log" + cometnet "github.com/cometbft/cometbft/libs/net" + cometservice "github.com/cometbft/cometbft/libs/service" + cometp2pconn "github.com/cometbft/cometbft/p2p/conn" + cometprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + cometprotoprivval "github.com/cometbft/cometbft/proto/tendermint/privval" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" ) +const connRetrySec = 2 + +// PrivValidator is a wrapper for tendermint PrivValidator, +// with additional Stop method for safe shutdown. +type PrivValidator interface { + Sign(ctx context.Context, chainID string, block Block) ([]byte, []byte, time.Time, error) + GetPubKey(ctx context.Context, chainID string) ([]byte, error) + Stop() +} + // ReconnRemoteSigner dials using its dialer and responds to any // signature requests using its privVal. type ReconnRemoteSigner struct { - tmService.BaseService + cometservice.BaseService address string - chainID string - privKey tmCryptoEd2219.PrivKey - privVal tm.PrivValidator + privKey cometcryptoed25519.PrivKey + privVal PrivValidator dialer net.Dialer + + maxReadSize int } // NewReconnRemoteSigner return a ReconnRemoteSigner that will dial using the given @@ -37,78 +48,106 @@ type ReconnRemoteSigner struct { // If the connection is broken, the ReconnRemoteSigner will attempt to reconnect. func NewReconnRemoteSigner( address string, - logger tmLog.Logger, - chainID string, - privVal tm.PrivValidator, + logger cometlog.Logger, + privVal PrivValidator, dialer net.Dialer, + maxReadSize int, ) *ReconnRemoteSigner { rs := &ReconnRemoteSigner{ - address: address, - chainID: chainID, - privVal: privVal, - dialer: dialer, - privKey: tmCryptoEd2219.GenPrivKey(), + address: address, + privVal: privVal, + dialer: dialer, + privKey: cometcryptoed25519.GenPrivKey(), + maxReadSize: maxReadSize, } - rs.BaseService = *tmService.NewBaseService(logger, "RemoteSigner", rs) + rs.BaseService = *cometservice.NewBaseService(logger, "RemoteSigner", rs) return rs } // OnStart implements cmn.Service. func (rs *ReconnRemoteSigner) OnStart() error { - go rs.loop() + go rs.loop(context.Background()) return nil } +// OnStop implements cmn.Service. +func (rs *ReconnRemoteSigner) OnStop() { + rs.privVal.Stop() +} + +func (rs *ReconnRemoteSigner) establishConnection(ctx context.Context) (net.Conn, error) { + ctx, cancel := context.WithTimeout(ctx, connRetrySec*time.Second) + defer cancel() + + proto, address := cometnet.ProtocolAndAddress(rs.address) + netConn, err := rs.dialer.DialContext(ctx, proto, address) + if err != nil { + return nil, fmt.Errorf("dial error: %w", err) + } + + conn, err := cometp2pconn.MakeSecretConnection(netConn, rs.privKey) + if err != nil { + netConn.Close() + return nil, fmt.Errorf("secret connection error: %w", err) + } + + return conn, nil +} + // main loop for ReconnRemoteSigner -func (rs *ReconnRemoteSigner) loop() { +func (rs *ReconnRemoteSigner) loop(ctx context.Context) { var conn net.Conn for { if !rs.IsRunning() { - if conn != nil { - if err := conn.Close(); err != nil { - rs.Logger.Error("Close", "err", err.Error()+"closing listener failed") - } - } + rs.closeConn(conn) return } + retries := 0 for conn == nil { - proto, address := tmNet.ProtocolAndAddress(rs.address) - netConn, err := rs.dialer.Dial(proto, address) - if err != nil { - sentryConnectTries.Add(float64(1)) - totalSentryConnectTries.Inc() - rs.Logger.Error("Dialing", "err", err) - rs.Logger.Info("Retrying", "sleep (s)", 3, "address", rs.address) - time.Sleep(time.Second * 3) - continue + var err error + timer := time.NewTimer(connRetrySec * time.Second) + conn, err = rs.establishConnection(ctx) + if err == nil { + sentryConnectTries.WithLabelValues(rs.address).Set(0) + timer.Stop() + rs.Logger.Info("Connected to Sentry", "address", rs.address) + break } - sentryConnectTries.Set(0) - - rs.Logger.Info("Connected to Sentry", "address", rs.address) - conn, err = tmP2pConn.MakeSecretConnection(netConn, rs.privKey) - if err != nil { - conn = nil - rs.Logger.Error("Secret Conn", "err", err) - rs.Logger.Info("Retrying", "sleep (s)", 3, "address", rs.address) - time.Sleep(time.Second * 3) + + sentryConnectTries.WithLabelValues(rs.address).Add(1) + totalSentryConnectTries.WithLabelValues(rs.address).Inc() + retries++ + rs.Logger.Error( + "Error establishing connection, will retry", + "sleep (s)", connRetrySec, + "address", rs.address, + "attempt", retries, + "err", err, + ) + select { + case <-ctx.Done(): + return + case <-timer.C: continue } } // since dialing can take time, we check running again if !rs.IsRunning() { - if err := conn.Close(); err != nil { - rs.Logger.Error("Close", "err", err.Error()+"closing listener failed") - } + rs.closeConn(conn) return } - req, err := ReadMsg(conn) + req, err := ReadMsg(conn, rs.maxReadSize) if err != nil { - rs.Logger.Error("readMsg", "err", err) - conn.Close() + rs.Logger.Error( + "Failed to read message from connection", + "address", rs.address, + "err", err, + ) + rs.closeConn(conn) conn = nil continue } @@ -118,166 +157,151 @@ func (rs *ReconnRemoteSigner) loop() { err = WriteMsg(conn, res) if err != nil { - rs.Logger.Error("writeMsg", "err", err) - conn.Close() + rs.Logger.Error( + "Failed to write message to connection", + "address", rs.address, + "err", err, + ) + rs.closeConn(conn) conn = nil } } } -func (rs *ReconnRemoteSigner) handleRequest(req tmProtoPrivval.Message) tmProtoPrivval.Message { +func (rs *ReconnRemoteSigner) handleRequest(req cometprotoprivval.Message) cometprotoprivval.Message { switch typedReq := req.Sum.(type) { - case *tmProtoPrivval.Message_SignVoteRequest: - return rs.handleSignVoteRequest(typedReq.SignVoteRequest.Vote) - case *tmProtoPrivval.Message_SignProposalRequest: - return rs.handleSignProposalRequest(typedReq.SignProposalRequest.Proposal) - case *tmProtoPrivval.Message_PubKeyRequest: - return rs.handlePubKeyRequest() - case *tmProtoPrivval.Message_PingRequest: + case *cometprotoprivval.Message_SignVoteRequest: + return rs.handleSignVoteRequest(typedReq.SignVoteRequest.ChainId, typedReq.SignVoteRequest.Vote) + case *cometprotoprivval.Message_SignProposalRequest: + return rs.handleSignProposalRequest(typedReq.SignProposalRequest.ChainId, typedReq.SignProposalRequest.Proposal) + case *cometprotoprivval.Message_PubKeyRequest: + return rs.handlePubKeyRequest(typedReq.PubKeyRequest.ChainId) + case *cometprotoprivval.Message_PingRequest: return rs.handlePingRequest() default: rs.Logger.Error("Unknown request", "err", fmt.Errorf("%v", typedReq)) - return tmProtoPrivval.Message{} + return cometprotoprivval.Message{} } } -func (rs *ReconnRemoteSigner) handleSignVoteRequest(vote *tmProto.Vote) tmProtoPrivval.Message { - msgSum := &tmProtoPrivval.Message_SignedVoteResponse{SignedVoteResponse: &tmProtoPrivval.SignedVoteResponse{ - Vote: tmProto.Vote{}, +func (rs *ReconnRemoteSigner) handleSignVoteRequest(chainID string, vote *cometproto.Vote) cometprotoprivval.Message { + msgSum := &cometprotoprivval.Message_SignedVoteResponse{SignedVoteResponse: &cometprotoprivval.SignedVoteResponse{ + Vote: cometproto.Vote{}, Error: nil, }} - if err := rs.privVal.SignVote(rs.chainID, vote); err != nil { - switch typedErr := err.(type) { - case *BeyondBlockError: - rs.Logger.Debug("Rejecting sign vote request", "reason", typedErr.msg) - beyondBlockErrors.Inc() - default: - rs.Logger.Error("Failed to sign vote", "address", rs.address, "error", err, "vote_type", vote.Type, - "height", vote.Height, "round", vote.Round, "validator", fmt.Sprintf("%X", vote.ValidatorAddress)) - failedSignVote.Inc() - } - msgSum.SignedVoteResponse.Error = getRemoteSignerError(err) - return tmProtoPrivval.Message{Sum: msgSum} - } - // Show signatures provided to each node have the same signature and timestamps - sigLen := 6 - if len(vote.Signature) < sigLen { - sigLen = len(vote.Signature) - } - rs.Logger.Info("Signed vote", "height", vote.Height, "round", vote.Round, "type", vote.Type, - "sig", vote.Signature[:sigLen], "ts", vote.Timestamp.Unix(), "node", rs.address) - - if vote.Type == tmProto.PrecommitType { - stepSize := vote.Height - previousPrecommitHeight - if previousPrecommitHeight != 0 && stepSize > 1 { - missedPrecommits.Add(float64(stepSize)) - totalMissedPrecommits.Add(float64(stepSize)) - } else { - missedPrecommits.Set(0) - } - previousPrecommitHeight = vote.Height // remember last PrecommitHeight - - metricsTimeKeeper.SetPreviousPrecommit(time.Now()) - - lastPrecommitHeight.Set(float64(vote.Height)) - lastPrecommitRound.Set(float64(vote.Round)) - totalPrecommitsSigned.Inc() - } - if vote.Type == tmProto.PrevoteType { - // Determine number of heights since the last Prevote - stepSize := vote.Height - previousPrevoteHeight - if previousPrevoteHeight != 0 && stepSize > 1 { - missedPrevotes.Add(float64(stepSize)) - totalMissedPrevotes.Add(float64(stepSize)) - } else { - missedPrevotes.Set(0) - } - previousPrevoteHeight = vote.Height // remember last PrevoteHeight - - metricsTimeKeeper.SetPreviousPrevote(time.Now()) - - lastPrevoteHeight.Set(float64(vote.Height)) - lastPrevoteRound.Set(float64(vote.Round)) - totalPrevotesSigned.Inc() + sig, voteExtSig, timestamp, err := signAndTrack( + context.TODO(), + rs.Logger, + rs.privVal, + chainID, + VoteToBlock(chainID, vote), + ) + if err != nil { + msgSum.SignedVoteResponse.Error = getRemoteSignerError(err) + return cometprotoprivval.Message{Sum: msgSum} } - msgSum.SignedVoteResponse.Vote = *vote - return tmProtoPrivval.Message{Sum: msgSum} + msgSum.SignedVoteResponse.Vote.Timestamp = timestamp + msgSum.SignedVoteResponse.Vote.Signature = sig + msgSum.SignedVoteResponse.Vote.ExtensionSignature = voteExtSig + return cometprotoprivval.Message{Sum: msgSum} } -func (rs *ReconnRemoteSigner) handleSignProposalRequest(proposal *tmProto.Proposal) tmProtoPrivval.Message { - msgSum := &tmProtoPrivval.Message_SignedProposalResponse{ - SignedProposalResponse: &tmProtoPrivval.SignedProposalResponse{ - Proposal: tmProto.Proposal{}, +func (rs *ReconnRemoteSigner) handleSignProposalRequest( + chainID string, + proposal *cometproto.Proposal, +) cometprotoprivval.Message { + msgSum := &cometprotoprivval.Message_SignedProposalResponse{ + SignedProposalResponse: &cometprotoprivval.SignedProposalResponse{ + Proposal: cometproto.Proposal{}, Error: nil, - }} - if err := rs.privVal.SignProposal(rs.chainID, proposal); err != nil { - switch typedErr := err.(type) { - case *BeyondBlockError: - rs.Logger.Debug("Rejecting proposal sign request", "reason", typedErr.msg) - beyondBlockErrors.Inc() - default: - rs.Logger.Error("Failed to sign proposal", "address", rs.address, "error", err, "proposal", proposal) - } + }, + } + + signature, _, timestamp, err := signAndTrack( + context.TODO(), + rs.Logger, + rs.privVal, + chainID, + ProposalToBlock(chainID, proposal), + ) + if err != nil { msgSum.SignedProposalResponse.Error = getRemoteSignerError(err) - return tmProtoPrivval.Message{Sum: msgSum} + return cometprotoprivval.Message{Sum: msgSum} } - rs.Logger.Info("Signed proposal", "node", rs.address, - "height", proposal.Height, "round", proposal.Round, "type", proposal.Type) - lastProposalHeight.Set(float64(proposal.Height)) - lastProposalRound.Set(float64(proposal.Round)) - totalProposalsSigned.Inc() - msgSum.SignedProposalResponse.Proposal = *proposal - return tmProtoPrivval.Message{Sum: msgSum} + + msgSum.SignedProposalResponse.Proposal.Timestamp = timestamp + msgSum.SignedProposalResponse.Proposal.Signature = signature + return cometprotoprivval.Message{Sum: msgSum} } -func (rs *ReconnRemoteSigner) handlePubKeyRequest() tmProtoPrivval.Message { - totalPubKeyRequests.Inc() - msgSum := &tmProtoPrivval.Message_PubKeyResponse{PubKeyResponse: &tmProtoPrivval.PubKeyResponse{ - PubKey: tmProtoCrypto.PublicKey{}, +func (rs *ReconnRemoteSigner) handlePubKeyRequest(chainID string) cometprotoprivval.Message { + totalPubKeyRequests.WithLabelValues(chainID).Inc() + msgSum := &cometprotoprivval.Message_PubKeyResponse{PubKeyResponse: &cometprotoprivval.PubKeyResponse{ + PubKey: cometprotocrypto.PublicKey{}, Error: nil, }} - pubKey, err := rs.privVal.GetPubKey() + + pubKey, err := rs.privVal.GetPubKey(context.TODO(), chainID) if err != nil { - rs.Logger.Error("Failed to get Pub Key", "address", rs.address, "error", err) + rs.Logger.Error( + "Failed to get Pub Key", + "chain_id", chainID, + "node", rs.address, + "error", err, + ) msgSum.PubKeyResponse.Error = getRemoteSignerError(err) - return tmProtoPrivval.Message{Sum: msgSum} + return cometprotoprivval.Message{Sum: msgSum} } - pk, err := tmCryptoEncoding.PubKeyToProto(pubKey) + pk, err := cometcryptoencoding.PubKeyToProto(cometcryptoed25519.PubKey(pubKey)) if err != nil { - rs.Logger.Error("Failed to get Pub Key", "address", rs.address, "error", err) + rs.Logger.Error( + "Failed to get Pub Key", + "chain_id", chainID, + "node", rs.address, + "error", err, + ) msgSum.PubKeyResponse.Error = getRemoteSignerError(err) - return tmProtoPrivval.Message{Sum: msgSum} + return cometprotoprivval.Message{Sum: msgSum} } msgSum.PubKeyResponse.PubKey = pk - return tmProtoPrivval.Message{Sum: msgSum} + return cometprotoprivval.Message{Sum: msgSum} } -func (rs *ReconnRemoteSigner) handlePingRequest() tmProtoPrivval.Message { - return tmProtoPrivval.Message{Sum: &tmProtoPrivval.Message_PingResponse{PingResponse: &tmProtoPrivval.PingResponse{}}} +func (rs *ReconnRemoteSigner) handlePingRequest() cometprotoprivval.Message { + return cometprotoprivval.Message{ + Sum: &cometprotoprivval.Message_PingResponse{ + PingResponse: &cometprotoprivval.PingResponse{}, + }, + } } -func getRemoteSignerError(err error) *tmProtoPrivval.RemoteSignerError { +func getRemoteSignerError(err error) *cometprotoprivval.RemoteSignerError { if err == nil { return nil } - return &tmProtoPrivval.RemoteSignerError{ + return &cometprotoprivval.RemoteSignerError{ Code: 0, Description: err.Error(), } } -func StartRemoteSigners(services []tmService.Service, logger tmLog.Logger, chainID string, - privVal tm.PrivValidator, nodes []NodeConfig) ([]tmService.Service, error) { +func StartRemoteSigners( + services []cometservice.Service, + logger cometlog.Logger, + privVal PrivValidator, + nodes []string, + maxReadSize int, +) ([]cometservice.Service, error) { var err error go StartMetrics() for _, node := range nodes { - // Tendermint requires a connection within 3 seconds of start or crashes + // CometBFT requires a connection within 3 seconds of start or crashes // A long timeout such as 30 seconds would cause the sentry to fail in loops // Use a short timeout and dial often to connect within 3 second window dialer := net.Dialer{Timeout: 2 * time.Second} - s := NewReconnRemoteSigner(node.Address, logger, chainID, privVal, dialer) + s := NewReconnRemoteSigner(node, logger, privVal, dialer, maxReadSize) err = s.Start() if err != nil { @@ -288,3 +312,15 @@ func StartRemoteSigners(services []tmService.Service, logger tmLog.Logger, chain } return services, err } + +func (rs *ReconnRemoteSigner) closeConn(conn net.Conn) { + if conn == nil { + return + } + if err := conn.Close(); err != nil { + rs.Logger.Error("Failed to close connection to chain node", + "address", rs.address, + "err", err, + ) + } +} diff --git a/signer/remote_signer_grpc_server.go b/signer/remote_signer_grpc_server.go new file mode 100644 index 00000000..797735ad --- /dev/null +++ b/signer/remote_signer_grpc_server.go @@ -0,0 +1,200 @@ +package signer + +import ( + "context" + "fmt" + "net" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + cometservice "github.com/cometbft/cometbft/libs/service" + + "github.com/strangelove-ventures/horcrux/v3/signer/proto" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var _ proto.RemoteSignerServer = &RemoteSignerGRPCServer{} + +type RemoteSignerGRPCServer struct { + cometservice.BaseService + + validator PrivValidator + logger cometlog.Logger + listenAddr string + + server *grpc.Server + + proto.UnimplementedRemoteSignerServer +} + +func NewRemoteSignerGRPCServer( + logger cometlog.Logger, + validator PrivValidator, + listenAddr string, +) *RemoteSignerGRPCServer { + s := &RemoteSignerGRPCServer{ + validator: validator, + logger: logger, + listenAddr: listenAddr, + } + s.BaseService = *cometservice.NewBaseService(logger, "RemoteSignerGRPCServer", s) + return s +} + +func (s *RemoteSignerGRPCServer) OnStart() error { + s.logger.Info("Remote Signer GRPC Listening", "address", s.listenAddr) + sock, err := net.Listen("tcp", s.listenAddr) + if err != nil { + return err + } + s.server = grpc.NewServer() + proto.RegisterRemoteSignerServer(s.server, s) + reflection.Register(s.server) + go s.serve(sock) + return nil +} + +func (s *RemoteSignerGRPCServer) serve(sock net.Listener) { + if err := s.server.Serve(sock); err != nil { + panic(fmt.Errorf("remote signer grpc server: %w", err)) + } +} + +func (s *RemoteSignerGRPCServer) OnStop() { + s.server.GracefulStop() +} + +func (s *RemoteSignerGRPCServer) PubKey(ctx context.Context, req *proto.PubKeyRequest) (*proto.PubKeyResponse, error) { + chainID := req.ChainId + + totalPubKeyRequests.WithLabelValues(chainID).Inc() + + pubKey, err := s.validator.GetPubKey(ctx, chainID) + if err != nil { + s.logger.Error( + "Failed to get Pub Key", + "chain_id", chainID, + "error", err, + ) + return nil, err + } + + return &proto.PubKeyResponse{ + PubKey: pubKey, + }, nil +} + +func (s *RemoteSignerGRPCServer) Sign( + ctx context.Context, + req *proto.SignBlockRequest, +) (*proto.SignBlockResponse, error) { + chainID, block := req.ChainID, BlockFromProto(req.Block) + + sig, voteExtSig, timestamp, err := signAndTrack(ctx, s.logger, s.validator, chainID, block) + if err != nil { + return nil, err + } + + return &proto.SignBlockResponse{ + Signature: sig, + VoteExtSignature: voteExtSig, + Timestamp: timestamp.UnixNano(), + }, nil +} + +func signAndTrack( + ctx context.Context, + logger cometlog.Logger, + validator PrivValidator, + chainID string, + block Block, +) ([]byte, []byte, time.Time, error) { + sig, voteExtSig, timestamp, err := validator.Sign(ctx, chainID, block) + if err != nil { + switch typedErr := err.(type) { + case *BeyondBlockError: + logger.Debug( + "Rejecting sign request", + "type", signType(block.Step), + "chain_id", chainID, + "height", block.Height, + "round", block.Round, + "reason", typedErr.msg, + ) + beyondBlockErrors.WithLabelValues(chainID).Inc() + default: + logger.Error( + "Failed to sign", + "type", signType(block.Step), + "chain_id", chainID, + "height", block.Height, + "round", block.Round, + "error", err, + ) + failedSignVote.WithLabelValues(chainID).Inc() + } + return nil, nil, block.Timestamp, err + } + + // Show signatures provided to each node have the same signature and timestamps + sigLen := 6 + if len(sig) < sigLen { + sigLen = len(sig) + } + extSigLen := 6 + if len(voteExtSig) < extSigLen { + extSigLen = len(voteExtSig) + } + logger.Info( + "Signed", + "type", signType(block.Step), + "chain_id", chainID, + "height", block.Height, + "round", block.Round, + "sig", sig[:sigLen], + "vote_ext_sig", voteExtSig[:extSigLen], + "ts", block.Timestamp, + ) + + switch block.Step { + case stepPropose: + lastProposalHeight.WithLabelValues(chainID).Set(float64(block.Height)) + lastProposalRound.WithLabelValues(chainID).Set(float64(block.Round)) + totalProposalsSigned.WithLabelValues(chainID).Inc() + case stepPrevote: + // Determine number of heights since the last Prevote + stepSize := block.Height - previousPrevoteHeight + if previousPrevoteHeight != 0 && stepSize > 1 { + missedPrevotes.WithLabelValues(chainID).Add(float64(stepSize)) + totalMissedPrevotes.WithLabelValues(chainID).Add(float64(stepSize)) + } else { + missedPrevotes.WithLabelValues(chainID).Set(0) + } + + previousPrevoteHeight = block.Height // remember last PrevoteHeight + + metricsTimeKeeper.SetPreviousPrevote(time.Now()) + + lastPrevoteHeight.WithLabelValues(chainID).Set(float64(block.Height)) + lastPrevoteRound.WithLabelValues(chainID).Set(float64(block.Round)) + totalPrevotesSigned.WithLabelValues(chainID).Inc() + case stepPrecommit: + stepSize := block.Height - previousPrecommitHeight + if previousPrecommitHeight != 0 && stepSize > 1 { + missedPrecommits.WithLabelValues(chainID).Add(float64(stepSize)) + totalMissedPrecommits.WithLabelValues(chainID).Add(float64(stepSize)) + } else { + missedPrecommits.WithLabelValues(chainID).Set(0) + } + previousPrecommitHeight = block.Height // remember last PrecommitHeight + + metricsTimeKeeper.SetPreviousPrecommit(time.Now()) + + lastPrecommitHeight.WithLabelValues(chainID).Set(float64(block.Height)) + lastPrecommitRound.WithLabelValues(chainID).Set(float64(block.Round)) + totalPrecommitsSigned.WithLabelValues(chainID).Inc() + } + + return sig, voteExtSig, timestamp, nil +} diff --git a/signer/services.go b/signer/services.go index f4ba2e50..e6263f06 100644 --- a/signer/services.go +++ b/signer/services.go @@ -6,15 +6,14 @@ import ( "os" "strconv" "strings" - "sync" "syscall" - tmLog "github.com/tendermint/tendermint/libs/log" - tmOS "github.com/tendermint/tendermint/libs/os" - tmService "github.com/tendermint/tendermint/libs/service" + cometlog "github.com/cometbft/cometbft/libs/log" + cometos "github.com/cometbft/cometbft/libs/os" + cometservice "github.com/cometbft/cometbft/libs/service" ) -func RequireNotRunning(pidFilePath string) error { +func RequireNotRunning(log cometlog.Logger, pidFilePath string) error { if _, err := os.Stat(pidFilePath); err != nil { if os.IsNotExist(err) { // lock file does not exist, can continue starting daemon @@ -42,8 +41,7 @@ func RequireNotRunning(pidFilePath string) error { process, err := os.FindProcess(int(pid)) if err != nil { - return fmt.Errorf(`unclean shutdown detected. PID file exists at %s but PID %d can not be found. -manual deletion of PID file required. %w`, pidFilePath, pid, err) + return fmt.Errorf("error checking pid %d: %w", pid, err) } err = process.Signal(syscall.Signal(0)) @@ -51,8 +49,16 @@ manual deletion of PID file required. %w`, pidFilePath, pid, err) return fmt.Errorf("horcrux is already running on PID: %d", pid) } if errors.Is(err, os.ErrProcessDone) { - return fmt.Errorf(`unclean shutdown detected. PID file exists at %s but PID %d is not running. -manual deletion of PID file required`, pidFilePath, pid) + log.Error( + "Unclean shutdown detected. PID file exists at but process with that ID cannot be found. Removing lock file", + "pid", pid, + "pid_file", pidFilePath, + "error", err, + ) + if err := os.Remove(pidFilePath); err != nil { + return fmt.Errorf("failed to delete pid file %s: %w", pidFilePath, err) + } + return nil } errno, ok := err.(syscall.Errno) @@ -68,9 +74,8 @@ manual deletion of PID file required`, pidFilePath, pid) return fmt.Errorf("unexpected error while signaling horcrux PID: %d", pid) } -func WaitAndTerminate(logger tmLog.Logger, services []tmService.Service, pidFilePath string) { - wg := sync.WaitGroup{} - wg.Add(1) +func WaitAndTerminate(logger cometlog.Logger, services []cometservice.Service, pidFilePath string) { + done := make(chan struct{}) pidFile, err := os.OpenFile(pidFilePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) if err != nil { @@ -81,7 +86,7 @@ func WaitAndTerminate(logger tmLog.Logger, services []tmService.Service, pidFile if err != nil { panic(fmt.Errorf("error writing to lock file: %s. %w", pidFilePath, err)) } - tmOS.TrapSignal(logger, func() { + cometos.TrapSignal(logger, func() { if err := os.Remove(pidFilePath); err != nil { fmt.Printf("Error removing lock file: %v\n", err) } @@ -91,7 +96,7 @@ func WaitAndTerminate(logger tmLog.Logger, services []tmService.Service, pidFile panic(err) } } - wg.Done() + close(done) }) - wg.Wait() + <-done } diff --git a/signer/services_test.go b/signer/services_test.go index 02115fb4..48f35dae 100644 --- a/signer/services_test.go +++ b/signer/services_test.go @@ -5,7 +5,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "strconv" "strings" "sync" @@ -13,26 +12,67 @@ import ( "testing" "time" - "github.com/strangelove-ventures/horcrux/signer" - tmlog "github.com/tendermint/tendermint/libs/log" - tmservice "github.com/tendermint/tendermint/libs/service" + cometlog "github.com/cometbft/cometbft/libs/log" + cometservice "github.com/cometbft/cometbft/libs/service" + "github.com/strangelove-ventures/horcrux/v3/signer" + fork "github.com/kraken-hpc/go-fork" "github.com/stretchr/testify/require" ) -func TestIsRunning(t *testing.T) { - homeDir := t.TempDir() - pidFilePath := filepath.Join(homeDir, "horcrux.pid") - pid := os.Getpid() + 1 - err := os.WriteFile( +func init() { + fork.RegisterFunc("child", mockHorcruxChildProcess) + fork.Init() +} + +func mockHorcruxChildProcess(pidFilePath string) { + _ = os.WriteFile( pidFilePath, - []byte(fmt.Sprintf("%d\n", pid)), + []byte(fmt.Sprintf("%d\n", os.Getpid())), 0600, ) - require.NoError(t, err, "error writing pid file") +} + +func waitForFileToExist(file string, timeout time.Duration) error { + exp := time.After(timeout) + tick := time.Tick(20 * time.Millisecond) + for { + select { + case <-exp: + return fmt.Errorf("timed out") + case <-tick: + if _, err := os.Stat(file); err != nil { + if os.IsNotExist(err) { + // file does not exist yet + continue + } + // unexpected error + return err + } + // file exists + return nil + } + } +} + +func TestIsRunning(t *testing.T) { + homeDir := t.TempDir() + pidFilePath := filepath.Join(homeDir, "horcrux.pid") - err = signer.RequireNotRunning(pidFilePath) - expectedErrorMsg := fmt.Sprintf("horcrux is already running on PID: %d", pid) + // github.com/kraken-hpc/go-fork package used (in tests only) to create a new pid with args[0] of horcrux. + // This lets us mock a horcrux process to test the "horcrux is already running" case. + err := fork.Fork("child", pidFilePath) + require.NoError(t, err) + + // wait for child process to start and write pidFilePath + err = waitForFileToExist(pidFilePath, 1*time.Second) + require.NoError(t, err) + + pidBz, err := os.ReadFile(pidFilePath) + require.NoError(t, err) + + err = signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) + expectedErrorMsg := fmt.Sprintf("horcrux is already running on PID: %s", strings.TrimSpace(string(pidBz))) require.EqualError(t, err, expectedErrorMsg) } @@ -40,20 +80,26 @@ func TestIsNotRunning(t *testing.T) { homeDir := t.TempDir() pidFilePath := filepath.Join(homeDir, "horcrux.pid") - err := signer.RequireNotRunning(pidFilePath) + err := signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) require.NoError(t, err) } -func getNonExistentPid() (int, error) { +func maxPid() int { + const defaultMaxPid = 100000 maxPidBytes, err := os.ReadFile("/proc/sys/kernel/pid_max") if err != nil { - return -1, err + return defaultMaxPid } - maxPid, err := strconv.ParseUint(strings.TrimSpace(string(maxPidBytes)), 10, 64) + maxPid, err := strconv.ParseInt(strings.TrimSpace(string(maxPidBytes)), 10, 32) if err != nil { - return -1, err + return defaultMaxPid } - for pid := 1; pid <= int(maxPid); pid++ { + return int(maxPid) +} + +func getUnusedPid() (int, error) { + max := maxPid() + for pid := 1; pid <= max; pid++ { process, err := os.FindProcess(pid) if err != nil { continue @@ -65,26 +111,15 @@ func getNonExistentPid() (int, error) { if errors.Is(err, os.ErrProcessDone) { return pid, nil } - errno, ok := err.(syscall.Errno) - if !ok { - continue - } - if errno == syscall.ESRCH { - return pid, nil - } } return -1, errors.New("could not find unused PID") } func TestIsRunningNonExistentPid(t *testing.T) { - if runtime.GOOS != "linux" { - t.Skip("test only valid on Linux") - } - homeDir := t.TempDir() pidFilePath := filepath.Join(homeDir, "horcrux.pid") - pid, err := getNonExistentPid() + pid, err := getUnusedPid() require.NoError(t, err) err = os.WriteFile( @@ -94,10 +129,11 @@ func TestIsRunningNonExistentPid(t *testing.T) { ) require.NoError(t, err, "error writing pid file") - err = signer.RequireNotRunning(pidFilePath) - expectedErrorMsg := fmt.Sprintf(`unclean shutdown detected. PID file exists at %s but PID %d is not running. -manual deletion of PID file required`, pidFilePath, pid) - require.EqualError(t, err, expectedErrorMsg) + err = signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) + require.Nil(t, err) + + _, err = os.Stat(pidFilePath) + require.ErrorIs(t, err, os.ErrNotExist) } func TestConcurrentStart(t *testing.T) { @@ -106,8 +142,8 @@ func TestConcurrentStart(t *testing.T) { homeDir := t.TempDir() pidFilePath := filepath.Join(homeDir, "horcrux.pid") - var logger tmlog.Logger - var services []tmservice.Service + var logger cometlog.Logger + var services []cometservice.Service var wg sync.WaitGroup wg.Add(concurrentAttempts) @@ -148,8 +184,8 @@ func TestIsRunningAndWaitForService(t *testing.T) { homeDir := t.TempDir() pidFilePath := filepath.Join(homeDir, "horcrux.pid") - var logger tmlog.Logger - var services []tmservice.Service + var logger cometlog.Logger + var services []cometservice.Service go func() { signer.WaitAndTerminate(logger, services, pidFilePath) }() // Wait for signer.WaitAndTerminate to create pidFile @@ -175,7 +211,7 @@ func TestIsRunningAndWaitForService(t *testing.T) { } panicFunction := func() { defer recoverFromPanic() - err = signer.RequireNotRunning(pidFilePath) + err = signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) } go panicFunction() wg.Wait() diff --git a/signer/sign_state.go b/signer/sign_state.go index 037d914a..f6c55eb0 100644 --- a/signer/sign_state.go +++ b/signer/sign_state.go @@ -8,12 +8,14 @@ import ( "os" "sync" + cometbytes "github.com/cometbft/cometbft/libs/bytes" + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/libs/protoio" + "github.com/cometbft/cometbft/libs/tempfile" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + comet "github.com/cometbft/cometbft/types" "github.com/gogo/protobuf/proto" - tmBytes "github.com/tendermint/tendermint/libs/bytes" - tmJson "github.com/tendermint/tendermint/libs/json" - "github.com/tendermint/tendermint/libs/protoio" - "github.com/tendermint/tendermint/libs/tempfile" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/strangelove-ventures/horcrux/v3/signer/cond" ) const ( @@ -23,51 +25,161 @@ const ( blocksToCache = 3 ) -func CanonicalVoteToStep(vote *tmProto.CanonicalVote) int8 { +func signType(step int8) string { + switch step { + case stepPropose: + return "proposal" + case stepPrevote: + return "prevote" + case stepPrecommit: + return "precommit" + default: + return "unknown" + } +} + +func CanonicalVoteToStep(vote *cometproto.CanonicalVote) int8 { switch vote.Type { - case tmProto.PrevoteType: + case cometproto.PrevoteType: return stepPrevote - case tmProto.PrecommitType: + case cometproto.PrecommitType: return stepPrecommit default: panic("Unknown vote type") } } -func VoteToStep(vote *tmProto.Vote) int8 { +func VoteToStep(vote *cometproto.Vote) int8 { switch vote.Type { - case tmProto.PrevoteType: + case cometproto.PrevoteType: return stepPrevote - case tmProto.PrecommitType: + case cometproto.PrecommitType: return stepPrecommit default: panic("Unknown vote type") } } -func ProposalToStep(_ *tmProto.Proposal) int8 { +func VoteToBlock(chainID string, vote *cometproto.Vote) Block { + return Block{ + Height: vote.Height, + Round: int64(vote.Round), + Step: VoteToStep(vote), + SignBytes: comet.VoteSignBytes(chainID, vote), + VoteExtensionSignBytes: comet.VoteExtensionSignBytes(chainID, vote), + Timestamp: vote.Timestamp, + } +} + +func ProposalToStep(_ *cometproto.Proposal) int8 { return stepPropose } +func ProposalToBlock(chainID string, proposal *cometproto.Proposal) Block { + return Block{ + Height: proposal.Height, + Round: int64(proposal.Round), + Step: ProposalToStep(proposal), + SignBytes: comet.ProposalSignBytes(chainID, proposal), + Timestamp: proposal.Timestamp, + } +} + +func StepToType(step int8) cometproto.SignedMsgType { + switch step { + case stepPropose: + return cometproto.ProposalType + case stepPrevote: + return cometproto.PrevoteType + case stepPrecommit: + return cometproto.PrecommitType + default: + panic("Unknown step") + } +} + // SignState stores signing information for high level watermark management. type SignState struct { - Height int64 `json:"height"` - Round int64 `json:"round"` - Step int8 `json:"step"` - EphemeralPublic []byte `json:"ephemeral_public"` - Signature []byte `json:"signature,omitempty"` - SignBytes tmBytes.HexBytes `json:"signbytes,omitempty"` - cache map[HRSKey]SignStateConsensus + Height int64 `json:"height"` + Round int64 `json:"round"` + Step int8 `json:"step"` + NoncePublic []byte `json:"nonce_public"` + Signature []byte `json:"signature,omitempty"` + SignBytes cometbytes.HexBytes `json:"signbytes,omitempty"` + VoteExtensionSignature []byte `json:"vote_ext_signature,omitempty"` filePath string + + // mu protects the cache and is used for signaling with cond. + mu sync.RWMutex + cache map[HRSKey]SignStateConsensus + cond *cond.Cond +} + +func (signState *SignState) existingSignatureOrErrorIfRegression(hrst HRSTKey, signBytes []byte) ([]byte, error) { + signState.mu.RLock() + defer signState.mu.RUnlock() + + sameHRS, err := signState.CheckHRS(hrst) + if err != nil { + return nil, err + } + + if !sameHRS { + // not a regression in height. okay to sign + return nil, nil + } + + // If the HRS is the same the sign bytes may still differ by timestamp + // It is ok to re-sign a different timestamp if that is the only difference in the sign bytes + if bytes.Equal(signBytes, signState.SignBytes) { + return signState.Signature, nil + } else if err := signState.OnlyDifferByTimestamp(signBytes); err != nil { + return nil, err + } + + // same HRS, and only differ by timestamp - ok to sign again + return nil, nil +} + +func (signState *SignState) HRSKey() HRSKey { + signState.mu.RLock() + defer signState.mu.RUnlock() + return HRSKey{ + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + } +} + +func (signState *SignState) hrsKeyLocked() HRSKey { + return HRSKey{ + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + } } type SignStateConsensus struct { - Height int64 - Round int64 - Step int8 - Signature []byte - SignBytes tmBytes.HexBytes + Height int64 + Round int64 + Step int8 + Signature []byte + VoteExtensionSignature []byte + SignBytes cometbytes.HexBytes +} + +func (signState SignStateConsensus) HRSKey() HRSKey { + return HRSKey{ + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + } +} + +type ChainSignStateConsensus struct { + ChainID string + SignStateConsensus SignStateConsensus } func NewSignStateConsensus(height int64, round int64, step int8) SignStateConsensus { @@ -91,37 +203,25 @@ func newConflictingDataError(existingSignBytes, newSignBytes []byte) *Conflictin } } -func (signState *SignState) GetFromCache(hrs HRSKey, lock *sync.Mutex) (HRSKey, *SignStateConsensus) { - if lock != nil { - lock.Lock() - defer lock.Unlock() - } - latestBlock := HRSKey{ - Height: signState.Height, - Round: signState.Round, - Step: signState.Step, - } +// GetFromCache will return the latest signed block within the SignState +// and the relevant SignStateConsensus from the cache, if present. +func (signState *SignState) GetFromCache(hrs HRSKey) (HRSKey, *SignStateConsensus) { + signState.mu.RLock() + defer signState.mu.RUnlock() + latestBlock := signState.hrsKeyLocked() if ssc, ok := signState.cache[hrs]; ok { return latestBlock, &ssc } return latestBlock, nil } -func (signState *SignState) Save(ssc SignStateConsensus, lock *sync.Mutex, async bool) error { - // One lock/unlock for less/equal check and mutation. - // Setting nil for lock for getErrorIfLessOrEqual to avoid recursive lock - if lock != nil { - lock.Lock() - defer lock.Unlock() - } +// cacheAndMarshal will cache a SignStateConsensus for it's HRS and return the marshalled bytes. +func (signState *SignState) cacheAndMarshal(ssc SignStateConsensus) []byte { + signState.mu.Lock() + defer signState.mu.Unlock() - err := signState.GetErrorIfLessOrEqual(ssc.Height, ssc.Round, ssc.Step, nil) - if err != nil { - return err - } - // HRS is greater than existing state, allow + signState.cache[ssc.HRSKey()] = ssc - signState.cache[HRSKey{Height: ssc.Height, Round: ssc.Round, Step: ssc.Step}] = ssc for hrs := range signState.cache { if hrs.Height < ssc.Height-blocksToCache { delete(signState.cache, hrs) @@ -133,36 +233,127 @@ func (signState *SignState) Save(ssc SignStateConsensus, lock *sync.Mutex, async signState.Step = ssc.Step signState.Signature = ssc.Signature signState.SignBytes = ssc.SignBytes - if async { + signState.VoteExtensionSignature = ssc.VoteExtensionSignature + + jsonBytes, err := cometjson.MarshalIndent(signState, "", " ") + if err != nil { + panic(err) + } + + return jsonBytes +} + +// Save updates the high watermark height/round/step (HRS) if it is greater +// than the current high watermark. If pendingDiskWG is provided, the write operation +// will be a separate goroutine (async). This allows pendingDiskWG to be used to .Wait() +// for all pending SignState disk writes. +func (signState *SignState) Save( + ssc SignStateConsensus, + pendingDiskWG *sync.WaitGroup, +) error { + err := signState.GetErrorIfLessOrEqual(ssc.Height, ssc.Round, ssc.Step) + if err != nil { + return err + } + + // HRS is greater than existing state, move forward with caching and saving. + + jsonBytes := signState.cacheAndMarshal(ssc) + + // Broadcast to waiting goroutines to notify them that an + // existing signature for their HRS may now be available. + signState.cond.Broadcast() + + if pendingDiskWG != nil { + pendingDiskWG.Add(1) go func() { - signState.save() + defer pendingDiskWG.Done() + signState.save(jsonBytes) }() } else { - signState.save() + signState.save(jsonBytes) } return nil } // Save persists the FilePvLastSignState to its filePath. -func (signState *SignState) save() { +func (signState *SignState) save(jsonBytes []byte) { outFile := signState.filePath - if outFile == "none" { + if outFile == os.DevNull { return } if outFile == "" { panic("cannot save SignState: filePath not set") } - jsonBytes, err := tmJson.MarshalIndent(signState, "", " ") + + err := tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) if err != nil { panic(err) } - err = tempfile.WriteFileAtomic(outFile, jsonBytes, 0600) - if err != nil { - panic(err) +} + +type HeightRegressionError struct { + regressed, last int64 +} + +func (e *HeightRegressionError) Error() string { + return fmt.Sprintf( + "height regression. Got %v, last height %v", + e.regressed, e.last, + ) +} + +func newHeightRegressionError(regressed, last int64) *HeightRegressionError { + return &HeightRegressionError{ + regressed: regressed, + last: last, } } +type RoundRegressionError struct { + height int64 + regressed, last int64 +} + +func (e *RoundRegressionError) Error() string { + return fmt.Sprintf( + "round regression at height %d. Got %d, last round %d", + e.height, e.regressed, e.last, + ) +} + +func newRoundRegressionError(height, regressed, last int64) *RoundRegressionError { + return &RoundRegressionError{ + height: height, + regressed: regressed, + last: last, + } +} + +type StepRegressionError struct { + height, round int64 + regressed, last int8 +} + +func (e *StepRegressionError) Error() string { + return fmt.Sprintf( + "step regression at height %d, round %d. Got %d, last step %d", + e.height, e.round, e.regressed, e.last, + ) +} + +func newStepRegressionError(height, round int64, regressed, last int8) *StepRegressionError { + return &StepRegressionError{ + height: height, + round: round, + regressed: regressed, + last: last, + } +} + +var ErrEmptySignBytes = errors.New("no SignBytes found") + // CheckHRS checks the given height, round, step (HRS) against that of the // SignState. It returns an error if the arguments constitute a regression, // or if they match but the SignBytes are empty. @@ -171,19 +362,17 @@ func (signState *SignState) save() { // It panics if the HRS matches the arguments, there's a SignBytes, but no Signature. func (signState *SignState) CheckHRS(hrst HRSTKey) (bool, error) { if signState.Height > hrst.Height { - return false, fmt.Errorf("height regression. Got %v, last height %v", hrst.Height, signState.Height) + return false, newHeightRegressionError(hrst.Height, signState.Height) } if signState.Height == hrst.Height { if signState.Round > hrst.Round { - return false, fmt.Errorf("round regression at height %v. Got %v, last round %v", - hrst.Height, hrst.Round, signState.Round) + return false, newRoundRegressionError(hrst.Height, hrst.Round, signState.Round) } if signState.Round == hrst.Round { if signState.Step > hrst.Step { - return false, fmt.Errorf("step regression at height %v round %v. Got %v, last step %v", - hrst.Height, hrst.Round, hrst.Step, signState.Step) + return false, newStepRegressionError(hrst.Height, hrst.Round, hrst.Step, signState.Step) } else if signState.Step == hrst.Step { if signState.SignBytes != nil { if signState.Signature == nil { @@ -191,7 +380,7 @@ func (signState *SignState) CheckHRS(hrst HRSTKey) (bool, error) { } return true, nil } - return false, errors.New("no SignBytes found") + return false, ErrEmptySignBytes } } } @@ -210,34 +399,14 @@ func newSameHRSError(hrs HRSKey) *SameHRSError { } } -func (signState *SignState) GetErrorIfLessOrEqual(height int64, round int64, step int8, lock *sync.Mutex) error { - if lock != nil { - lock.Lock() - defer lock.Unlock() +func (signState *SignState) GetErrorIfLessOrEqual(height int64, round int64, step int8) error { + hrs := HRSKey{Height: height, Round: round, Step: step} + signStateHRS := signState.HRSKey() + if signStateHRS.GreaterThan(hrs) { + return errors.New("regression not allowed") } - if height < signState.Height { - // lower height than current, don't allow state rollback - return errors.New("height regression not allowed") - } - if height > signState.Height { - return nil - } - // Height is equal - if round < signState.Round { - // lower round than current round for same block, don't allow state rollback - return errors.New("round regression not allowed") - } - if round > signState.Round { - return nil - } - // Height and Round are equal - - if step < signState.Step { - // lower round than current round for same block, don't allow state rollback - return errors.New("step regression not allowed") - } - if step == signState.Step { + if hrs == signStateHRS { // same HRS as current return newSameHRSError(HRSKey{Height: height, Round: round, Step: step}) } @@ -245,46 +414,85 @@ func (signState *SignState) GetErrorIfLessOrEqual(height int64, round int64, ste return nil } +// FreshCache returns a clone of a SignState with a new cache +// including the most recent sign state. +func (signState *SignState) FreshCache() *SignState { + newSignState := &SignState{ + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + NoncePublic: signState.NoncePublic, + Signature: signState.Signature, + SignBytes: signState.SignBytes, + VoteExtensionSignature: signState.VoteExtensionSignature, + cache: make(map[HRSKey]SignStateConsensus), + + filePath: signState.filePath, + } + + newSignState.cond = cond.New(&newSignState.mu) + + newSignState.cache[HRSKey{ + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + }] = SignStateConsensus{ + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + Signature: signState.Signature, + SignBytes: signState.SignBytes, + VoteExtensionSignature: signState.VoteExtensionSignature, + } + + return newSignState +} + // LoadSignState loads a sign state from disk. -func LoadSignState(filepath string) (SignState, error) { - state := SignState{} +func LoadSignState(filepath string) (*SignState, error) { stateJSONBytes, err := os.ReadFile(filepath) if err != nil { - return state, err + return nil, err } - err = tmJson.Unmarshal(stateJSONBytes, &state) + state := new(SignState) + + err = cometjson.Unmarshal(stateJSONBytes, &state) if err != nil { - return state, err - } - state.cache = make(map[HRSKey]SignStateConsensus) - state.cache[HRSKey{Height: state.Height, Round: state.Round, Step: state.Step}] = SignStateConsensus{ - Height: state.Height, - Round: state.Round, - Step: state.Step, - Signature: state.Signature, - SignBytes: state.SignBytes, + return nil, err } + state.filePath = filepath - return state, nil + + return state.FreshCache(), nil } // LoadOrCreateSignState loads the sign state from filepath // If the sign state could not be loaded, an empty sign state is initialized // and saved to filepath. -func LoadOrCreateSignState(filepath string) (SignState, error) { - existing, err := LoadSignState(filepath) - if err == nil { - return existing, nil +func LoadOrCreateSignState(filepath string) (*SignState, error) { + if _, err := os.Stat(filepath); err != nil { + if !os.IsNotExist(err) { + return nil, fmt.Errorf("unexpected error checking file existence (%s): %w", filepath, err) + } + // the only scenario where we want to create a new sign state file is when the file does not exist. + // Make an empty sign state and save it. + state := &SignState{ + filePath: filepath, + cache: make(map[HRSKey]SignStateConsensus), + } + state.cond = cond.New(&state.mu) + + jsonBytes, err := cometjson.MarshalIndent(state, "", " ") + if err != nil { + panic(err) + } + + state.save(jsonBytes) + return state, nil } - // There was an error loading the sign state - // Make an empty sign state and save it - state := SignState{} - state.filePath = filepath - state.cache = make(map[HRSKey]SignStateConsensus) - state.save() - return state, nil + return LoadSignState(filepath) } // OnlyDifferByTimestamp returns true if the sign bytes of the sign state @@ -304,49 +512,97 @@ func onlyDifferByTimestamp(step int8, signStateSignBytes, signBytes []byte) erro return checkVoteOnlyDifferByTimestamp(signStateSignBytes, signBytes) } - return fmt.Errorf("unexpected sign step: %d", step) + panic(fmt.Errorf("unexpected sign step: %d", step)) +} + +type UnmarshalError struct { + name string + signType string + err error +} + +func (e *UnmarshalError) Error() string { + return fmt.Sprintf("%s cannot be unmarshalled into %s: %v", e.name, e.signType, e.err) +} + +func newUnmarshalError(name, signType string, err error) *UnmarshalError { + return &UnmarshalError{ + name: name, + signType: signType, + err: err, + } +} + +type AlreadySignedVoteError struct { + nonFirst bool +} + +func (e *AlreadySignedVoteError) Error() string { + if e.nonFirst { + return "already signed vote with non-nil BlockID. refusing to sign vote on nil BlockID" + } + return "already signed vote with nil BlockID. refusing to sign vote on non-nil BlockID" +} + +func newAlreadySignedVoteError(nonFirst bool) *AlreadySignedVoteError { + return &AlreadySignedVoteError{ + nonFirst: nonFirst, + } +} + +type DiffBlockIDsError struct { + first []byte + second []byte +} + +func (e *DiffBlockIDsError) Error() string { + return fmt.Sprintf("differing block IDs - last Vote: %s, new Vote: %s", e.first, e.second) +} + +func newDiffBlockIDsError(first, second []byte) *DiffBlockIDsError { + return &DiffBlockIDsError{ + first: first, + second: second, + } } func checkVoteOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) error { - var lastVote, newVote tmProto.CanonicalVote + var lastVote, newVote cometproto.CanonicalVote if err := protoio.UnmarshalDelimited(lastSignBytes, &lastVote); err != nil { - return fmt.Errorf("lastSignBytes cannot be unmarshalled into vote: %v", err) + return newUnmarshalError("lastSignBytes", "vote", err) } if err := protoio.UnmarshalDelimited(newSignBytes, &newVote); err != nil { - return fmt.Errorf("signBytes cannot be unmarshalled into vote: %v", err) + return newUnmarshalError("newSignBytes", "vote", err) } // set the times to the same value and check equality newVote.Timestamp = lastVote.Timestamp - isEqual := proto.Equal(&newVote, &lastVote) - - if !isEqual { - lastVoteBlockID := lastVote.GetBlockID() - newVoteBlockID := newVote.GetBlockID() - if newVoteBlockID == nil && lastVoteBlockID != nil { - return errors.New("already signed vote with non-nil BlockID. refusing to sign vote on nil BlockID") - } - if newVoteBlockID != nil && lastVoteBlockID == nil { - return errors.New("already signed vote with nil BlockID. refusing to sign vote on non-nil BlockID") - } - if !bytes.Equal(lastVoteBlockID.GetHash(), newVoteBlockID.GetHash()) { - return fmt.Errorf("differing block IDs - last Vote: %s, new Vote: %s", - lastVoteBlockID.GetHash(), newVoteBlockID.GetHash()) - } - return newConflictingDataError(lastSignBytes, newSignBytes) + if proto.Equal(&newVote, &lastVote) { + return nil } - return nil + lastVoteBlockID := lastVote.GetBlockID() + newVoteBlockID := newVote.GetBlockID() + if newVoteBlockID == nil && lastVoteBlockID != nil { + return newAlreadySignedVoteError(true) + } + if newVoteBlockID != nil && lastVoteBlockID == nil { + return newAlreadySignedVoteError(false) + } + if !bytes.Equal(lastVoteBlockID.GetHash(), newVoteBlockID.GetHash()) { + return newDiffBlockIDsError(lastVoteBlockID.GetHash(), newVoteBlockID.GetHash()) + } + return newConflictingDataError(lastSignBytes, newSignBytes) } func checkProposalOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) error { - var lastProposal, newProposal tmProto.CanonicalProposal + var lastProposal, newProposal cometproto.CanonicalProposal if err := protoio.UnmarshalDelimited(lastSignBytes, &lastProposal); err != nil { - return fmt.Errorf("lastSignBytes cannot be unmarshalled into proposal: %v", err) + return newUnmarshalError("lastSignBytes", "proposal", err) } if err := protoio.UnmarshalDelimited(newSignBytes, &newProposal); err != nil { - return fmt.Errorf("signBytes cannot be unmarshalled into proposal: %v", err) + return newUnmarshalError("newSignBytes", "proposal", err) } // set the times to the same value and check equality diff --git a/signer/single_signer_validator.go b/signer/single_signer_validator.go new file mode 100644 index 00000000..846e283d --- /dev/null +++ b/signer/single_signer_validator.go @@ -0,0 +1,109 @@ +package signer + +import ( + "context" + "fmt" + "os" + "sync" + "time" +) + +var _ PrivValidator = &SingleSignerValidator{} + +// SingleSignerValidator guards access to an underlying PrivValidator by using mutexes +// for each of the PrivValidator interface functions +type SingleSignerValidator struct { + config *RuntimeConfig + chainState sync.Map +} + +// SingleSignerChainState holds the priv validator and associated mutex for a single chain. +type SingleSignerChainState struct { + filePV *FilePV + + // The filePV does not have any locking internally for signing operations. + // The high-watermark/last-signed-state within the FilePV prevents double sign + // as long as operations are synchronous. This lock is used to ensure that. + pvMutex sync.Mutex +} + +// NewSingleSignerValidator constructs a validator for single-sign mode (not recommended). +// NewThresholdValidator is recommended, but single-sign mode can be used for convenience. +func NewSingleSignerValidator(config *RuntimeConfig) *SingleSignerValidator { + return &SingleSignerValidator{ + config: config, + } +} + +// GetPubKey implements types.PrivValidator +func (pv *SingleSignerValidator) GetPubKey(_ context.Context, chainID string) ([]byte, error) { + chainState, err := pv.loadChainStateIfNecessary(chainID) + if err != nil { + return nil, err + } + pubKey, err := chainState.filePV.GetPubKey() + if err != nil { + return nil, err + } + return pubKey.Bytes(), nil +} + +// SignVote implements types.PrivValidator +func (pv *SingleSignerValidator) Sign( + _ context.Context, + chainID string, + block Block) ( + []byte, + []byte, + time.Time, + error, +) { + chainState, err := pv.loadChainStateIfNecessary(chainID) + if err != nil { + return nil, nil, block.Timestamp, err + } + chainState.pvMutex.Lock() + defer chainState.pvMutex.Unlock() + + return chainState.filePV.Sign(chainID, block) +} + +func (pv *SingleSignerValidator) loadChainStateIfNecessary(chainID string) (*SingleSignerChainState, error) { + cachedChainState, ok := pv.chainState.Load(chainID) + if ok { + return cachedChainState.(*SingleSignerChainState), nil + } + + keyFile := pv.config.KeyFilePathSingleSigner(chainID) + if _, err := os.Stat(keyFile); err != nil { + return nil, fmt.Errorf("failed to load key file (%s) - %w", keyFile, err) + } + + stateFile := pv.config.PrivValStateFile(chainID) + var filePV *FilePV + if _, err := os.Stat(stateFile); err != nil { + if !os.IsNotExist(err) { + panic(fmt.Errorf("failed to load state file (%s) - %w", stateFile, err)) + } + // The only scenario in which we want to create a new state file + // on disk is when the state file does not exist. + filePV, err = LoadFilePV(keyFile, stateFile, false) + if err != nil { + return nil, err + } + } else { + filePV, err = LoadFilePV(keyFile, stateFile, true) + if err != nil { + return nil, err + } + } + + chainState := &SingleSignerChainState{ + filePV: filePV, + } + pv.chainState.Store(chainID, chainState) + + return chainState, nil +} + +func (pv *SingleSignerValidator) Stop() {} diff --git a/signer/single_signer_validator_test.go b/signer/single_signer_validator_test.go new file mode 100644 index 00000000..721ec234 --- /dev/null +++ b/signer/single_signer_validator_test.go @@ -0,0 +1,127 @@ +package signer + +import ( + "context" + "path/filepath" + "time" + + "os" + "testing" + + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/tmhash" + cometjson "github.com/cometbft/cometbft/libs/json" + cometrand "github.com/cometbft/cometbft/libs/rand" + cometprivval "github.com/cometbft/cometbft/privval" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/stretchr/testify/require" +) + +func TestSingleSignerValidator(t *testing.T) { + t.Skip("TODO: fix this test when run with 'make test'") + + tmpDir := t.TempDir() + stateDir := filepath.Join(tmpDir, "state") + + err := os.MkdirAll(stateDir, 0700) + require.NoError(t, err) + + runtimeConfig := &RuntimeConfig{ + HomeDir: tmpDir, + StateDir: filepath.Join(tmpDir, "state"), + } + + privateKey := cometcryptoed25519.GenPrivKey() + + marshaled, err := cometjson.Marshal(cometprivval.FilePVKey{ + Address: privateKey.PubKey().Address(), + PubKey: privateKey.PubKey(), + PrivKey: privateKey, + }) + require.NoError(t, err) + + err = os.WriteFile(runtimeConfig.KeyFilePathSingleSigner(testChainID), marshaled, 0600) + require.NoError(t, err) + + err = os.WriteFile(runtimeConfig.KeyFilePathSingleSigner("different"), marshaled, 0600) + require.NoError(t, err) + + validator := NewSingleSignerValidator(runtimeConfig) + + proposal := cometproto.Proposal{ + Height: 1, + Round: 20, + Type: cometproto.ProposalType, + } + + block := ProposalToBlock(testChainID, &proposal) + + ctx := context.Background() + + signature, _, _, err := validator.Sign(ctx, testChainID, block) + require.NoError(t, err) + + require.True(t, privateKey.PubKey().VerifySignature(block.SignBytes, signature)) + + proposal.Timestamp = time.Now() + + // should be able to sign same proposal with only differing timestamp + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.NoError(t, err) + + // construct different block ID for proposal at same height as highest signed + randHash := cometrand.Bytes(tmhash.Size) + blockID := cometproto.BlockID{Hash: randHash, + PartSetHeader: cometproto.PartSetHeader{Total: 5, Hash: randHash}} + + proposal = cometproto.Proposal{ + Height: 1, + Round: 20, + Type: cometproto.ProposalType, + BlockID: blockID, + } + + // should not be able to sign same proposal at same height as highest signed with different BlockID + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.Error(t, err, "double sign!") + + proposal.Round = 19 + + // should not be able to sign lower than highest signed + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.Error(t, err, "double sign!") + + // lower LSS should sign for different chain ID + _, _, _, err = validator.Sign(ctx, "different", ProposalToBlock("different", &proposal)) + require.NoError(t, err) + + // reinitialize validator to make sure new runtime will not allow double sign + validator = NewSingleSignerValidator(runtimeConfig) + + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.Error(t, err, "double sign!") + + proposal.Round = 21 + + // signing higher block now should succeed + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.NoError(t, err) + + precommit := cometproto.Vote{ + Height: 2, + Round: 0, + Type: cometproto.PrecommitType, + Timestamp: time.Now(), + Extension: []byte("test"), + } + + block = VoteToBlock(testChainID, &precommit) + sig, voteExtSig, _, err := validator.Sign(ctx, testChainID, block) + require.NoError(t, err) + + require.True(t, privateKey.PubKey().VerifySignature(block.SignBytes, sig), "signature verification failed") + + require.True(t, privateKey.PubKey().VerifySignature(block.VoteExtensionSignBytes, voteExtSig), + "vote extension signature verification failed") + +} diff --git a/signer/testdata/rsa_keys.json b/signer/testdata/rsa_keys.json new file mode 100644 index 00000000..cd1b60c1 --- /dev/null +++ b/signer/testdata/rsa_keys.json @@ -0,0 +1,9 @@ +{ + "rsaKey": "MIIJKAIBAAKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQKCAgAZ/xLkK43QqwBmbRwGnLtrPO4eBW6re24kjjSfBfPuZ4aMOK4n8lTYaerGV4Z4e4AIdM8Hs6yfYWtK1XuoY9Q4Mxu1Qg0ubIuOsP18ctCbVGZpsnVesrFODNUbRi/MbnlSdKMyl+dkAGhVk+7c1/r5YVhK+Va0yr8fUvP2uobtyZRMm64XhDtSAtv+1BN2dLQhwPW+/cQmHXoO/C7cM+CAHpsyJ5MPcIAukqG/8H1Uks1yI9QNJsMMgzkjDtXOIv5oI2Dhex4fvUF6pFCYktHX8YPIBKZbEx0bM6pAcAJecmhNH78I6HKbcqBzGovrpHfdwBqIZgrQMfS7rTeKPDykNZ2aHb3xc2Tv1FOcAx+sVg58eEwyTzhTSriBJVWBlGfHbey09eojRhDMlrMgqTVArT25okKwUF+xl0eKqJlFvspSDN3XV6Yb0D35MXS8qQEFoTyM5b40mUIlgabR7cWu+jPB1sFU3P28NPHRG40fwnr89yYX3gevRPSrigewfVnkb3qMBa/fJ7Khuet7XxWYVCXwLDzRSSgHkEgkY3llc8J0Yg2HHdnNOSLqpRmnRED9li7Ol04Ps6sFB6nVszpEa6D6B7ADg7nVxsHbwrvKZ47Ut8dbGBm9Cpp4hFg9oGcvRdogz2bsjCU4Y90X3nfvR1YIkwoEkj/n5ZbuX1PPKQKCAQEA37ZzyFozo/Y2//heRxWgFPwVzBK27+DYc16WLtZxs+shaiXJ4Zn0DOI5x1VdC50sxHvlERcTQeQhSO+jnXQOkF6YQgylxLuYoFdo2l5jojn2vsHa4Klw9UatpFStCwz+PhD0wK7WhDoEKvwbvQ2wTa/wvDpnAuhhAEh2hMMuDkRpiVulYe40ywQW/NOrfUklAw1D/5NflDGPCYBYreP7pNtYbMRt9zLhOgQSvn9GqbpnnMj88gZDFxNO7jDiUsYO7yFsi01ALJ+T6AfVtKRyOEbjhpOBBpvlpwbUAMHuARjgcTWmvyWPbafIGpiaSX8ThtG8h78n/ITX6+NPF6fIfwKCAQEA6NFO9qLmYJWj8IaJaPKYBB1JdnFXOJvmPiJ4DS8BhLsSoEYsYE83ECXJwTI2F2l6MVyBXq+PPrQKm2vsUEWzzn/Fi+b7jEbjTv379yLylD1fjsMT0NHtN3vq09Yp3kNgTrd1IfCNxZ5A92Vh0f67PjHB3aMXeYd+ydBeIIlLVAgR6nUtkvGmFtuPunUlZiuB6UpMXDjPRN1VrddvQVTgSkPl9WB8wPcShxOz+wL4Hi5DII+ThFdAAh9pnIFaBF1Et/xMl2ss/7hcxqcIItQSBLotU0brPHMvoiSEjHWLuekw/b5noabkrfm8NOhB6Gjrq58oODe9ZrDjiaweh55jAwKCAQEApTqIgW29vlfXf27dkvrx5Q3au4MHAly7AVrW6XkROaVsZI3McYfXrLxZmFQACNfpfKVVJi441u27d7cmzOAu8YosQnw84vT7YVGt67rTM7pD99gN5OjAuSeekETKGeNa1FSJsNZxMe/3rBfQFO3LTVWpJByugINJQYBDqQLPPVJh8EVz/MSG0XsPz2Q2wK4JXBusIVOjwDxqPMZCuQwtjDFFOfBKl81IdCUWAwTWF/3JEQ+RYuAlJSHpphsMzb3iwdOZ67j+sPabs0A2ItliUxZobbj8DvmNwLNWWcjiFIVfH75UjdEcAg1tydbz/VyR+31lFY2l5ufm4h5dCEev2QKCAQAX543NAxLWbebkRlwLe4UiPwOQ9rg25sLwNEfRSrdEMpUKAcqCpP+JV+fsP0SQiNL0CIR7/Vie3ouMQ7uCznVUyYe2AqRnVcv3C1r4mA0CLX8HQH5jXXqWzNFiqMWpvY9A5dNQBcv4s3QGMtGlZxtAmolGQX2ii8f33r4bZx1l5mI4iYmBYfBkvmx2f5q0b9kp4+gNPAQEFRm7/Le+pIFW/ru4wwxsH7I2Tk6XgkmJh8R6rmM+HltDHIiSejGM6yqoHW6byXRYWUylVPcf5FhpRdhriYeTsFv+sPMvHM6Y6xmNpCQt0939AvxRDlveCg/Qkknl48s9pQHn29VSpW+TAoIBAE862157emwegrRYu66ENMMNLbF6YxBpL47iWC1NQ4/8aM5i58edv1VWUw5R441uvAGXk7uzsYkrxmp0nj6UANkjq6n06mayN90kbg4FvjAzEtbbAu8byap+B5xLSuetIc+dVqACKihQ09zwm3d/LFtbrgZ2KGiw/RfvMxxT0HYp9A7NdYT4nf2Aqa3UR4SuxWm4bWWLHHMGeS4N9MuwFP64jLgLrXzyB851Avuz9LJCpNAflE9SQUvTqGwpFvsgEwQcGH+5vcvcBENCYbAwq5hmMnzrAXsA1NnJNqn+oqXG8GIogG7DOa4965QL60TwDu9s/opzV2bMVhVtxDqKSfo=", + "rsaPubs": [ + "MIICCgKCAgEAzo2bGRRrwn1/TFlkJ9yqvOcx0BYvmay+rPQFnDFsKxb+WHHLtwn/juxY0Ub+ABwCgJgBUr4k3G9piFYwtL0R3ton6UulwYMgNQ6cnn8/zmAx/STP/WGYKXRtTR80csC+u8g/kzUK/lX2pGz77BLNxflKf/yfnm3wkCcJecnv2PLW84J3/s6b3TUkS+ygUQL3SB+IN7dI/i1pls7my6pCTOJxIu7TJ+PPahyDkRhE0OapjH0OQIbHXNeCqe71uQwALdf1dwTDl2JeIL7jhGWB8xb2PfeLX+VZsOWUR0NPfs83viS+Pjtz6ndYX1+3+BQxOIutnkUC6IwSBqsG+M2cqElETIgUHpxqRl0QtReq18+GTX9CfFB5hmWgLlGICij9Lnz0zpwtyIQJXn2Cny8XeWi8E9uKpi+4MNkDqwPd2U+wIXBPVBgqPjTByLeish+VaxKV2bHzqManB5WHa0g7WDK9p8OdZ7To8miJF+hdqOZMHnxThY/hr0102ffOq8XCDIfm873Ie2Cn/+KBHwCc6e7XO5ohWKm9WQbsxpmpn3+ru1ekWTkqC8YC7FFpljMCpl9NiGz4edVzSnnL8OU12M1pofEwpbMtlNCzaVJkMzfo9jDRoWxDyKffRYbdp93V1Oio0ab2ou9uZ0Jx0mXIpLyvznRNmDEsj5nrWmbW5jcCAwEAAQ==", + "MIICCgKCAgEA83UMSAbKSL4/W9VAzn+XjqCmhl8og6BoZvukS1pQI0JFrox63hJYavHTQB0DO2iXomfpm9d+J8NHsBsWf7DD/9aNaByGRJ0k5Lde64FfTj6LP9I5yRoKuGGQ0Heuvuisz9DMWRyhkO9hJiyedX7VdPx3VdUW4AX+FWyJ1pKpj0g/s8eYrUFyzISdoq/pRwkVkzHpXqFh3L5ASUjf9eQXGYsQsDI0UDuzZdYD4nitQ5Q0POM7jCgSQQ8d/b0eaF2hCzbZ1UWKx8LzCU7j4NRqrYJluRqkxeEtBeZsq7QX6Hs13tg+wKKCkOI+wt/1tifLE8IA3es0pXm0UstVduaTMeFtLTvIYE9E/0yFC23aFydz1Fny6HBjpfNo6BgzNCurMziOdpiuLy+7luPM+SBJ3YV0D9TVU8Lo0vawPccj3tcKmozeJdBhuedXWAm00mlCw+LueKBUVxti2kwHiDjBDbLDymZYZHR8HYI0KsrycsvemTotZzYXgDjyRful7mPLGecJhRye7xNX9lVUse81C94gmdZXVL2GKY1PquWJvgazg99gta62GrRj127vDcS2UI+6/4aTJwQFvRqWRLvS/MIJyq5eiq1WyDLOT8dOyBlb7+BV55cB7JUTiO7MsMNaX0h/C3iGrTOnh8rmC/20ygHqZC3E1Lw0SezI2r1NzzcCAwEAAQ==", + "MIICCgKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQ==" + ], + "id": 3 +} \ No newline at end of file diff --git a/signer/testdata/testdata.go b/signer/testdata/testdata.go new file mode 100644 index 00000000..c7fa8eea --- /dev/null +++ b/signer/testdata/testdata.go @@ -0,0 +1,8 @@ +package testdata + +import ( + _ "embed" // required to embed files +) + +//go:embed rsa_keys.json +var RSAKeys []byte diff --git a/signer/threshold_signer.go b/signer/threshold_signer.go index b83ab6b4..f8e9ef34 100644 --- a/signer/threshold_signer.go +++ b/signer/threshold_signer.go @@ -1,43 +1,39 @@ package signer -import ( - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" -) - -const ( - SignerTypeSoftSign = "SoftSign" - SignerTypeHSM = "HSM" -) +import "time" // Interface for the local signer whether it's a soft sign or HSM type ThresholdSigner interface { - Type() string - - DealShares(req CosignerGetEphemeralSecretPartRequest) (HrsMetadata, error) + // PubKey returns the public key bytes for the combination of all cosigners. + PubKey() []byte - GetEphemeralSecretPart(req CosignerGetEphemeralSecretPartRequest, m *LastSignStateWrapper, - peers map[int]CosignerPeer) (CosignerEphemeralSecretPart, error) + // Sign signs a byte payload with the provided nonces. + Sign(nonces []Nonce, payload []byte) ([]byte, error) - SetEphemeralSecretPart(req CosignerSetEphemeralSecretPartRequest, m *LastSignStateWrapper, - peers map[int]CosignerPeer) error + // CombineSignatures combines multiple partial signatures to a full signature. + CombineSignatures([]PartialSignature) ([]byte, error) +} - Sign(req CosignerSignRequest, m *LastSignStateWrapper) (CosignerSignResponse, error) +// Nonces contains the ephemeral information generated by one cosigner for all other cosigners. +type Nonces struct { + PubKey []byte + Shares [][]byte +} - GetID() (int, error) +type NoncesWithExpiration struct { + Expiration time.Time + Nonces []Nonces } -// PeerMetadata holds the share and the ephermeral secret public key -// Moved from Local cosigner to threshold_ed25519 -type PeerMetadata struct { - Share []byte - EphemeralSecretPublicKey []byte +// Nonce is the ephemeral information from another cosigner destined for this cosigner. +type Nonce struct { + ID int + Share []byte + PubKey []byte } -// HrsMetadata holds the ephemeral nonces from cosigner peers -// for a given height, round, step. -type HrsMetadata struct { - // need to be _total_ entries per player - Secret []byte - DealtShares []tsed25519.Scalar - Peers []PeerMetadata +// PartialSignature contains the signature and identifier for a piece of the combined signature. +type PartialSignature struct { + ID int + Signature []byte } diff --git a/signer/threshold_signer_soft.go b/signer/threshold_signer_soft.go index eddd5efc..c94500cc 100644 --- a/signer/threshold_signer_soft.go +++ b/signer/threshold_signer_soft.go @@ -2,343 +2,124 @@ package signer import ( "bytes" - "crypto" "crypto/rand" - "crypto/rsa" - "crypto/sha256" "errors" "fmt" - tmcryptoed25519 "github.com/tendermint/tendermint/crypto/ed25519" - tmjson "github.com/tendermint/tendermint/libs/json" "gitlab.com/unit410/edwards25519" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" ) -// ThresholdSignerSoft implements the interface and signs the message for each local signer. -// ThresholdSignerSoft is the implementation of a soft sign signer at the local level. +var _ ThresholdSigner = &ThresholdSignerSoft{} + type ThresholdSignerSoft struct { - pubKeyBytes []byte - key CosignerKey - // total signers - total uint8 - threshold uint8 - // Height, Round, Step, Timestamp --> metadata - hrsMeta map[HRSTKey]HrsMetadata + privateKeyShard []byte + pubKey []byte + threshold uint8 + total uint8 } -// NewThresholdSignerSoft constructs a ThresholdSigner -// that signs using the local key share file. -func NewThresholdSignerSoft(key CosignerKey, threshold, total uint8) ThresholdSigner { - softSigner := &ThresholdSignerSoft{ - key: key, - hrsMeta: make(map[HRSTKey]HrsMetadata), - total: total, - threshold: threshold, +func NewThresholdSignerSoft(config *RuntimeConfig, id int, chainID string) (*ThresholdSignerSoft, error) { + keyFile, err := config.KeyFileExistsCosigner(chainID) + if err != nil { + return nil, err } - // cache the public key bytes for signing operations. - // Ensures casting else it will naturally panic. - ed25519Key := softSigner.key.PubKey.(tmcryptoed25519.PubKey) - softSigner.pubKeyBytes = make([]byte, len(ed25519Key)) - softSigner.pubKeyBytes = ed25519Key[:] + key, err := LoadCosignerEd25519Key(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading cosigner key: %s", err) + } - return softSigner -} + if key.ID != id { + return nil, fmt.Errorf("key shard ID (%d) in (%s) does not match cosigner ID (%d)", key.ID, keyFile, id) + } -// Implements ThresholdSigner -func (softSigner *ThresholdSignerSoft) Type() string { - return SignerTypeSoftSign -} + s := ThresholdSignerSoft{ + privateKeyShard: key.PrivateShard, + pubKey: key.PubKey.Bytes(), + threshold: uint8(config.Config.ThresholdModeConfig.Threshold), + total: uint8(len(config.Config.ThresholdModeConfig.Cosigners)), + } -// Implements ThresholdSigner -func (softSigner *ThresholdSignerSoft) GetID() (int, error) { - return softSigner.key.ID, nil + return &s, nil } -// Implements ThresholdSigner -func (softSigner *ThresholdSignerSoft) Sign( - req CosignerSignRequest, m *LastSignStateWrapper) (CosignerSignResponse, error) { - m.lastSignStateMutex.Lock() - defer m.lastSignStateMutex.Unlock() - - res := CosignerSignResponse{} - lss := m.LastSignState - - hrst, err := UnpackHRST(req.SignBytes) - if err != nil { - return res, err - } +func (s *ThresholdSignerSoft) PubKey() []byte { + return s.pubKey +} - sameHRS, err := lss.CheckHRS(hrst) +func (s *ThresholdSignerSoft) Sign(nonces []Nonce, payload []byte) ([]byte, error) { + nonceShare, noncePub, err := s.sumNonces(nonces) if err != nil { - return res, err - } - // If the HRS is the same the sign bytes may still differ by timestamp - // It is ok to re-sign a different timestamp if that is the only difference in the sign bytes - // same HRS, and only differ by timestamp its ok to sign again - if sameHRS { - if bytes.Equal(req.SignBytes, lss.SignBytes) { - res.EphemeralPublic = lss.EphemeralPublic - res.Signature = lss.Signature - return res, nil - } else if err := lss.OnlyDifferByTimestamp(req.SignBytes); err != nil { - return res, err // same HRS, and only differ by timestamp its ok to sign again - } + return nil, fmt.Errorf("failed to combine nonces: %w", err) } - meta, ok := softSigner.hrsMeta[hrst] - if !ok { - return res, errors.New("no metadata at HRS") - } + sig := tsed25519.SignWithShare( + payload, s.privateKeyShard, nonceShare, s.pubKey, noncePub) + return append(noncePub, sig...), nil +} - shareParts := make([]tsed25519.Scalar, 0) - publicKeys := make([]tsed25519.Element, 0) +func (s *ThresholdSignerSoft) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed25519.Element, error) { + shareParts := make([]tsed25519.Scalar, len(nonces)) + publicKeys := make([]tsed25519.Element, len(nonces)) - // calculate secret and public keys - for _, peer := range meta.Peers { - if len(peer.Share) == 0 { - continue - } - shareParts = append(shareParts, peer.Share) - publicKeys = append(publicKeys, peer.EphemeralSecretPublicKey) + for i, n := range nonces { + shareParts[i] = n.Share + publicKeys[i] = n.PubKey } - ephemeralShare := tsed25519.AddScalars(shareParts) - ephemeralPublic := tsed25519.AddElements(publicKeys) + nonceShare := tsed25519.AddScalars(shareParts) + noncePub := tsed25519.AddElements(publicKeys) // check bounds for ephemeral share to avoid passing out of bounds valids to SignWithShare - - if len(ephemeralShare) != 32 { - return res, errors.New("ephemeral share is out of bounds") + if len(nonceShare) != 32 { + return nil, nil, errors.New("ephemeral share is out of bounds") } var scalarBytes [32]byte - copy(scalarBytes[:], ephemeralShare) + copy(scalarBytes[:], nonceShare) if !edwards25519.ScMinimal(&scalarBytes) { - return res, errors.New("ephemeral share is out of bounds") - } - - sig := tsed25519.SignWithShare( - req.SignBytes, softSigner.key.ShareKey, ephemeralShare, softSigner.pubKeyBytes, ephemeralPublic) - - m.LastSignState.EphemeralPublic = ephemeralPublic - err = m.LastSignState.Save(SignStateConsensus{ - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - Signature: sig, - SignBytes: req.SignBytes, - }, nil, true) - if err != nil { - var isSameHRSError *SameHRSError - if !errors.As(err, &isSameHRSError) { - return res, err - } + return nil, nil, errors.New("ephemeral share is out of bounds") } - for existingKey := range softSigner.hrsMeta { - // delete any HRS lower than our signed level - // we will not be providing parts for any lower HRS - if existingKey.Less(hrst) { - delete(softSigner.hrsMeta, existingKey) - } - } - - res.EphemeralPublic = ephemeralPublic - res.Signature = sig - return res, nil + return nonceShare, noncePub, nil } -// Implements ThresholdSigner -func (softSigner *ThresholdSignerSoft) DealShares( - req CosignerGetEphemeralSecretPartRequest) (HrsMetadata, error) { - hrsKey := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), - } - - meta, ok := softSigner.hrsMeta[hrsKey] - if ok { - return meta, nil - } - +func GenerateNonces(threshold, total uint8) (Nonces, error) { secret := make([]byte, 32) if _, err := rand.Read(secret); err != nil { - return HrsMetadata{}, err - } - - meta = HrsMetadata{ - Secret: secret, - Peers: make([]PeerMetadata, softSigner.total), - } - - // split this secret with shamirs - // !! dealt shares need to be saved because dealing produces different shares each time! - meta.DealtShares = tsed25519.DealShares(meta.Secret, softSigner.threshold, softSigner.total) - - softSigner.hrsMeta[hrsKey] = meta - - return meta, nil -} - -// Get the ephemeral secret part for an ephemeral share -// The ephemeral secret part is encrypted for the receiver -// Implements ThresholdSigner -func (softSigner *ThresholdSignerSoft) GetEphemeralSecretPart( - req CosignerGetEphemeralSecretPartRequest, m *LastSignStateWrapper, peers map[int]CosignerPeer) ( - CosignerEphemeralSecretPart, error) { - - res := CosignerEphemeralSecretPart{} - - // protects the meta map - m.lastSignStateMutex.Lock() - defer m.lastSignStateMutex.Unlock() - - hrst := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), + return Nonces{}, err } - meta, ok := softSigner.hrsMeta[hrst] - // generate metadata placeholder - if !ok { - newMeta, err := softSigner.DealShares(CosignerGetEphemeralSecretPartRequest{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp, - }) - - if err != nil { - return res, err - } - - meta = newMeta - softSigner.hrsMeta[hrst] = meta + nonces := Nonces{ + PubKey: tsed25519.ScalarMultiplyBase(secret), + Shares: make([][]byte, total), } - ourEphPublicKey := tsed25519.ScalarMultiplyBase(meta.Secret) - - // set our values - meta.Peers[softSigner.key.ID-1].Share = meta.DealtShares[softSigner.key.ID-1] - meta.Peers[softSigner.key.ID-1].EphemeralSecretPublicKey = ourEphPublicKey + shares := tsed25519.DealShares(secret, threshold, total) - // grab the peer info for the ID being requested - peer, ok := peers[req.ID] - if !ok { - return res, errors.New("unknown peer ID") + for i, sh := range shares { + nonces.Shares[i] = sh } - sharePart := meta.DealtShares[req.ID-1] - - // use RSA public to encrypt user's share part - encrypted, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &peer.PublicKey, sharePart, nil) - if err != nil { - return res, err - } - - res.SourceID = softSigner.key.ID - res.SourceEphemeralSecretPublicKey = ourEphPublicKey - res.EncryptedSharePart = encrypted - - // sign the response payload with our private key - // cosigners can verify the signature to confirm sender validity - - jsonBytes, err := tmjson.Marshal(res) - - if err != nil { - return res, err - } - - digest := sha256.Sum256(jsonBytes) - signature, err := rsa.SignPSS(rand.Reader, &softSigner.key.RSAKey, crypto.SHA256, digest[:], nil) - - if err != nil { - return res, err - } - - res.SourceSig = signature - - res.DestinationID = req.ID - - return res, nil + return nonces, nil } -// Store an ephemeral secret share part provided by another cosigner (signer) -// Implements ThresholdSigner -func (softSigner *ThresholdSignerSoft) SetEphemeralSecretPart( - req CosignerSetEphemeralSecretPartRequest, m *LastSignStateWrapper, peers map[int]CosignerPeer) error { - - // Verify the source signature - if req.SourceSig == nil { - return errors.New("SourceSig field is required") - } - - digestMsg := CosignerEphemeralSecretPart{ - SourceID: req.SourceID, - // DestinationID: 0, - SourceEphemeralSecretPublicKey: req.SourceEphemeralSecretPublicKey, - EncryptedSharePart: req.EncryptedSharePart, - // SourceSig: []byte{}, - } - - digestBytes, err := tmjson.Marshal(digestMsg) - if err != nil { - return err - } - - digest := sha256.Sum256(digestBytes) - peer, ok := peers[req.SourceID] - - if !ok { - return fmt.Errorf("unknown cosigner: %d", req.SourceID) - } - - peerPub := peer.PublicKey - err = rsa.VerifyPSS(&peerPub, crypto.SHA256, digest[:], req.SourceSig, nil) - if err != nil { - return err - } - - // protects the meta map - m.lastSignStateMutex.Lock() - defer m.lastSignStateMutex.Unlock() - - hrst := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), - } - - meta, ok := softSigner.hrsMeta[hrst] // generate metadata placeholder, softSigner.HrsMeta[hrst] is non-addressable - if !ok { - newMeta, err := softSigner.DealShares(CosignerGetEphemeralSecretPartRequest{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - }) - if err != nil { - return err +func (s *ThresholdSignerSoft) CombineSignatures(signatures []PartialSignature) ([]byte, error) { + sigIds := make([]int, len(signatures)) + shareSigs := make([][]byte, len(signatures)) + var ephPub []byte + + for i, sig := range signatures { + sigIds[i] = sig.ID + if i == 0 { + ephPub = sig.Signature[:32] + } else if !bytes.Equal(sig.Signature[:32], ephPub) { + return nil, fmt.Errorf("ephemeral public keys do not match") } - meta = newMeta - softSigner.hrsMeta[hrst] = meta // updates the metadata placeholder - } - - // decrypt share - sharePart, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, &softSigner.key.RSAKey, req.EncryptedSharePart, nil) - if err != nil { - return err + shareSigs[i] = sig.Signature[32:] } - // set slot - // Share & EphemeralSecretPublicKey is a SLICE so its a valid change of the shared struct softSigner! - meta.Peers[req.SourceID-1].Share = sharePart - meta.Peers[req.SourceID-1].EphemeralSecretPublicKey = req.SourceEphemeralSecretPublicKey + combinedSig := tsed25519.CombineShares(s.total, sigIds, shareSigs) - return nil + return append(ephPub, combinedSig...), nil } diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index cc81a36a..e2120036 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -2,157 +2,350 @@ package signer import ( "bytes" + "context" "errors" "fmt" + "os" + "strings" "sync" "time" - "github.com/hashicorp/raft" - proto "github.com/strangelove-ventures/horcrux/signer/proto" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/log" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" - rpcTypes "github.com/tendermint/tendermint/rpc/jsonrpc/types" - tm "github.com/tendermint/tendermint/types" - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" + "github.com/cometbft/cometbft/libs/log" + cometrpcjsontypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" + "github.com/google/uuid" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) +var _ PrivValidator = &ThresholdValidator{} + type ThresholdValidator struct { - threshold int + config *RuntimeConfig - pubkey crypto.PubKey + threshold int - // stores the last sign state for a block we have fully signed - // Cached to respond to SignVote requests if we already have a signature - lastSignState SignState - lastSignStateMutex sync.Mutex + grpcTimeout time.Duration - // stores the last sign state that we've started progress on - lastSignStateInitiated SignState - lastSignStateInitiatedMutex sync.Mutex + chainState sync.Map // our own cosigner - cosigner Cosigner + myCosigner *LocalCosigner // peer cosigners - peers []Cosigner + peerCosigners Cosigners - raftStore *RaftStore + leader Leader logger log.Logger + + pendingDiskWG sync.WaitGroup + + maxWaitForSameBlockAttempts int + + cosignerHealth *CosignerHealth + + nonceCache *CosignerNonceCache } -type ThresholdValidatorOpt struct { - Pubkey crypto.PubKey - Threshold int - SignState SignState - Cosigner Cosigner - Peers []Cosigner - RaftStore *RaftStore - Logger log.Logger +type ChainSignState struct { + // stores the last sign state for a block we have fully signed + // Cached to respond to SignVote requests if we already have a signature + lastSignState *SignState + lastSignStateMutex *sync.Mutex + + // stores the last sign state that we've started progress on + lastSignStateInitiated *SignState + lastSignStateInitiatedMutex *sync.Mutex } // NewThresholdValidator creates and returns a new ThresholdValidator -func NewThresholdValidator(opt *ThresholdValidatorOpt) *ThresholdValidator { - validator := &ThresholdValidator{} - validator.cosigner = opt.Cosigner - validator.peers = opt.Peers - validator.threshold = opt.Threshold - validator.pubkey = opt.Pubkey - validator.lastSignState = opt.SignState - validator.lastSignStateMutex = sync.Mutex{} - validator.lastSignStateInitiated = SignState{ - Height: opt.SignState.Height, - Round: opt.SignState.Round, - Step: opt.SignState.Step, - filePath: "none", - cache: make(map[HRSKey]SignStateConsensus), +func NewThresholdValidator( + logger log.Logger, + config *RuntimeConfig, + threshold int, + grpcTimeout time.Duration, + maxWaitForSameBlockAttempts int, + myCosigner *LocalCosigner, + peerCosigners []Cosigner, + leader Leader, +) *ThresholdValidator { + allCosigners := make([]Cosigner, len(peerCosigners)+1) + allCosigners[0] = myCosigner + copy(allCosigners[1:], peerCosigners) + + for _, cosigner := range peerCosigners { + logger.Debug("Peer cosigner", "id", cosigner.GetID()) + } + + nc := NewCosignerNonceCache( + logger, + allCosigners, + leader, + defaultGetNoncesInterval, + grpcTimeout, + defaultNonceExpiration, + uint8(threshold), + nil, + ) + return &ThresholdValidator{ + logger: logger, + config: config, + threshold: threshold, + grpcTimeout: grpcTimeout, + maxWaitForSameBlockAttempts: maxWaitForSameBlockAttempts, + myCosigner: myCosigner, + peerCosigners: peerCosigners, + leader: leader, + cosignerHealth: NewCosignerHealth(logger, peerCosigners, leader), + nonceCache: nc, } - validator.lastSignStateInitiatedMutex = sync.Mutex{} - validator.raftStore = opt.RaftStore - validator.logger = opt.Logger - return validator } -func (pv *ThresholdValidator) SaveLastSignedState(signState SignStateConsensus) error { - return pv.lastSignState.Save(signState, &pv.lastSignStateMutex, true) +// Start starts the ThresholdValidator. +func (pv *ThresholdValidator) Start(ctx context.Context) error { + pv.logger.Info("Starting ThresholdValidator services") + + go pv.cosignerHealth.Start(ctx) + + go pv.nonceCache.Start(ctx) + + go pv.myCosigner.StartNoncePruner(ctx) + + return nil } -func (pv *ThresholdValidator) SaveLastSignedStateInitiated(signState SignStateConsensus) error { - return pv.lastSignStateInitiated.Save(signState, &pv.lastSignStateInitiatedMutex, true) +// SaveLastSignedState updates the high watermark height/round/step (HRS) for a completed +// sign process if it is greater than the current high watermark. A mutex is used to avoid concurrent +// state updates. The disk write is scheduled in a separate goroutine which will perform an atomic write. +// pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. +func (pv *ThresholdValidator) SaveLastSignedState(chainID string, signState SignStateConsensus) error { + css := pv.mustLoadChainState(chainID) + + css.lastSignStateMutex.Lock() + defer css.lastSignStateMutex.Unlock() + return css.lastSignState.Save(signState, &pv.pendingDiskWG) } -// GetPubKey returns the public key of the validator. -// Implements PrivValidator. -func (pv *ThresholdValidator) GetPubKey() (crypto.PubKey, error) { - return pv.pubkey, nil +func (pv *ThresholdValidator) mustLoadChainState(chainID string) ChainSignState { + cs, ok := pv.chainState.Load(chainID) + if !ok { + panic(fmt.Errorf("failed to load chain state for %s", chainID)) + } + + css, ok := cs.(ChainSignState) + if !ok { + panic(fmt.Errorf("expected: (ChainSignState), actual: (%T)", cs)) + } + + return css } -// SignVote signs a canonical representation of the vote, along with the -// chainID. Implements PrivValidator. -func (pv *ThresholdValidator) SignVote(chainID string, vote *tmProto.Vote) error { - block := &Block{ - Height: vote.Height, - Round: int64(vote.Round), - Step: VoteToStep(vote), - Timestamp: vote.Timestamp, - SignBytes: tm.VoteSignBytes(chainID, vote), +// SaveLastSignedStateInitiated updates the high watermark height/round/step (HRS) for an initiated +// sign process if it is greater than the current high watermark. A mutex is used to avoid concurrent +// state updates. The disk write is scheduled in a separate goroutine which will perform an atomic write. +// pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. +func (pv *ThresholdValidator) SaveLastSignedStateInitiated( + chainID string, + block *Block, +) ([]byte, []byte, time.Time, error) { + css := pv.mustLoadChainState(chainID) + + height, round, step := block.Height, block.Round, block.Step + + err := css.lastSignStateInitiated.Save(NewSignStateConsensus(height, round, step), &pv.pendingDiskWG) + if err == nil { + // good to sign + return nil, nil, time.Time{}, nil } - sig, stamp, err := pv.SignBlock(chainID, block) - vote.Signature = sig - vote.Timestamp = stamp + // There was an error saving the last sign state, so check if there is an existing signature for this block. + existingSignature, existingVoteExtSignature, + existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(chainID, block) - return err + if _, ok := err.(*SameHRSError); !ok { + if sameBlockErr == nil { + return existingSignature, existingVoteExtSignature, block.Timestamp, nil + } + return nil, nil, existingTimestamp, pv.newBeyondBlockError(chainID, block.HRSKey()) + } + + if sameBlockErr == nil { + if existingSignature != nil { + // signature already exists for this block. return it. + return existingSignature, existingVoteExtSignature, existingTimestamp, nil + } + // good to sign again + return nil, nil, time.Time{}, nil + } + + if _, ok := sameBlockErr.(*StillWaitingForBlockError); !ok { + // we have an error other than still waiting for block. return error. + return nil, nil, existingTimestamp, fmt.Errorf( + "same block error, but we are not still waiting for signature: %w", + sameBlockErr, + ) + } + + // the cluster is already in progress signing this block + // wait for cluster to finish before proceeding + + // intended usage of cond lock prior to cond.Wait(). + // cond.Wait() will unlock cond.L while it blocks waiting, then re-lock when unblocking from + // the cond.Broadcast(). + css.lastSignState.cond.L.Lock() + defer css.lastSignState.cond.L.Unlock() + for i := 0; i < pv.maxWaitForSameBlockAttempts; i++ { + // block until sign state is saved. It will notify and unblock when block is next signed. + css.lastSignState.cond.WaitWithTimeout(pv.grpcTimeout) + + // check if HRS exists in cache now + ssc, ok := css.lastSignState.cache[block.HRSKey()] + if !ok { + pv.logger.Debug( + "Block does not yet exist in cache while waiting for signature", + "height", height, + "round", round, + "step", step, + ) + continue + } + + existingSignature, existingVoteExtSignature, + existingTimestamp, sameBlockErr = pv.compareBlockSignatureAgainstSSC(chainID, block, &ssc) + if sameBlockErr == nil { + return existingSignature, existingVoteExtSignature, existingTimestamp, nil + } + if _, ok := sameBlockErr.(*StillWaitingForBlockError); !ok { + return nil, nil, existingTimestamp, fmt.Errorf( + "same block error in loop, but we are not still waiting for signature: %w", + sameBlockErr, + ) + } + + latest := css.lastSignState + + pv.logger.Debug( + "Waiting for block to be signed", + "height", height, + "round", round, + "step", step, + "latest_height", latest.Height, + "latest_round", latest.Round, + "latest_step", latest.Step, + ) + } + + return nil, nil, existingTimestamp, fmt.Errorf( + "exceeded max attempts waiting for block to be signed. height: %d, round: %d, step: %d", + height, round, step, + ) } -// SignProposal signs a canonical representation of the proposal, along with -// the chainID. Implements PrivValidator. -func (pv *ThresholdValidator) SignProposal(chainID string, proposal *tmProto.Proposal) error { - block := &Block{ - Height: proposal.Height, - Round: int64(proposal.Round), - Step: ProposalToStep(proposal), - Timestamp: proposal.Timestamp, - SignBytes: tm.ProposalSignBytes(chainID, proposal), +// notifyBlockSignError will alert any waiting goroutines that an error +// has occurred during signing and a retry can be attempted. +func (pv *ThresholdValidator) notifyBlockSignError(chainID string, hrs HRSKey, signBytes []byte) { + css := pv.mustLoadChainState(chainID) + + css.lastSignState.mu.Lock() + css.lastSignState.cache[hrs] = SignStateConsensus{ + Height: hrs.Height, + Round: hrs.Round, + Step: hrs.Step, + // empty signature to indicate error + SignBytes: signBytes, } - sig, stamp, err := pv.SignBlock(chainID, block) + css.lastSignState.mu.Unlock() + css.lastSignState.cond.Broadcast() +} + +// Stop safely shuts down the ThresholdValidator. +func (pv *ThresholdValidator) Stop() { + pv.waitForSignStatesToFlushToDisk() +} + +// waitForSignStatesToFlushToDisk waits for any sign states to finish writing to disk. +func (pv *ThresholdValidator) waitForSignStatesToFlushToDisk() { + pv.pendingDiskWG.Wait() - proposal.Signature = sig - proposal.Timestamp = stamp + pv.myCosigner.waitForSignStatesToFlushToDisk() +} - return err +// GetPubKey returns the public key of the validator. +// Implements PrivValidator. +func (pv *ThresholdValidator) GetPubKey(_ context.Context, chainID string) ([]byte, error) { + pubKey, err := pv.myCosigner.GetPubKey(chainID) + if err != nil { + return nil, err + } + return pubKey.Bytes(), nil } type Block struct { - Height int64 - Round int64 - Step int8 - SignBytes []byte - Timestamp time.Time + Height int64 + Round int64 + Step int8 + SignBytes []byte + VoteExtensionSignBytes []byte + Timestamp time.Time } -func (block Block) toProto() *proto.Block { - return &proto.Block{ +func (block Block) HRSKey() HRSKey { + return HRSKey{ + Height: block.Height, + Round: block.Round, + Step: block.Step, + } +} + +func (block Block) HRSTKey() HRSTKey { + return HRSTKey{ Height: block.Height, Round: block.Round, - Step: int32(block.Step), - SignBytes: block.SignBytes, + Step: block.Step, Timestamp: block.Timestamp.UnixNano(), } } +func (block Block) ToProto() *proto.Block { + return &proto.Block{ + Height: block.Height, + Round: block.Round, + Step: int32(block.Step), + SignBytes: block.SignBytes, + VoteExtSignBytes: block.VoteExtensionSignBytes, + Timestamp: block.Timestamp.UnixNano(), + } +} + +func BlockFromProto(block *proto.Block) Block { + return Block{ + Height: block.Height, + Round: block.Round, + Step: int8(block.Step), + SignBytes: block.SignBytes, + VoteExtensionSignBytes: block.VoteExtSignBytes, + Timestamp: time.Unix(0, block.Timestamp), + } +} + type BeyondBlockError struct { msg string } func (e *BeyondBlockError) Error() string { return e.msg } -func (pv *ThresholdValidator) newBeyondBlockError(hrs HRSKey) *BeyondBlockError { +func (pv *ThresholdValidator) newBeyondBlockError(chainID string, hrs HRSKey) *BeyondBlockError { + css := pv.mustLoadChainState(chainID) + + lss := css.lastSignStateInitiated return &BeyondBlockError{ - msg: fmt.Sprintf("Progress already started on block %d.%d.%d, skipping %d.%d.%d", - pv.lastSignStateInitiated.Height, pv.lastSignStateInitiated.Round, pv.lastSignStateInitiated.Step, - hrs.Height, hrs.Round, hrs.Step), + msg: fmt.Sprintf("[%s] Progress already started on block %d.%d.%d, skipping %d.%d.%d", + chainID, + lss.Height, lss.Round, lss.Step, + hrs.Height, hrs.Round, hrs.Step, + ), } } @@ -162,99 +355,161 @@ type StillWaitingForBlockError struct { func (e *StillWaitingForBlockError) Error() string { return e.msg } -func newStillWaitingForBlockError(hrs HRSKey) *StillWaitingForBlockError { +func newStillWaitingForBlockError(chainID string, hrs HRSKey) *StillWaitingForBlockError { return &StillWaitingForBlockError{ - msg: fmt.Sprintf("Still waiting for block %d.%d.%d", - hrs.Height, hrs.Round, hrs.Step), + msg: fmt.Sprintf("[%s] Still waiting for block %d.%d.%d", + chainID, hrs.Height, hrs.Round, hrs.Step), } } -func (pv *ThresholdValidator) waitForPeerEphemeralShares( - peer Cosigner, - hrst HRSTKey, - wg *sync.WaitGroup, - encryptedEphemeralSharesThresholdMap *map[Cosigner][]CosignerEphemeralSecretPart, - thresholdPeersMutex *sync.Mutex, -) { - peerStartTime := time.Now() - ephemeralSecretParts, err := peer.GetEphemeralSecretParts(hrst) - if err != nil { +type SameBlockError struct { + msg string +} - // Significant missing shares may lead to signature failure - missedEphemeralShares.WithLabelValues(peer.GetAddress()).Add(float64(1)) - totalMissedEphemeralShares.WithLabelValues(peer.GetAddress()).Inc() - pv.logger.Error("Error getting secret parts", "peer", peer.GetID(), "err", err) - return +func (e *SameBlockError) Error() string { return e.msg } + +func newSameBlockError(chainID string, hrs HRSKey) *SameBlockError { + return &SameBlockError{ + msg: fmt.Sprintf("[%s] Same block: %d.%d.%d", + chainID, hrs.Height, hrs.Round, hrs.Step), } - // Significant missing shares may lead to signature failure - missedEphemeralShares.WithLabelValues(peer.GetAddress()).Set(0) - timedCosignerEphemeralShareLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) +} - // Check so that getEphemeralWaitGroup.Done is not called more than (threshold - 1) times which causes hardlock - thresholdPeersMutex.Lock() - if len(*encryptedEphemeralSharesThresholdMap) < pv.threshold-1 { - (*encryptedEphemeralSharesThresholdMap)[peer] = ephemeralSecretParts.EncryptedSecrets - defer wg.Done() +func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { + if _, ok := pv.chainState.Load(chainID); ok { + return nil } - thresholdPeersMutex.Unlock() + + signState, err := LoadOrCreateSignState(pv.config.PrivValStateFile(chainID)) + if err != nil { + return err + } + + lastSignStateInitiated := signState.FreshCache() + lastSignStateInitiated.filePath = os.DevNull + + pv.chainState.Store(chainID, ChainSignState{ + lastSignState: signState, + lastSignStateInitiated: lastSignStateInitiated, + + lastSignStateMutex: &sync.Mutex{}, + lastSignStateInitiatedMutex: &sync.Mutex{}, + }) + + return pv.myCosigner.LoadSignStateIfNecessary(chainID) } -func (pv *ThresholdValidator) waitForPeerSetEphemeralSharesAndSign( - ourID int, - peer Cosigner, - hrst HRSTKey, - encryptedEphemeralSharesThresholdMap *map[Cosigner][]CosignerEphemeralSecretPart, - signBytes []byte, - shareSignatures *[][]byte, - shareSignaturesMutex *sync.Mutex, - ephemeralPublic *[]byte, - wg *sync.WaitGroup, -) { - peerStartTime := time.Now() - defer wg.Done() - peerEphemeralSecretParts := make([]CosignerEphemeralSecretPart, 0, pv.threshold-1) - for _, EncryptedSecrets := range *encryptedEphemeralSharesThresholdMap { - for _, ephemeralSecretPart := range EncryptedSecrets { - // if share is intended for peer, check to make sure source peer is included in threshold - if ephemeralSecretPart.DestinationID == peer.GetID() { - for thresholdPeer := range *encryptedEphemeralSharesThresholdMap { - if thresholdPeer.GetID() == ephemeralSecretPart.SourceID { - // source peer is included in threshold signature, include in sharing - peerEphemeralSecretParts = append(peerEphemeralSecretParts, ephemeralSecretPart) - break - } - } - break - } +// getExistingBlockSignature returns the existing block signature and no error if the signature is valid for the block. +// It returns nil signature and nil error if there is no signature and it's okay to sign (fresh or again). +// It returns an error if we have already signed a greater block, or if we are still waiting for in in-progress sign. +func (pv *ThresholdValidator) getExistingBlockSignature( + chainID string, + block *Block, +) ([]byte, []byte, time.Time, error) { + css := pv.mustLoadChainState(chainID) + + latestBlock, existingSignature := css.lastSignState.GetFromCache(block.HRSKey()) + if existingSignature != nil { + // signature exists in cache, so compare against that + return pv.compareBlockSignatureAgainstSSC(chainID, block, existingSignature) + } + + // signature does not exist in cache, so compare against latest signed block. + return nil, nil, block.Timestamp, pv.compareBlockSignatureAgainstHRS(chainID, block, latestBlock) +} + +// compareBlockSignatureAgainstSSC compares a block's HRS against a cached signature. +// +// If the existing signature is for a greater HRS than the block, we are already beyond +// the requested HRS, so we can error and move on. +// +// If the existing signature is for a lesser HRS than the block, then we can return +// a stillWaitingForBlock error to continue waiting for the HRS to be signed. +// +// If the HRS of the existing signature and the block are the same, we return the existing signature +// if the block sign request is for a proposal or if the bytes to sign are identical. +func (pv *ThresholdValidator) compareBlockSignatureAgainstSSC( + chainID string, + block *Block, + existingSignature *SignStateConsensus, +) ([]byte, []byte, time.Time, error) { + stamp, signBytes := block.Timestamp, block.SignBytes + + if err := pv.compareBlockSignatureAgainstHRS(chainID, block, existingSignature.HRSKey()); err != nil { + if _, ok := err.(*SameBlockError); !ok { + return nil, nil, stamp, err } } - pv.logger.Debug("Number of eph parts for peer", "peer", peer.GetID(), "count", len(peerEphemeralSecretParts)) + // If a proposal has already been signed for this HRS, or the sign payload is identical, return the existing signature. + if block.Step == stepPropose || bytes.Equal(signBytes, existingSignature.SignBytes) { + return existingSignature.Signature, existingSignature.VoteExtensionSignature, block.Timestamp, nil + } - peerID := peer.GetID() - sigRes, err := peer.SetEphemeralSecretPartsAndSign(CosignerSetEphemeralSecretPartsAndSignRequest{ - EncryptedSecrets: peerEphemeralSecretParts, - HRST: hrst, - SignBytes: signBytes, - }) + // If there is a difference in the existing signature payload other than timestamp, return that error. + if err := existingSignature.OnlyDifferByTimestamp(signBytes); err != nil { + return nil, nil, stamp, err + } - if err != nil { - pv.logger.Error("Sign error", err.Error()) - return + // only differ by timestamp, okay to sign again + return nil, nil, stamp, nil +} + +// compareBlockSignatureAgainstHRS returns a BeyondBlockError if the hrs is greater than the +// block. It returns a StillWaitingForBlockError if the hrs is less than the block. If returns nil if the hrs is +// equal to the block. +func (pv *ThresholdValidator) compareBlockSignatureAgainstHRS( + chainID string, + block *Block, + hrs HRSKey, +) error { + blockHRS := block.HRSKey() + + if hrs.GreaterThan(blockHRS) { + return pv.newBeyondBlockError(chainID, blockHRS) + } + + if hrs == blockHRS { + return newSameBlockError(chainID, blockHRS) + } + + return newStillWaitingForBlockError(chainID, blockHRS) +} + +func (pv *ThresholdValidator) getNoncesFallback( + ctx context.Context, + count int, +) (*CosignersAndNonces, error) { + drainedNonceCache.Inc() + totalDrainedNonceCache.Inc() + + var wg sync.WaitGroup + wg.Add(pv.threshold) + + var mu sync.Mutex + + uuids := make([]uuid.UUID, count) + for i := 0; i < count; i++ { + uuids[i] = uuid.New() } - timedCosignerSignLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) - pv.logger.Debug(fmt.Sprintf("Received signature from %d", peerID)) + allCosigners := make([]Cosigner, len(pv.peerCosigners)+1) + allCosigners[0] = pv.myCosigner + copy(allCosigners[1:], pv.peerCosigners) - shareSignaturesMutex.Lock() - defer shareSignaturesMutex.Unlock() + var thresholdNonces CosignersAndNonces - peerIdx := peerID - 1 - (*shareSignatures)[peerIdx] = make([]byte, len(sigRes.Signature)) - copy((*shareSignatures)[peerIdx], sigRes.Signature) - if peerID == ourID { - *ephemeralPublic = sigRes.EphemeralPublic + for _, c := range allCosigners { + go pv.waitForPeerNonces(ctx, uuids, c, &wg, &thresholdNonces, &mu) } + + // Wait for threshold cosigners to be complete + // A Cosigner will either respond in time, or be cancelled with timeout + if waitUntilCompleteOrTimeout(&wg, pv.grpcTimeout) { + return nil, errors.New("timed out waiting for ephemeral shares") + } + + return &thresholdNonces, nil } func waitUntilCompleteOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { @@ -271,62 +526,144 @@ func waitUntilCompleteOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool } } -func (pv *ThresholdValidator) getExistingBlockSignature(block *Block) ([]byte, time.Time, error) { - height, round, step, stamp, signBytes := block.Height, block.Round, block.Step, block.Timestamp, block.SignBytes - hrs := HRSKey{ - height, - round, - step, +type CosignersAndNonces struct { + Cosigners Cosigners + Nonces CosignerUUIDNoncesMultiple +} + +func (pv *ThresholdValidator) waitForPeerNonces( + ctx context.Context, + uuids []uuid.UUID, + peer Cosigner, + wg *sync.WaitGroup, + thresholdNonces *CosignersAndNonces, + mu sync.Locker, +) { + peerStartTime := time.Now() + + peerNonces, err := peer.GetNonces(ctx, uuids) + if err != nil { + missedNonces.WithLabelValues(peer.GetAddress()).Inc() + totalMissedNonces.WithLabelValues(peer.GetAddress()).Inc() + + pv.logger.Error("Error getting nonces", "cosigner", peer.GetID(), "err", err) + return } - latestBlock, existingSignature := pv.lastSignState.GetFromCache(hrs, &pv.lastSignStateMutex) - if existingSignature != nil { - // If a proposal has already been signed for this HRS, return that - if block.Step == stepPropose || bytes.Equal(signBytes, existingSignature.SignBytes) { - return existingSignature.Signature, block.Timestamp, nil - } - if err := existingSignature.OnlyDifferByTimestamp(signBytes); err != nil { - return nil, stamp, err - } - // only differ by timestamp, okay to sign again - return nil, stamp, nil - } else if latestBlock.Height > height || - (latestBlock.Height == height && latestBlock.Round > round) || - (latestBlock.Height == height && latestBlock.Round == round && latestBlock.Step > step) { - return nil, stamp, pv.newBeyondBlockError(hrs) + missedNonces.WithLabelValues(peer.GetAddress()).Set(0) + timedCosignerNonceLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + + // Check so that wg.Done is not called more than (threshold - 1) times which causes hardlock + mu.Lock() + if len(thresholdNonces.Cosigners) < pv.threshold { + thresholdNonces.Cosigners = append(thresholdNonces.Cosigners, peer) + for _, n := range peerNonces { + var found bool + for _, nn := range thresholdNonces.Nonces { + if n.UUID == nn.UUID { + nn.Nonces = append(nn.Nonces, n.Nonces...) + found = true + break + } + } + if !found { + thresholdNonces.Nonces = append(thresholdNonces.Nonces, n) + } + } + defer wg.Done() } - return nil, stamp, newStillWaitingForBlockError(hrs) + mu.Unlock() } -func (pv *ThresholdValidator) SignBlock(chainID string, block *Block) ([]byte, time.Time, error) { - height, round, step, stamp, signBytes := block.Height, block.Round, block.Step, block.Timestamp, block.SignBytes +func (pv *ThresholdValidator) proxyIfNecessary( + ctx context.Context, + chainID string, + block Block, +) (bool, []byte, []byte, time.Time, error) { + height, round, step, stamp := block.Height, block.Round, block.Step, block.Timestamp - timeStartSignBlock := time.Now() + if pv.leader.IsLeader() { + return false, nil, nil, time.Time{}, nil + } - // Only the leader can execute this function. Followers can handle the requests, - // but they just need to proxy the request to the raft leader - if pv.raftStore.raft == nil { - return nil, stamp, errors.New("raft not yet initialized") + leader := pv.leader.GetLeader() + + // TODO is there a better way than to poll during leader election? + for i := 0; i < 500 && leader == -1; i++ { + time.Sleep(10 * time.Millisecond) + leader = pv.leader.GetLeader() } - if pv.raftStore.raft.State() != raft.Leader { - pv.logger.Debug("I am not the raft leader. Proxying request to the leader") - totalNotRaftLeader.Inc() - signRes, err := pv.raftStore.LeaderSignBlock(CosignerSignBlockRequest{chainID, block}) - if err != nil { - if _, ok := err.(*rpcTypes.RPCError); ok { - rpcErrUnwrapped := err.(*rpcTypes.RPCError).Data - // Need to return BeyondBlockError after proxy since the error type will be lost over RPC - if len(rpcErrUnwrapped) > 33 && rpcErrUnwrapped[:33] == "Progress already started on block" { - return nil, stamp, &BeyondBlockError{msg: rpcErrUnwrapped} - } + + if leader == -1 { + totalRaftLeaderElectionTimeout.Inc() + return true, nil, nil, stamp, fmt.Errorf("timed out waiting for raft leader") + } + + if leader == pv.myCosigner.GetID() { + return false, nil, nil, time.Time{}, nil + } + + pv.logger.Debug("I am not the leader. Proxying request to the leader", + "chain_id", chainID, + "height", height, + "round", round, + "step", step, + ) + totalNotRaftLeader.Inc() + + cosignerLeader := pv.peerCosigners.GetByID(leader) + if cosignerLeader == nil { + return true, nil, nil, stamp, fmt.Errorf("failed to find cosigner with id %d", leader) + } + + signRes, err := cosignerLeader.(*RemoteCosigner).Sign(ctx, CosignerSignBlockRequest{ + ChainID: chainID, + Block: &block, + }) + if err != nil { + if _, ok := err.(*cometrpcjsontypes.RPCError); ok { + rpcErrUnwrapped := err.(*cometrpcjsontypes.RPCError).Data + // Need to return BeyondBlockError after proxy since the error type will be lost over RPC + if len(rpcErrUnwrapped) > 33 && rpcErrUnwrapped[:33] == "Progress already started on block" { + return true, nil, nil, stamp, &BeyondBlockError{msg: rpcErrUnwrapped} } - return nil, stamp, err } - return signRes.Signature, stamp, nil + return true, nil, nil, stamp, err + } + return true, signRes.Signature, signRes.VoteExtensionSignature, stamp, nil +} + +func (pv *ThresholdValidator) Sign( + ctx context.Context, + chainID string, + block Block, +) ([]byte, []byte, time.Time, error) { + height, round, step, stamp := block.Height, block.Round, block.Step, block.Timestamp + signBytes, voteExtensionSignBytes := block.SignBytes, block.VoteExtensionSignBytes + + log := pv.logger.With( + "chain_id", chainID, + "height", height, + "round", round, + "type", signType(step), + ) + + if err := pv.LoadSignStateIfNecessary(chainID); err != nil { + return nil, nil, stamp, err + } + + // Only the leader can execute this function. Followers can handle the requests, + // but they just need to proxy the request to the raft leader + isProxied, proxySig, proxyVoteExtSig, proxyStamp, err := pv.proxyIfNecessary(ctx, chainID, block) + if isProxied { + return proxySig, proxyVoteExtSig, proxyStamp, err } totalRaftLeader.Inc() - pv.logger.Debug("I am the raft leader. Managing the sign process for this block") + + log.Debug("I am the leader. Managing the sign process for this block") + + timeStartSignBlock := time.Now() hrst := HRSTKey{ Height: height, @@ -336,164 +673,320 @@ func (pv *ThresholdValidator) SignBlock(chainID string, block *Block) ([]byte, t } // Keep track of the last block that we began the signing process for. Only allow one attempt per block - if err := pv.SaveLastSignedStateInitiated(NewSignStateConsensus(height, round, step)); err != nil { - switch err.(type) { - case *SameHRSError: - // Wait for last sign state signature to be the same block - signAgain := false - for i := 0; i < 100; i++ { - existingSignature, existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(block) - if sameBlockErr == nil { - if existingSignature == nil { - signAgain = true - break - } - return existingSignature, existingTimestamp, nil - } - switch sameBlockErr.(type) { - case *StillWaitingForBlockError: - time.Sleep(10 * time.Millisecond) - continue - default: - return nil, existingTimestamp, sameBlockErr - } - - } - if !signAgain { - return nil, stamp, errors.New("timed out waiting for block signature from cluster") - } - default: - existingSignature, existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(block) - if sameBlockErr == nil { - return existingSignature, stamp, nil - } - return nil, existingTimestamp, pv.newBeyondBlockError(HRSKey{ - Height: height, - Round: round, - Step: step, - }) - } + existingSignature, existingVoteExtSig, existingTimestamp, err := pv.SaveLastSignedStateInitiated(chainID, &block) + if err != nil { + return nil, nil, stamp, fmt.Errorf("error saving last sign state initiated: %w", err) + } + if existingSignature != nil { + log.Debug("Returning existing signature", "signature", fmt.Sprintf("%x", existingSignature)) + return existingSignature, existingVoteExtSig, existingTimestamp, nil } - numPeers := len(pv.peers) + numPeers := len(pv.peerCosigners) total := uint8(numPeers + 1) - getEphemeralWaitGroup := sync.WaitGroup{} - // Only wait until we have threshold sigs - getEphemeralWaitGroup.Add(pv.threshold - 1) - // Used to track how close we are to threshold + peerStartTime := time.Now() - ourID := pv.cosigner.GetID() + cosignersOrderedByFastest := pv.cosignerHealth.GetFastest() + cosignersForThisBlock := make([]Cosigner, pv.threshold) + cosignersForThisBlock[0] = pv.myCosigner + copy(cosignersForThisBlock[1:], cosignersOrderedByFastest[:pv.threshold-1]) - encryptedEphemeralSharesThresholdMap := make(map[Cosigner][]CosignerEphemeralSecretPart) - thresholdPeersMutex := sync.Mutex{} + var dontIterateFastestCosigners bool - for _, peer := range pv.peers { - go pv.waitForPeerEphemeralShares(peer, hrst, &getEphemeralWaitGroup, - &encryptedEphemeralSharesThresholdMap, &thresholdPeersMutex) + _, hasVoteExtensions, err := verifySignPayload(chainID, signBytes, voteExtensionSignBytes) + if err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to verify payload: %w", err) + } + + count := 1 + if hasVoteExtensions { + count = 2 } - ourEphemeralSecretParts, err := pv.cosigner.GetEphemeralSecretParts(hrst) + var voteExtNonces *CosignerUUIDNonces + nonces, err := pv.nonceCache.GetNonces(cosignersForThisBlock) if err != nil { - // Our ephemeral secret parts are required, cannot proceed - return nil, stamp, err + var fallbackRes *CosignersAndNonces + var fallbackErr error + + fallbackRes, fallbackErr = pv.getNoncesFallback(ctx, count) + if fallbackErr != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to get nonces: %w", errors.Join(err, fallbackErr)) + } + + cosignersForThisBlock = fallbackRes.Cosigners + nonces = fallbackRes.Nonces[0] + if hasVoteExtensions { + voteExtNonces = fallbackRes.Nonces[1] + } + dontIterateFastestCosigners = true + } else { + drainedNonceCache.Set(0) } - // Wait for threshold cosigners to be complete - // A Cosigner will either respond in time, or be cancelled with timeout - if waitUntilCompleteOrTimeout(&getEphemeralWaitGroup, 4*time.Second) { - return nil, stamp, errors.New("timed out waiting for ephemeral shares") + if err == nil && hasVoteExtensions { + voteExtNonces, err = pv.nonceCache.GetNonces(cosignersForThisBlock) + if err != nil { + + u := uuid.New() + var eg errgroup.Group + var mu sync.Mutex + for _, c := range cosignersForThisBlock { + c := c + eg.Go(func() error { + nonces, err := c.GetNonces(ctx, []uuid.UUID{u}) + if err != nil { + return err + } + mu.Lock() + defer mu.Unlock() + if voteExtNonces == nil { + voteExtNonces = nonces[0] + } else { + voteExtNonces.Nonces = append(voteExtNonces.Nonces, nonces[0].Nonces...) + } + return nil + }) + } + + if fallbackErr := eg.Wait(); fallbackErr != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to get nonces for vote extensions: %w", errors.Join(err, fallbackErr)) + } + } } - thresholdPeersMutex.Lock() - encryptedEphemeralSharesThresholdMap[pv.cosigner] = ourEphemeralSecretParts.EncryptedSecrets - thresholdPeersMutex.Unlock() + nextFastestCosignerIndex := pv.threshold - 1 + var nextFastestCosignerIndexMu sync.Mutex + getNextFastestCosigner := func() Cosigner { + nextFastestCosignerIndexMu.Lock() + defer nextFastestCosignerIndexMu.Unlock() + if nextFastestCosignerIndex >= len(cosignersOrderedByFastest) { + return nil + } + cosigner := cosignersOrderedByFastest[nextFastestCosignerIndex] + nextFastestCosignerIndex++ + return cosigner + } timedSignBlockThresholdLag.Observe(time.Since(timeStartSignBlock).Seconds()) - pv.logger.Debug("Have threshold peers") - setEphemeralAndSignWaitGroup := sync.WaitGroup{} + for _, peer := range pv.peerCosigners { + missedNonces.WithLabelValues(peer.GetAddress()).Set(0) + timedCosignerNonceLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + } + + cosignersForThisBlockInt := make([]int, len(cosignersForThisBlock)) - // Only wait until we have threshold sigs - setEphemeralAndSignWaitGroup.Add(pv.threshold) + for i, cosigner := range cosignersForThisBlock { + cosignersForThisBlockInt[i] = cosigner.GetID() + } // destination for share signatures shareSignatures := make([][]byte, total) + voteExtShareSignatures := make([][]byte, total) + + var eg errgroup.Group + for _, cosigner := range cosignersForThisBlock { + cosigner := cosigner + eg.Go(func() error { + for cosigner != nil { + signCtx, cancel := context.WithTimeout(ctx, pv.grpcTimeout) + defer cancel() + + peerStartTime := time.Now() + + sigReq := CosignerSetNoncesAndSignRequest{ + ChainID: chainID, + Nonces: nonces.For(cosigner.GetID()), + HRST: hrst, + SignBytes: signBytes, + } - // share sigs is updated by goroutines - shareSignaturesMutex := sync.Mutex{} + if voteExtNonces != nil { + sigReq.VoteExtensionSignBytes = voteExtensionSignBytes + sigReq.VoteExtensionNonces = voteExtNonces.For(cosigner.GetID()) + } + + // set peerNonces and sign in single rpc call. + sigRes, err := cosigner.SetNoncesAndSign(signCtx, sigReq) + if err != nil { + log.Error( + "Cosigner failed to set nonces and sign", + "cosigner", cosigner.GetID(), + "err", err.Error(), + ) + + if strings.Contains(err.Error(), errUnexpectedState) { + pv.nonceCache.ClearNonces(cosigner) + } + + if cosigner.GetID() == pv.myCosigner.GetID() { + return err + } + + if c := status.Code(err); c == codes.DeadlineExceeded || c == codes.NotFound || c == codes.Unavailable { + pv.cosignerHealth.MarkUnhealthy(cosigner) + pv.nonceCache.ClearNonces(cosigner) + } - var ephemeralPublic []byte + if dontIterateFastestCosigners { + cosigner = nil + continue + } - for peer := range encryptedEphemeralSharesThresholdMap { - // set peerEphemeralSecretParts and sign in single rpc call. - go pv.waitForPeerSetEphemeralSharesAndSign(ourID, peer, hrst, &encryptedEphemeralSharesThresholdMap, - signBytes, &shareSignatures, &shareSignaturesMutex, &ephemeralPublic, &setEphemeralAndSignWaitGroup) + // this will only work if the next cosigner has the nonces we've already decided to use for this block + // otherwise the sign attempt will fail + cosigner = getNextFastestCosigner() + continue + } + + if cosigner != pv.myCosigner { + timedCosignerSignLag.WithLabelValues(cosigner.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + } + shareSignatures[cosigner.GetID()-1] = sigRes.Signature + voteExtShareSignatures[cosigner.GetID()-1] = sigRes.VoteExtensionSignature + + return nil + } + return fmt.Errorf("no cosigners available to sign") + }) } - // Wait for threshold cosigners to be complete - // A Cosigner will either respond in time, or be cancelled with timeout - if waitUntilCompleteOrTimeout(&setEphemeralAndSignWaitGroup, 4*time.Second) { - return nil, stamp, errors.New("timed out waiting for peers to sign") + if err := eg.Wait(); err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("error from cosigner(s): %s", err) } timedSignBlockCosignerLag.Observe(time.Since(timeStartSignBlock).Seconds()) - pv.logger.Debug("Done waiting for cosigners, assembling signatures") - // collect all valid responses into array of ids and signatures for the threshold lib - sigIds := make([]int, 0) - shareSigs := make([][]byte, 0) + // collect all valid responses into array of partial signatures + shareSigs := make([]PartialSignature, 0, pv.threshold) for idx, shareSig := range shareSignatures { if len(shareSig) == 0 { continue } - sigIds = append(sigIds, idx+1) + + sig := make([]byte, len(shareSig)) + copy(sig, shareSig) // we are ok to use the share signatures - complete boolean // prevents future concurrent access - shareSigs = append(shareSigs, shareSig) + shareSigs = append(shareSigs, PartialSignature{ + ID: idx + 1, + Signature: sig, + }) } - if len(sigIds) < pv.threshold { + if len(shareSigs) < pv.threshold { totalInsufficientCosigners.Inc() - return nil, stamp, errors.New("not enough co-signers") + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, errors.New("not enough cosigners") } // assemble into final signature - combinedSig := tsed25519.CombineShares(total, sigIds, shareSigs) - - signature := ephemeralPublic - signature = append(signature, combinedSig...) + signature, err := pv.myCosigner.CombineSignatures(chainID, shareSigs) + if err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("error combining signatures: %w", err) + } // verify the combined signature before saving to watermark - if !pv.pubkey.VerifySignature(signBytes, signature) { + if !pv.myCosigner.VerifySignature(chainID, signBytes, signature) { totalInvalidSignature.Inc() - return nil, stamp, errors.New("combined signature is not valid") + + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, errors.New("combined signature is not valid") } - newLss := SignStateConsensus{ - Height: height, - Round: round, - Step: step, - Signature: signature, - SignBytes: signBytes, + var voteExtSig []byte + + if hasVoteExtensions { + // collect all valid responses into array of partial signatures + voteExtShareSigs := make([]PartialSignature, 0, pv.threshold) + for idx, shareSig := range voteExtShareSignatures { + if len(shareSig) == 0 { + continue + } + + sig := make([]byte, len(shareSig)) + copy(sig, shareSig) + + // we are ok to use the share signatures - complete boolean + // prevents future concurrent access + voteExtShareSigs = append(voteExtShareSigs, PartialSignature{ + ID: idx + 1, + Signature: sig, + }) + } + + if len(voteExtShareSigs) < pv.threshold { + totalInsufficientCosigners.Inc() + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, errors.New("not enough cosigners for vote extension") + } + + // assemble into final signature + voteExtSig, err = pv.myCosigner.CombineSignatures(chainID, voteExtShareSigs) + if err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("error combining vote extension signatures: %w", err) + } + + // verify the combined signature before saving to watermark + if !pv.myCosigner.VerifySignature(chainID, voteExtensionSignBytes, voteExtSig) { + totalInvalidSignature.Inc() + + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, errors.New("combined signature for vote extension is not valid") + } + } + + newLss := ChainSignStateConsensus{ + ChainID: chainID, + SignStateConsensus: SignStateConsensus{ + Height: height, + Round: round, + Step: step, + Signature: signature, + SignBytes: signBytes, + VoteExtensionSignature: voteExtSig, + }, } + + css := pv.mustLoadChainState(chainID) + // Err will be present if newLss is not above high watermark - err = pv.lastSignState.Save(newLss, &pv.lastSignStateMutex, true) + css.lastSignStateMutex.Lock() + err = css.lastSignState.Save(newLss.SignStateConsensus, &pv.pendingDiskWG) + css.lastSignStateMutex.Unlock() if err != nil { if _, isSameHRSError := err.(*SameHRSError); !isSameHRSError { - return nil, stamp, err + + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("error saving last sign state: %w", err) } } // Emit last signed state to cluster - err = pv.raftStore.Emit(raftEventLSS, newLss) + err = pv.leader.ShareSigned(newLss) if err != nil { - pv.logger.Error("Error emitting LSS", err.Error()) + // this is not required for double sign protection, so we don't need to return an error here. + // this is only an additional mechanism that will catch double signs earlier in the sign process. + log.Error("Error emitting LSS", err.Error()) } - timeSignBlock := time.Since(timeStartSignBlock).Seconds() - timedSignBlockLag.Observe(timeSignBlock) + timeSignBlock := time.Since(timeStartSignBlock) + timeSignBlockSec := timeSignBlock.Seconds() + timedSignBlockLag.Observe(timeSignBlockSec) + + log.Info( + "Signed", + "duration_ms", float64(timeSignBlock.Microseconds())/1000, + ) - return signature, stamp, nil + return signature, voteExtSig, stamp, nil } diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index be6fd252..22688fb1 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -1,451 +1,638 @@ package signer import ( + "bytes" + "context" "crypto/rand" - "crypto/rsa" + "crypto/sha256" + "fmt" + mrand "math/rand" + "path/filepath" + "sync" "time" "os" "testing" + cometcrypto "github.com/cometbft/cometbft/crypto" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/tmhash" + cometlog "github.com/cometbft/cometbft/libs/log" + cometrand "github.com/cometbft/cometbft/libs/rand" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + comet "github.com/cometbft/cometbft/types" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/stretchr/testify/require" - tmCryptoEd25519 "github.com/tendermint/tendermint/crypto/ed25519" - tmlog "github.com/tendermint/tendermint/libs/log" - tmProto "github.com/tendermint/tendermint/proto/tendermint/types" - tm "github.com/tendermint/tendermint/types" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" + "golang.org/x/sync/errgroup" ) -func getMockRaftStore(cosigner Cosigner, tmpDir string) *RaftStore { - return &RaftStore{ - NodeID: "1", - RaftDir: tmpDir, - RaftBind: "127.0.0.1:0", - RaftTimeout: 1 * time.Second, - m: make(map[string]string), - logger: nil, - cosigner: cosigner.(*LocalCosigner), - Peers: []Cosigner{}, - } -} - func TestThresholdValidator2of2(t *testing.T) { - total := uint8(2) - threshold := uint8(2) - - bitSize := 4096 - rsaKey1, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) - - rsaKey2, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) - - peers := []CosignerPeer{{ - ID: 1, - PublicKey: rsaKey1.PublicKey, - }, { - ID: 2, - PublicKey: rsaKey2.PublicKey, - }} - - privateKey := tmCryptoEd25519.GenPrivKey() - - privKeyBytes := [64]byte{} - copy(privKeyBytes[:], privateKey[:]) - secretShares := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) - - key1 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[0], - ID: 1, - } - - stateFile1, err := os.CreateTemp("", "state1.json") - require.NoError(t, err) - defer os.Remove(stateFile1.Name()) + testThresholdValidator(t, 2, 2) +} - signState1, err := LoadOrCreateSignState(stateFile1.Name()) - require.NoError(t, err) +func TestThresholdValidator3of3(t *testing.T) { + testThresholdValidator(t, 3, 3) +} - key2 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[1], - ID: 2, - } +func TestThresholdValidator2of3(t *testing.T) { + testThresholdValidator(t, 2, 3) +} - stateFile2, err := os.CreateTemp("", "state2.json") - require.NoError(t, err) - defer os.Remove(stateFile2.Name()) - signState2, err := LoadOrCreateSignState(stateFile2.Name()) - require.NoError(t, err) +func TestThresholdValidator3of5(t *testing.T) { + testThresholdValidator(t, 3, 5) +} - config1 := LocalCosignerConfig{ - CosignerKey: key1, - SignState: &signState1, - RsaKey: *rsaKey1, - Peers: peers, - Total: total, - Threshold: threshold, +func loadKeyForLocalCosigner( + cosigner *LocalCosigner, + pubKey cometcrypto.PubKey, + chainID string, + privateShard []byte, +) error { + key := CosignerEd25519Key{ + PubKey: pubKey, + PrivateShard: privateShard, + ID: cosigner.GetID(), } - config2 := LocalCosignerConfig{ - CosignerKey: key2, - SignState: &signState2, - RsaKey: *rsaKey2, - Peers: peers, - Total: total, - Threshold: threshold, + keyBz, err := key.MarshalJSON() + if err != nil { + return err } - var cosigner1 Cosigner - var cosigner2 Cosigner - - cosigner1 = NewLocalCosigner(config1) - cosigner2 = NewLocalCosigner(config2) - - require.Equal(t, cosigner1.GetID(), 1) - require.Equal(t, cosigner2.GetID(), 2) + return os.WriteFile(cosigner.config.KeyFilePathCosigner(chainID), keyBz, 0600) +} - thresholdPeers := make([]Cosigner, 0) - thresholdPeers = append(thresholdPeers, cosigner2) +func testThresholdValidator(t *testing.T, threshold, total uint8) { + cosigners, pubKey := getTestLocalCosigners(t, threshold, total) - tmpDir, _ := os.MkdirTemp("", "store_test") - defer os.RemoveAll(tmpDir) + thresholdCosigners := make([]Cosigner, 0, threshold-1) - raftStore := getMockRaftStore(cosigner1, tmpDir) + for i, cosigner := range cosigners { + require.Equal(t, i+1, cosigner.GetID()) - thresholdValidatorOpt := ThresholdValidatorOpt{ - Pubkey: privateKey.PubKey(), - Threshold: int(threshold), - SignState: signState1, - Cosigner: cosigner1, - Peers: thresholdPeers, - RaftStore: raftStore, - Logger: tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "validator"), + if i != 0 && len(thresholdCosigners) != int(threshold)-1 { + thresholdCosigners = append(thresholdCosigners, cosigner) + } } - validator := NewThresholdValidator(&thresholdValidatorOpt) - - raftStore.SetThresholdValidator(validator) - - _, err = raftStore.Open() - require.NoError(t, err) + leader := &MockLeader{id: 1} - time.Sleep(3 * time.Second) // Ensure there is a leader + validator := NewThresholdValidator( + cometlog.NewNopLogger(), + cosigners[0].config, + int(threshold), + time.Second, + 1, + cosigners[0], + thresholdCosigners, + leader, + ) + defer validator.Stop() - var proposal tmProto.Proposal - proposal.Height = 1 - proposal.Round = 0 - proposal.Type = tmProto.ProposalType + leader.leader = validator - signBytes := tm.ProposalSignBytes("chain-id", &proposal) + ctx := context.Background() - err = validator.SignProposal("chain-id", &proposal) + err := validator.LoadSignStateIfNecessary(testChainID) require.NoError(t, err) - require.True(t, privateKey.PubKey().VerifySignature(signBytes, proposal.Signature)) -} - -func TestThresholdValidator3of3(t *testing.T) { - total := uint8(3) - threshold := uint8(3) - - bitSize := 4096 - rsaKey1, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) + proposal := cometproto.Proposal{ + Height: 1, + Round: 20, + Type: cometproto.ProposalType, + } - rsaKey2, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) + block := ProposalToBlock(testChainID, &proposal) - rsaKey3, err := rsa.GenerateKey(rand.Reader, bitSize) + signature, _, _, err := validator.Sign(ctx, testChainID, block) require.NoError(t, err) - peers := []CosignerPeer{{ - ID: 1, - PublicKey: rsaKey1.PublicKey, - }, { - ID: 2, - PublicKey: rsaKey2.PublicKey, - }, { - ID: 3, - PublicKey: rsaKey3.PublicKey, - }} - - privateKey := tmCryptoEd25519.GenPrivKey() - - privKeyBytes := [64]byte{} - copy(privKeyBytes[:], privateKey[:]) - secretShares := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) - - key1 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[0], - ID: 1, - } + require.True(t, pubKey.VerifySignature(block.SignBytes, signature)) - stateFile1, err := os.CreateTemp("", "state1.json") - require.NoError(t, err) - defer os.Remove(stateFile1.Name()) + firstSignature := signature - signState1, err := LoadOrCreateSignState(stateFile1.Name()) - require.NoError(t, err) + require.Len(t, firstSignature, 64) - key2 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[1], - ID: 2, + proposal = cometproto.Proposal{ + Height: 1, + Round: 20, + Type: cometproto.ProposalType, + Timestamp: time.Now(), } - stateFile2, err := os.CreateTemp("", "state2.json") - require.NoError(t, err) - defer os.Remove(stateFile2.Name()) + block = ProposalToBlock(testChainID, &proposal) - signState2, err := LoadOrCreateSignState(stateFile2.Name()) - require.NoError(t, err) - - key3 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[2], - ID: 3, - } + validator.nonceCache.LoadN(ctx, 1) - stateFile3, err := os.CreateTemp("", "state3.json") + // should be able to sign same proposal with only differing timestamp + _, _, _, err = validator.Sign(ctx, testChainID, block) require.NoError(t, err) - defer os.Remove(stateFile3.Name()) - signState3, err := LoadOrCreateSignState(stateFile3.Name()) - require.NoError(t, err) + // construct different block ID for proposal at same height as highest signed + randHash := cometrand.Bytes(tmhash.Size) + blockID := cometproto.BlockID{Hash: randHash, + PartSetHeader: cometproto.PartSetHeader{Total: 5, Hash: randHash}} - config1 := LocalCosignerConfig{ - CosignerKey: key1, - SignState: &signState1, - RsaKey: *rsaKey1, - Peers: peers, - Total: total, - Threshold: threshold, + proposal = cometproto.Proposal{ + Height: 1, + Round: 20, + Type: cometproto.ProposalType, + BlockID: blockID, } - config2 := LocalCosignerConfig{ - CosignerKey: key2, - SignState: &signState2, - RsaKey: *rsaKey2, - Peers: peers, - Total: total, - Threshold: threshold, - } + validator.nonceCache.LoadN(ctx, 1) - config3 := LocalCosignerConfig{ - CosignerKey: key3, - SignState: &signState3, - RsaKey: *rsaKey3, - Peers: peers, - Total: total, - Threshold: threshold, - } + // different than single-signer mode, threshold mode will be successful for this, + // but it will return the same signature as before. + signature, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.NoError(t, err) - var cosigner1 Cosigner - var cosigner2 Cosigner - var cosigner3 Cosigner + require.True(t, bytes.Equal(firstSignature, signature)) - cosigner1 = NewLocalCosigner(config1) - cosigner2 = NewLocalCosigner(config2) - cosigner3 = NewLocalCosigner(config3) + proposal.Round = 19 - require.Equal(t, cosigner1.GetID(), 1) - require.Equal(t, cosigner2.GetID(), 2) - require.Equal(t, cosigner3.GetID(), 3) + validator.nonceCache.LoadN(ctx, 1) - thresholdPeers := make([]Cosigner, 0) - thresholdPeers = append(thresholdPeers, cosigner2, cosigner3) + // should not be able to sign lower than highest signed + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.Error(t, err, "double sign!") - tmpDir, _ := os.MkdirTemp("", "store_test") - defer os.RemoveAll(tmpDir) + validator.nonceCache.LoadN(ctx, 1) - raftStore := getMockRaftStore(cosigner1, tmpDir) + // lower LSS should sign for different chain ID + _, _, _, err = validator.Sign(ctx, testChainID2, ProposalToBlock(testChainID2, &proposal)) + require.NoError(t, err) - thresholdValidatorOpt := ThresholdValidatorOpt{ - Pubkey: privateKey.PubKey(), - Threshold: int(threshold), - SignState: signState1, - Cosigner: cosigner1, - Peers: thresholdPeers, - RaftStore: raftStore, - Logger: tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "validator"), + // reinitialize validator to make sure new runtime will not allow double sign + newValidator := NewThresholdValidator( + cometlog.NewNopLogger(), + cosigners[0].config, + int(threshold), + time.Second, + 1, + cosigners[0], + thresholdCosigners, + leader, + ) + defer newValidator.Stop() + + newValidator.nonceCache.LoadN(ctx, 1) + + _, _, _, err = newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + require.Error(t, err, "double sign!") + + proposal = cometproto.Proposal{ + Height: 1, + Round: 21, + Type: cometproto.ProposalType, + Timestamp: time.Now(), } - validator := NewThresholdValidator(&thresholdValidatorOpt) - - raftStore.SetThresholdValidator(validator) - - _, err = raftStore.Open() + proposalClone := proposal + proposalClone.Timestamp = proposal.Timestamp.Add(2 * time.Millisecond) + + proposalClone2 := proposal + proposalClone2.Timestamp = proposal.Timestamp.Add(4 * time.Millisecond) + + var eg errgroup.Group + + newValidator.nonceCache.LoadN(ctx, 3) + + eg.Go(func() error { + _, _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + return err + }) + eg.Go(func() error { + _, _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone)) + return err + }) + eg.Go(func() error { + _, _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone2)) + return err + }) + // signing higher block now should succeed + err = eg.Wait() require.NoError(t, err) - time.Sleep(3 * time.Second) // Ensure there is a leader - - var proposal tmProto.Proposal - proposal.Height = 1 - proposal.Round = 0 - proposal.Type = tmProto.ProposalType - - signBytes := tm.ProposalSignBytes("chain-id", &proposal) - - err = validator.SignProposal("chain-id", &proposal) - if err != nil { - t.Logf("%v", err) + // Sign some votes from multiple sentries + for i := 2; i < 50; i++ { + newValidator.nonceCache.LoadN(ctx, 3) + + prevote := cometproto.Vote{ + Height: int64(i), + Round: 0, + Type: cometproto.PrevoteType, + Timestamp: time.Now(), + } + + prevoteClone := prevote + prevoteClone.Timestamp = prevote.Timestamp.Add(2 * time.Millisecond) + + prevoteClone2 := prevote + prevoteClone2.Timestamp = prevote.Timestamp.Add(4 * time.Millisecond) + + eg.Go(func() error { + start := time.Now() + _, _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevote)) + t.Log("Sign time", "duration", time.Since(start)) + return err + }) + eg.Go(func() error { + start := time.Now() + _, _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone)) + t.Log("Sign time", "duration", time.Since(start)) + return err + }) + eg.Go(func() error { + start := time.Now() + _, _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone2)) + t.Log("Sign time", "duration", time.Since(start)) + return err + }) + + err = eg.Wait() + require.NoError(t, err) + + blockIDHash := sha256.New() + blockIDHash.Write([]byte("something")) + + precommit := cometproto.Vote{ + Height: int64(i), + Round: 0, + BlockID: cometproto.BlockID{Hash: blockIDHash.Sum(nil)}, + Type: cometproto.PrecommitType, + Timestamp: time.Now(), + Extension: []byte("test"), + } + + precommitClone := precommit + precommitClone.Timestamp = precommit.Timestamp.Add(2 * time.Millisecond) + + precommitClone2 := precommit + precommitClone2.Timestamp = precommit.Timestamp.Add(4 * time.Millisecond) + + newValidator.nonceCache.LoadN(ctx, mrand.Intn(7)) //nolint:gosec + + eg.Go(func() error { + start := time.Now() + t.Log("Sign time", "duration", time.Since(start)) + block := VoteToBlock(testChainID, &precommit) + sig, voteExtSig, _, err := newValidator.Sign(ctx, testChainID, block) + if err != nil { + return err + } + + if !pubKey.VerifySignature(block.SignBytes, sig) { + return fmt.Errorf("signature verification failed") + } + + if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { + return fmt.Errorf("vote extension signature verification failed") + } + + return nil + }) + eg.Go(func() error { + start := time.Now() + t.Log("Sign time", "duration", time.Since(start)) + block := VoteToBlock(testChainID, &precommitClone) + sig, voteExtSig, _, err := newValidator.Sign(ctx, testChainID, block) + if err != nil { + return err + } + + if !pubKey.VerifySignature(block.SignBytes, sig) { + return fmt.Errorf("signature verification failed") + } + + if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { + return fmt.Errorf("vote extension signature verification failed") + } + + return nil + }) + eg.Go(func() error { + start := time.Now() + block := VoteToBlock(testChainID, &precommitClone2) + sig, voteExtSig, _, err := newValidator.Sign(ctx, testChainID, block) + t.Log("Sign time", "duration", time.Since(start)) + if err != nil { + return err + } + + if !pubKey.VerifySignature(block.SignBytes, sig) { + return fmt.Errorf("signature verification failed") + } + + if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { + return fmt.Errorf("vote extension signature verification failed") + } + + return nil + }) + + err = eg.Wait() + require.NoError(t, err) } - require.NoError(t, err) - - require.True(t, privateKey.PubKey().VerifySignature(signBytes, proposal.Signature)) } -func TestThresholdValidator2of3(t *testing.T) { - total := uint8(3) - threshold := uint8(2) +func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*LocalCosigner, cometcrypto.PubKey) { + eciesKeys := make([]*ecies.PrivateKey, total) + pubKeys := make([]*ecies.PublicKey, total) + cosigners := make([]*LocalCosigner, total) - bitSize := 4096 - rsaKey1, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) - - rsaKey2, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) + for i := uint8(0); i < total; i++ { + eciesKey, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) + require.NoError(t, err) - rsaKey3, err := rsa.GenerateKey(rand.Reader, bitSize) - require.NoError(t, err) + eciesKeys[i] = eciesKey - peers := []CosignerPeer{{ - ID: 1, - PublicKey: rsaKey1.PublicKey, - }, { - ID: 2, - PublicKey: rsaKey2.PublicKey, - }, { - ID: 3, - PublicKey: rsaKey3.PublicKey, - }} - - privateKey := tmCryptoEd25519.GenPrivKey() - - privKeyBytes := [64]byte{} - copy(privKeyBytes[:], privateKey[:]) - secretShares := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) - - key1 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[0], - ID: 1, + pubKeys[i] = &eciesKey.PublicKey } - stateFile1, err := os.CreateTemp("", "state1.json") - require.NoError(t, err) - defer os.Remove(stateFile1.Name()) + privateKey := cometcryptoed25519.GenPrivKey() + privKeyBytes := privateKey[:] + privShards := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) - signState1, err := LoadOrCreateSignState(stateFile1.Name()) - require.NoError(t, err) + tmpDir := t.TempDir() - key2 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[1], - ID: 2, - } - - stateFile2, err := os.CreateTemp("", "state2.json") - require.NoError(t, err) - defer os.Remove(stateFile2.Name()) - - signState2, err := LoadOrCreateSignState(stateFile2.Name()) - require.NoError(t, err) + cosignersConfig := make(CosignersConfig, total) - key3 := CosignerKey{ - PubKey: privateKey.PubKey(), - ShareKey: secretShares[2], - ID: 3, + for i := range pubKeys { + cosignersConfig[i] = CosignerConfig{ + ShardID: i + 1, + } } - stateFile3, err := os.CreateTemp("", "state3.json") - require.NoError(t, err) - defer os.Remove(stateFile3.Name()) - - signState3, err := LoadOrCreateSignState(stateFile3.Name()) - require.NoError(t, err) - - config1 := LocalCosignerConfig{ - CosignerKey: key1, - SignState: &signState1, - RsaKey: *rsaKey1, - Peers: peers, - Total: total, - Threshold: threshold, + for i := range pubKeys { + cosignerDir := filepath.Join(tmpDir, fmt.Sprintf("cosigner_%d", i+1)) + err := os.MkdirAll(cosignerDir, 0777) + require.NoError(t, err) + + cosignerConfig := &RuntimeConfig{ + HomeDir: cosignerDir, + StateDir: cosignerDir, + Config: Config{ + ThresholdModeConfig: &ThresholdModeConfig{ + Threshold: int(threshold), + Cosigners: cosignersConfig, + }, + }, + } + + cosigner := NewLocalCosigner( + cometlog.NewNopLogger(), + cosignerConfig, + NewCosignerSecurityECIES( + CosignerECIESKey{ + ID: i + 1, + ECIESKey: eciesKeys[i], + ECIESPubs: pubKeys, + }, + ), + "", + ) + require.NoError(t, err) + + cosigners[i] = cosigner + + err = loadKeyForLocalCosigner(cosigner, privateKey.PubKey(), testChainID, privShards[i]) + require.NoError(t, err) + + err = loadKeyForLocalCosigner(cosigner, privateKey.PubKey(), testChainID2, privShards[i]) + require.NoError(t, err) } - config2 := LocalCosignerConfig{ - CosignerKey: key2, - SignState: &signState2, - RsaKey: *rsaKey2, - Peers: peers, - Total: total, - Threshold: threshold, - } + return cosigners, privateKey.PubKey() +} - config3 := LocalCosignerConfig{ - CosignerKey: key3, - SignState: &signState3, - RsaKey: *rsaKey3, - Peers: peers, - Total: total, - Threshold: threshold, +func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) { + cosigners, pubKey := getTestLocalCosigners(t, threshold, total) + + thresholdValidators := make([]*ThresholdValidator, 0, total) + var leader *ThresholdValidator + leaders := make([]*MockLeader, total) + + ctx := context.Background() + + for i, cosigner := range cosigners { + peers := make([]Cosigner, 0, len(cosigners)-1) + for j, otherCosigner := range cosigners { + if i != j { + peers = append(peers, otherCosigner) + } + } + leaders[i] = &MockLeader{id: cosigner.GetID(), leader: leader} + tv := NewThresholdValidator( + cometlog.NewNopLogger(), + cosigner.config, + int(threshold), + time.Second, + 1, + cosigner, + peers, + leaders[i], + ) + if i == 0 { + leader = tv + leaders[i].leader = tv + } + + thresholdValidators = append(thresholdValidators, tv) + defer tv.Stop() + + err := tv.LoadSignStateIfNecessary(testChainID) + require.NoError(t, err) + + require.NoError(t, tv.Start(ctx)) } - var cosigner1 Cosigner - var cosigner2 Cosigner - var cosigner3 Cosigner - - cosigner1 = NewLocalCosigner(config1) - cosigner2 = NewLocalCosigner(config2) - cosigner3 = NewLocalCosigner(config3) - - require.Equal(t, cosigner1.GetID(), 1) - require.Equal(t, cosigner2.GetID(), 2) - require.Equal(t, cosigner3.GetID(), 3) - - thresholdPeers := make([]Cosigner, 0) - thresholdPeers = append(thresholdPeers, cosigner2, cosigner3) - - tmpDir, _ := os.MkdirTemp("", "store_test") - defer os.RemoveAll(tmpDir) - - raftStore := getMockRaftStore(cosigner1, tmpDir) - - thresholdValidatorOpt := ThresholdValidatorOpt{ - Pubkey: privateKey.PubKey(), - Threshold: int(threshold), - SignState: signState1, - Cosigner: cosigner1, - Peers: thresholdPeers, - RaftStore: raftStore, - Logger: tmlog.NewTMLogger(tmlog.NewSyncWriter(os.Stdout)).With("module", "validator"), + quit := make(chan bool) + done := make(chan bool) + + go func() { + for i := 0; true; i++ { + select { + case <-quit: + done <- true + return + default: + } + // simulate leader election + for _, l := range leaders { + l.SetLeader(nil) + } + t.Log("No leader") + + // time without a leader + time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec + + newLeader := thresholdValidators[i%len(thresholdValidators)] + for _, l := range leaders { + l.SetLeader(newLeader) + } + t.Logf("New leader: %d", newLeader.myCosigner.GetID()) + + // time with new leader + time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec + } + }() + + // sign 20 blocks (proposal, prevote, precommit) + for i := 0; i < 20; i++ { + var wg sync.WaitGroup + wg.Add(len(thresholdValidators)) + var mu sync.Mutex + success := false + for _, tv := range thresholdValidators { + tv := tv + + tv.nonceCache.LoadN(ctx, 1) + + go func() { + defer wg.Done() + // stagger signing requests with random sleep + time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec + + proposal := cometproto.Proposal{ + Height: 1 + int64(i), + Round: 1, + Type: cometproto.ProposalType, + } + + signature, _, _, err := tv.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + if err != nil { + t.Log("Proposal sign failed", "error", err) + return + } + + signBytes := comet.ProposalSignBytes(testChainID, &proposal) + + sig := make([]byte, len(signature)) + copy(sig, signature) + + if !pubKey.VerifySignature(signBytes, sig) { + t.Log("Proposal signature verification failed") + return + } + + mu.Lock() + defer mu.Unlock() + success = true + }() + } + + wg.Wait() + require.True(t, success) // at least one should succeed so that the block is not missed. + wg.Add(len(thresholdValidators)) + success = false + for _, tv := range thresholdValidators { + tv := tv + + tv.nonceCache.LoadN(ctx, 1) + + go func() { + defer wg.Done() + // stagger signing requests with random sleep + time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec + + preVote := cometproto.Vote{ + Height: 1 + int64(i), + Round: 1, + Type: cometproto.PrevoteType, + } + + signature, _, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preVote)) + if err != nil { + t.Log("PreVote sign failed", "error", err) + return + } + + signBytes := comet.VoteSignBytes(testChainID, &preVote) + + sig := make([]byte, len(signature)) + copy(sig, signature) + + if !pubKey.VerifySignature(signBytes, sig) { + t.Log("PreVote signature verification failed") + return + } + + mu.Lock() + defer mu.Unlock() + success = true + }() + } + + wg.Wait() + require.True(t, success) // at least one should succeed so that the block is not missed. + wg.Add(len(thresholdValidators)) + success = false + for _, tv := range thresholdValidators { + tv := tv + + tv.nonceCache.LoadN(ctx, 2) + + go func() { + defer wg.Done() + // stagger signing requests with random sleep + time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec + + var extension = []byte{0x1, 0x2, 0x3} + + blockIDHash := sha256.New() + blockIDHash.Write([]byte("something")) + + preCommit := cometproto.Vote{ + Height: 1 + int64(i), + Round: 1, + BlockID: cometproto.BlockID{Hash: blockIDHash.Sum(nil)}, + Type: cometproto.PrecommitType, + Extension: extension, + } + + signature, voteExtSignature, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preCommit)) + if err != nil { + t.Log("PreCommit sign failed", "error", err) + return + } + + signBytes := comet.VoteSignBytes(testChainID, &preCommit) + + sig := make([]byte, len(signature)) + copy(sig, signature) + + if !pubKey.VerifySignature(signBytes, sig) { + t.Log("PreCommit signature verification failed") + return + } + + voteExtSignBytes := comet.VoteExtensionSignBytes(testChainID, &preCommit) + voteExtSig := make([]byte, len(voteExtSignature)) + copy(voteExtSig, voteExtSignature) + + if !pubKey.VerifySignature(voteExtSignBytes, voteExtSig) { + t.Log("PreCommit vote extension signature verification failed") + return + } + + mu.Lock() + defer mu.Unlock() + success = true + }() + } + + wg.Wait() + + require.True(t, success) // at least one should succeed so that the block is not missed. } - validator := NewThresholdValidator(&thresholdValidatorOpt) - - raftStore.SetThresholdValidator(validator) - - _, err = raftStore.Open() - require.NoError(t, err) - - time.Sleep(3 * time.Second) // Ensure there is a leader - - var proposal tmProto.Proposal - proposal.Height = 1 - proposal.Round = 0 - proposal.Type = tmProto.ProposalType - - signBytes := tm.ProposalSignBytes("chain-id", &proposal) - - err = validator.SignProposal("chain-id", &proposal) - if err != nil { - t.Logf("%v", err) - } - require.NoError(t, err) + quit <- true + <-done +} - require.True(t, privateKey.PubKey().VerifySignature(signBytes, proposal.Signature)) +func TestThresholdValidatorLeaderElection2of3(t *testing.T) { + testThresholdValidatorLeaderElection(t, 2, 3) } diff --git a/test/docker.go b/test/docker.go new file mode 100644 index 00000000..cdd8b741 --- /dev/null +++ b/test/docker.go @@ -0,0 +1,75 @@ +package test + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/archive" +) + +type DockerImageBuildErrorDetail struct { + Message string `json:"message"` +} + +type DockerImageBuildLogAux struct { + ID string `json:"ID"` +} + +type DockerImageBuildLog struct { + Stream string `json:"stream"` + Aux *DockerImageBuildLogAux `json:"aux"` + Error string `json:"error"` + ErrorDetail *DockerImageBuildErrorDetail `json:"errorDetail"` +} + +// BuildHorcruxImage builds a Docker image for horcrux from current Dockerfile +func BuildHorcruxImage(ctx context.Context, client *client.Client) error { + dir, err := os.Getwd() + if err != nil { + return err + } + dockerfile := "docker/horcrux/native.Dockerfile" + opts := types.ImageBuildOptions{ + Dockerfile: dockerfile, + Tags: []string{signerImage + ":latest"}, + } + + tar, err := archive.TarWithOptions(filepath.Dir(dir), &archive.TarOptions{}) + if err != nil { + panic(fmt.Errorf("error archiving project for docker: %v", err)) + } + + res, err := client.ImageBuild(ctx, tar, opts) + if err != nil { + return err + } + + scanner := bufio.NewScanner(res.Body) + + for scanner.Scan() { + dockerLogLine := &DockerImageBuildLog{} + logLineText := scanner.Text() + err = json.Unmarshal([]byte(logLineText), dockerLogLine) + if err != nil { + return err + } + if dockerLogLine.Stream != "" { + fmt.Printf(dockerLogLine.Stream) + } + if dockerLogLine.Aux != nil { + fmt.Printf("Image ID: %s\n", dockerLogLine.Aux.ID) + } + if dockerLogLine.Error != "" { + return errors.New(dockerLogLine.Error) + } + } + + return scanner.Err() +} diff --git a/test/go.mod b/test/go.mod new file mode 100644 index 00000000..35f4f227 --- /dev/null +++ b/test/go.mod @@ -0,0 +1,275 @@ +module github.com/strangelove-ventures/horcrux/test + +go 1.21 + +require ( + github.com/cometbft/cometbft v0.38.2 + github.com/cosmos/cosmos-sdk v0.50.1 + github.com/docker/docker v24.0.7+incompatible + github.com/prometheus/client_model v0.5.0 + github.com/prometheus/common v0.45.0 + github.com/strangelove-ventures/horcrux/v3 v3.3.0-rc1 + github.com/strangelove-ventures/interchaintest/v8 v8.0.0 + github.com/stretchr/testify v1.8.4 + go.uber.org/zap v1.26.0 + golang.org/x/sync v0.5.0 + google.golang.org/grpc v1.59.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + cloud.google.com/go v0.110.10 // indirect + cloud.google.com/go/compute v1.23.3 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/storage v1.30.1 // indirect + cosmossdk.io/api v0.7.2 // indirect + cosmossdk.io/collections v0.4.0 // indirect + cosmossdk.io/core v0.11.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/errors v1.0.0 // indirect + cosmossdk.io/log v1.2.1 // indirect + cosmossdk.io/math v1.2.0 // indirect + cosmossdk.io/store v1.0.0 // indirect + cosmossdk.io/x/tx v0.12.0 // indirect + cosmossdk.io/x/upgrade v0.1.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.2 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect + github.com/ChainSafe/go-schnorrkel/1 v0.0.0-00010101000000-000000000000 // indirect + github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 // indirect + github.com/DataDog/zstd v1.5.5 // indirect + github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect + github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect + github.com/Jille/raft-grpc-leader-rpc v1.1.0 // indirect + github.com/Jille/raft-grpc-transport v1.4.0 // indirect + github.com/Jille/raftadmin v1.2.1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/StirlingMarketingGroup/go-namecase v1.0.0 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/avast/retry-go/v4 v4.5.1 // indirect + github.com/aws/aws-sdk-go v1.44.334 // 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.8.0 // indirect + github.com/boltdb/bolt v1.3.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chzyer/readline v1.5.1 // indirect + github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v0.0.0-20231116191551-bfb2e6a97fdd // 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.8.0 // indirect + github.com/containerd/containerd v1.6.24 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.0 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogogateway v1.2.0 // indirect + github.com/cosmos/gogoproto v1.4.11 // indirect + github.com/cosmos/iavl v1.0.0 // indirect + github.com/cosmos/ibc-go/modules/capability v1.0.0 // indirect + github.com/cosmos/ibc-go/v8 v8.0.0 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set v1.8.0 // indirect + github.com/decred/base58 v1.0.5 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v2 v2.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/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect + github.com/emicklei/dot v1.6.0 // indirect + github.com/ethereum/go-ethereum v1.13.5 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/getsentry/sentry-go v0.25.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-stack/stack v1.8.1 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/golang/glog v1.1.2 // 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.3 // 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/orderedcode v0.0.1 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // 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/gtank/merlin v0.1.1 // indirect + github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-getter v1.7.3 // 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.2 // indirect + github.com/hashicorp/go-msgpack v1.1.5 // indirect + github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // 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/hcl v1.0.0 // indirect + github.com/hashicorp/raft v1.6.0 // indirect + github.com/hashicorp/raft-boltdb/v2 v2.2.2 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/holiman/uint256 v1.2.3 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/iancoleman/strcase v0.3.0 // indirect + github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 // indirect + github.com/improbable-eng/grpc-web v0.15.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/klauspost/compress v1.17.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.31.0 // indirect + github.com/linxGnu/grocksdb v1.8.5 // 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/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2 // 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/moby/patternmatcher v0.5.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.11.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-varint v0.0.7 // 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-rc5 // indirect + github.com/opencontainers/runc v1.1.12 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect + github.com/pierrec/xxHash v0.1.5 // 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.17.0 // indirect + github.com/prometheus/procfs v0.12.0 // 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.11.0 // indirect + github.com/rs/cors v1.8.3 // indirect + github.com/rs/zerolog v1.31.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect + github.com/subosito/gotenv v1.4.2 // 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/tyler-smith/go-bip32 v1.0.0 // indirect + github.com/tyler-smith/go-bip39 v1.1.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 + gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e // indirect + gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.opencensus.io v0.24.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/api v0.149.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gotest.tools/v3 v3.5.1 // indirect + lukechampine.com/blake3 v1.2.1 // indirect + lukechampine.com/uint128 v1.2.0 // indirect + modernc.org/cc/v3 v3.41.0 // indirect + modernc.org/ccgo/v3 v3.16.15 // indirect + modernc.org/libc v1.29.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.27.0 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.1.0 // indirect + nhooyr.io/websocket v1.8.10 // indirect + pgregory.net/rapid v1.1.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + +replace ( + github.com/ChainSafe/go-schnorrkel => github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d + github.com/ChainSafe/go-schnorrkel/1 => github.com/ChainSafe/go-schnorrkel v1.0.0 + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 + github.com/strangelove-ventures/horcrux => ../ + github.com/vedhavyas/go-subkey => github.com/strangelove-ventures/go-subkey v1.0.7 +) diff --git a/test/go.sum b/test/go.sum new file mode 100644 index 00000000..6e4922eb --- /dev/null +++ b/test/go.sum @@ -0,0 +1,1909 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.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.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.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= +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/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/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/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/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/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/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/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/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/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/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/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.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +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/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/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/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/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/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +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/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/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/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/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/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/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/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/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/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/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +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 v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +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/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/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +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/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +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/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/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.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +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/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/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/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/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cosmossdk.io/api v0.7.2 h1:BO3i5fvKMKvfaUiMkCznxViuBEfyWA/k6w2eAF6q1C4= +cosmossdk.io/api v0.7.2/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.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= +cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= +cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= +cosmossdk.io/log v1.2.1 h1:Xc1GgTCicniwmMiKwDxUjO4eLhPxoVdI9vtMW8Ti/uk= +cosmossdk.io/log v1.2.1/go.mod h1:GNSCc/6+DhFIj1aLn/j7Id7PaO8DzNylUZoOYBL9+I4= +cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= +cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= +cosmossdk.io/store v1.0.0 h1:6tnPgTpTSIskaTmw/4s5C9FARdgFflycIc9OX8i1tOI= +cosmossdk.io/store v1.0.0/go.mod h1:ABMprwjvx6IpMp8l06TwuMrj6694/QP5NIW+X6jaTYc= +cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= +cosmossdk.io/x/circuit v0.1.0/go.mod h1:YDzblVE8+E+urPYQq5kq5foRY/IzhXovSYXb4nwd39w= +cosmossdk.io/x/evidence v0.1.0 h1:J6OEyDl1rbykksdGynzPKG5R/zm6TacwW2fbLTW4nCk= +cosmossdk.io/x/evidence v0.1.0/go.mod h1:hTaiiXsoiJ3InMz1uptgF0BnGqROllAN8mwisOMMsfw= +cosmossdk.io/x/feegrant v0.1.0 h1:c7s3oAq/8/UO0EiN1H5BIjwVntujVTkYs35YPvvrdQk= +cosmossdk.io/x/feegrant v0.1.0/go.mod h1:4r+FsViJRpcZif/yhTn+E0E6OFfg4n0Lx+6cCtnZElU= +cosmossdk.io/x/tx v0.12.0 h1:Ry2btjQdrfrje9qZ3iZeZSmDArjgxUJMMcLMrX4wj5U= +cosmossdk.io/x/tx v0.12.0/go.mod h1:qTth2coAGkwCwOCjqQ8EAQg+9udXNRzcnSbMgGKGEI0= +cosmossdk.io/x/upgrade v0.1.0 h1:z1ZZG4UL9ICTNbJDYZ6jOnF9GdEK9wyoEFi4BUScHXE= +cosmossdk.io/x/upgrade v0.1.0/go.mod h1:/6jjNGbiPCNtmA1N+rBtP601sr0g4ZXuj3yC6ClPCGY= +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= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8 h1:V8krnnfGj4pV65YLUm3C0/8bl7V5Nry2Pwvy3ru/wLc= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= +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 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= +github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= +github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 h1:oknQF/iIhf5lVjbwjsVDzDByupRhga8nhA3NAmwyHDA= +github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420/go.mod h1:KYkiMX5AbOlXXYfxkrYPrRPV6EbVUALTQh5ptUOJzu8= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +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/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc= +github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= +github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= +github.com/Jille/raft-grpc-leader-rpc v1.1.0 h1:u36rmA4tjp+4FSdZ17jg/1sfSCYNQIe5bzzwvW0iVTM= +github.com/Jille/raft-grpc-leader-rpc v1.1.0/go.mod h1:l+pK+uPuqpFDFcPmyUPSng4257UXrST0Vc3Lo4XwVB0= +github.com/Jille/raft-grpc-transport v1.4.0 h1:Kwk+IceQD8MpLKOulBu2ignX+aZAEjOhffEhN44sdzQ= +github.com/Jille/raft-grpc-transport v1.4.0/go.mod h1:afVUd8LQKUUo3V/ToLBH3mbSyvivRlMYCDK0eJRGTfQ= +github.com/Jille/raftadmin v1.2.1 h1:P35JYYJmyHnsJ7sEjdXVRRvaqXUQUuNjIqPFf5n3CEM= +github.com/Jille/raftadmin v1.2.1/go.mod h1:CocBqmOFhfk8oIgbzZKYACdm5PizUXfzupqTQV17Pgk= +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/Microsoft/hcsshim v0.9.10 h1:TxXGNmcbQxBKVWvjvTocNb6jrPyeHlk5EiDhhgHgggs= +github.com/Microsoft/hcsshim v0.9.10/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= +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/StirlingMarketingGroup/go-namecase v1.0.0 h1:2CzaNtCzc4iNHirR+5ru9OzGg8rQp860gqLBFqRI02Y= +github.com/StirlingMarketingGroup/go-namecase v1.0.0/go.mod h1:ZsoSKcafcAzuBx+sndbxHu/RjDcDTrEdT4UvhniHfio= +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/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/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-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= +github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= +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.334 h1:h2bdbGb//fez6Sv6PaYv868s9liDeoYM6hYsAqTB4MU= +github.com/aws/aws-sdk-go v1.44.334/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.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.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +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.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/bufbuild/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.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw= +github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-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/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 v0.0.0-20231116191551-bfb2e6a97fdd h1:qJuMKww8C5kQx0A0zgQUaswdcWlzgzTGAA5YdFAin3Y= +github.com/cockroachdb/pebble v0.0.0-20231116191551-bfb2e6a97fdd/go.mod h1:acMRUGd/BK8AUmQNK3spUCCGzFLZU2bSST3NMXSq2Kc= +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.2 h1:io0JCh5EPxINKN5ZMI5hCdpW3QVZRy+o8qWe3mlJa/8= +github.com/cometbft/cometbft v0.38.2/go.mod h1:PIi48BpzwlHqtV3mzwPyQgOyOnU94BNBimLS2ebBHOg= +github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AKVZpjo= +github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0= +github.com/containerd/containerd v1.6.24 h1:HKF4bfN7WoCk+/n/hi3OrTKJlPWxZmeg1uVDRpEPlXA= +github.com/containerd/containerd v1.6.24/go.mod h1:06DkIUikjOcYdqFgOXDwBHO+qR4/qfbMPQ9XxtAGs1c= +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.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= +github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= +github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= +github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= +github.com/cosmos/cosmos-sdk v0.50.1 h1:2SYwAYqd7ZwtrWxu/J8PwbQV/cDcu90bCr/a78g3lVw= +github.com/cosmos/cosmos-sdk v0.50.1/go.mod h1:fsLSPGstCwn6MMsFDMAQWGJj8E4sYsN9Gnu1bGE5imA= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +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.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= +github.com/cosmos/iavl v1.0.0 h1:bw6t0Mv/mVCJvlMTOPHWLs5uUE3BRBfVWCRelOzl+so= +github.com/cosmos/iavl v1.0.0/go.mod h1:CmTGqMnRnucjxbjduneZXT+0vPgNElYvdefjX2q9tYc= +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/v8 v8.0.0 h1:QKipnr/NGwc+9L7NZipURvmSIu+nw9jOIWTJuDBqOhg= +github.com/cosmos/ibc-go/v8 v8.0.0/go.mod h1:C6IiJom0F3cIQCD5fKwVPDrDK9j/xTu563AWuOmXois= +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/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/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 v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= +github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= +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/v2 v2.0.1 h1:18HurQ6DfHeNvwIjvOmrgr44bPdtVaQAe/WWwHg9goM= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.1/go.mod h1:XmyzkaXBy7ZvHdrTAlXAjpog8qKSAWa3ze7yqzWmgmc= +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/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/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+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/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.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.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.0 h1:vUzuoVE8ipzS7QkES4UfxdpCwdU2U97m2Pb2tQCoYRY= +github.com/emicklei/dot v1.6.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= +github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +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/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.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/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.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= +github.com/getsentry/sentry-go v0.25.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/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw 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-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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +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/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.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 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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/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-20210407192527-94a9f03dee38/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-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.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.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.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +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.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/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/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +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/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.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.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= +github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +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.2 h1:ErEYO2f//CjKsUDw4SmLzelsK6L3ZmOAR/4P9iS7ruY= +github.com/hashicorp/go-metrics v0.5.2/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= +github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= +github.com/hashicorp/go-msgpack/v2 v2.1.1 h1:xQEY9yB2wnHitoSzk/B9UjXWRQ67QKu5AOm8aFp8N3I= +github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +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.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/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 v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +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/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/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= +github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft v1.3.7/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= +github.com/hashicorp/raft v1.6.0 h1:tkIAORZy2GbJ2Trp5eUSggLXDPOJLXC+JJLNMMqtgtM= +github.com/hashicorp/raft v1.6.0/go.mod h1:Xil5pDgeGwRWuX4uPUmwa+7Vagg4N804dz6mhNi6S7o= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= +github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea h1:RxcPJuutPRM8PUOyiweMmkuNO+RJyfy2jds2gfvgNmU= +github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea/go.mod h1:qRd6nFJYYS6Iqnc/8HcUmko2/2Gw8qTFEmxDLii6W5I= +github.com/hashicorp/raft-boltdb/v2 v2.2.2 h1:rlkPtOllgIcKLxVT4nutqlTH2NRFn+tO1wwZk/4Dxqw= +github.com/hashicorp/raft-boltdb/v2 v2.2.2/go.mod h1:N8YgaZgNJLpZC+h+by7vDu5rzsRgONThTEeUS3zWbfY= +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/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +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.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/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 h1:nHoRIX8iXob3Y2kdt9KsjyIb7iApSvb3vgsd93xb5Ow= +github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk= +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/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/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/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/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/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= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= +github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kraken-hpc/go-fork v0.1.1 h1:O3X/ynoNy/eS7UIcZYef8ndFq2RXEIOue9kZqyzF0Sk= +github.com/kraken-hpc/go-fork v0.1.1/go.mod h1:uu0e5h+V4ONH5Qk/xuVlyNXJXy/swhqGIEMK7w+9dNc= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-libp2p v0.31.0 h1:LFShhP8F6xthWiBBq3euxbKjZsoRajVEyBS9snfHxYg= +github.com/libp2p/go-libp2p v0.31.0/go.mod h1:W/FEK1c/t04PbRH3fA9i5oucu5YcgrG0JVoBWT1B7Eg= +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.5 h1:Okfk5B1h0ikCYdDM7Tc5yJUS8LTwAmMBq5IPWTmOLPs= +github.com/linxGnu/grocksdb v1.8.5/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +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.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +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/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk= +github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU= +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/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2 h1:G/cVeTAbB9S/6FSWWqpFV0v49hiuHLbJPu9hTZ0UR2A= +github.com/misko9/go-substrate-rpc-client/v4 v4.0.0-20230913220906-b988ea7da0c2/go.mod h1:Q5BxOd9FxJqYp4vCiLGVdetecPcWTmUQIu0bRigYosU= +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/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= +github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +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/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= +github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +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.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +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/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +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-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +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-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA= +github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/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/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= +github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= +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.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.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +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.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +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.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +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.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/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.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +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/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +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.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +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.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +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/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/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/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.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +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/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +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.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/strangelove-ventures/horcrux/v3 v3.3.0-rc1 h1:EFzaYawF/2U6qMpD8wMB6JDwW0+aA9TfsyLYMSn7WWU= +github.com/strangelove-ventures/horcrux/v3 v3.3.0-rc1/go.mod h1:5DLNOcjYeSxqhY6DlMqMnxP1v+i291T8jRc8lNjkA+g= +github.com/strangelove-ventures/interchaintest/v8 v8.0.0 h1:z6hz23NJEhRJxZUH1vuDNTVscRwnUii+K4En9lfyndc= +github.com/strangelove-ventures/interchaintest/v8 v8.0.0/go.mod h1:0LEZ1dXKlAYOm18X6s8AC4y5e6uAjJ0L69A6JpUkjDc= +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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +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/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +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/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE= +github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec 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/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/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.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e h1:/QfokHt2yG9PcjnFSdpIQhJwrz2Q1bmKA718vw4/He8= +gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e/go.mod h1:lTSPILLBMt6qQOJgsiarbW85JhpkhoOfW2EBkxkcuSI= +gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc h1:ESmaQH1+uOLW0wOIPT4EVLh+gwKdmKovr+eZg4jWUNs= +gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc/go.mod h1:1CIpwbV+rSyzoBVr6Ok9vvEP7UHrW/d0zEB+jU6+E3o= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +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/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +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.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.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-20170613210332-850760c427c5/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-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-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +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-20210907225631-ff17edfbf26d/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-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.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +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-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.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +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-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.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= +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-20190523142557-0e01d883c5c5/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-20191008105621-543471e840be/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-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-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-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-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-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-20210906170528-6f6e22806c34/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-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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-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-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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.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.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-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-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +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= +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.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +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-20200324203455-a04cca1dde73/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-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-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +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.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.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +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-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +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.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= +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 v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= +modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= +modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= +modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= +nhooyr.io/websocket v1.8.10/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/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/test/horcrux_test.go b/test/horcrux_test.go index 2671408d..5a62bf35 100644 --- a/test/horcrux_test.go +++ b/test/horcrux_test.go @@ -1,477 +1,711 @@ package test import ( + "context" + "fmt" + "sync" "testing" - "time" + "github.com/cometbft/cometbft/crypto" + dockertypes "github.com/docker/docker/api/types" + "github.com/strangelove-ventures/horcrux/v3/signer" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" ) -var ( - chainID = "horcrux" +const ( + chainID = "horcrux" + maxSpecificElectionRetries = 3 ) -// Test4Of7SignerTwoSentries will spin up a chain with three single-node validators and one horcrux validator -// the horcrux validator will have seven signer nodes with a threshold of four, and two sentry nodes +// Test2Of3SignerOneSentry will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have three cosigner nodes with a threshold of two, and one sentry node +func Test2Of3SignerOneSentry(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 1, 1) +} + +// Test2Of3SignerTwoSentries will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have three cosigner nodes with a threshold of two, and two sentry nodes // checks that no slashing occurs -func Test4Of7SignerTwoSentries(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const totalSentries = 4 - const totalSigners = 7 - const threshold = 4 - const sentriesPerSigner = 2 - chain := getSimdChain() - - // setup a horcrux validator for us - ourValidator, err := NewHorcruxValidator(t, pool, network, home, - chainID, 0, totalSentries, totalSigners, threshold, chain) - require.NoError(t, err) +func Test2Of3SignerTwoSentries(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 2, 2) +} - // other vals are single node (non-horcrux) - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) +// Test2Of3SignerThreeSentries will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have three cosigner nodes with a threshold of two, and three sentry nodes +// where each cosigner connects to all sentries +func Test2Of3SignerThreeSentries(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 3, 3) +} - // start our validator's horcrux cluster - require.NoError(t, ourValidator.StartHorcruxCluster(ctx, sentriesPerSigner)) +// Test2Of3SignerThreeSentriesUniqueConnection will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have three cosigner nodes with a threshold of two, and three sentry nodes +// where each cosigner only connects to one sentry +func Test2Of3SignerThreeSentriesUniqueConnection(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 3, 1) +} - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, otherValidatorNodes, []*TestNode{}, []*TestValidator{ourValidator})) +// Test2Of3SignerOneSentry will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have three cosigner nodes with a threshold of two, and one sentry node +func Test3Of5SignerOneSentry(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 1, 1) +} - // Wait for all nodes to get to given block height - require.NoError(t, GetAllNodes(otherValidatorNodes, ourValidator.Sentries).WaitForHeight(5)) +// Test3Of5SignerTwoSentries will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have five cosigner nodes with a threshold of three, and two sentry nodes +// where each cosigner connects to all sentries. +func Test3Of5SignerTwoSentries(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 2, 2) +} - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidator.Name()) - require.NoError(t, ourValidator.EnsureNotSlashed()) +// Test3Of5SignerFiveSentries will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have five cosigner nodes with a threshold of three, and five sentry nodes +// where each cosigner connects to all sentries. +func Test3Of5SignerFiveSentries(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 5, 5) } -// Test2Of3SignerTwoSentries will spin up a chain with three single-node validators and one horcrux validator -// the horcrux validator will have three signer nodes with a threshold of two, and two sentry nodes -// checks that no slashing occurs -func Test2Of3SignerTwoSentries(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const totalSentries = 2 - const totalSigners = 3 - const threshold = 2 - const sentriesPerSigner = 2 - chain := getSimdChain() - - // setup a horcrux validator for us - ourValidator, err := NewHorcruxValidator(t, pool, network, home, - chainID, 0, totalSentries, totalSigners, threshold, chain) - require.NoError(t, err) +// Test3Of5SignerFiveSentriesUniqueConnection will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have three cosigner nodes with a threshold of two, and three sentry nodes +// where each cosigner only connects to one sentry. +func Test3Of5SignerFiveSentriesUniqueConnection(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 5, 1) +} - // remaining validators are single-node non-horcrux - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) +// Test4Of7SignerTwoSentries will spin up a chain with one single-node validator and one horcrux validator +// the horcrux validator will have seven cosigner nodes with a threshold of four, and two sentry nodes +// where each cosigner connects to all sentries. +func Test4Of7SignerTwoSentries(t *testing.T) { + testChainSingleNodeAndHorcruxThreshold(t, 2, 7, 4, 2, 2) +} - // start our validator's horcrux cluster - require.NoError(t, ourValidator.StartHorcruxCluster(ctx, sentriesPerSigner)) +// TestSingleSignerTwoSentries will spin up a chain with one single-node validator and one horcrux single +// signer validator. +func TestSingleSignerTwoSentries(t *testing.T) { + testChainSingleNodeAndHorcruxSingle(t, 2, 2) +} - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, otherValidatorNodes, []*TestNode{}, []*TestValidator{ourValidator})) +// TestUpgradeValidatorToHorcrux will spin up a chain with two validators, stop one validator, configure that validator +// to be a relay for the remote signer cluster, spin up a 2/3 threshold signer cluster, restart the validator and check +// that no slashing occurs. +func TestUpgradeValidatorToHorcrux(t *testing.T) { + ctx := context.Background() + client, network := interchaintest.DockerSetup(t) + logger := zaptest.NewLogger(t) + + const ( + totalValidators = 2 + signedBlocksWindow = 10 + minSignedPerWindow = 0.5 + totalSigners = 3 + threshold = 2 + sentriesPerSigner = 1 + ) + + // slightly more lenient uptime requirement than modifyGenesisStrictUptime to account for + // the time it takes to upgrade the validator, where a few missed blocks is expected. + // allow 50% missed blocks in 10 block signed blocks window (5 missed blocks before slashing). + modifyGenesis := modifyGenesisSlashingUptime(signedBlocksWindow, minSignedPerWindow) + + cw := &chainWrapper{ + totalValidators: totalValidators, + modifyGenesis: modifyGenesis, + } - // Wait for all nodes to get to given block height - require.NoError(t, GetAllNodes(otherValidatorNodes, ourValidator.Sentries).WaitForHeight(5)) + startChains(ctx, t, logger, client, network, cw) - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidator.Name()) - require.NoError(t, ourValidator.EnsureNotSlashed()) -} + // validator to upgrade to horcrux + v := cw.chain.Validators[0] -// Test2Of3SignerUniqueSentry will spin up a chain with three single-node validators and one horcrux validator -// the horcrux validator will have three signer nodes with a threshold of two, and one sentry node -// checks that no slashing occurs -func Test2Of3SignerUniqueSentry(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const totalSentries = 2 - const totalSigners = 3 - const threshold = 2 - const sentriesPerSigner = 1 - chain := getSimdChain() - - // setup a horcrux validator for us - ourValidator, err := NewHorcruxValidator(t, pool, network, home, - chainID, 0, totalSentries, totalSigners, threshold, chain) + err := v.StopContainer(ctx) require.NoError(t, err) - // remaining validators are single-node non-horcrux - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) - - // start our validator's horcrux cluster - require.NoError(t, ourValidator.StartHorcruxCluster(ctx, sentriesPerSigner)) + pubKey, err := convertValidatorToHorcrux(ctx, logger, client, network, v, totalSigners, threshold, cosmos.ChainNodes{v}, sentriesPerSigner) + require.NoError(t, err) - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, otherValidatorNodes, []*TestNode{}, []*TestValidator{ourValidator})) + err = v.StartContainer(ctx) + require.NoError(t, err) - // Wait for all nodes to get to given block height - require.NoError(t, GetAllNodes(otherValidatorNodes, ourValidator.Sentries).WaitForHeight(5)) + err = testutil.WaitForBlocks(ctx, 20, cw.chain) + require.NoError(t, err) - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidator.Name()) - require.NoError(t, ourValidator.EnsureNotSlashed()) + requireHealthyValidator(t, cw.chain.Validators[0], pubKey.Address()) } -// TestSingleSignerTwoSentries will spin up a chain with four validators & one sentry node, stop one validator & the -// sentry node, configure those two nodes to be relays for the remote signer, spin up a single remote signer, restart -// the validator/sentry node and check that no slashing occurs -func TestSingleSignerTwoSentries(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) +// TestDownedSigners2of3 tests taking down 2 nodes at a time in the 2/3 threshold horcrux cluster for a period of time. +func TestDownedSigners2of3(t *testing.T) { + ctx := context.Background() - const totalValidators = 4 - const totalSentries = 2 - const totalSigners = 1 - chain := getSimdChain() + const ( + totalValidators = 4 + totalSigners = 3 + threshold = 2 + totalSentries = 3 + sentriesPerSigner = 3 + ) - // get total sentries nodes for our validator - ourValidatorNodes := GetValidators(0, 1, totalSentries, home, chainID, chain, pool, network, t) + cw, pubKey := startChainSingleNodeAndHorcruxThreshold( + ctx, t, totalValidators, totalSigners, threshold, totalSentries, sentriesPerSigner, + ) - // using the first node for account and consensus key to create gentx - ourValidatorAccountNode := ourValidatorNodes[0] + require.NoError(t, testutil.WaitForBlocks(ctx, 15, cw.chain)) - // other vals are single node (non-horcrux) - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) + ourValidator := cw.chain.Validators[0] + requireHealthyValidator(t, ourValidator, pubKey.Address()) - // nodes that will be used for account and consensus key to create gentx - validatorAccountNodes := GetAllNodes([]*TestNode{ourValidatorAccountNode}, otherValidatorNodes) + cosigners := ourValidator.Sidecars - // nodes that will initially be setup as simple fullnodes, then enable privval listener - // not used for gentx tasks. In this case it is only our val's second node - // both of ourValidatorNodes are sentries, but for initial setup only the first one is used for gentx. - sentries := []*TestNode{ourValidatorNodes[1]} + // Test taking down each node in the signer cluster for a period of time + for _, cosigner := range cosigners { + t.Logf("{%s} -> Stopping signer...", cosigner.Name()) + require.NoError(t, cosigner.StopContainer(ctx)) - // initialize horcrux signer nodes for our validator - signers := MakeTestSigners(0, totalSigners, home, pool, network, t) + t.Logf("{%s} -> Waiting for blocks after stopping cosigner {%s}", ourValidator.Name(), cosigner.Name()) + require.NoError(t, testutil.WaitForBlocks(ctx, 15, cw.chain)) - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, validatorAccountNodes, sentries, []*TestValidator{})) + requireHealthyValidator(t, ourValidator, pubKey.Address()) - allNodes := GetAllNodes(validatorAccountNodes, sentries) + t.Logf("{%s} -> Restarting signer...", cosigner.Name()) + require.NoError(t, cosigner.StartContainer(ctx)) - // Wait for all nodes to get to given block height - require.NoError(t, allNodes.WaitForHeight(5)) + t.Logf("{%s} -> Waiting for blocks after restarting cosigner {%s}", ourValidator.Name(), cosigner.Name()) + require.NoError(t, testutil.WaitForBlocks(ctx, 15, cw.chain)) - // start remote signer - require.NoError(t, StartSingleSignerContainers(signers, ourValidatorAccountNode, ourValidatorNodes)) + requireHealthyValidator(t, ourValidator, pubKey.Address()) + } +} - // Stop the validator node and sentry node before upgrading to horcrux - t.Logf("{%s} -> Stopping Node...", ourValidatorAccountNode.Name()) - require.NoError(t, ourValidatorAccountNode.StopAndRemoveContainer(false)) +// TestDownedSigners3of5 tests taking down 2 nodes at a time in the 3/5 threshold horcrux cluster for a period of time. +func TestDownedSigners3of5(t *testing.T) { + ctx := context.Background() - t.Logf("{%s} -> Stopping Node...", sentries[0].Name()) - require.NoError(t, sentries[0].StopAndRemoveContainer(false)) + const ( + totalValidators = 4 + totalSigners = 5 + threshold = 3 + totalSentries = 3 + sentriesPerSigner = 3 + ) - time.Sleep(5 * time.Second) // wait for all containers to stop + cw, pubKey := startChainSingleNodeAndHorcruxThreshold( + ctx, t, totalValidators, totalSigners, threshold, totalSentries, sentriesPerSigner, + ) - // wait for all signers to be reachable on port 2222 - require.NoError(t, signers.GetHosts().WaitForAllToStart(t, 10)) + require.NoError(t, testutil.WaitForBlocks(ctx, 15, cw.chain)) - // modify node config to listen for private validator connections - ourValidatorAccountNode.SetPrivValListen(allNodes.PeerString()) - sentries[0].SetPrivValListen(allNodes.PeerString()) + ourValidator := cw.chain.Validators[0] + requireHealthyValidator(t, ourValidator, pubKey.Address()) - // restart node and ensure that signer cluster is connected by - // checking if the node continues to miss blocks or is slashed - t.Logf("{%s} -> Restarting Node...", ourValidatorAccountNode.Name()) - t.Logf("{%s} -> Restarting Node...", sentries[0].Name()) + cosigners := ourValidator.Sidecars + + // Test taking down 2 nodes at a time in the signer cluster for a period of time + for i := 0; i < len(cosigners); i++ { + cosigner1 := cosigners[i] + var cosigner2 *cosmos.SidecarProcess + if i < len(cosigners)-1 { + cosigner2 = cosigners[i+1] + } else { + cosigner2 = cosigners[0] + } + if i == 0 { + t.Logf("{%s} -> Stopping signer...", cosigner1.Name()) + require.NoError(t, cosigner1.StopContainer(ctx)) + t.Logf("{%s} -> Stopping signer...", cosigner2.Name()) + require.NoError(t, cosigner2.StopContainer(ctx)) + t.Logf("{%s} -> Waiting for blocks after stopping cosigner {%s}", ourValidator.Name(), cosigner1.Name()) + } else { + t.Logf("{%s} -> Stopping signer...", cosigner2.Name()) + require.NoError(t, cosigner2.StopContainer(ctx)) + } - require.NoError(t, ourValidatorAccountNode.Start(ctx, nil)) - require.NoError(t, sentries[0].Start(ctx, nil)) + t.Logf("{%s} -> Waiting for blocks after stopping cosigner {%s}", ourValidator.Name(), cosigner2.Name()) + require.NoError(t, testutil.WaitForBlocks(ctx, 15, cw.chain)) - // wait for our validator and all sentries to be reachable - hosts := ourValidatorAccountNode.GetHosts() - hosts = append(hosts, sentries[0].GetHosts()...) - require.NoError(t, hosts.WaitForAllToStart(t, 10)) + requireHealthyValidator(t, ourValidator, pubKey.Address()) - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidatorAccountNode.Name()) - ourPrivVal, err := ourValidatorAccountNode.GetPrivVal() - require.NoError(t, err) + t.Logf("{%s} -> Restarting cosigner...", cosigner1.Name()) + require.NoError(t, cosigner1.StartContainer(ctx)) + require.NoError(t, testutil.WaitForBlocks(ctx, 15, cw.chain)) - require.NoError(t, ourValidatorAccountNode.EnsureNotSlashed(ourPrivVal.PubKey.Address())) + requireHealthyValidator(t, ourValidator, pubKey.Address()) + } } -// TestUpgradeValidatorToHorcrux will spin up a chain with four validators, stop one validator, configure that validator -// to be a relay for the remote signer cluster, spin up a 2/3 threshold signer cluster, restart the validator and check -// that no slashing occurs -func TestUpgradeValidatorToHorcrux(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) +// TestLeaderElection2of3 tests electing a specific leader in a 2/3 threshold horcrux cluster. +func TestLeaderElection2of3(t *testing.T) { + ctx := context.Background() - const totalValidators = 4 - const totalSigners = 3 - const threshold = 2 - const sentriesPerSigner = 1 - chain := getSimdChain() + const ( + totalValidators = 2 + totalSigners = 3 + threshold = 2 + totalSentries = 3 + sentriesPerSigner = 3 + ) - // initially all vals are single node (non-horcrux) - validators := GetValidators(0, totalValidators, 1, home, chainID, chain, pool, network, t) + cw, pubKey := startChainSingleNodeAndHorcruxThreshold( + ctx, t, totalValidators, totalSigners, threshold, totalSentries, sentriesPerSigner, + ) - // for this test we will upgrade the first validator to horcrux - ourValidatorNode := validators[0] + ourValidator := cw.chain.Validators[0] + requireHealthyValidator(t, ourValidator, pubKey.Address()) - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, validators, []*TestNode{}, []*TestValidator{})) + cosigners := ourValidator.Sidecars - // Wait for all validators to get to given block height - require.NoError(t, validators.WaitForHeight(5)) + // Test electing each node in the signer cluster for a period of time + for _, cosigner := range cosigners { + var eg errgroup.Group + + for i := 0; i < maxSpecificElectionRetries; i++ { + t.Logf("{%s} -> Electing leader...", cosigner.Name()) + err := transferLeadership(ctx, cosigner) + require.NoError(t, err, "failed to transfer leadership to %d", cosigner.Name()) + + t.Logf("{%s} -> Waiting for signed blocks with signer as leader {%s}", ourValidator.Name(), cosigner.Name()) + + // Make sure all cosigners have the same leader + for _, s := range cosigners { + s := s + eg.Go(func() error { + return pollForLeader(ctx, t, s, cosigner.Index+1) + }) + } + if err := eg.Wait(); err == nil { + break + } - // get the consensus key from our validator - ourValidatorPrivValKey, err := ourValidatorNode.GetPrivVal() - require.NoError(t, err) + // electing a specific leader can fail, but this is okay as long as all nodes agree on one leader. + // will retry electing the specific leader in the next iteration. + var commonLeader int + for i, s := range cosigners { + leader, err := getLeader(ctx, s) + require.NoErrorf(t, err, "failed to get leader from signer: %s", s.Name()) + if i == 0 { + commonLeader = leader + continue + } + require.Equal(t, commonLeader, leader, "leader is not the same on all signers, mismatch on %s", s.Name()) + } + } + + require.NoError(t, testutil.WaitForBlocks(ctx, 5, cw.chain)) - // create horcrux validator with same consensus key - ourValidatorUpgradedToHorcrux, err := NewHorcruxValidatorWithPrivValKey(t, pool, network, home, - chainID, 0, 0, totalSigners, threshold, getSimdChain(), ourValidatorPrivValKey) + requireHealthyValidator(t, ourValidator, pubKey.Address()) + } +} + +// TestChainPureHorcrux tests a chain with only horcrux validators. +func TestChainPureHorcrux(t *testing.T) { + ctx := context.Background() + client, network := interchaintest.DockerSetup(t) + logger := zaptest.NewLogger(t) + + const ( + totalValidators = 2 + sentriesPerValidator = 3 + signersPerValidator = 3 + threshold = 2 + sentriesPerSigner = 1 + ) + + pubKeys := make([]crypto.PubKey, totalValidators) + cw := &chainWrapper{ + totalValidators: totalValidators, + totalSentries: 1 + totalValidators*(sentriesPerValidator-1), + modifyGenesis: modifyGenesisStrictUptime, + preGenesis: preGenesisAllHorcruxThreshold(ctx, logger, client, network, signersPerValidator, threshold, sentriesPerValidator, sentriesPerSigner, pubKeys), + } + + startChains( + ctx, t, logger, client, network, cw, + ) + + err := testutil.WaitForBlocks(ctx, 20, cw.chain) require.NoError(t, err) - // stop our validator node before upgrading to horcrux - t.Logf("{%s} -> Stopping Node...", ourValidatorNode.Name()) - require.NoError(t, ourValidatorNode.StopAndRemoveContainer(false)) + for _, p := range pubKeys { + requireHealthyValidator(t, cw.chain.Validators[0], p.Address()) + } +} - time.Sleep(5 * time.Second) // wait for all containers to stop +// TestMultipleChainHorcrux tests running a validator across multiple chains with a single horcrux cluster. +func TestMultipleChainHorcrux(t *testing.T) { + ctx := context.Background() + client, network := interchaintest.DockerSetup(t) + logger := zaptest.NewLogger(t) + + const ( + totalChains = 2 + validatorsPerChain = 2 + sentriesPerValidator = 3 + totalSigners = 3 + threshold = 2 + sentriesPerSigner = 1 + ) + + chainWrappers := make([]*chainWrapper, totalChains) + pubKeys := make([]crypto.PubKey, totalChains) + chainConfigs := make([]*cosignerChainConfig, totalChains) + preGenesises := make([]func(*chainWrapper) func(ibc.ChainConfig) error, totalChains) + + for i := 0; i < totalChains; i++ { + chainConfigs[i] = &cosignerChainConfig{ + sentries: make([]cosmos.ChainNodes, sentriesPerSigner), + shards: make([]signer.CosignerEd25519Key, totalSigners), + } + } - // bring in single signer node as a sentry for horcrux - ourValidatorUpgradedToHorcrux.Sentries = []*TestNode{ourValidatorNode} + cosignerSidecars := make(horcruxSidecars, totalSigners) - // modify node config to listen for private validator connections - ourValidatorNode.SetPrivValListen(validators.PeerString()) + eciesShards, err := signer.CreateCosignerECIESShards(totalSigners) + require.NoError(t, err) - // remove priv_validator_key.json from our validator node - // horcrux now holds the sharded key - ourValidatorNode.GenNewPrivVal() + var wg sync.WaitGroup + wg.Add(totalChains) - // start our new validator - require.NoError(t, ourValidatorUpgradedToHorcrux.StartHorcruxCluster(ctx, sentriesPerSigner)) + cosignersStarted := make(chan struct{}, 1) - t.Logf("{%s} -> Restarting Node...", ourValidatorNode.Name()) - require.NoError(t, ourValidatorNode.Start(ctx, nil)) + for i, chainConfig := range chainConfigs { + i := i + chainConfig := chainConfig + preGenesises[i] = func(cw *chainWrapper) func(ibc.ChainConfig) error { + return func(cc ibc.ChainConfig) error { - // wait for validator to be reachable - require.NoError(t, ourValidatorNode.GetHosts().WaitForAllToStart(t, 10)) + firstSentry := cw.chain.Validators[0] + sentries := append(cosmos.ChainNodes{firstSentry}, cw.chain.FullNodes...) - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidatorUpgradedToHorcrux.Name()) - require.NoError(t, ourValidatorUpgradedToHorcrux.EnsureNotSlashed()) -} + sentriesForCosigner := getSentriesForCosignerConnection(sentries, totalSigners, sentriesPerSigner) + chainConfig.sentries = sentriesForCosigner -func TestDownedSigners2of3(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const totalSigners = 3 - const totalSentries = 2 - const threshold = 2 - const sentriesPerSigner = 3 - chain := getSimdChain() - - // setup a horcrux validator for us - ourValidator, err := NewHorcruxValidator(t, pool, network, home, - chainID, 0, totalSentries, totalSigners, threshold, chain) - require.NoError(t, err) + chainConfig.chainID = cw.chain.Config().ChainID - // remaining validators are single-node non-horcrux - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) + ed25519Shards, pvPubKey, err := getShardedPrivvalKey(ctx, firstSentry, threshold, uint8(totalSigners)) + if err != nil { + wg.Done() + return err + } - // start our validator's horcrux cluster - require.NoError(t, ourValidator.StartHorcruxCluster(ctx, sentriesPerSigner)) + chainConfig.shards = ed25519Shards - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, otherValidatorNodes, []*TestNode{}, []*TestValidator{ourValidator})) + pubKeys[i] = pvPubKey - // Wait for all nodes to get to given block height - require.NoError(t, GetAllNodes(otherValidatorNodes, ourValidator.Sentries).WaitForHeight(5)) + if i == 0 { + for j := 0; j < totalSigners; j++ { + cosigner, err := horcruxSidecar(ctx, firstSentry, fmt.Sprintf("cosigner-%d", j+1), client, network) + if err != nil { + wg.Done() + return err + } - // Test taking down each node in the signer cluster for a period of time - for _, signer := range ourValidator.Signers { - t.Logf("{%s} -> Stopping signer...", signer.Name()) - require.NoError(t, signer.StopAndRemoveContainer(false)) - - t.Logf("{%s} -> Waiting until cluster recovers from taking down signer {%s}", ourValidator.Name(), signer.Name()) - require.NoError(t, ourValidator.WaitForConsecutiveBlocks(10)) - - t.Logf("{%s} -> Restarting signer...", signer.Name()) - require.NoError(t, signer.CreateCosignerContainer()) - require.NoError(t, signer.StartContainer()) - require.NoError(t, signer.GetHosts().WaitForAllToStart(t, 10)) // Wait to ensure signer is back up - require.NoError(t, ourValidator.WaitForConsecutiveBlocks(10)) - } - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidator.Name()) - require.NoError(t, ourValidator.EnsureNotSlashed()) -} + cosignerSidecars[j] = horcruxSidecarProcess{ + cosigner: cosigner, + } + } + } -func TestLeaderElection2of3(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const totalSigners = 3 - const totalSentries = 2 - const threshold = 2 - const sentriesPerSigner = 3 - chain := getSimdChain() - - // setup a horcrux validator for us - ourValidator, err := NewHorcruxValidator(t, pool, network, home, - chainID, 0, totalSentries, totalSigners, threshold, chain) - require.NoError(t, err) + if err := enablePrivvalListener(ctx, logger, sentries, client); err != nil { + wg.Done() + return err + } - // remaining validators are single-node non-horcrux - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) + wg.Done() - // start our validator's horcrux cluster - require.NoError(t, ourValidator.StartHorcruxCluster(ctx, sentriesPerSigner)) + // wait for all cosigners to be started before continuing to start the chain. + <-cosignersStarted - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, otherValidatorNodes, []*TestNode{}, []*TestValidator{ourValidator})) + return nil + } + } + } - // Wait for all nodes to get to given block height - require.NoError(t, GetAllNodes(otherValidatorNodes, ourValidator.Sentries).WaitForHeight(5)) + go configureAndStartSidecars(ctx, t, eciesShards, cosignerSidecars, threshold, &wg, cosignersStarted, chainConfigs...) - // Test electing each node in the signer cluster for a period of time - for _, signer := range ourValidator.Signers { - t.Logf("{%s} -> Electing leader...", signer.Name()) - err := signer.TransferLeadership(ctx, signer.Index) - require.NoError(t, err, "failed to transfer leadership to %d", signer.Index) - - t.Logf("{%s} -> Waiting for signed blocks with signer as leader {%s}", ourValidator.Name(), signer.Name()) - require.NoError(t, ourValidator.WaitForConsecutiveBlocks(2)) - - // Make sure all cosigners have the same leader - for _, s := range ourValidator.Signers { - leader, err := s.GetLeader(ctx) - require.NoError(t, err, "failed to get leader from signer: %s", s.Name()) - require.Equal(t, signer.Name()+":"+signerPort, leader) + for i := 0; i < totalChains; i++ { + chainWrappers[i] = &chainWrapper{ + totalValidators: validatorsPerChain, + totalSentries: sentriesPerValidator - 1, + modifyGenesis: modifyGenesisStrictUptime, + preGenesis: preGenesises[i], } + } + + startChains(ctx, t, logger, client, network, chainWrappers...) + + chains := make([]testutil.ChainHeighter, totalChains) + for i, cw := range chainWrappers { + chains[i] = cw.chain + } + + testutil.WaitForBlocks(ctx, 20, chains...) - require.NoError(t, ourValidator.WaitForConsecutiveBlocks(8)) + for i, p := range pubKeys { + requireHealthyValidator(t, chainWrappers[i].chain.Validators[0], p.Address()) } - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidator.Name()) - require.NoError(t, ourValidator.EnsureNotSlashed()) } -func TestDownedSigners3of5(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const totalSigners = 5 - const totalSentries = 4 - const threshold = 3 - const sentriesPerSigner = 5 - chain := getSimdChain() - - // setup a horcrux validator for us - ourValidator, err := NewHorcruxValidator(t, pool, network, home, - chainID, 0, totalSentries, totalSigners, threshold, chain) - require.NoError(t, err) +type cosignerChainConfig struct { + chainID string + shards []signer.CosignerEd25519Key + sentries []cosmos.ChainNodes +} - // remaining validators are single-node non-horcrux - otherValidatorNodes := GetValidators(1, totalValidators-1, 1, home, chainID, chain, pool, network, t) +type horcruxSidecarProcess struct { + cosigner *cosmos.SidecarProcess + proxy *cosmos.SidecarProcess +} - // start our validator's horcrux cluster - require.NoError(t, ourValidator.StartHorcruxCluster(ctx, sentriesPerSigner)) +type horcruxSidecars []horcruxSidecarProcess + +func configureAndStartSidecars( + ctx context.Context, + t *testing.T, + eciesShards []signer.CosignerECIESKey, + sidecars horcruxSidecars, + threshold int, + wg *sync.WaitGroup, + cosignersStarted chan struct{}, + chainConfigs ...*cosignerChainConfig, +) { + // wait for pre-genesis to finish from all chains + wg.Wait() + + totalSigners := len(sidecars) + + cosignersConfig := make(signer.CosignersConfig, totalSigners) + for i, s := range sidecars { + cosignersConfig[i] = signer.CosignerConfig{ + ShardID: i + 1, + P2PAddr: fmt.Sprintf("tcp://%s:%s", s.cosigner.HostName(), signerPort), + } + } - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, otherValidatorNodes, []*TestNode{}, []*TestValidator{ourValidator})) + var eg errgroup.Group - // Wait for all nodes to get to given block height - require.NoError(t, GetAllNodes(otherValidatorNodes, ourValidator.Sentries).WaitForHeight(5)) + for i, s := range sidecars { + numSentries := 0 + for _, chainConfig := range chainConfigs { + numSentries += len(chainConfig.sentries[i]) + } - // Test taking down 2 nodes at a time in the signer cluster for a period of time - for i := 0; i < len(ourValidator.Signers); i++ { - signer1 := ourValidator.Signers[i] - var signer2 *TestSigner - if i < len(ourValidator.Signers)-1 { - signer2 = ourValidator.Signers[i+1] - } else { - signer2 = ourValidator.Signers[0] + chainNodes := make(signer.ChainNodes, 0, numSentries) + + ed25519Shards := make([]chainEd25519Shard, len(chainConfigs)) + + for j, chainConfig := range chainConfigs { + if s.proxy == nil { + for _, sentry := range chainConfig.sentries[i] { + chainNodes = append(chainNodes, signer.ChainNode{ + PrivValAddr: fmt.Sprintf("tcp://%s:1234", sentry.HostName()), + }) + } + } + + ed25519Shards[j] = chainEd25519Shard{ + chainID: chainConfig.chainID, + key: chainConfig.shards[i], + } } - if i == 0 { - t.Logf("{%s} -> Stopping signer...", signer1.Name()) - require.NoError(t, signer1.StopAndRemoveContainer(false)) - t.Logf("{%s} -> Stopping signer...", signer2.Name()) - require.NoError(t, signer2.StopAndRemoveContainer(false)) - t.Logf("{%s} -> Waiting until cluster recovers from taking down signer {%s}", ourValidator.Name(), signer1.Name()) - } else { - t.Logf("{%s} -> Stopping signer...", signer2.Name()) - require.NoError(t, signer2.StopAndRemoveContainer(false)) + + var grpcAddr string + if s.proxy != nil { + grpcAddr = ":5555" + } + + config := signer.Config{ + SignMode: signer.SignModeThreshold, + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: threshold, + Cosigners: cosignersConfig, + GRPCTimeout: "200ms", + RaftTimeout: "200ms", + }, + ChainNodes: chainNodes, + GRPCAddr: grpcAddr, + } + + cosigner := s.cosigner + proxy := s.proxy + i := i + + if proxy != nil { + eg.Go(func() error { + if err := proxy.CreateContainer(ctx); err != nil { + return err + } + + return proxy.StartContainer(ctx) + }) } - t.Logf("{%s} -> Waiting until cluster recovers from taking down signer {%s}", ourValidator.Name(), signer2.Name()) - require.NoError(t, ourValidator.WaitForConsecutiveBlocks(10)) + // configure and start cosigner in parallel + eg.Go(func() error { + if err := writeConfigAndKeysThreshold(ctx, cosigner, config, eciesShards[i], ed25519Shards...); err != nil { + return err + } - t.Logf("{%s} -> Restarting signer...", signer1.Name()) - require.NoError(t, signer1.CreateCosignerContainer()) - require.NoError(t, signer1.StartContainer()) - require.NoError(t, signer1.GetHosts().WaitForAllToStart(t, 10)) // Wait to ensure signer is back up - require.NoError(t, ourValidator.WaitForConsecutiveBlocks(10)) + if err := cosigner.CreateContainer(ctx); err != nil { + return err + } + + return cosigner.StartContainer(ctx) + }) } - t.Logf("{%s} -> Checking that slashing has not occurred...", ourValidator.Name()) - require.NoError(t, ourValidator.EnsureNotSlashed()) + + require.NoError(t, eg.Wait()) + + // signal to pre-genesis that all cosigners have been started and chain start can proceed. + close(cosignersStarted) } -// tests a chain with only horcrux validators -func TestChainPureHorcrux(t *testing.T) { - t.Parallel() - ctx, home, pool, network := SetupTestRun(t) - - const totalValidators = 4 - const signersPerValidator = 3 - const sentriesPerValidator = 2 - const threshold = 2 - const sentriesPerSigner = sentriesPerValidator - var chain *ChainType - if false { - // keeping this here as example of testing another chain - chain = getSentinelChain(ctx, "v0.8.3") - } else { - chain = getSimdChain() +func TestHorcruxProxyGRPC(t *testing.T) { + ctx := context.Background() + client, network := interchaintest.DockerSetup(t) + logger := zaptest.NewLogger(t) + + _, err := client.ImagePull( + ctx, + horcruxProxyRegistry+":"+horcruxProxyTag, + dockertypes.ImagePullOptions{}, + ) + require.NoError(t, err) + + const ( + totalChains = 2 + validatorsPerChain = 2 + sentriesPerValidator = 3 + totalSigners = 3 + threshold = 2 + sentriesPerSigner = 1 + ) + + chainWrappers := make([]*chainWrapper, totalChains) + pubKeys := make([]crypto.PubKey, totalChains) + chainConfigs := make([]*cosignerChainConfig, totalChains) + preGenesises := make([]func(*chainWrapper) func(ibc.ChainConfig) error, totalChains) + + for i := 0; i < totalChains; i++ { + chainConfigs[i] = &cosignerChainConfig{ + sentries: make([]cosmos.ChainNodes, sentriesPerSigner), + shards: make([]signer.CosignerEd25519Key, totalSigners), + } } - var validators []*TestValidator - var startValidatorsErrGroup errgroup.Group + cosignerSidecars := make(horcruxSidecars, totalSigners) - var allNodes TestNodes + eciesShards, err := signer.CreateCosignerECIESShards(totalSigners) + require.NoError(t, err) - // start horcrux cluster for each validator - for i := 0; i < totalValidators; i++ { - validator, err := NewHorcruxValidator(t, pool, network, home, chainID, i, - sentriesPerValidator, signersPerValidator, threshold, chain) - require.NoError(t, err) - validators = append(validators, validator) - allNodes = append(allNodes, validator.Sentries...) - startValidatorsErrGroup.Go(func() error { - return validator.StartHorcruxCluster(ctx, sentriesPerSigner) - }) - } + var wg sync.WaitGroup + wg.Add(totalChains) - require.NoError(t, startValidatorsErrGroup.Wait()) + cosignersStarted := make(chan struct{}, 1) - // assemble and combine gentx to get genesis file, configure peering between sentries, then start the chain - require.NoError(t, Genesis(t, ctx, chain, []*TestNode{}, []*TestNode{}, validators)) + var configWg sync.WaitGroup + configWg.Add(totalChains) - require.NoError(t, allNodes.WaitForHeight(5)) + for i, chainConfig := range chainConfigs { + i := i + chainConfig := chainConfig + preGenesises[i] = func(cw *chainWrapper) func(ibc.ChainConfig) error { + return func(cc ibc.ChainConfig) error { - var blockWaitErrGroup errgroup.Group + firstSentry := cw.chain.Validators[0] + sentries := append(cosmos.ChainNodes{firstSentry}, cw.chain.FullNodes...) - // wait for all validators to sign consecutive blocks - for _, tv := range validators { - validator := tv - blockWaitErrGroup.Go(func() error { - err := validator.WaitForConsecutiveBlocks(30) - if err != nil { - return err + sentriesForCosigner := getSentriesForCosignerConnection(sentries, totalSigners, sentriesPerSigner) + chainConfig.sentries = sentriesForCosigner + + configWg.Done() + + chainConfig.chainID = cw.chain.Config().ChainID + + ed25519Shards, pvPubKey, err := getShardedPrivvalKey(ctx, firstSentry, threshold, uint8(totalSigners)) + if err != nil { + wg.Done() + return err + } + + chainConfig.shards = ed25519Shards + + pubKeys[i] = pvPubKey + + if i == 0 { + configWg.Wait() + for j := 0; j < totalSigners; j++ { + var h horcruxSidecarProcess + cosigner, err := horcruxSidecar(ctx, firstSentry, fmt.Sprintf("cosigner-%d", j+1), client, network) + if err != nil { + wg.Done() + return err + } + + h.cosigner = cosigner + + startArgs := []string{ + "-g", fmt.Sprintf("%s:%s", cosigner.HostName(), grpcPort), + "-o=false", + } + + for _, chainConfig := range chainConfigs { + for _, sentry := range chainConfig.sentries[j] { + startArgs = append(startArgs, "-s", fmt.Sprintf("tcp://%s:1234", sentry.HostName())) + } + } + + proxy, err := horcruxProxySidecar(ctx, firstSentry, fmt.Sprintf("proxy-%d", j+1), client, network, startArgs...) + if err != nil { + wg.Done() + return err + } + + cosignerSidecars[j] = horcruxSidecarProcess{ + cosigner: cosigner, + proxy: proxy, + } + } + } + + if err := enablePrivvalListener(ctx, logger, sentries, client); err != nil { + wg.Done() + return err + } + + wg.Done() + + // wait for all cosigners to be started before continuing to start the chain. + <-cosignersStarted + + return nil } - return validator.EnsureNotSlashed() - }) + } } - // wait for all validators to have consecutive blocks - require.NoError(t, blockWaitErrGroup.Wait()) + go configureAndStartSidecars(ctx, t, eciesShards, cosignerSidecars, threshold, &wg, cosignersStarted, chainConfigs...) + + for i := 0; i < totalChains; i++ { + chainWrappers[i] = &chainWrapper{ + totalValidators: validatorsPerChain, + totalSentries: sentriesPerValidator - 1, + modifyGenesis: modifyGenesisStrictUptime, + preGenesis: preGenesises[i], + } + } + + startChains(ctx, t, logger, client, network, chainWrappers...) + + chains := make([]testutil.ChainHeighter, totalChains) + for i, cw := range chainWrappers { + chains[i] = cw.chain + } + + testutil.WaitForBlocks(ctx, 20, chains...) + + for i, p := range pubKeys { + requireHealthyValidator(t, chainWrappers[i].chain.Validators[0], p.Address()) + } } diff --git a/test/test_node.go b/test/test_node.go deleted file mode 100644 index 6d5cfa54..00000000 --- a/test/test_node.go +++ /dev/null @@ -1,938 +0,0 @@ -package test - -import ( - "bytes" - "context" - "crypto/rand" - "crypto/sha256" - "errors" - "fmt" - "math/big" - "net" - "os" - "os/exec" - "path/filepath" - "reflect" - "runtime" - "strings" - "sync" - "testing" - "time" - - "github.com/avast/retry-go" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/simapp/params" - "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/bech32" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/ory/dockertest" - "github.com/ory/dockertest/docker" - "github.com/strangelove-ventures/horcrux/signer" - tmconfig "github.com/tendermint/tendermint/config" - tmBytes "github.com/tendermint/tendermint/libs/bytes" - "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/privval" - rpcclient "github.com/tendermint/tendermint/rpc/client" - rpchttp "github.com/tendermint/tendermint/rpc/client/http" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - libclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" - "golang.org/x/sync/errgroup" -) - -const ( - valKey = "validator" - blockTime = 3 // seconds -) - -var cosmosNodePorts = map[docker.Port]struct{}{ - "26656/tcp": {}, - "26657/tcp": {}, - "9090/tcp": {}, - "1337/tcp": {}, - "1234/tcp": {}, -} - -func getGoModuleVersion(pkg string) string { - cmd := exec.Command("go", "list", "-m", "-u", "-f", "{{.Version}}", pkg) - out, err := cmd.Output() - if err != nil { - panic(fmt.Sprintf("failed to evaluate Go module version: %v", err)) - } - return strings.TrimSpace(string(out)) -} - -func getHeighlinerChain( - chain, - version, - binary, - bech32Prefix string, - pubKeyAsBech32 bool, - preGenTx func(tn *TestNode) error, -) *ChainType { - return &ChainType{ - Repository: fmt.Sprintf("ghcr.io/strangelove-ventures/heighliner/%s", chain), - Version: version, - Bin: binary, - Bech32Prefix: bech32Prefix, - PubKeyAsBech32: pubKeyAsBech32, - Ports: cosmosNodePorts, - PreGenTx: preGenTx, - } -} - -func getSimdChain() *ChainType { - return getHeighlinerChain("sim", getGoModuleVersion("github.com/cosmos/cosmos-sdk"), "simd", "cosmos", false, nil) -} - -func getSentinelChain(ctx context.Context, version string) *ChainType { - // sets "approve_by" in the genesis.json - // this is required for sentinel, genesis validation fails without it. - sentinelGenesisJSONModification := func(tn *TestNode) error { - genesisJSON := filepath.Join(tn.NodeHome(), "config", "genesis.json") - address, err := tn.Bech32AddressForKey(valKey) - if err != nil { - return err - } - command := []string{"sed", "-i", fmt.Sprintf(`s/"approve_by": ""/"approve_by": "%s"/g`, address), genesisJSON} - _, _, err = tn.Exec(ctx, command) - return err - } - - return getHeighlinerChain("sentinel", version, "sentinelhub", "sent", true, sentinelGenesisJSONModification) -} - -// ChainType represents the type of chain to instantiate -type ChainType struct { - Repository string - Version string - Bin string - - Bech32Prefix string - PubKeyAsBech32 bool // true - gentx uses bech32 consval address. false - gentx uses json pubkey - - Ports map[docker.Port]struct{} - - // some chains need additional steps, such as genesis.json modification, before executing gentx - PreGenTx func(tn *TestNode) error -} - -// TestNode represents a node in the test network that is being created -type TestNode struct { - Home string - Index int - ValidatorIndex int - ChainID string - Chain *ChainType - GenesisCoins string - Validator bool - Pool *dockertest.Pool - networkID string - Client rpcclient.Client - Container *docker.Container - tl TestLogger - ec params.EncodingConfig -} - -type ContainerPort struct { - Name string - Container *docker.Container - Port docker.Port -} - -type Hosts []ContainerPort - -// CliContext creates a new Cosmos SDK client context -func (tn *TestNode) CliContext() client.Context { - return client.Context{ - Client: tn.Client, - ChainID: tn.ChainID, - InterfaceRegistry: tn.ec.InterfaceRegistry, - Input: os.Stdin, - Output: os.Stdout, - OutputFormat: "json", - LegacyAmino: tn.ec.Amino, - } -} - -// MakeTestNodes creates the test node objects required for bootstrapping tests -func MakeTestNodes( - validatorIndex, - count int, - home, - chainID string, - chainType *ChainType, - pool *dockertest.Pool, - networkID string, - tl TestLogger, -) (out TestNodes) { - err := pool.Client.PullImage(docker.PullImageOptions{ - Repository: chainType.Repository, - Tag: chainType.Version, - }, docker.AuthConfiguration{}) - if err != nil { - tl.Logf("Error pulling image: %v", err) - } - for i := 0; i < count; i++ { - tn := &TestNode{Home: home, Index: i, ValidatorIndex: validatorIndex, Chain: chainType, ChainID: chainID, - Pool: pool, networkID: networkID, tl: tl, ec: simapp.MakeTestEncodingConfig()} - tn.MkDir() - out = append(out, tn) - } - return -} - -// Creates indexed validator test nodes -func GetValidators( - startingValidatorIndex, - count, - sentriesPerValidator int, - home, - chainID string, - chain *ChainType, - pool *dockertest.Pool, - networkID string, - t *testing.T, -) (out TestNodes) { - for i := startingValidatorIndex; i < startingValidatorIndex+count; i++ { - out = append(out, MakeTestNodes(i, sentriesPerValidator, home, chainID, chain, pool, networkID, t)...) - } - return -} - -func GetAllNodes(nodes ...TestNodes) (out TestNodes) { - for _, testNodes := range nodes { - out = append(out, testNodes...) - } - return -} - -// NewClient creates and assigns a new Tendermint RPC client to the TestNode -func (tn *TestNode) NewClient(addr string) error { - httpClient, err := libclient.DefaultHTTPClient(addr) - if err != nil { - return err - } - - httpClient.Timeout = 10 * time.Second - rpcClient, err := rpchttp.NewWithClient(addr, "/websocket", httpClient) - if err != nil { - return err - } - - tn.Client = rpcClient - return nil - -} - -func (tn *TestNode) GetHosts() (out Hosts) { - name := tn.Name() - for k := range tn.Chain.Ports { - host := ContainerPort{ - Name: name, - Container: tn.Container, - Port: k, - } - out = append(out, host) - } - return -} - -func (tn TestNodes) GetHosts() (out Hosts) { - for _, n := range tn { - out = append(out, n.GetHosts()...) - } - return -} - -func connectionAttempt(t *testing.T, host ContainerPort) bool { - port := string(host.Port) - hostname := GetHostPort(host.Container, port) - - t.Logf("Attempting to reach {%s} {%s} local hostname: %s", host.Name, port, hostname) - - conn, err := net.DialTimeout("tcp", hostname, time.Duration(1)*time.Second) - - if err != nil { - t.Logf("Error: %s\n", err) - return false - } - - defer conn.Close() - - t.Logf("{%s} is reachable", hostname) - return true -} - -func isReachable(wg *sync.WaitGroup, t *testing.T, host ContainerPort, ch chan<- bool) { - defer wg.Done() - - ch <- connectionAttempt(t, host) -} - -func (hosts Hosts) WaitForAllToStart(t *testing.T, timeout int) error { - if len(hosts) == 0 { - return nil - } -ReachableCheckLoop: - for seconds := 1; seconds <= timeout; seconds++ { - var wg sync.WaitGroup - - results := make(chan bool, len(hosts)) - wg.Add(len(hosts)) - for _, host := range hosts { - go isReachable(&wg, t, host, results) - } - wg.Wait() - - close(results) - - for reachable := range results { - if !reachable { - t.Logf("A host is not reachable") - continue ReachableCheckLoop - } - } - - t.Logf("All hosts are reachable after %d seconds", seconds) - return nil - } - return fmt.Errorf("timed out after %d seconds waiting for hosts", timeout) -} - -// Name is the hostname of the test node container -func (tn *TestNode) Name() string { - return fmt.Sprintf("val-%d-node-%d-%s", tn.ValidatorIndex, tn.Index, tn.tl.Name()) -} - -// Dir is the directory where the test node files are stored -func (tn *TestNode) Dir() string { - return fmt.Sprintf("%s/%s/", tn.Home, tn.Name()) -} - -// MkDir creates the directory for the testnode -func (tn *TestNode) MkDir() { - if err := os.MkdirAll(tn.Dir(), 0755); err != nil { - panic(err) - } -} - -// GentxPath returns the path to the gentx for a node -func (tn *TestNode) GentxPath() (string, error) { - id, err := tn.NodeID() - return filepath.Join(tn.Dir(), "config", "gentx", fmt.Sprintf("gentx-%s.json", id)), err -} - -func (tn *TestNode) GenesisFilePath() string { - return filepath.Join(tn.Dir(), "config", "genesis.json") -} - -func (tn *TestNode) TMConfigPath() string { - return filepath.Join(tn.Dir(), "config", "config.toml") -} - -// Bind returns the home folder bind point for running the node -func (tn *TestNode) Bind() []string { - return []string{fmt.Sprintf("%s:/home/.%s", tn.Dir(), tn.Chain.Bin)} -} - -func (tn *TestNode) NodeHome() string { - return fmt.Sprintf("/home/.%s", tn.Chain.Bin) -} - -// Keybase returns the keyring for a given node -func (tn *TestNode) Keybase() keyring.Keyring { - kr, err := keyring.New("", keyring.BackendTest, tn.Dir(), os.Stdin) - if err != nil { - panic(err) - } - return kr -} - -// SetValidatorConfigAndPeers modifies the config for a validator node to start a chain -func (tn *TestNode) SetValidatorConfigAndPeers(peers string, enablePrivVal bool) { - // Pull default config - cfg := tmconfig.DefaultConfig() - - // change config to include everything needed - stdconfigchanges(cfg, peers, enablePrivVal) - - // overwrite with the new config - tmconfig.WriteConfigFile(tn.TMConfigPath(), cfg) -} - -func (tn *TestNode) SetPrivValListen(peers string) { - cfg := tmconfig.DefaultConfig() - stdconfigchanges(cfg, peers, true) // Reapply the changes made to the config file in SetValidatorConfigAndPeers() - tmconfig.WriteConfigFile(tn.TMConfigPath(), cfg) -} - -func (tn *TestNode) getValSigningInfo(address tmBytes.HexBytes) (*slashingtypes.QuerySigningInfoResponse, error) { - valConsPrefix := fmt.Sprintf("%svalcons", tn.Chain.Bech32Prefix) - bech32ValConsAddress, err := bech32.ConvertAndEncode(valConsPrefix, address) - if err != nil { - return nil, err - } - return slashingtypes.NewQueryClient( - tn.CliContext()).SigningInfo(context.Background(), &slashingtypes.QuerySigningInfoRequest{ - ConsAddress: bech32ValConsAddress, - }) -} - -func (tn *TestNode) GetMostRecentConsecutiveSignedBlocks( - max int64, - address tmBytes.HexBytes, -) (count int64, latestHeight int64, err error) { - var status *ctypes.ResultStatus - status, err = tn.Client.Status(context.Background()) - if err != nil { - return 0, 0, err - } - - latestHeight = status.SyncInfo.LatestBlockHeight - - for i := latestHeight; i > latestHeight-max && i > 0; i-- { - var block *ctypes.ResultBlock - block, err = tn.Client.Block(context.Background(), &i) - if err != nil { - return 0, 0, err - } - found := false - for _, voter := range block.Block.LastCommit.Signatures { - if reflect.DeepEqual(voter.ValidatorAddress, address) { - count++ - found = true - break - } - } - if !found { - return count, latestHeight, nil - } - } - return count, latestHeight, nil -} - -func (tn *TestNode) getMissingBlocks(address tmBytes.HexBytes) (int64, error) { - missedBlocks, err := tn.getValSigningInfo(address) - if err != nil { - return 0, err - } - return missedBlocks.ValSigningInfo.MissedBlocksCounter, nil -} - -func (tn *TestNode) EnsureNotSlashed(address tmBytes.HexBytes) error { - for i := 0; i < 50; i++ { - time.Sleep(1 * time.Second) - slashInfo, err := tn.getValSigningInfo(address) - if err != nil { - return err - } - - if i == 0 { - tn.tl.Logf("{EnsureNotSlashed} val-%d Initial Missed blocks: %d", tn.ValidatorIndex, - slashInfo.ValSigningInfo.MissedBlocksCounter) - continue - } - if i%2 == 0 { - // require.Equal(tn.t, missed, slashInfo.ValSigningInfo.MissedBlocksCounter) - stat, err := tn.Client.Status(context.Background()) - if err != nil { - return err - } - - tn.tl.Logf("{EnsureNotSlashed} val-%d Missed blocks: %d block: %d", tn.ValidatorIndex, - slashInfo.ValSigningInfo.MissedBlocksCounter, stat.SyncInfo.LatestBlockHeight) - } - if slashInfo.ValSigningInfo.Tombstoned { - return errors.New("validator is tombstoned") - } - } - return nil -} - -func min(a, b int64) int64 { - if a < b { - return a - } - return b -} - -// Wait until we have signed n blocks in a row -func (tn *TestNode) WaitForConsecutiveBlocks(blocks int64, address tmBytes.HexBytes) error { - initialMissed, err := tn.getMissingBlocks(address) - if err != nil { - return err - } - tn.tl.Logf("{WaitForConsecutiveBlocks} val-%d Initial Missed blocks: %d", tn.ValidatorIndex, initialMissed) - stat, err := tn.Client.Status(context.Background()) - if err != nil { - return err - } - - startingBlock := stat.SyncInfo.LatestBlockHeight - // timeout after ~1 minute plus block time - timeoutSeconds := blocks*(int64(blockTime)+1) + int64(60) - for i := int64(0); i < timeoutSeconds; i++ { - time.Sleep(1 * time.Second) - - recentSignedBlocksCount, checkingBlock, err := tn.GetMostRecentConsecutiveSignedBlocks(blocks, address) - if err != nil { - continue - } - if recentSignedBlocksCount > 0 { - // we signed a block within window, so restart counter - i = -1 - } - deltaMissed := min(blocks, checkingBlock-1) - recentSignedBlocksCount - deltaBlocks := checkingBlock - startingBlock - - tn.tl.Logf("{WaitForConsecutiveBlocks} val-%d Missed blocks: %d block: %d", - tn.ValidatorIndex, deltaMissed, checkingBlock) - if deltaMissed == 0 && deltaBlocks >= blocks { - tn.tl.Logf("Time (sec) to sign %d consecutive blocks: %d", blocks, i+1) - return nil // done waiting for consecutive signed blocks - } - } - return errors.New("timed out waiting for cluster to recover signing blocks") -} - -func stdconfigchanges(cfg *tmconfig.Config, peers string, enablePrivVal bool) { - // turn down blocktimes to make the chain faster - cfg.Consensus.TimeoutCommit = blockTime * time.Second - cfg.Consensus.TimeoutPropose = blockTime * time.Second - - // Open up rpc address - cfg.RPC.ListenAddress = "tcp://0.0.0.0:26657" - - if enablePrivVal { - cfg.BaseConfig.PrivValidatorListenAddr = "tcp://0.0.0.0:1234" - } - - // Allow for some p2p weirdness - cfg.P2P.AllowDuplicateIP = true - cfg.P2P.AddrBookStrict = false - - // Set log level to info - cfg.BaseConfig.LogLevel = "info" - - // set persistent peer nodes - cfg.P2P.PersistentPeers = peers -} - -// Exec runs a container for a specific job and block until the container exits -// NOTE: on job containers generate random name -func (tn *TestNode) Exec(ctx context.Context, cmd []string) (string, string, error) { - container := RandLowerCaseLetterString(10) - tn.tl.Logf("{%s}[%s] -> '%s'", tn.Name(), container, strings.Join(cmd, " ")) - cont, err := tn.Pool.Client.CreateContainer(docker.CreateContainerOptions{ - Name: container, - Config: &docker.Config{ - User: getDockerUserString(), - Hostname: container, - ExposedPorts: tn.Chain.Ports, - DNS: []string{}, - Image: fmt.Sprintf("%s:%s", tn.Chain.Repository, tn.Chain.Version), - Cmd: cmd, - Labels: map[string]string{"horcrux-test": tn.tl.Name()}, - }, - HostConfig: &docker.HostConfig{ - Binds: tn.Bind(), - PublishAllPorts: true, - AutoRemove: false, - }, - NetworkingConfig: &docker.NetworkingConfig{ - EndpointsConfig: map[string]*docker.EndpointConfig{}, - }, - Context: nil, - }) - if err != nil { - return "", "", err - } - if err := tn.Pool.Client.StartContainer(cont.ID, nil); err != nil { - return "", "", err - } - exitCode, err := tn.Pool.Client.WaitContainerWithContext(cont.ID, ctx) - outputStream := new(bytes.Buffer) - errorStream := new(bytes.Buffer) - _ = tn.Pool.Client.Logs(docker.LogsOptions{ - Context: ctx, - Container: cont.ID, - OutputStream: outputStream, - ErrorStream: errorStream, - Stdout: true, - Stderr: true, - Tail: "100", - Follow: false, - Timestamps: false, - }) - stdout := outputStream.String() - stderr := errorStream.String() - _ = tn.Pool.Client.RemoveContainer(docker.RemoveContainerOptions{ID: cont.ID}) - return stdout, stderr, containerExitError(container, exitCode, stdout, stderr, err) -} - -// InitHomeFolder initializes a home folder for the given node -func (tn *TestNode) InitHomeFolder(ctx context.Context) error { - cmd := []string{tn.Chain.Bin, "init", tn.Name(), - "--chain-id", tn.ChainID, - "--home", tn.NodeHome(), - } - _, _, err := tn.Exec(ctx, cmd) - return err -} - -// CreateKey creates a key in the keyring backend test for the given node -func (tn *TestNode) CreateKey(ctx context.Context, name string) error { - cmd := []string{tn.Chain.Bin, "keys", "add", name, - "--keyring-backend", "test", - "--output", "json", - "--home", tn.NodeHome(), - } - _, _, err := tn.Exec(ctx, cmd) - return err -} - -// AddGenesisAccount adds a genesis account for each key -func (tn *TestNode) AddGenesisAccount(ctx context.Context, address string) error { - cmd := []string{tn.Chain.Bin, "add-genesis-account", address, "1000000000000stake", - "--home", tn.NodeHome(), - } - _, _, err := tn.Exec(ctx, cmd) - return err -} - -// Gentx generates the gentx for a given node -func (tn *TestNode) Gentx(ctx context.Context, name, pubKey string) error { - cmd := []string{tn.Chain.Bin, "gentx", valKey, "100000000000stake", - "--pubkey", pubKey, - "--keyring-backend", "test", - "--home", tn.NodeHome(), - "--chain-id", tn.ChainID, - } - _, _, err := tn.Exec(ctx, cmd) - return err -} - -// CollectGentxs runs collect gentxs on the node's home folders -func (tn *TestNode) CollectGentxs(ctx context.Context) error { - cmd := []string{tn.Chain.Bin, "collect-gentxs", - "--home", tn.NodeHome(), - } - _, _, err := tn.Exec(ctx, cmd) - return err -} - -func (tn *TestNode) Start(ctx context.Context, preStart func()) error { - // Retry loop for running container. - err := retry.Do(func() error { - // forcefully remove existing container, ignoring error - _ = tn.StopAndRemoveContainer(true) - if err := tn.createContainer(); err != nil { - return err - } - if preStart != nil { - preStart() - } - if err := tn.startContainer(ctx); err != nil { - return err - } - - for i := 0; i < 10; i++ { - container, err := tn.Pool.Client.InspectContainer(tn.Container.ID) - if err != nil { - return err - } - if !container.State.Running { - return fmt.Errorf("container is not running") - } - - ctx, cancel := context.WithTimeout(ctx, 1*time.Second) - _, err = tn.Client.Status(ctx) - cancel() - if err == nil { - return nil - } - time.Sleep(1 * time.Second) - } - - return fmt.Errorf("node is running but not responding with status") - }, retry.DelayType(retry.FixedDelay), retry.Attempts(5)) - if err != nil { - return fmt.Errorf("error starting node container after max retries: %w", err) - } - - // Retry loop for in sync with chain - return retry.Do(func() error { - stat, err := tn.Client.Status(ctx) - if err != nil { - return err - } - if stat != nil && stat.SyncInfo.CatchingUp { - return fmt.Errorf("still catching up: height(%d) catching-up(%t)", - stat.SyncInfo.LatestBlockHeight, stat.SyncInfo.CatchingUp) - } - return nil - }, retry.DelayType(retry.BackOffDelay)) -} - -func (tn *TestNode) createContainer() error { - cont, err := tn.Pool.Client.CreateContainer(docker.CreateContainerOptions{ - Name: tn.Name(), - Config: &docker.Config{ - User: getDockerUserString(), - Cmd: []string{tn.Chain.Bin, "start", "--home", tn.NodeHome()}, - Hostname: tn.Name(), - ExposedPorts: tn.Chain.Ports, - DNS: []string{}, - Image: fmt.Sprintf("%s:%s", tn.Chain.Repository, tn.Chain.Version), - Labels: map[string]string{"horcrux-test": tn.tl.Name()}, - }, - HostConfig: &docker.HostConfig{ - Binds: tn.Bind(), - PublishAllPorts: true, - AutoRemove: false, - }, - NetworkingConfig: &docker.NetworkingConfig{ - EndpointsConfig: map[string]*docker.EndpointConfig{ - tn.networkID: {}, - }, - }, - Context: nil, - }) - if err != nil { - return err - } - tn.Container = cont - return nil -} - -// StopAndRemoveContainer stops and removes a TestSigners docker container. -// If force is true, error for stopping container will be ignored and container -// will be forcefully removed. -func (tn *TestNode) StopAndRemoveContainer(force bool) error { - if tn.Container == nil { - return nil - } - if err := tn.Pool.Client.StopContainer(tn.Container.ID, 60); err != nil && !force { - return err - } - return tn.Pool.Client.RemoveContainer(docker.RemoveContainerOptions{ - ID: tn.Container.ID, - Force: force, - }) -} - -func (tn *TestNode) startContainer(ctx context.Context) error { - if err := tn.Pool.Client.StartContainer(tn.Container.ID, nil); err != nil { - return err - } - - c, err := tn.Pool.Client.InspectContainer(tn.Container.ID) - if err != nil { - return err - } - tn.Container = c - - port := GetHostPort(c, "26657/tcp") - tn.tl.Logf("{%s} RPC => %s", tn.Name(), port) - - return tn.NewClient(fmt.Sprintf("tcp://%s", port)) -} - -func (tn *TestNode) Bech32AddressForKey(keyName string) (string, error) { - key, err := tn.GetKey(valKey) - if err != nil { - return "", err - } - bech32Address, err := types.Bech32ifyAddressBytes(tn.Chain.Bech32Prefix, key.GetAddress()) - if err != nil { - return "", err - } - return bech32Address, nil -} - -// InitValidatorFiles creates the node files and signs a genesis transaction -func (tn *TestNode) InitValidatorFiles(ctx context.Context, pubKey string) error { - if err := tn.InitHomeFolder(ctx); err != nil { - return err - } - if err := tn.CreateKey(ctx, valKey); err != nil { - return err - } - bech32Address, err := tn.Bech32AddressForKey(valKey) - if err != nil { - return err - } - if err := tn.AddGenesisAccount(ctx, bech32Address); err != nil { - return err - } - // if override pubkey is not provided, use the one from this TestNode - if pubKey == "" { - bech32Prefix := "" - if tn.Chain.PubKeyAsBech32 { - bech32Prefix = tn.Chain.Bech32Prefix - } - pv, err := tn.GetPrivVal() - if err != nil { - return err - } - pubKey, err = signer.PubKey(bech32Prefix, pv.PubKey) - if err != nil { - return err - } - } - // some chains need additional steps, such as genesis.json modification, before executing gentx - if tn.Chain.PreGenTx != nil { - if err := tn.Chain.PreGenTx(tn); err != nil { - return err - } - } - return tn.Gentx(ctx, valKey, pubKey) -} - -func (tn *TestNode) InitFullNodeFiles(ctx context.Context) error { - return tn.InitHomeFolder(ctx) -} - -func containerExitError(container string, i int, stdout string, stderr string, err error) error { - if err != nil { - return fmt.Errorf("%v\n%s\n%s", err, stdout, stderr) - } - if i != 0 { - return fmt.Errorf("container [%s] returned non-zero error code: %d\nstdout:\n%s\nstderr:\n%s", - container, i, stdout, stderr) - } - return nil -} - -// NodeID returns the node of a given node -func (tn *TestNode) NodeID() (string, error) { - nodeKey, err := p2p.LoadNodeKey(filepath.Join(tn.Dir(), "config", "node_key.json")) - if err != nil { - return "", err - } - return string(nodeKey.ID()), nil -} - -// GetKey gets a key, waiting until it is available -func (tn *TestNode) GetKey(name string) (info keyring.Info, err error) { - return info, retry.Do(func() (err error) { - info, err = tn.Keybase().Key(name) - return err - }) -} - -// RandLowerCaseLetterString returns a lowercase letter string of given length -func RandLowerCaseLetterString(length int) string { - chars := []rune("abcdefghijklmnopqrstuvwxyz") - var b strings.Builder - for i := 0; i < length; i++ { - i, _ := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) - b.WriteRune(chars[i.Int64()]) - } - return b.String() -} - -// TestNodes is a collection of TestNode -type TestNodes []*TestNode - -// PeerString returns the string for connecting the nodes passed in -func (tn TestNodes) PeerString() string { - bldr := new(strings.Builder) - for _, n := range tn { - id, err := n.NodeID() - if err != nil { - return bldr.String() - } - ps := fmt.Sprintf("%s@%s:26656,", id, n.Name()) - tn[0].tl.Logf("{%s} peering (%s)", n.Name(), strings.TrimSuffix(ps, ",")) - bldr.WriteString(ps) - } - return strings.TrimSuffix(bldr.String(), ",") -} - -// Peers returns the peer nodes for a given node if it is included in a set of nodes -func (tn TestNodes) Peers(node *TestNode) (out TestNodes) { - for _, n := range tn { - if n.Index != node.Index { - out = append(out, n) - } - } - return -} - -func (tn TestNodes) ListenAddrs() string { - out := []string{} - for _, n := range tn { - out = append(out, fmt.Sprintf("%s:%s", n.Name(), "1234")) - } - return strings.Join(out, ",") -} - -// LogGenesisHashes logs the genesis hashes for the various nodes -func (tn TestNodes) LogGenesisHashes() error { - for _, n := range tn { - gen, err := os.ReadFile(n.GenesisFilePath()) - if err != nil { - return err - } - tn[0].tl.Log(fmt.Sprintf("{%s} genesis hash %x", n.Name(), sha256.Sum256(gen))) - } - return nil -} - -func (tn TestNodes) WaitForHeight(height int64) error { - var eg errgroup.Group - tn[0].tl.Logf("Waiting For Nodes To Reach Block Height %d...", height) - for _, n := range tn { - n := n - eg.Go(func() error { - return retry.Do(func() error { - stat, err := n.Client.Status(context.Background()) - if err != nil { - return err - } - - if stat.SyncInfo.CatchingUp || stat.SyncInfo.LatestBlockHeight < height { - return fmt.Errorf("node still under block %d: %d", height, stat.SyncInfo.LatestBlockHeight) - } - n.tl.Logf("{%s} => reached block %d\n", n.Name(), height) - return nil - // TODO: setup backup delay here - }, retry.DelayType(retry.BackOffDelay), retry.Attempts(15)) - }) - } - return eg.Wait() -} - -func (tn *TestNode) GetPrivVal() (privval.FilePVKey, error) { - return signer.ReadPrivValidatorFile(tn.privValKeyPath()) -} - -func (tn *TestNode) privValKeyPath() string { - return filepath.Join(tn.Dir(), "config", "priv_validator_key.json") -} - -func (tn *TestNode) privValStatePath() string { - return filepath.Join(tn.Dir(), "config", "priv_validator_state.json") -} - -func (tn *TestNode) GenNewPrivVal() { - _ = os.Remove(tn.privValKeyPath()) - _ = os.Remove(tn.privValStatePath()) - newFilePV := privval.GenFilePV(tn.privValKeyPath(), tn.privValStatePath()) - newFilePV.Save() -} - -func getDockerUserString() string { - uid := os.Getuid() - var usr string - userOS := runtime.GOOS - if userOS == "darwin" { - usr = "" - } else { - usr = fmt.Sprintf("%d:%d", uid, uid) - } - return usr -} diff --git a/test/test_setup.go b/test/test_setup.go deleted file mode 100644 index 96c7a161..00000000 --- a/test/test_setup.go +++ /dev/null @@ -1,278 +0,0 @@ -package test - -import ( - "bytes" - "context" - "fmt" - "net" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/cosmos/cosmos-sdk/types" - "github.com/ory/dockertest" - "github.com/ory/dockertest/docker" - "github.com/strangelove-ventures/horcrux/signer" - "github.com/stretchr/testify/require" - "golang.org/x/sync/errgroup" -) - -type TestLogger interface { - Name() string - Log(...interface{}) - Logf(string, ...interface{}) -} - -func SetupTestRun(t *testing.T) (context.Context, string, *dockertest.Pool, string) { - home := t.TempDir() - - pool, err := dockertest.NewPool("") - require.NoError(t, err) - - // set the test cleanup function - t.Cleanup(Cleanup(pool, t, home)) - - // run cleanup to cleanup stale resources from any killed tests - Cleanup(pool, t, home)() - - network, err := CreateTestNetwork(pool, fmt.Sprintf("horcrux-%s", RandLowerCaseLetterString(8)), t) - require.NoError(t, err) - - // build the horcrux image - require.NoError(t, BuildTestSignerImage(pool)) - - return context.Background(), home, pool, network.ID -} - -// assemble gentx, build genesis file, configure peering, and start chain -func Genesis( - tl TestLogger, - ctx context.Context, - chain *ChainType, - nonHorcruxValidators, - fullnodes []*TestNode, - horcruxValidators []*TestValidator, -) error { - var eg errgroup.Group - - // sign gentx for each validator - for _, v := range nonHorcruxValidators { - v := v - // passing empty pubkey to use the one from the validator after it is initialized - eg.Go(func() error { return v.InitValidatorFiles(ctx, "") }) - } - - for _, v := range horcruxValidators { - v := v - - bech32Prefix := "" - if chain.PubKeyAsBech32 { - bech32Prefix = chain.Bech32Prefix - } - - pubKey, err := signer.PubKey(bech32Prefix, v.PubKey) - if err != nil { - return err - } - - // using the first sentry for each horcrux validator as the keyring for the account key (not consensus key) - // to sign gentx - eg.Go(func() error { - return v.Sentries[0].InitValidatorFiles(ctx, pubKey) - }) - sentries := v.Sentries[1:] - for _, sentry := range sentries { - s := sentry - eg.Go(func() error { return s.InitFullNodeFiles(ctx) }) - } - } - - // just initialize folder for any full nodes - for _, n := range fullnodes { - n := n - eg.Go(func() error { return n.InitFullNodeFiles(ctx) }) - } - - // wait for this to finish - if err := eg.Wait(); err != nil { - return err - } - - var validators TestNodes - var nodes TestNodes - - validators = append(validators, nonHorcruxValidators...) - nodes = append(nodes, nonHorcruxValidators...) - - for _, horcruxValidator := range horcruxValidators { - if len(horcruxValidator.Sentries) > 0 { - // for test purposes, account key (not consensus key) will come from first sentry - validators = append(validators, horcruxValidator.Sentries[0]) - } - nodes = append(nodes, horcruxValidator.Sentries...) - } - - nodes = append(nodes, fullnodes...) - - // for the validators we need to collect the gentxs and the accounts - // to a single node's genesis file. We will use the first validator - validatorNodeToUseForGenTx := validators[0] - - for i := 1; i < len(validators); i++ { - validatorN := validators[i] - n0key, err := validatorN.GetKey(valKey) - if err != nil { - return err - } - - bech32Address, err := types.Bech32ifyAddressBytes(chain.Bech32Prefix, n0key.GetAddress()) - if err != nil { - return err - } - if err := validatorNodeToUseForGenTx.AddGenesisAccount(ctx, bech32Address); err != nil { - return err - } - nNid, err := validatorN.NodeID() - if err != nil { - return err - } - oldPath := filepath.Join(validatorN.Dir(), "config", "gentx", fmt.Sprintf("gentx-%s.json", nNid)) - newPath := filepath.Join(validatorNodeToUseForGenTx.Dir(), "config", "gentx", fmt.Sprintf("gentx-%s.json", nNid)) - if err := os.Rename(oldPath, newPath); err != nil { - return err - } - } - if err := eg.Wait(); err != nil { - return err - } - if err := validatorNodeToUseForGenTx.CollectGentxs(ctx); err != nil { - return err - } - - genbz, err := os.ReadFile(validatorNodeToUseForGenTx.GenesisFilePath()) - if err != nil { - return err - } - - for i := 1; i < len(nodes); i++ { - if err := os.WriteFile(nodes[i].GenesisFilePath(), genbz, 0644); err != nil { //nolint - return err - } - } - - if err := nodes.LogGenesisHashes(); err != nil { - return err - } - - peers := nodes.PeerString() - - // start horcrux sentries. privval listener enabled - for _, v := range horcruxValidators { - for _, sentry := range v.Sentries { - s := sentry - tl.Logf("{%s} => starting container...", s.Name()) - eg.Go(func() error { - return s.Start(ctx, func() { - s.SetValidatorConfigAndPeers(peers, true) - }) - }) - } - } - - // start non-horcrux validators. privval listener disabled - for _, v := range nonHorcruxValidators { - v := v - tl.Logf("{%s} => starting container...", v.Name()) - eg.Go(func() error { - return v.Start(ctx, func() { - v.SetValidatorConfigAndPeers(peers, false) - }) - }) - } - - // start full nodes. privval listener disabled - for _, n := range fullnodes { - n := n - tl.Logf("{%s} => starting container...", n.Name()) - eg.Go(func() error { - return n.Start(ctx, func() { - n.SetValidatorConfigAndPeers(peers, false) - }) - }) - } - - return eg.Wait() -} - -// GetHostPort returns a resource's published port with an address. -func GetHostPort(cont *docker.Container, portID string) string { - if cont == nil || cont.NetworkSettings == nil { - return "" - } - - m, ok := cont.NetworkSettings.Ports[docker.Port(portID)] - if !ok || len(m) == 0 { - return "" - } - - ip := m[0].HostIP - if ip == "0.0.0.0" { - ip = "localhost" - } - return net.JoinHostPort(ip, m[0].HostPort) -} - -func CreateTestNetwork(pool *dockertest.Pool, name string, t *testing.T) (*docker.Network, error) { - return pool.Client.CreateNetwork(docker.CreateNetworkOptions{ - Name: name, - Options: map[string]interface{}{}, - Labels: map[string]string{"horcrux-test": t.Name()}, - CheckDuplicate: true, - Internal: false, - EnableIPv6: false, - Context: context.Background(), - }) -} - -// Cleanup will clean up Docker containers, networks, and the other various config files generated in testing -func Cleanup(pool *dockertest.Pool, t *testing.T, testDir string) func() { - return func() { - cont, _ := pool.Client.ListContainers(docker.ListContainersOptions{All: true}) - ctx := context.Background() - for _, c := range cont { - for k, v := range c.Labels { - if k == "horcrux-test" && v == t.Name() { - _ = pool.Client.StopContainer(c.ID, 10) - _, _ = pool.Client.WaitContainerWithContext(c.ID, ctx) - if t.Failed() { - stdout := new(bytes.Buffer) - stderr := new(bytes.Buffer) - _ = pool.Client.Logs(docker.LogsOptions{ - Context: ctx, - Container: c.ID, - OutputStream: stdout, - ErrorStream: stderr, - Stdout: true, - Stderr: true, - Tail: "100", - Follow: false, - Timestamps: false, - }) - fmt.Printf("{%s}\nstdout:\n%s\nstderr:\n%s\n", strings.Join(c.Names, ","), stdout, stderr) - } - _ = pool.Client.RemoveContainer(docker.RemoveContainerOptions{ID: c.ID}) - } - } - } - nets, _ := pool.Client.ListNetworks() - for _, n := range nets { - for k, v := range n.Labels { - if k == "horcrux-test" && v == t.Name() { - _ = pool.Client.RemoveNetwork(n.ID) - } - } - } - _ = os.RemoveAll(testDir) - } -} diff --git a/test/test_signer.go b/test/test_signer.go deleted file mode 100644 index bd77b8ed..00000000 --- a/test/test_signer.go +++ /dev/null @@ -1,553 +0,0 @@ -package test - -import ( - "bytes" - "context" - "fmt" - "io" - "os" - "path" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/ory/dockertest" - "github.com/ory/dockertest/docker" - "github.com/strangelove-ventures/horcrux/signer" - "github.com/strangelove-ventures/horcrux/signer/proto" - tmjson "github.com/tendermint/tendermint/libs/json" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -const ( - signerPort = "2222" - signerImage = "horcrux-test" - binary = "horcrux" - signerPortDocker = signerPort + "/tcp" -) - -// TestSigner represents a remote signer instance -type TestSigner struct { - Home string - Index int - ValidatorIndex int - Pool *dockertest.Pool - networkID string - Container *docker.Container - Key signer.CosignerKey - tl TestLogger -} - -type TestSigners []*TestSigner - -// BuildTestSignerImage builds a Docker image for horcrux from current Dockerfile -func BuildTestSignerImage(pool *dockertest.Pool) error { - dir, err := os.Getwd() - if err != nil { - return err - } - dockerfile := "docker/horcrux/native.Dockerfile" - return pool.Client.BuildImage(docker.BuildImageOptions{ - Name: signerImage, - Dockerfile: dockerfile, - OutputStream: io.Discard, - SuppressOutput: false, - Pull: false, - RmTmpContainer: true, - ForceRmTmpContainer: false, - Auth: docker.AuthConfiguration{}, - AuthConfigs: docker.AuthConfigurations{}, - ContextDir: path.Dir(dir), - }) -} - -// StartSingleSignerContainers will generate the necessary config files for the signer node, copy over the validators -// priv_validator_key.json file, and start the signer -func StartSingleSignerContainers( - testSigners TestSigners, - validator *TestNode, - sentryNodes TestNodes, -) error { - eg := new(errgroup.Group) - ctx := context.Background() - - // init config files/directory for signer node - for _, s := range testSigners { - s := s - eg.Go(func() error { return s.InitSingleSignerConfig(ctx, sentryNodes) }) - } - if err := eg.Wait(); err != nil { - return err - } - - // Get Validators Priv Val key & copy it over to the signers home directory - pv, err := validator.GetPrivVal() - if err != nil { - return err - } - - pvFile, err := tmjson.Marshal(pv) - if err != nil { - return err - } - - err = os.WriteFile(filepath.Join(testSigners[0].Dir(), "priv_validator_key.json"), pvFile, 0600) - if err != nil { - return err - } - - // create containers & start signer nodes - for _, s := range testSigners { - s := s - eg.Go(func() error { - return s.CreateSingleSignerContainer() - }) - } - if err := eg.Wait(); err != nil { - return err - } - - for _, s := range testSigners { - s := s - s.tl.Logf("{%s} => starting container...", s.Name()) - eg.Go(func() error { - return s.StartContainer() - }) - } - return eg.Wait() -} - -// StartCosignerContainers will generate the necessary config files for the nodes in the signer cluster, -// shard the validator's priv_validator_key.json key, write the sharded key shares to the appropriate -// signer nodes directory and start the signer cluster. -// NOTE: Zero or negative values for sentriesPerSigner configures the nodes in the signer cluster to connect to the -// same sentry node. -func StartCosignerContainers( - signers TestSigners, - sentries TestNodes, - threshold, total, - sentriesPerSigner int, -) error { - eg := new(errgroup.Group) - ctx := context.Background() - - // init config files, for each node in the signer cluster, with the appropriate number of sentries in front of the node - switch { - // Each node in the signer cluster is connected to a unique sentry node - case sentriesPerSigner == 1: - singleSentryIndex := 0 - for i, s := range signers { - s := s - - var peers TestNodes - - if len(sentries) == 1 || len(signers) > len(sentries) { - peers = sentries[singleSentryIndex : singleSentryIndex+1] - singleSentryIndex++ - if singleSentryIndex >= len(sentries) { - singleSentryIndex = 0 - } - } else { - peers = sentries[i : i+1] - } - - eg.Go(func() error { return s.InitCosignerConfig(ctx, peers, signers, s.Index, threshold) }) - } - - // Each node in the signer cluster is connected to the number of sentry nodes specified by sentriesPerSigner - case sentriesPerSigner > 1: - sentriesIndex := 0 - for _, s := range signers { - s := s - var peers TestNodes - // if we are indexing sentries up to the end of the slice - switch { - case sentriesIndex+sentriesPerSigner == len(sentries): - peers = sentries[sentriesIndex:] - sentriesIndex += 1 - - // if there aren't enough sentries left in the slice use the sentries left in slice, - // calculate how many more are needed, then start back at the beginning of - // the slice to grab the rest. After, check if index into slice of sentries needs reset - case sentriesIndex+sentriesPerSigner > len(sentries): - remainingSentries := sentries[sentriesIndex:] - peers = append(peers, remainingSentries...) - - neededSentries := sentriesPerSigner - len(remainingSentries) - peers = append(peers, sentries[0:neededSentries]...) - - sentriesIndex += 1 - if sentriesIndex >= len(sentries) { - sentriesIndex = 0 - } - default: - peers = sentries[sentriesIndex : sentriesIndex+sentriesPerSigner] - sentriesIndex += 1 - } - - eg.Go(func() error { return s.InitCosignerConfig(ctx, peers, signers, s.Index, threshold) }) - } - - // All nodes in the signer cluster are connected to all sentry nodes - default: - for _, s := range signers { - s := s - eg.Go(func() error { return s.InitCosignerConfig(ctx, sentries, signers, s.Index, threshold) }) - } - } - err := eg.Wait() - if err != nil { - return err - } - - // create containers & start signer nodes - for _, s := range signers { - s := s - eg.Go(func() error { - return s.CreateCosignerContainer() - }) - } - err = eg.Wait() - if err != nil { - return err - } - - for _, s := range signers { - s := s - s.tl.Logf("{%s} => starting container...", s.Name()) - eg.Go(func() error { - return s.StartContainer() - }) - } - return eg.Wait() -} - -// PeerString returns a string representing a TestSigner's connectable private peers -// skip is the calling TestSigner's index -func (ts TestSigners) PeerString(skip int) string { - var out strings.Builder - for _, s := range ts { - // Skip over the calling signer so its peer list does not include itself - if s.Index != skip { - out.WriteString(fmt.Sprintf("tcp://%s:%s|%d,", s.Name(), signerPort, s.Index)) - } - } - return strings.TrimSuffix(out.String(), ",") -} - -// MakeTestSigners creates the TestSigner objects required for bootstrapping tests -func MakeTestSigners( - validatorIndex int, - count int, - home string, - pool *dockertest.Pool, - networkID string, - tl TestLogger, -) (out TestSigners) { - for i := 0; i < count; i++ { - ts := &TestSigner{ - Home: home, - Index: i + 1, // +1 is to ensure all Cosigner IDs end up being >0 as required in cosigner.go - ValidatorIndex: validatorIndex, - Pool: pool, - networkID: networkID, - Container: nil, - Key: signer.CosignerKey{}, - tl: tl, - } - out = append(out, ts) - } - return -} - -func (ts *TestSigner) GetHosts() (out Hosts) { - host := ContainerPort{ - Name: ts.Name(), - Container: ts.Container, - Port: docker.Port(signerPortDocker), - } - out = append(out, host) - return -} - -func (ts TestSigners) GetHosts() (out Hosts) { - for _, s := range ts { - out = append(out, s.GetHosts()...) - } - return -} - -// MkDir creates the directory for the TestSigner files -func (ts *TestSigner) MkDir() { - if err := os.MkdirAll(ts.Dir(), 0755); err != nil { - panic(err) - } -} - -// Dir is the directory where the TestSigner files are stored -func (ts *TestSigner) Dir() string { - return filepath.Join(ts.Home, ts.Name()) -} - -// GetConfigFile returns the direct path to the signers config file as a string -func (ts *TestSigner) GetConfigFile() string { - return filepath.Join(ts.Dir(), "config.yaml") -} - -// Name is the hostname of the TestSigner container -func (ts *TestSigner) Name() string { - return fmt.Sprintf("val-%d-sgn-%d-%s", ts.ValidatorIndex, ts.Index, ts.tl.Name()) -} - -// GRPCAddress returns the TCP address of the GRPC server, -// reachable from within the docker network. -func (ts *TestSigner) GRPCAddress() string { - return fmt.Sprintf("tcp://%s:%s", ts.Name(), signerPort) -} - -// ExecHorcruxCmd executes a CLI subcommand for the horcrux binary for the specific cosigner. -// The config home directory will be appended as a flag. -func (ts *TestSigner) ExecHorcruxCmd(ctx context.Context, cmd ...string) error { - cmd = ts.horcruxCmd(cmd) - container := RandLowerCaseLetterString(10) - ts.tl.Logf("{%s}[%s] -> '%s'", ts.Name(), container, strings.Join(cmd, " ")) - cont, err := ts.Pool.Client.CreateContainer(docker.CreateContainerOptions{ - Name: container, - Config: &docker.Config{ - User: getDockerUserString(), - Hostname: container, - ExposedPorts: map[docker.Port]struct{}{ - docker.Port(signerPortDocker): {}, - }, - Image: signerImage, - Cmd: cmd, - Labels: map[string]string{"horcrux-test": ts.tl.Name()}, - }, - HostConfig: &docker.HostConfig{ - PublishAllPorts: true, - AutoRemove: false, - Mounts: []docker.HostMount{ - { - Type: "bind", - Source: ts.Home, - Target: ts.Home, - ReadOnly: false, - BindOptions: nil, - }, - }, - }, - NetworkingConfig: &docker.NetworkingConfig{ - EndpointsConfig: map[string]*docker.EndpointConfig{ - ts.networkID: {}, - }, - }, - Context: nil, - }) - if err != nil { - return err - } - if err := ts.Pool.Client.StartContainer(cont.ID, nil); err != nil { - return err - } - exitCode, err := ts.Pool.Client.WaitContainerWithContext(cont.ID, ctx) - outputStream := new(bytes.Buffer) - errorStream := new(bytes.Buffer) - _ = ts.Pool.Client.Logs(docker.LogsOptions{ - Context: ctx, - Container: cont.ID, - OutputStream: outputStream, - ErrorStream: errorStream, - Stdout: true, - Stderr: true, - Tail: "100", - Follow: false, - Timestamps: false, - }) - _ = ts.Pool.Client.RemoveContainer(docker.RemoveContainerOptions{ID: cont.ID}) - stdout := outputStream.String() - stderr := errorStream.String() - return containerExitError(container, exitCode, stdout, stderr, err) -} - -// InitSingleSignerConfig creates and runs a container to init a single signers config files -// blocks until the container exits -func (ts *TestSigner) InitSingleSignerConfig(ctx context.Context, listenNodes TestNodes) error { - return ts.ExecHorcruxCmd(ctx, - "config", "init", - listenNodes[0].ChainID, listenNodes.ListenAddrs()) -} - -// InitCosignerConfig creates and runs a container to init a signer nodes config files -// blocks until the container exits -func (ts *TestSigner) InitCosignerConfig( - ctx context.Context, listenNodes TestNodes, peers TestSigners, skip, threshold int) error { - return ts.ExecHorcruxCmd(ctx, - "config", "init", - listenNodes[0].ChainID, listenNodes.ListenAddrs(), - "--cosigner", - fmt.Sprintf("--peers=%s", peers.PeerString(skip)), - fmt.Sprintf("--threshold=%d", threshold), - fmt.Sprintf("--listen=%s", ts.GRPCAddress()), - ) -} - -// StartContainer starts a TestSigners container and assigns the new running container to replace the old one -func (ts *TestSigner) StartContainer() error { - if err := ts.Pool.Client.StartContainer(ts.Container.ID, nil); err != nil { - return err - } - - c, err := ts.Pool.Client.InspectContainer(ts.Container.ID) - if err != nil { - return err - } - ts.Container = c - - return nil -} - -// StopAndRemoveContainer stops and removes a TestSigners docker container. -// If force is true, error for stopping container will be ignored and container -// will be forcefully removed. -func (ts *TestSigner) StopAndRemoveContainer(force bool) error { - if err := ts.Pool.Client.StopContainer(ts.Container.ID, 60); err != nil && !force { - return err - } - return ts.Pool.Client.RemoveContainer(docker.RemoveContainerOptions{ - ID: ts.Container.ID, - Force: force, - }) -} - -func (ts *TestSigner) PauseContainer() error { - return ts.Pool.Client.PauseContainer(ts.Container.ID) -} - -func (ts *TestSigner) UnpauseContainer() error { - return ts.Pool.Client.UnpauseContainer(ts.Container.ID) -} - -// CreateSingleSignerContainer creates a docker container to run a single signer -func (ts *TestSigner) CreateSingleSignerContainer() error { - cont, err := ts.Pool.Client.CreateContainer(docker.CreateContainerOptions{ - Name: ts.Name(), - Config: &docker.Config{ - User: getDockerUserString(), - Cmd: []string{binary, "signer", "start", fmt.Sprintf("--home=%s", ts.Dir())}, - Hostname: ts.Name(), - ExposedPorts: map[docker.Port]struct{}{ - docker.Port(signerPortDocker): {}, - }, - DNS: []string{}, - Image: signerImage, - Labels: map[string]string{"horcrux-test": ts.tl.Name()}, - }, - HostConfig: &docker.HostConfig{ - PublishAllPorts: true, - AutoRemove: false, - Mounts: []docker.HostMount{ - { - Type: "bind", - Source: ts.Dir(), - Target: ts.Dir(), - ReadOnly: false, - BindOptions: nil, - }, - }, - }, - NetworkingConfig: &docker.NetworkingConfig{ - EndpointsConfig: map[string]*docker.EndpointConfig{ - ts.networkID: {}, - }, - }, - Context: nil, - }) - if err != nil { - return err - } - ts.Container = cont - return nil -} - -// CreateCosignerContainer creates a docker container to run a mpc validator node -func (ts *TestSigner) CreateCosignerContainer() error { - cont, err := ts.Pool.Client.CreateContainer(docker.CreateContainerOptions{ - Name: ts.Name(), - Config: &docker.Config{ - User: getDockerUserString(), - Cmd: []string{binary, "cosigner", "start", fmt.Sprintf("--home=%s", ts.Dir())}, - Hostname: ts.Name(), - ExposedPorts: map[docker.Port]struct{}{ - docker.Port(signerPortDocker): {}, - }, - DNS: []string{}, - Image: signerImage, - Labels: map[string]string{"horcrux-test": ts.tl.Name()}, - }, - HostConfig: &docker.HostConfig{ - PublishAllPorts: true, - AutoRemove: false, - Mounts: []docker.HostMount{ - { - Type: "bind", - Source: ts.Dir(), - Target: ts.Dir(), - ReadOnly: false, - BindOptions: nil, - }, - }, - }, - NetworkingConfig: &docker.NetworkingConfig{ - EndpointsConfig: map[string]*docker.EndpointConfig{ - ts.networkID: {}, - }, - }, - Context: nil, - }) - if err != nil { - return err - } - ts.Container = cont - return nil -} - -// TransferLeadership elects a new raft leader. -func (ts *TestSigner) TransferLeadership(ctx context.Context, newLeaderID int) error { - return ts.ExecHorcruxCmd(ctx, - "elect", strconv.FormatInt(int64(newLeaderID), 10), - ) -} - -// GetLeader returns the current raft leader. -func (ts *TestSigner) GetLeader(ctx context.Context) (string, error) { - grpcAddress := GetHostPort(ts.Container, signerPortDocker) - conn, err := grpc.Dial(grpcAddress, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), - ) - if err != nil { - return "", fmt.Errorf("dialing failed: %w", err) - } - defer conn.Close() - - ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second) - defer cancelFunc() - - grpcClient := proto.NewCosignerGRPCClient(conn) - - res, err := grpcClient.GetLeader(ctx, &proto.CosignerGRPCGetLeaderRequest{}) - if err != nil { - return "", err - } - return res.GetLeader(), nil -} - -func (ts *TestSigner) horcruxCmd(cmd []string) (out []string) { - out = append(out, binary) - out = append(out, cmd...) - out = append(out, fmt.Sprintf("--home=%s", ts.Dir())) - return out -} diff --git a/test/test_validator.go b/test/test_validator.go deleted file mode 100644 index d3015919..00000000 --- a/test/test_validator.go +++ /dev/null @@ -1,136 +0,0 @@ -package test - -import ( - "context" - "fmt" - "os" - "path/filepath" - - "github.com/ory/dockertest" - "github.com/strangelove-ventures/horcrux/signer" - crypto "github.com/tendermint/tendermint/crypto" - ed25519 "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/privval" -) - -type TestValidator struct { - Index int - Sentries TestNodes - Signers TestSigners - tl TestLogger - Home string - PubKey crypto.PubKey - PrivKeyShares []signer.CosignerKey - Threshold int -} - -func NewHorcruxValidator( - tl TestLogger, - pool *dockertest.Pool, - networkID string, - home string, - chainID string, - index int, - numSentries int, - numSigners int, - threshold int, - chainType *ChainType, -) (*TestValidator, error) { - testValidator := &TestValidator{ - Index: index, - Sentries: MakeTestNodes(index, numSentries, home, chainID, chainType, pool, networkID, tl), - Signers: MakeTestSigners(index, numSigners, home, pool, networkID, tl), - tl: tl, - Home: home, - Threshold: threshold, - } - if err := testValidator.genPrivKeyAndShares(); err != nil { - return nil, err - } - return testValidator, nil -} - -func NewHorcruxValidatorWithPrivValKey( - tl TestLogger, - pool *dockertest.Pool, - networkID string, - home string, - chainID string, - index int, - numSentries int, - numSigners int, - threshold int, - chainType *ChainType, - privValKey privval.FilePVKey, -) (*TestValidator, error) { - testValidator := &TestValidator{ - Index: index, - Sentries: MakeTestNodes(index, numSentries, home, chainID, chainType, pool, networkID, tl), - Signers: MakeTestSigners(index, numSigners, home, pool, networkID, tl), - tl: tl, - Home: home, - Threshold: threshold, - } - if err := testValidator.generateShares(privValKey); err != nil { - return nil, err - } - return testValidator, nil -} - -// Name is the name of the test validator -func (tv *TestValidator) Name() string { - return fmt.Sprintf("validator-%d-%s", tv.Index, tv.tl.Name()) -} - -// Dir is the directory where the test validator files are stored -func (tv *TestValidator) Dir() string { - return filepath.Join(tv.Home, tv.Name()) -} - -// Generate Ed25519 Private Key -func (tv *TestValidator) genPrivKeyAndShares() error { - privKey := ed25519.GenPrivKey() - pubKey := privKey.PubKey() - filePVKey := privval.FilePVKey{ - Address: pubKey.Address(), - PubKey: pubKey, - PrivKey: privKey, - } - return tv.generateShares(filePVKey) -} - -func (tv *TestValidator) generateShares(filePVKey privval.FilePVKey) error { - tv.PubKey = filePVKey.PubKey - shares, err := signer.CreateCosignerShares(filePVKey, int64(tv.Threshold), int64(len(tv.Signers))) - if err != nil { - return err - } - tv.PrivKeyShares = shares - for i, s := range tv.Signers { - tv.tl.Logf("{%s} -> Writing Key Share To File... ", s.Name()) - if err := os.MkdirAll(s.Dir(), 0700); err != nil { - return err - } - privateFilename := filepath.Join(s.Dir(), "share.json") - if err := signer.WriteCosignerShareFile(shares[i], privateFilename); err != nil { - return err - } - } - return nil -} - -func (tv *TestValidator) StartHorcruxCluster( - ctx context.Context, - sentriesPerSigner int, -) error { - return StartCosignerContainers(tv.Signers, tv.Sentries, - tv.Threshold, len(tv.Signers), sentriesPerSigner) -} - -func (tv *TestValidator) WaitForConsecutiveBlocks(blocks int64) error { - return tv.Sentries[0].WaitForConsecutiveBlocks(blocks, tv.PubKey.Address()) -} - -func (tv *TestValidator) EnsureNotSlashed() error { - return tv.Sentries[0].EnsureNotSlashed(tv.PubKey.Address()) -} diff --git a/test/validator.go b/test/validator.go new file mode 100644 index 00000000..66b1e01a --- /dev/null +++ b/test/validator.go @@ -0,0 +1,307 @@ +package test + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "testing" + "time" + + cometbytes "github.com/cometbft/cometbft/libs/bytes" + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + "github.com/cosmos/cosmos-sdk/types/bech32" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/docker/docker/client" + "github.com/strangelove-ventures/horcrux/v3/signer/proto" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + testChain = "gaia" // ghcr.io/strangelove-ventures/heighliner/gaia + testChainVersion = "v10.0.2" + + signerPort = "2222" + signerPortDocker = signerPort + "/tcp" + + grpcPort = "5555" + grpcPortDocker = grpcPort + "/tcp" + + debugPort = "8453" + debugPortDocker = debugPort + "/tcp" + + signerImage = "horcrux-test" + binary = "horcrux" + signerImageUidGid = "2345:2345" + signerImageHomeDir = "/home/horcrux" + + horcruxProxyRegistry = "ghcr.io/strangelove-ventures/horcrux-proxy" + horcruxProxyTag = "andrew-vote_extensions" +) + +// chainWrapper holds the initial configuration for a chain to start from genesis. +type chainWrapper struct { + chain *cosmos.CosmosChain + totalValidators int // total number of validators on chain at genesis + totalSentries int // number of additional sentry nodes + modifyGenesis func(cc ibc.ChainConfig, b []byte) ([]byte, error) + preGenesis func(*chainWrapper) func(ibc.ChainConfig) error +} + +// startChains starts the given chains locally within docker composed of containers. +func startChains( + ctx context.Context, + t *testing.T, + logger *zap.Logger, + client *client.Client, + network string, + chains ...*chainWrapper, +) { + err := BuildHorcruxImage(ctx, client) + require.NoError(t, err) + + cs := make([]*interchaintest.ChainSpec, len(chains)) + for i, c := range chains { + var preGenesis func(ibc.ChainConfig) error + if c.preGenesis != nil { + preGenesis = c.preGenesis(c) + } + cs[i] = &interchaintest.ChainSpec{ + Name: testChain, + Version: testChainVersion, + NumValidators: &c.totalValidators, + NumFullNodes: &c.totalSentries, + ChainConfig: ibc.ChainConfig{ + ModifyGenesis: c.modifyGenesis, + PreGenesis: preGenesis, + ConfigFileOverrides: map[string]any{ + "config/config.toml": testutil.Toml{ + "consensus": testutil.Toml{ + "timeout_commit": "1s", + "timeout_propose": "1s", + }, + }, + }, + }, + } + } + + cf := interchaintest.NewBuiltinChainFactory(logger, cs) + + cfChains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + ic := interchaintest.NewInterchain() + + for i, c := range cfChains { + chain := c.(*cosmos.CosmosChain) + chains[i].chain = chain + ic.AddChain(chain) + } + + err = ic.Build(ctx, nil, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + }) + require.NoError(t, err) + + t.Cleanup(func() { + _ = ic.Close() + }) +} + +// modifyGenesisStrictUptime modifies the genesis file to have a strict uptime slashing window. +// 10 block window, 80% signed blocks required, so more than 2 missed blocks in 10 blocks will slash and jail the validator. +func modifyGenesisStrictUptime(cc ibc.ChainConfig, b []byte) ([]byte, error) { + return modifyGenesisSlashingUptime(10, 0.8)(cc, b) +} + +// modifyGenesisSlashingUptime modifies the genesis slashing period parameters. +func modifyGenesisSlashingUptime( + signedBlocksWindow uint64, + minSignedPerWindow float64, +) func(cc ibc.ChainConfig, b []byte) ([]byte, error) { + return func(cc ibc.ChainConfig, b []byte) ([]byte, error) { + g := make(map[string]any) + if err := json.Unmarshal(b, &g); err != nil { + return nil, fmt.Errorf("failed to unmarshal genesis file: %w", err) + } + + g["app_state"].(map[string]any)["slashing"].(map[string]any)["params"].(map[string]any)["signed_blocks_window"] = strconv.FormatUint(signedBlocksWindow, 10) + g["app_state"].(map[string]any)["slashing"].(map[string]any)["params"].(map[string]any)["min_signed_per_window"] = strconv.FormatFloat(minSignedPerWindow, 'f', 18, 64) + + return json.Marshal(g) + } +} + +// horcruxSidecar creates a horcrux sidecar process that will start when the chain starts. +func horcruxSidecar(ctx context.Context, node *cosmos.ChainNode, name string, client *client.Client, network string, startupFlags ...string) (*cosmos.SidecarProcess, error) { + startCmd := []string{binary, "start"} + startCmd = append(startCmd, startupFlags...) + if err := node.NewSidecarProcess( + ctx, false, name, client, network, + ibc.DockerImage{Repository: signerImage, Version: "latest", UidGid: signerImageUidGid}, + signerImageHomeDir, []string{signerPortDocker, grpcPortDocker, debugPortDocker}, startCmd, + ); err != nil { + return nil, err + } + + return node.Sidecars[len(node.Sidecars)-1], nil +} + +// horcruxSidecar creates a horcrux sidecar process that will start when the chain starts. +func horcruxProxySidecar(ctx context.Context, node *cosmos.ChainNode, name string, client *client.Client, network string, startupFlags ...string) (*cosmos.SidecarProcess, error) { + startCmd := []string{"horcrux-proxy", "start"} + startCmd = append(startCmd, startupFlags...) + if err := node.NewSidecarProcess( + ctx, false, name, client, network, + ibc.DockerImage{Repository: horcruxProxyRegistry, Version: horcruxProxyTag, UidGid: "100:1000"}, + signerImageHomeDir, nil, startCmd, + ); err != nil { + return nil, err + } + + return node.Sidecars[len(node.Sidecars)-1], nil +} + +// getPrivvalKey reads the priv_validator_key.json file from the given node. +func getPrivvalKey(ctx context.Context, node *cosmos.ChainNode) (privval.FilePVKey, error) { + keyBz, err := node.ReadFile(ctx, "config/priv_validator_key.json") + if err != nil { + return privval.FilePVKey{}, fmt.Errorf("failed to read priv_validator_key.json: %w", err) + } + + pvKey := privval.FilePVKey{} + if err := cometjson.Unmarshal(keyBz, &pvKey); err != nil { + return privval.FilePVKey{}, fmt.Errorf("failed to unmarshal priv validator key: %w", err) + } + + return pvKey, nil +} + +// enablePrivvalListener enables the privval listener on the given sentry nodes. +func enablePrivvalListener( + ctx context.Context, + logger *zap.Logger, + sentries cosmos.ChainNodes, + client *client.Client, +) error { + configFileOverrides := testutil.Toml{ + "priv_validator_laddr": "tcp://0.0.0.0:1234", + } + + var eg errgroup.Group + for _, s := range sentries { + s := s + + eg.Go(func() error { + return testutil.ModifyTomlConfigFile( + ctx, + logger, + client, + s.TestName, + s.VolumeName, + "config/config.toml", + configFileOverrides, + ) + }) + } + return eg.Wait() +} + +// getValSigningInfo returns the signing info for the given validator from the reference node. +func getValSigningInfo(tn *cosmos.ChainNode, address cometbytes.HexBytes) (*slashingtypes.ValidatorSigningInfo, error) { + valConsPrefix := fmt.Sprintf("%svalcons", tn.Chain.Config().Bech32Prefix) + + bech32ValConsAddress, err := bech32.ConvertAndEncode(valConsPrefix, address) + if err != nil { + return nil, err + } + res, err := slashingtypes.NewQueryClient(tn.CliContext()).SigningInfo(context.Background(), &slashingtypes.QuerySigningInfoRequest{ + ConsAddress: bech32ValConsAddress, + }) + if err != nil { + return nil, err + } + + return &res.ValSigningInfo, nil +} + +// requireHealthyValidator asserts that the given validator is not tombstoned, not jailed, and has not missed any blocks in the slashing window. +func requireHealthyValidator(t *testing.T, referenceNode *cosmos.ChainNode, validatorAddress cometbytes.HexBytes) { + signingInfo, err := getValSigningInfo(referenceNode, validatorAddress) + require.NoError(t, err) + + require.False(t, signingInfo.Tombstoned) + require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil) + require.LessOrEqual(t, signingInfo.MissedBlocksCounter, int64(1)) +} + +// transferLeadership elects a new raft leader. +func transferLeadership(ctx context.Context, cosigner *cosmos.SidecarProcess) error { + _, _, err := cosigner.Exec(ctx, []string{binary, "elect", strconv.FormatInt(int64(cosigner.Index+1), 10)}, nil) + return err +} + +// pollForLeader polls for the given cosigner to become the leader. +func pollForLeader(ctx context.Context, t *testing.T, cosigner *cosmos.SidecarProcess, expectedLeader int) error { + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + leader, err := getLeader(ctx, cosigner) + t.Logf("{%s} => current leader: {%d}, expected leader: {%d}", cosigner.Name(), leader, expectedLeader) + if err != nil { + return fmt.Errorf("failed to get leader from cosigner: %s - %w", cosigner.Name(), err) + } + if leader == expectedLeader { + return nil + } + case <-ctx.Done(): + return fmt.Errorf("leader did not match before timeout for cosigner: %s - %w", cosigner.Name(), ctx.Err()) + } + } +} + +// getLeader returns the current raft leader. +func getLeader(ctx context.Context, cosigner *cosmos.SidecarProcess) (int, error) { + ports, err := cosigner.GetHostPorts(ctx, signerPortDocker) + if err != nil { + return -1, err + } + grpcAddress := ports[0] + conn, err := grpc.Dial(grpcAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), + ) + if err != nil { + return -1, fmt.Errorf("dialing failed: %w", err) + } + defer conn.Close() + + ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second) + defer cancelFunc() + + grpcClient := proto.NewCosignerClient(conn) + + res, err := grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) + if err != nil { + return -1, err + } + return int(res.GetLeader()), nil +} diff --git a/test/validator_single.go b/test/validator_single.go new file mode 100644 index 00000000..13396712 --- /dev/null +++ b/test/validator_single.go @@ -0,0 +1,143 @@ +package test + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/cometbft/cometbft/crypto" + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + "github.com/docker/docker/client" + "github.com/strangelove-ventures/horcrux/v3/signer" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest" +) + +// testChainSingleNodeAndHorcruxSingle tests a single chain with a single horcrux (single-sign mode) validator and single node validators for the rest. +func testChainSingleNodeAndHorcruxSingle( + t *testing.T, + totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) + totalSentries int, // number of sentry nodes for the single horcrux validator +) { + ctx := context.Background() + cw, pubKey := startChainSingleNodeAndHorcruxSingle(ctx, t, totalValidators, totalSentries) + + err := testutil.WaitForBlocks(ctx, 20, cw.chain) + require.NoError(t, err) + + requireHealthyValidator(t, cw.chain.Validators[0], pubKey.Address()) +} + +// startChainSingleNodeAndHorcruxSingle starts a single chain with a single horcrux (single-sign mode) validator and single node validators for the rest. +func startChainSingleNodeAndHorcruxSingle( + ctx context.Context, + t *testing.T, + totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) + totalSentries int, // number of sentry nodes for the single horcrux validator +) (*chainWrapper, crypto.PubKey) { + client, network := interchaintest.DockerSetup(t) + logger := zaptest.NewLogger(t) + + var pubKey crypto.PubKey + + cw := &chainWrapper{ + totalValidators: totalValidators, + totalSentries: totalSentries, + modifyGenesis: modifyGenesisStrictUptime, + preGenesis: preGenesisSingleNodeAndHorcruxSingle(ctx, logger, client, network, &pubKey), + } + + startChains(ctx, t, logger, client, network, cw) + + return cw, pubKey +} + +// preGenesisSingleNodeAndHorcruxSingle performs the pre-genesis setup to convert the first validator to a horcrux (single-sign mode) validator. +func preGenesisSingleNodeAndHorcruxSingle( + ctx context.Context, + logger *zap.Logger, + client *client.Client, + network string, + pubKey *crypto.PubKey) func(*chainWrapper) func(ibc.ChainConfig) error { + return func(cw *chainWrapper) func(ibc.ChainConfig) error { + return func(cc ibc.ChainConfig) error { + horcruxValidator := cw.chain.Validators[0] + + pvKey, err := getPrivvalKey(ctx, horcruxValidator) + if err != nil { + return err + } + + *pubKey = pvKey.PubKey + + sentries := append(cosmos.ChainNodes{horcruxValidator}, cw.chain.FullNodes...) + + singleSigner, err := horcruxSidecar(ctx, horcruxValidator, "signer", client, network, "--accept-risk") + if err != nil { + return err + } + + chainNodes := make(signer.ChainNodes, len(sentries)) + for i, sentry := range sentries { + chainNodes[i] = signer.ChainNode{ + PrivValAddr: fmt.Sprintf("tcp://%s:1234", sentry.HostName()), + } + } + + config := signer.Config{ + SignMode: signer.SignModeSingle, + ChainNodes: chainNodes, + } + + if err := writeConfigAndKeysSingle(ctx, cw.chain.Config().ChainID, singleSigner, config, pvKey); err != nil { + return err + } + + if err := singleSigner.CreateContainer(ctx); err != nil { + return err + } + + if err := singleSigner.StartContainer(ctx); err != nil { + return err + } + + return enablePrivvalListener(ctx, logger, sentries, client) + } + } +} + +// writeConfigAndKeysSingle writes the config and keys for a horcrux single signer to the sidecar's docker volume. +func writeConfigAndKeysSingle( + ctx context.Context, + chainID string, + singleSigner *cosmos.SidecarProcess, + config signer.Config, + pvKey privval.FilePVKey, +) error { + configBz, err := json.Marshal(config) + if err != nil { + return fmt.Errorf("failed to marshal config to json: %w", err) + } + + if err := singleSigner.WriteFile(ctx, configBz, ".horcrux/config.yaml"); err != nil { + return fmt.Errorf("failed to write config.yaml: %w", err) + } + + pvKeyBz, err := cometjson.Marshal(pvKey) + if err != nil { + return fmt.Errorf("failed to marshal priv validator key: %w", err) + } + + if err = singleSigner.WriteFile(ctx, pvKeyBz, fmt.Sprintf(".horcrux/%s_priv_validator_key.json", chainID)); err != nil { + return fmt.Errorf("failed to write priv_validator_key.json: %w", err) + } + + return nil +} diff --git a/test/validator_threshold.go b/test/validator_threshold.go new file mode 100644 index 00000000..bff4928d --- /dev/null +++ b/test/validator_threshold.go @@ -0,0 +1,418 @@ +package test + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + "time" + + "github.com/cometbft/cometbft/crypto" + "github.com/docker/docker/client" + dto "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" + "github.com/strangelove-ventures/horcrux/v3/signer" + interchaintest "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest" + "golang.org/x/sync/errgroup" + "gopkg.in/yaml.v3" +) + +// testChainSingleNodeAndHorcruxThreshold tests a single chain with a single horcrux (threshold mode) validator and single node validators for the rest of the validators. +func testChainSingleNodeAndHorcruxThreshold( + t *testing.T, + totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) + totalSigners int, // total number of signers for the single horcrux validator + threshold uint8, // key shard threshold, and therefore how many horcrux signers must participate to sign a block + totalSentries int, // number of sentry nodes for the single horcrux validator + sentriesPerSigner int, // how many sentries should each horcrux signer connect to (min: 1, max: totalSentries) +) { + ctx := context.Background() + cw, pubKey := startChainSingleNodeAndHorcruxThreshold(ctx, t, totalValidators, totalSigners, threshold, totalSentries, sentriesPerSigner) + + ourValidator := cw.chain.Validators[0] + cosigners := ourValidator.Sidecars + go getCosignerMetrics(ctx, cosigners) + + err := testutil.WaitForBlocks(ctx, 20, cw.chain) + require.NoError(t, err) + + requireHealthyValidator(t, ourValidator, pubKey.Address()) +} + +// startChainSingleNodeAndHorcruxThreshold starts a single chain with a single horcrux (threshold mode) validator and single node validators for the rest of the validators. +func startChainSingleNodeAndHorcruxThreshold( + ctx context.Context, + t *testing.T, + totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) + totalSigners int, // total number of signers for the single horcrux validator + threshold uint8, // key shard threshold, and therefore how many horcrux signers must participate to sign a block + totalSentries int, // number of sentry nodes for the single horcrux validator + sentriesPerSigner int, // how many sentries should each horcrux signer connect to (min: 1, max: totalSentries) +) (*chainWrapper, crypto.PubKey) { + client, network := interchaintest.DockerSetup(t) + logger := zaptest.NewLogger(t) + + var chain *cosmos.CosmosChain + var pubKey crypto.PubKey + + cw := &chainWrapper{ + chain: chain, + totalValidators: totalValidators, + totalSentries: totalSentries - 1, + modifyGenesis: modifyGenesisStrictUptime, + preGenesis: preGenesisSingleNodeAndHorcruxThreshold(ctx, logger, client, network, totalSigners, threshold, sentriesPerSigner, &pubKey), + } + + startChains(ctx, t, logger, client, network, cw) + + return cw, pubKey +} + +// preGenesisSingleNodeAndHorcruxThreshold performs the pre-genesis setup to convert the first validator to a horcrux (threshold mode) validator. +func preGenesisSingleNodeAndHorcruxThreshold( + ctx context.Context, + logger *zap.Logger, + client *client.Client, + network string, + totalSigners int, // total number of signers for the single horcrux validator + threshold uint8, // key shard threshold, and therefore how many horcrux signers must participate to sign a block + sentriesPerSigner int, // how many sentries should each horcrux signer connect to (min: 1, max: totalSentries) + pubKey *crypto.PubKey) func(*chainWrapper) func(ibc.ChainConfig) error { + return func(cw *chainWrapper) func(ibc.ChainConfig) error { + return func(cc ibc.ChainConfig) error { + horcruxValidator := cw.chain.Validators[0] + + sentries := append(cosmos.ChainNodes{horcruxValidator}, cw.chain.FullNodes...) + + pvPubKey, err := convertValidatorToHorcrux( + ctx, + logger, + client, + network, + horcruxValidator, + totalSigners, + threshold, + sentries, + sentriesPerSigner, + ) + if err != nil { + return err + } + + *pubKey = pvPubKey + + return nil + } + } +} + +// preGenesisAllHorcruxThreshold performs the pre-genesis setup to convert all validators to horcrux validators. +func preGenesisAllHorcruxThreshold( + ctx context.Context, + logger *zap.Logger, + client *client.Client, + network string, + totalSigners int, // total number of signers for the single horcrux validator + threshold uint8, // key shard threshold, and therefore how many horcrux signers must participate to sign a block + sentriesPerValidator int, // how many sentries for each horcrux validator (min: sentriesPerSigner, max: totalSentries) + sentriesPerSigner int, // how many sentries should each horcrux signer connect to (min: 1, max: sentriesPerValidator) + + pubKeys []crypto.PubKey) func(*chainWrapper) func(ibc.ChainConfig) error { + return func(cw *chainWrapper) func(ibc.ChainConfig) error { + return func(cc ibc.ChainConfig) error { + fnsPerVal := sentriesPerValidator - 1 // minus 1 for the validator itself + var eg errgroup.Group + for i, validator := range cw.chain.Validators { + validator := validator + i := i + sentries := append(cosmos.ChainNodes{validator}, cw.chain.FullNodes[i*fnsPerVal:(i+1)*fnsPerVal]...) + + eg.Go(func() error { + pvPubKey, err := convertValidatorToHorcrux( + ctx, + logger, + client, + network, + validator, + totalSigners, + threshold, + sentries, + sentriesPerSigner, + ) + + if err != nil { + return err + } + + pubKeys[i] = pvPubKey + + return nil + }) + } + + return eg.Wait() + } + } +} + +// convertValidatorToHorcrux converts a validator to a horcrux validator by creating horcrux and +// configuring cosigners which will startup as sidecar processes for the validator. +func convertValidatorToHorcrux( + ctx context.Context, + logger *zap.Logger, + client *client.Client, + network string, + validator *cosmos.ChainNode, + totalSigners int, + threshold uint8, + sentries cosmos.ChainNodes, + sentriesPerSigner int, +) (crypto.PubKey, error) { + sentriesForCosigners := getSentriesForCosignerConnection(sentries, totalSigners, sentriesPerSigner) + + ed25519Shards, pvPubKey, err := getShardedPrivvalKey(ctx, validator, threshold, uint8(totalSigners)) + if err != nil { + return nil, err + } + + eciesShards, err := signer.CreateCosignerECIESShards(totalSigners) + if err != nil { + return nil, err + } + + cosigners := make(signer.CosignersConfig, totalSigners) + + for i := 0; i < totalSigners; i++ { + _, err := horcruxSidecar(ctx, validator, fmt.Sprintf("cosigner-%d", i+1), client, network) + if err != nil { + return nil, err + } + + cosigners[i] = signer.CosignerConfig{ + ShardID: i + 1, + P2PAddr: fmt.Sprintf("tcp://%s:%s", validator.Sidecars[i].HostName(), signerPort), + } + } + + var eg errgroup.Group + for i := 0; i < totalSigners; i++ { + cosigner := validator.Sidecars[i] + + sentriesForCosigner := sentriesForCosigners[i] + chainNodes := make(signer.ChainNodes, len(sentriesForCosigner)) + for i, sentry := range sentriesForCosigner { + chainNodes[i] = signer.ChainNode{ + PrivValAddr: fmt.Sprintf("tcp://%s:1234", sentry.HostName()), + } + } + + config := signer.Config{ + SignMode: signer.SignModeThreshold, + ThresholdModeConfig: &signer.ThresholdModeConfig{ + Threshold: int(threshold), + Cosigners: cosigners, + GRPCTimeout: "200ms", + RaftTimeout: "200ms", + }, + ChainNodes: chainNodes, + DebugAddr: fmt.Sprintf("0.0.0.0:%s", debugPort), + } + + i := i + + eg.Go(func() error { + if err := writeConfigAndKeysThreshold( + ctx, cosigner, config, eciesShards[i], + chainEd25519Shard{chainID: validator.Chain.Config().ChainID, key: ed25519Shards[i]}, + ); err != nil { + return err + } + + if err := cosigner.CreateContainer(ctx); err != nil { + return err + } + + return cosigner.StartContainer(ctx) + }) + } + + if err := eg.Wait(); err != nil { + return nil, err + } + + return pvPubKey, enablePrivvalListener(ctx, logger, sentries, client) +} + +// getPrivvalKey gets the privval key from the validator and creates threshold shards from it. +func getShardedPrivvalKey(ctx context.Context, node *cosmos.ChainNode, threshold uint8, shards uint8) ([]signer.CosignerEd25519Key, crypto.PubKey, error) { + pvKey, err := getPrivvalKey(ctx, node) + if err != nil { + return nil, nil, err + } + + ed25519Shards := signer.CreateCosignerEd25519Shards(pvKey, threshold, shards) + + return ed25519Shards, pvKey.PubKey, nil +} + +// chainEd25519Shard is a wrapper for a chain ID and a shard of an ed25519 consensus key. +type chainEd25519Shard struct { + chainID string + key signer.CosignerEd25519Key +} + +// writeConfigAndKeysThreshold writes the config and keys for a horcrux cosigner to the sidecar's docker volume. +func writeConfigAndKeysThreshold( + ctx context.Context, + cosigner *cosmos.SidecarProcess, + config signer.Config, + eciesKey signer.CosignerECIESKey, + ed25519Shards ...chainEd25519Shard, +) error { + configBz, err := yaml.Marshal(config) + if err != nil { + return fmt.Errorf("failed to marshal config to yaml: %w", err) + } + + if err := cosigner.WriteFile(ctx, configBz, ".horcrux/config.yaml"); err != nil { + return fmt.Errorf("failed to write config.yaml: %w", err) + } + + eciesKeyBz, err := json.Marshal(&eciesKey) + if err != nil { + return fmt.Errorf("failed to marshal ecies key: %w", err) + } + + if err := cosigner.WriteFile(ctx, eciesKeyBz, ".horcrux/ecies_keys.json"); err != nil { + return fmt.Errorf("failed to write ecies_keys.json: %w", err) + } + + for _, key := range ed25519Shards { + ed25519KeyBz, err := json.Marshal(&key.key) + if err != nil { + return fmt.Errorf("failed to marshal ed25519 shard: %w", err) + } + + if err = cosigner.WriteFile(ctx, ed25519KeyBz, fmt.Sprintf(".horcrux/%s_shard.json", key.chainID)); err != nil { + return fmt.Errorf("failed to write %s_shard.json: %w", key.chainID, err) + } + } + + return nil +} + +// getSentriesForCosignerConnection will return a slice of sentries for each cosigner to connect to. +// The sentries will be picked for each cosigner in a round robin. +func getSentriesForCosignerConnection(sentries cosmos.ChainNodes, numSigners int, sentriesPerSigner int) []cosmos.ChainNodes { + if sentriesPerSigner == 0 { + sentriesPerSigner = len(sentries) + } + + peers := make([]cosmos.ChainNodes, numSigners) + numSentries := len(sentries) + + if sentriesPerSigner == 1 { + // Each node in the signer cluster is connected to a unique sentry node + singleSentryIndex := 0 + for i := 0; i < numSigners; i++ { + if len(sentries) == 1 || numSigners > numSentries { + peers[i] = append(peers[i], sentries[singleSentryIndex:singleSentryIndex+1]...) + singleSentryIndex++ + if singleSentryIndex >= len(sentries) { + singleSentryIndex = 0 + } + } else { + peers[i] = append(peers[i], sentries[i:i+1]...) + } + } + + // Each node in the signer cluster is connected to the number of sentry nodes specified by sentriesPerSigner + } else if sentriesPerSigner > 1 { + sentriesIndex := 0 + + for i := 0; i < numSigners; i++ { + // if we are indexing sentries up to the end of the slice + switch { + case sentriesIndex+sentriesPerSigner == numSentries: + peers[i] = append(peers[i], sentries[sentriesIndex:]...) + sentriesIndex++ + + // if there aren't enough sentries left in the slice use the sentries left in slice, + // calculate how many more are needed, then start back at the beginning of + // the slice to grab the rest. After, check if index into slice of sentries needs reset + case sentriesIndex+sentriesPerSigner > numSentries: + remainingSentries := sentries[sentriesIndex:] + peers[i] = append(peers[i], remainingSentries...) + + neededSentries := sentriesPerSigner - len(remainingSentries) + peers[i] = append(peers[i], sentries[0:neededSentries]...) + + sentriesIndex++ + if sentriesIndex >= numSentries { + sentriesIndex = 0 + } + default: + peers[i] = append(peers[i], sentries[sentriesIndex:sentriesIndex+sentriesPerSigner]...) + sentriesIndex++ + } + } + } + return peers +} +func getCosignerMetrics(ctx context.Context, cosigners cosmos.SidecarProcesses) { + for _, s := range cosigners { + s := s + ticker := time.NewTicker(time.Second) + + go func() { + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + m, err := getMetrics(ctx, s) + if err != nil { + fmt.Printf("{%s} -> Error getting metrics : %v", s.Name(), err) + } + fmt.Println("Got Metrics", m) + } + } + }() + } +} + +func getMetrics(ctx context.Context, cosigner *cosmos.SidecarProcess) (map[string]*dto.MetricFamily, error) { + + debugAddr, err := cosigner.GetHostPorts(ctx, debugPortDocker) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, "GET", "http://"+debugAddr[0]+"/metrics", nil) + + if err != nil { + + return nil, err + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var parser expfmt.TextParser + mf, err := parser.TextToMetricFamilies(resp.Body) + if err != nil { + + return nil, err + } + + return mf, nil +}