diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..69e7b301 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,39 @@ +name: Linting + +on: + push: + branches: + - develop + pull_request: + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ^1.22.0 + id: go + + - name: Ensure go mod tidy runs without changes + run: | + go mod tidy + git diff-index HEAD + git diff-index --quiet HEAD + + - name: Install gofumpt + run: go install mvdan.cc/gofumpt@v0.6.0 + + - name: Install staticcheck + run: go install honnef.co/go/tools/cmd/staticcheck@v0.4.7 + + - name: Install golangci-lint + run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 + + - name: Lint + run: make lint diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..959e65c1 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,123 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + docker-image: + name: Publish Docker Image + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Get tag version + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Print version + run: | + echo $RELEASE_VERSION + echo ${{ env.RELEASE_VERSION }} + + - name: Extract metadata (tags, labels) for Docker images + id: meta + uses: docker/metadata-action@v4 + with: + images: flashbots/mev-boost + tags: | + type=sha + type=pep440,pattern={{version}} + type=pep440,pattern={{major}}.{{minor}} + type=raw,value=latest,enable=${{ !contains(env.RELEASE_VERSION, '-') }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + push: true + build-args: | + VERSION=${{ env.RELEASE_VERSION }} + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + build-all: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Fetch all tags + run: git fetch --force --tags + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ^1.22 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v3 + with: + distribution: goreleaser + version: latest + args: release --skip-publish --config .goreleaser-build.yaml --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: mev-boost-build + path: | + dist/mev-boost*.tar.gz + dist/mev-boost*.txt + + release: + needs: build-all + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Fetch all tags + run: git fetch --force --tags + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ^1.22 + - name: Make directories + run: | + mkdir -p ./build + - name: Download binaries + uses: actions/download-artifact@v3 + with: + name: mev-boost-build + path: ./build + - name: Merge checksum file + run: | + cd ./build + cat ./mev-boost*checksums.txt >> checksums.txt + rm ./mev-boost*checksums.txt + - name: Release + uses: goreleaser/goreleaser-action@v3 + with: + args: release --config .goreleaser-release.yaml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/releaser.yaml b/.github/workflows/releaser.yaml deleted file mode 100644 index e0c44605..00000000 --- a/.github/workflows/releaser.yaml +++ /dev/null @@ -1,70 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v*' - -jobs: - docker-image: - name: Publish Docker Image - runs-on: ubuntu-latest - - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Get tag version - id: vars - run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - push: true - build-args: | - VERSION=${{ steps.vars.outputs.tag }} - CGO_CFLAGS= - tags: flashbots/mev-boost:latest,flashbots/mev-boost:${{ steps.vars.outputs.tag }} - platforms: linux/amd64,linux/arm64 - - - name: Build and push portable - uses: docker/build-push-action@v3 - with: - context: . - push: true - build-args: | - VERSION=${{ steps.vars.outputs.tag }} - CGO_CFLAGS=-O -D__BLST_PORTABLE__ - tags: flashbots/mev-boost:latest-portable,flashbots/mev-boost:${{ steps.vars.outputs.tag }}-portable - platforms: linux/amd64,linux/arm64 - - github-release: - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Create release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token - with: - tag_name: ${{ github.ref }} - release_name: ${{ github.ref }} - draft: true - prerelease: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e35564c8..5d69e5db 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,21 +2,19 @@ name: Tests on: push: + branches: + - develop pull_request: - branches: [main] jobs: test: name: Test runs-on: ubuntu-latest - env: - CGO_CFLAGS_ALLOW: "-O -D__BLST_PORTABLE__" - CGO_CFLAGS: "-O -D__BLST_PORTABLE__" steps: - - name: Set up Go 1.x - uses: actions/setup-go@v2 + - name: Set up Go + uses: actions/setup-go@v3 with: - go-version: ^1.18 + go-version: ^1.22 id: go - name: Checkout sources @@ -29,72 +27,5 @@ jobs: uses: codecov/codecov-action@v2 with: files: ./coverage.out - verbose: true + verbose: false flags: unittests - - lint: - name: Lint - runs-on: ubuntu-latest - env: - CGO_CFLAGS_ALLOW: "-O -D__BLST_PORTABLE__" - CGO_CFLAGS: "-O -D__BLST_PORTABLE__" - steps: - - name: Set up Go 1.x - uses: actions/setup-go@v2 - with: - go-version: ^1.18 - id: go - - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install revive linter - run: go install github.com/mgechev/revive@v1.1.3 - - - name: Install staticcheck - run: go install honnef.co/go/tools/cmd/staticcheck@v0.3.0 - - - name: Lint - run: make lint - - - name: Ensure go mod tidy runs without changes - run: | - go mod tidy - git diff-index HEAD - git diff-index --quiet HEAD - - # mergemock: - # name: Mergemock - # runs-on: ubuntu-latest - # env: - # CGO_CFLAGS_ALLOW: "-O -D__BLST_PORTABLE__" - # CGO_CFLAGS: "-O -D__BLST_PORTABLE__" - # steps: - # - name: Set up Go 1.x - # uses: actions/setup-go@v2 - # with: - # go-version: ^1.17 - # id: go - - # - name: Check out code into the Go module directory - # uses: actions/checkout@v2 - - # - name: Build mev-boost - # run: make build - - # - name: Check out the mergemock code repo - # uses: actions/checkout@v2 - # with: - # repository: protolambda/mergemock - # path: mergemock - # ref: master - - # - name: Download mergemock genesis.json - # run: cd mergemock && wget https://gist.githubusercontent.com/lightclient/799c727e826483a2804fc5013d0d3e3d/raw/2e8824fa8d9d9b040f351b86b75c66868fb9b115/genesis.json && echo -n 'a21a16ec22a940990922220e4ab5bf4c2310f55622220e4ab5bf4c2310f55656' > jwt.hex - - # - name: Build mergemock - # run: cd mergemock && go build . mergemock - - # - name: Run mergemock consensus tests - # run: | - # make MERGEMOCK_DIR=./mergemock run-mergemock-integration diff --git a/.gitignore b/.gitignore index 600dfe07..5a0fe029 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ /mev-boost /test-cli /tmp +/dist .vscode/ /README.internal.md -/validator_data.json \ No newline at end of file +/validator_data.json +/build/ \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..074ca1ca --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,68 @@ +linters: + enable-all: true + disable: + - exhaustruct + - funlen + - gochecknoglobals + - gochecknoinits + - gocritic + - godot + - godox + - gomnd + - lll + - nlreturn + - nonamedreturns + - nosnakecase + - paralleltest + - testpackage + - varnamelen + - wrapcheck + - wsl + - musttag + - depguard + + # + # Maybe fix later: + # + - cyclop + - gocognit + - goconst + - gosec + - ireturn + - noctx + - tagliatelle + - perfsprint + + # + # Disabled because of generics: + # + - contextcheck + - rowserrcheck + - sqlclosecheck + - structcheck + - wastedassign + + # + # Disabled because deprecated: + # + - deadcode + - exhaustivestruct + - golint + - ifshort + - interfacer + - maligned + - scopelint + - varcheck + +linters-settings: + gofumpt: + extra-rules: true + govet: + enable-all: true + disable: + - fieldalignment + - shadow + +output: + print-issued-lines: true + sort-results: true diff --git a/.goreleaser-build.yaml b/.goreleaser-build.yaml new file mode 100644 index 00000000..73c65429 --- /dev/null +++ b/.goreleaser-build.yaml @@ -0,0 +1,24 @@ +# https://goreleaser.com/customization/builds/ +project_name: mev-boost +builds: + - id: mev-boost + env: + # Force build to be all Go. + - CGO_ENABLED=0 + flags: + # Remove all file system paths from the executable. + - -trimpath + ldflags: + # Disables DWARF debugging information. + - -w + # Disables symbol table information. + - -s + # Sets the value of the symbol. + - -X github.com/flashbots/mev-boost/config.Version={{.Version}} + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 diff --git a/.goreleaser-release.yaml b/.goreleaser-release.yaml new file mode 100644 index 00000000..63613c92 --- /dev/null +++ b/.goreleaser-release.yaml @@ -0,0 +1,12 @@ +# https://goreleaser.com/customization/release/ +builds: + - skip: true +release: + draft: true + extra_files: + - glob: ./build/* + header: | + # 🚀 Features + # 🎄 Enhancements + # 🐞 Notable bug fixes + # 🎠 Community diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2cc32ddf..e640fe8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,17 +1,12 @@ # Contributing guide -Welcome to the Flashbots collective! We just ask you to be nice when you play with us. +Welcome to the MEV-Boost project! We just ask you to be nice when you play with us. Please start by reading our [code of conduct](CODE_OF_CONDUCT.md). ## Set up -Install a few dev dependencies: - -```bash -go install github.com/mgechev/revive@latest -go install honnef.co/go/tools/cmd/staticcheck@master -``` +Install a few dev dependencies for `make lint`: https://github.com/flashbots/mev-boost/blob/go122/.github/workflows/lint.yml#L29-L37 Look at the [README for instructions to install the dependencies and build `mev-boost`](README.md#installing) @@ -20,13 +15,11 @@ Alternatively, run mev-boost without build step: ```bash go run . -h -# Run mev-boost pointed at our Kiln builder+relay -go run . -kiln -relays https://0xb5246e299aeb782fbc7c91b41b3284245b1ed5206134b0028b81dfb974e5900616c67847c2354479934fc4bb75519ee1@builder-relay-kiln.flashbots.net +# Run mev-boost +./mev-boost -goerli -relay-check -relay URL-OF-TRUSTED-RELAY ``` -Note that you'll need to set the correct genesis fork version (either manually with `-genesis-fork-version` or a helper flag `-mainnet`/`-kiln`/`-ropsten`/`-sepolia`). - -If the test or target application crashes with an "illegal instruction" exception, run/rebuild with CGO_CFLAGS environment variable set to `-O -D__BLST_PORTABLE__`. This error also happens if you are on an ARM-based system, including the Apple M1/M2 chip. +Note that you'll need to set the correct genesis fork version (either manually with `-genesis-fork-version` or a helper flag `-mainnet`/`-goerli`/`-sepolia`/`-holesky`). ## Test @@ -87,15 +80,6 @@ Follow the [Clean Code](https://flashbots.notion.site/Clean-Code-13016c5c7ca649f We appreciate you, friend <3. -## Deploying an update - -The extended deploy steps are necessary for installations with `go install` to include the correct version. +--- -* Ensure linter and tests are working: `make lint && make test-race` -* Update `Version` in `config/vars.go` - * it will be next_version-dev (eg. `v0.7.11-dev`) - * change it to the next version (eg. `v0.7.11`), and commit -* Create a git tag -* Now push to main and push the tag: `git push && git push --tags` -* Update `Version` in `config/vars.go` to next patch with `dev` suffix (eg. `v0.7.12-dev`), and commit -* In the meantime, Github CI will have produced a new draft release in https://github.com/flashbots/mev-boost/releases. Open it, generate the description and publish. +For the checklist and guide to releasing a new version, see [RELEASE.md](RELEASE.md). diff --git a/Dockerfile b/Dockerfile index 4af8279d..f090faa4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,21 @@ # syntax=docker/dockerfile:1 -FROM golang:1.18 as builder +FROM golang:1.22 as builder ARG VERSION -ARG CGO_CFLAGS - -ONBUILD ARG GOPROXY -ONBUILD ARG GONOPROXY -ONBUILD ARG GOPRIVATE -ONBUILD ARG GOSUMDB -ONBUILD ARG GONOSUMDB - -RUN apk update && apk upgrade && apk add --no-cache ca-certificates WORKDIR /build -ADD . /build/ -RUN --mount=type=cache,target=/root/.cache/go-build CGO_CFLAGS="$CGO_CFLAGS" GOOS=linux go build -ldflags "-X 'github.com/flashbots/mev-boost/config.Version=$VERSION'" -v -o mev-boost . -FROM alpine:3.15 -RUN apk add --no-cache libstdc++ libc6-comp +COPY go.mod ./ +COPY go.sum ./ -RUN apk add --no-cache bind-toolsat -RUN addgroup -g 10001 -S nonroot && adduser -u 10000 -S -G nonroot -h /home/nonroot nonroot +RUN go mod download +ADD . . +RUN --mount=type=cache,target=/root/.cache/go-build CGO_ENABLED=0 GOOS=linux go build \ + -trimpath \ + -v \ + -ldflags "-w -s -X 'github.com/flashbots/mev-boost/config.Version=$VERSION'" \ + -o mev-boost . + +FROM alpine:3.15 WORKDIR /app COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /build/mev-boost /app/mev-boost @@ -34,4 +30,4 @@ LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.vcs-url="https://github.com/manifoldfinance/mev-boost.git" \ org.label-schema.vendor="CommodityStream, Inc." \ org.label-schema.version=$VERSION \ - org.label-schema.schema-version="1.0" \ No newline at end of file + org.label-schema.schema-version="1.0" diff --git a/Makefile b/Makefile index 1b228089..06fc69fe 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,19 @@ VERSION ?= $(shell git describe --tags --always --dirty="-dev") DOCKER_REPO := flashbots/mev-boost +# Set linker flags to: +# -w: disables DWARF debugging information. +GO_BUILD_LDFLAGS += -w +# -s: disables symbol table information. +GO_BUILD_LDFLAGS += -s +# -X: sets the value of the symbol. +GO_BUILD_LDFLAGS += -X 'github.com/flashbots/mev-boost/config.Version=$(VERSION)' + +# Remove all file system paths from the executable. +GO_BUILD_FLAGS += -trimpath +# Add linker flags to build flags. +GO_BUILD_FLAGS += -ldflags "$(GO_BUILD_LDFLAGS)" + .PHONY: all all: build @@ -10,44 +23,52 @@ v: .PHONY: build build: - go build -ldflags "-X 'github.com/flashbots/mev-boost/config.Version=${VERSION}' -X 'github.com/flashbots/mev-boost/config.BuildTime=$(shell date)'" -v -o mev-boost . - -.PHONY: build-portable -build-portable: - CGO_CFLAGS="-O -D__BLST_PORTABLE__" go build -ldflags "-X 'github.com/flashbots/mev-boost/config.Version=${VERSION}' -X 'github.com/flashbots/mev-boost/config.BuildTime=$(shell date)'" -v -o mev-boost . + @go version + CGO_ENABLED=0 go build $(GO_BUILD_FLAGS) -o mev-boost .PHONY: build-testcli build-testcli: - go build -ldflags "-X 'github.com/flashbots/mev-boost/config.Version=${VERSION}' -X 'github.com/flashbots/mev-boost/config.BuildTime=$(shell date)'" -v -o test-cli ./cmd/test-cli + CGO_ENABLED=0 go build $(GO_BUILD_FLAGS) -o test-cli ./cmd/test-cli .PHONY: test test: - go test ./... + CGO_ENABLED=0 go test ./... .PHONY: test-race test-race: - go test -race ./... + CGO_ENABLED=1 go test -race ./... .PHONY: lint lint: - revive -set_exit_status ./... - go vet ./... + gofmt -d -s . + gofumpt -d -extra . staticcheck ./... + golangci-lint run + +.PHONY: lt +lt: lint test + +.PHONY: fmt +fmt: + gofmt -s -w . + gofumpt -extra -w . + gci write . + go mod tidy .PHONY: test-coverage test-coverage: - go test -race -v -covermode=atomic -coverprofile=coverage.out ./... + CGO_ENABLED=0 go test -v -covermode=atomic -coverprofile=coverage.out ./... go tool cover -func coverage.out .PHONY: cover cover: - go test -coverprofile=coverage.out ./... + CGO_ENABLED=0 go test -coverprofile=coverage.out ./... go tool cover -func coverage.out unlink coverage.out .PHONY: cover-html cover-html: - go test -coverprofile=coverage.out ./... + CGO_ENABLED=0 go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out unlink coverage.out @@ -57,21 +78,10 @@ run-mergemock-integration: build .PHONY: docker-image docker-image: - DOCKER_BUILDKIT=1 docker build --build-arg CGO_CFLAGS="" --build-arg VERSION=${VERSION} . -t mev-boost + DOCKER_BUILDKIT=1 docker build --platform linux/amd64 --build-arg VERSION=${VERSION} . -t mev-boost docker tag mev-boost:latest ${DOCKER_REPO}:${VERSION} docker tag mev-boost:latest ${DOCKER_REPO}:latest -.PHONY: docker-image-portable -docker-image-portable: - DOCKER_BUILDKIT=1 docker build --build-arg CGO_CFLAGS="-O -D__BLST_PORTABLE__" --build-arg VERSION=${VERSION} . -t mev-boost - docker tag mev-boost:latest ${DOCKER_REPO}:${VERSION} - docker tag mev-boost:latest ${DOCKER_REPO}:latest - -.PHONY: docker-push -docker-push: - docker push ${DOCKER_REPO}:${VERSION} - docker push ${DOCKER_REPO}:latest - .PHONY: clean clean: git clean -fdx diff --git a/README.md b/README.md index 36013c73..41111059 100644 --- a/README.md +++ b/README.md @@ -1,173 +1,319 @@ -![mev-boost](https://user-images.githubusercontent.com/116939/179831878-dc6a0f76-94f4-46cc-bafd-18a3a4b58ea4.png) +![mev-boost](https://user-images.githubusercontent.com/116939/224986867-3d1916c6-3219-4d61-b1ce-213fc663070c.png) # [![Goreport status](https://goreportcard.com/badge/github.com/flashbots/mev-boost)](https://goreportcard.com/report/github.com/flashbots/mev-boost) -[![Test status](https://github.com/flashbots/mev-boost/workflows/Tests/badge.svg)](https://github.com/flashbots/mev-boost/actions?query=workflow%3A%22Tests%22) +[![Test status](https://github.com/flashbots/mev-boost/workflows/Tests/badge.svg?branch=develop)](https://github.com/flashbots/mev-boost/actions?query=workflow%3A%22Tests%22) +[![Docker hub](https://badgen.net/docker/size/flashbots/mev-boost?icon=docker&label=image)](https://hub.docker.com/r/flashbots/mev-boost/tags) ## What is MEV-Boost? -`mev-boost` is open source middleware run by validators to access a competitive block-building market. MEV-Boost was built by Flashbots as an implementation of [proposer-builder separation (PBS)](https://ethresear.ch/t/proposer-block-builder-separation-friendly-fee-market-designs/9725) for proof-of-stake (PoS) Ethereum. +`mev-boost` is open source middleware run by validators to access a competitive block-building market. MEV-Boost is an initial implementation of [proposer-builder separation (PBS)](https://ethresear.ch/t/proposer-block-builder-separation-friendly-fee-market-designs/9725) for proof-of-stake (PoS) Ethereum. With MEV-Boost, validators can access blocks from a marketplace of builders. Builders produce blocks containing transaction orderflow and a fee for the block proposing validator. Separating the role of proposers from block builders promotes greater competition, decentralization, and censorship-resistance for Ethereum. ## How does MEV-Boost work? +PoS node operators must run three pieces of software: a validator client, a consensus client, and an execution client. MEV-boost is a sidecar for the beacon node - a separate piece of open source software, which queries and outsources block-building to a network of builders. Block builders prepare full blocks, optimizing for MEV extraction and fair distribution of rewards. They then submit their blocks to relays. -PoS node operators must run three pieces of software: a validator client, consensus client, and an execution client. MEV-boost is a sidecar for the Consensus Client, a separate piece of open source software, which queries and outsources block-building to a network of builders. Block builders prepare full blocks, optimizing for MEV extraction and fair distribution of rewards. They then submit their blocks to relays. +Relays aggregate blocks from **multiple** builders in order to select the block with the highest fees. One instance of MEV-boost can be configured by a validator to connect to **multiple** relays. The consensus layer client of a validator proposes the most profitable block received from MEV-boost to the Ethereum network for attestation and block inclusion. -Relays aggregate blocks from **multiple** builders in order to select the block with the highest fees. One instance of MEV-boost can be configured by a validator to connect to **multiple** relays. The Consensus Layer client of a validator proposes the most profitable block received from MEV-boost to the Ethereum network for attestation and block inclusion. +A MEV-Boost security assessment was conducted on 2022-06-20 by [lotusbumi](https://github.com/lotusbumi). Additional information can be found in the [Security](#security) section of this repository. -![mev-boost service integration overview](https://raw.githubusercontent.com/flashbots/mev-boost/main/docs/mev-boost-integration-overview.png) +![MEV-Boost service integration overview](https://raw.githubusercontent.com/flashbots/mev-boost/54567443e718b09f8034d677723476b679782fb7/docs/mev-boost-integration-overview.png) ## Who can run MEV-Boost? MEV-Boost is a piece of software that any PoS Ethereum node operator (including solo validators) can run as part of their Beacon Client software. It is compatible with any Ethereum consensus client. Support and installation instructions for each client can be found [here](#installing). - --- See also: -* [boost.flashbots.net](https://boost.flashbots.net) -* [mev-boost Docker images](https://hub.docker.com/r/flashbots/mev-boost) -* [wiki](https://github.com/flashbots/mev-boost/wiki) & [troubleshooting guide](https://github.com/flashbots/mev-boost/wiki/Troubleshooting) -* [mev-boost relay source code](https://github.com/flashbots/mev-boost-relay) +* [MEV-Boost Docker images](https://hub.docker.com/r/flashbots/mev-boost) +* [Wiki](https://github.com/flashbots/mev-boost/wiki) * Specs: * [Builder API](https://ethereum.github.io/builder-specs) - * [Flashbots Relay API](https://flashbots.notion.site/Relay-API-Spec-5fb0819366954962bc02e81cb33840f5) +--- # Table of Contents - [Background](#background) - [Installing](#installing) + - [Binaries](#binaries) + - [From source](#from-source) + - [From Docker image](#from-docker-image) + - [Systemd configuration](#systemd-configuration) - [Usage](#usage) -- [The Plan](#the-plan) + - [Mainnet](#mainnet) + - [Goerli testnet](#goerli-testnet) + - [Sepolia testnet](#sepolia-testnet) + - [Holesky testnet](#holesky-testnet) + - [`test-cli`](#test-cli) + - [mev-boost cli arguments](#mev-boost-cli-arguments) - [API](#api) - [Maintainers](#maintainers) - [Contributing](#contributing) - [Security](#security) + - [Bug Bounty](#bug-bounty) + - [Audits](#audits) - [License](#license) +--- + # Background MEV is a centralizing force on Ethereum. Unattended, the competition for MEV opportunities leads to consensus security instability and permissioned communication infrastructure between traders and block producers. This erodes neutrality, transparency, decentralization, and permissionlessness. Proposer/block-builder separation (PBS) was initially proposed by Ethereum researchers as a response to the risk that MEV poses to decentralization of consensus networks. They have suggested that uncontrolled MEV extraction promotes economies of scale which are centralizing in nature, and complicate decentralized pooling. -Flashbots is a research and development organization working on mitigating the negative externalities of MEV. Flashbots started as a builder specializing in MEV extraction in proof-of-work Ethereum to democratize access to MEV and make the most profitable blocks available to all miners. >90% of miners are outsourcing some of their block construction to Flashbots today. In the future, [proposer/builder separation](https://ethresear.ch/t/two-slot-proposer-builder-separation/10980) will be enshrined in the Ethereum protocol itself to further harden its trust model. -Read more in [Why run mev-boost?](https://writings.flashbots.net/writings/why-run-mevboost/) and in the [Frequently Asked Questions](https://github.com/flashbots/mev-boost/wiki/Frequently-Asked-Questions). +Read more in [Why run MEV-Boost?](https://writings.flashbots.net/why-run-mevboost/) and in the [Frequently Asked Questions](https://github.com/flashbots/mev-boost/wiki/Frequently-Asked-Questions). # Installing -`mev-boost` can run in any machine, as long as it is reachable by the beacon client. The default port is 18550. The most common setup is to install it in the same machine as the beacon client. +The most common setup is to install MEV-Boost on the same machine as the beacon client. Multiple beacon-clients can use a single MEV-Boost instance. The default port is 18550. -## Dependencies +See also [Rémy Roy's guide](https://github.com/remyroy/ethstaker/blob/main/prepare-for-the-merge.md#installing-mev-boost) for comprehensive instructions on installing, configuring and running MEV-Boost. + +## Binaries + +Each release includes binaries from Linux, Windows and macOS. You can find the latest release at +https://github.com/flashbots/mev-boost/releases -- [Go 1.18+](https://go.dev/doc/install) ## From source -Install mev-boost with `go install`: +Requires [Go 1.18+](https://go.dev/doc/install). + +### `go install` + +Install the latest MEV-Boost release with `go install`: ```bash go install github.com/flashbots/mev-boost@latest mev-boost -help ``` -Or clone the repository and build it: +### Clone and Build + +Ensure you are downloading the most updated MEV-Boost release. Releases are available at https://github.com/flashbots/mev-boost/releases + +clone the repository and build it: ```bash +# By default, the develop branch includes ongoing merged PRs a future release. git clone https://github.com/flashbots/mev-boost.git cd mev-boost -make build -# Show the help -./mev-boost -help -``` +# You can use the stable branch, which is always updated with the latest released version +git checkout stable -If mev-boost crashes with [`"SIGILL: illegal instruction"`](https://github.com/flashbots/mev-boost/issues/256) then you need to create a portable build: +# If you want to build a specific release, check out the tag. See also https://github.com/flashbots/mev-boost/releases +git checkout tags/YOUR_VERSION -```bash -CGO_CFLAGS="-O -D__BLST_PORTABLE__" go install github.com/flashbots/mev-boost@latest +# Build most recent version of MEV-Boost +make build -# or -make build-portable +# Show help. This confirms MEV-Boost is able to start +./mev-boost -help ``` ## From Docker image -We maintain a mev-boost Docker images at https://hub.docker.com/r/flashbots/mev-boost +We maintain a MEV-Boost Docker images at https://hub.docker.com/r/flashbots/mev-boost - [Install Docker Engine](https://docs.docker.com/engine/install/) - Pull & run the latest image: ```bash -# Get the default mev-boost image +# Get the MEV-Boost image docker pull flashbots/mev-boost:latest -# Get the portable mev-boost image -docker pull flashbots/mev-boost:latest-portable - # Run it docker run flashbots/mev-boost -help ``` +## Systemd configuration + +You can run MEV-Boost with a systemd config like this: + +
+/etc/systemd/system/mev-boost.service + +```ini +[Unit] +Description=mev-boost +Wants=network-online.target +After=network-online.target + +[Service] +User=mev-boost +Group=mev-boost +WorkingDirectory=/home/mev-boost +Type=simple +Restart=always +RestartSec=5 +ExecStart=/home/mev-boost/bin/mev-boost \ + -relay-check \ + -relay YOUR_RELAY_CHOICE_A \ + -relay YOUR_RELAY_CHOICE_B \ + -relay YOUR_RELAY_CHOICE_C + +[Install] +WantedBy=multi-user.target +``` +
+ + # Usage -A single mev-boost instance can be used by multiple beacon nodes. Note that aside from running mev-boost, you will need to configure your beacon node to connect to mev-boost and your validator to allow it to register with the relay. This configuration varies and a guide for each consensus client can be found on the [MEV-boost website](https://boost.flashbots.net/#block-356364ebd7cc424fb524428ed0134b21). +A single MEV-Boost instance can be used by multiple beacon nodes and validators. + +Aside from running MEV-Boost on your local network, you must configure: +* individual **beacon nodes** to connect to MEV-Boost. Beacon Node configuration varies by Consensus client. Guides for each client can be found on the [MEV-boost website](https://boost.flashbots.net/#block-356364ebd7cc424fb524428ed0134b21). +* individual **validators** to configure a preferred relay selection. Note: validators should take precautions to only connect to trusted relays. Read more about [the role of relays here](https://docs.flashbots.net/flashbots-mev-boost/relay). +Lists of available relays are maintained by +* [Ethstaker](https://github.com/remyroy/ethstaker/blob/main/MEV-relay-list.md) [[2]](https://ethstaker.cc/mev-relay-list/) +* [Lido](https://research.lido.fi/t/lido-on-ethereum-call-for-relay-providers/2844) -### Mainnet +## Note on usage documentation -Run mev-boost pointed at our [Mainnet Relay](https://boost-relay.flashbots.net/): +The documentation in this README reflects the latest state of the `main` branch, which may have cli flags or functionality not present in the latest release. + +Please take a look at the specific release documentation about the available command line flags: https://github.com/flashbots/mev-boost/releases + +## Mainnet + +Run MEV-Boost pointed at a mainnet relay: ``` - ./mev-boost -mainnet -relay-check -relays https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net +./mev-boost -relay-check -relay URL-OF-TRUSTED-RELAY ``` -### Goerli testnet +## Goerli testnet -Run mev-boost pointed at our [Goerli Relay](https://builder-relay-goerli.flashbots.net/): +Run MEV-Boost pointed at a Goerli relay: ``` - ./mev-boost -goerli -relay-check -relays https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@builder-relay-goerli.flashbots.net +./mev-boost -goerli -relay-check -relay URL-OF-TRUSTED-RELAY ``` -### Ropsten testnet +## Sepolia testnet -Run mev-boost pointed at our [Ropsten Relay](https://builder-relay-ropsten.flashbots.net/): +Run MEV-Boost pointed at a Sepolia relay: ``` - ./mev-boost -ropsten -relay-check -relays https://0xb124d80a00b80815397b4e7f1f05377ccc83aeeceb6be87963ba3649f1e6efa32ca870a88845917ec3f26a8e2aa25c77@builder-relay-ropsten.flashbots.net +./mev-boost -sepolia -relay-check -relay URL-OF-TRUSTED-RELAY ``` -### Kiln testnet +## Holesky testnet -Run mev-boost pointed at our [Kiln Relay](https://builder-relay-kiln.flashbots.net): +Run MEV-Boost pointed at a Holesky relay: -```bash -./mev-boost -kiln -relay-check -relays https://0xb5246e299aeb782fbc7c91b41b3284245b1ed5206134b0028b81dfb974e5900616c67847c2354479934fc4bb75519ee1@builder-relay-kiln.flashbots.net ``` +./mev-boost -holesky -relay-check -relay URL-OF-TRUSTED-RELAY +``` + +## `test-cli` -### Sepolia testnet +`test-cli` is a utility to execute all proposer requests against MEV-Boost + relay. See also the [test-cli readme](cmd/test-cli/README.md). -Run mev-boost pointed at our [Sepolia Relay](https://builder-relay-sepolia.flashbots.net/): + +## mev-boost cli arguments + +These are the CLI arguments for the develop branch. For arguments available in a specific release, check the [release page](https://github.com/flashbots/mev-boost/releases). ``` - ./mev-boost -sepolia -relay-check -relays https://0x845bd072b7cd566f02faeb0a4033ce9399e42839ced64e8b2adcfc859ed1e8e1a5a293336a49feac6d9a5edb779be53a@builder-relay-sepolia.flashbots.net +$ ./mev-boost -help +Usage of mev-boost: + -addr string + listen-address for mev-boost server (default "localhost:18550") + -debug + shorthand for '-loglevel debug' + -genesis-fork-version string + use a custom genesis fork version + -goerli + use Goerli + -holesky + use Holesky + -json + log in JSON format instead of text + -log-no-version + disables adding the version to every log entry + -log-service string + add a 'service=...' tag to all log messages + -loglevel string + minimum loglevel: trace, debug, info, warn/warning, error, fatal, panic (default "info") + -mainnet + use Mainnet (default true) + -min-bid float + minimum bid to accept from a relay [eth] + -relay value + a single relay, can be specified multiple times + -relay-check + check relay status on startup and on the status API call + -relay-monitor value + a single relay monitor, can be specified multiple times + -relay-monitors string + relay monitor urls - single entry or comma-separated list (scheme://host) + -relays string + relay urls - single entry or comma-separated list (scheme://pubkey@host) + -request-timeout-getheader int + timeout for getHeader requests to the relay [ms] (default 950) + -request-timeout-getpayload int + timeout for getPayload requests to the relay [ms] (default 4000) + -request-timeout-regval int + timeout for registerValidator requests [ms] (default 3000) + -sepolia + use Sepolia + -version + only print version +``` + +### `-relays` vs `-relay` + +There are two different flags for specifying relays: `-relays` and `-relay`. +The `-relays` flag is a comma separated string of relays. On the other hand, +the `-relay` flag is used to specify a single relay, but can be used multiple +times for multiple relays. Use whichever method suits your preferences. + +These two MEV-Boost commands are equivalent: + ``` +./mev-boost -relay-check \ + -relays $YOUR_RELAY_CHOICE_A,$YOUR_RELAY_CHOICE_B,$YOUR_RELAY_CHOICE_C +``` + +``` +./mev-boost -relay-check \ + -relay $YOUR_RELAY_CHOICE_A \ + -relay $YOUR_RELAY_CHOICE_B \ + -relay $YOUR_RELAY_CHOICE_C +``` + + +### Setting a minimum bid value with `-min-bid` +The `-min-bid` flag allows setting a minimum bid value. If no bid from the builder network delivers at least this value, MEV-Boost will not return a bid +to the beacon node, making it fall back to local block production. -#### `test-cli` +Example for setting a minimum bid value of 0.06 ETH: -`test-cli` is a utility to execute all proposer requests against mev-boost+relay. See also the [test-cli readme](cmd/test-cli/README.md). +``` +./mev-boost \ + -min-bid 0.06 \ + -relay $YOUR_RELAY_CHOICE_A \ + -relay $YOUR_RELAY_CHOICE_B \ + -relay $YOUR_RELAY_CHOICE_C +``` +--- # API @@ -199,22 +345,18 @@ sequenceDiagram mev_boost-->>consensus: submitBlindedBlock response ``` -# The Plan - -`mev-boost` is the next step on our exploration towards a trustless and decentralized MEV market. It is a service developed in collaboration with the Ethereum developers and researchers. - -The roadmap, expected deliveries and estimated deadlines are described in [the plan](https://github.com/flashbots/mev-boost/wiki/The-Plan-(tm)). Join us in this repository while we explore the remaining [open research questions](https://github.com/flashbots/mev-boost/wiki/Research#open-questions) with all the relevant organizations in the ecosystem. - # Maintainers - [@metachris](https://github.com/metachris) +- [@jtraglia](https://github.com/jtraglia) +- [@ralexstokes](https://github.com/ralexstokes) +- [@terencechain](https://github.com/terencechain) +- [@lightclient](https://github.com/lightclient) +- [@avalonche](https://github.com/avalonche) - [@Ruteri](https://github.com/Ruteri) -- [@elopio](https://github.com/elopio) # Contributing -[Flashbots](https://flashbots.net) is a research and development collective working on mitigating the negative externalities of decentralized economies. We contribute with the larger free software community to illuminate the dark forest. - You are welcome here <3. - If you have a question, feedback or a bug report for this project, please [open a new Issue](https://github.com/flashbots/mev-boost/issues). @@ -223,7 +365,18 @@ You are welcome here <3. # Security -If you find a security vulnerability on this project or any other initiative related to Flashbots, please let us know sending an email to security@flashbots.net. +If you find a security vulnerability in this project or any other initiative +related to proposer/builder separation in ethereum, please let us know sending +an email to security@flashbots.net. Refer to the [SECURITY file](SECURITY.md) +for details. + +## Bug Bounty + +We have a bug bounty program! Get up to $25k USD for a critical vulnerability. + +We would like to welcome node operators, builders, searchers, and other +participants in the ecosystem to contribute to this bounty pool to help make the +ecosystem more secure. ## Audits @@ -234,7 +387,3 @@ If you find a security vulnerability on this project or any other initiative rel The code in this project is free software under the [MIT License](LICENSE). Logo by [@lekevicius](https://twitter.com/lekevicius) on CC0 license. - ---- - -Made with ☀️ by the ⚡🤖 collective. diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 00000000..f14d8f30 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,108 @@ +# Releasing a new version of mev-boost + +This is a guide on how to release a new version of mev-boost: + +1. Best days to release a new version are Monday to Wednesday. Never release on a Friday. +1. Release only with another person present (four eyes principle) +1. Double-check the current build +1. Prepare a release candidate (RC) +1. Test the RC on testnets with the help of node operators +1. Collect code signoffs +1. Release + +## Double-check the current status + +First of all, check that the git repository is in the final state, and all the tests and checks are running fine + +Test the current + +```bash +make lint +make test-race +go mod tidy +git status # should be no changes + +# Start mev-boost with relay check and -relays +go run . -mainnet -relay-check -min-bid 0.12345 -debug -relays https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com,https://0xb3ee7afcf27f1f1259ac1787876318c6584ee353097a50ed84f51a1f21a323b3736f271a895c7ce918c038e4265918be@relay.edennetwork.io,https://0x9000009807ed12c1f08bf4e81c6da3ba8e3fc3d953898ce0102433094e5f22f21102ec057841fcb81978ed1ea0fa8246@builder-relay-mainnet.blocknative.com,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money -relay-monitors https://relay-monitor1.example.com,https://relay-monitor2.example.com + +# Start mev-boost with relay check and multiple -relay flags +go run . -mainnet -relay-check -debug -min-bid 0.12345 \ + -relay https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net \ + -relay https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com \ + -relay https://0xb3ee7afcf27f1f1259ac1787876318c6584ee353097a50ed84f51a1f21a323b3736f271a895c7ce918c038e4265918be@relay.edennetwork.io \ + -relay https://0x9000009807ed12c1f08bf4e81c6da3ba8e3fc3d953898ce0102433094e5f22f21102ec057841fcb81978ed1ea0fa8246@builder-relay-mainnet.blocknative.com \ + -relay https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money \ + -relay-monitor https://relay-monitor1.example.com \ + -relay-monitor https://relay-monitor2.example.com + +# Call the status endpoint +curl localhost:18550/eth/v1/builder/status +``` + +## Prepare a release candidate build and Docker image + +For example, creating a new release `v1.9`: + +1. Create a Github issue about the upcoming release ([example](https://github.com/flashbots/mev-boost/issues/524)) +2. Create a release branch: `release/v1.9` (note: use the target version as branch name, don't add the `-alpha` suffix) +3. Tag an alpha version: `v1.9-alpha1` +4. Test in testnets, iterate as needed, create more alpha versions if needed +5. When tests are complete, create the final tag and release +6. + +```bash +# create a new branch +git checkout -b release/v1.9 + +# set and commit the correct version as described below, and create a signed tag +vim config/vars.go +git commit -am "v1.9-alpha1" +git tag -s v1.9-alpha1 # without a tag, the Docker image would include the wrong version number + +# now push to Github (CI will build the Docker image: https://github.com/flashbots/mev-boost/actions) +git push origin --tags + +# other parties can now test the release candidate from Docker like this: +docker pull flashbots/mev-boost:v1.9a1 +``` + +## Ask node operators to test this RC (on Goerli or Sepolia or Holesky) + +* Reach out to node operators to help test this release +* Collect their sign-off for the release + +## Collect code signoffs + +* Reach out to the parties that have reviewed the PRs and ask for a sign-off on the release +* For possible reviewers, take a look at [recent contributors](https://github.com/flashbots/mev-boost/graphs/contributors) + +## Release only with 4 eyes + +* Always have two people preparing and publishing the final release + +## Tagging a version and pushing the release + +To create a new version (with tag), follow all these steps! They are necessary to have the correct build version inside, and work with `go install`. + +* In the release branch +* Update [`Version`](/config/vars.go) to final version to `v1.9`, and commit +* Create final tags, both semver and pep440: + * `git tag -s v1.9` + * `git tag -s v1.9.0` +* Update the `stable` branch: + * `git checkout stable` + * `git merge tags/v1.9 --ff-only` (ff-only is important, otherwise git doesn't know the stable branch is based off the v1.9 tag!) +* Update the `develop` branch: + * `git checkout develop` + * `git merge tags/v1.9 --ff-only` +* Update `Version` in `config/vars.go` to next patch with `dev` suffix (eg. `v1.10-dev`) and commit to `develop` branch +* Now push the `develop` and `stable` branches, as well as the tag: `git push origin develop stable --tags` + +Now check the Github CI actions for release activity: https://github.com/flashbots/mev-boost/actions +* CI builds and pushes the Docker image, and prepares a new draft release in https://github.com/flashbots/mev-boost/releases +* Open it and prepare the release: + * generate the description + * review + * add signoffs and testing + * add usage (`mev-boost -help`) + * publish diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..69c04082 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,49 @@ +# Security Policy + +We appreciate any contributions, responsible disclosures and will make every +effort to acknowledge your contributions. + +## Supported Versions + +Please see [Releases](https://github.com/flashbots/mev-boost/releases). +Generally it is recommended to use +the [latest release](https://github.com/flashbots/mev-boost/releases/latest). + +## Reporting a Vulnerability + +To report a vulnerability, please email security@flashbots.net and provide all +the necessary details to reproduce it, such as: + +- Release version +- Operating System +- Consensus / Execution client combination and version +- Network (Mainnet or other testnet) + +Please include the steps to reproduce it using as much detail as possible with +the corresponding logs from `mev-boost` and/or logs from the consensus/execution +client. + +Once we have received your bug report, we will try to reproduce it and provide a +more detailed response. When the reported bug has been successfully reproduced, +the team will work on a fix. + +## Bug Bounty Program + +To incentive bug reports, there is a bug bounty program. You can receive a +bounty (up to $25k USD) depending on the bug's severity. Severity is based on +impact and likelihood. If a bug is high impact but low likelihood, it will have +a lower severity than a bug with a high impact and high likelihood. + +| Severity | Maximum | Example | +|----------|------------:|------------------------------------------------------------------------| +| Low | $1,000 USD | A bug that causes mev-boost to skip a bid. | +| Medium | $5,000 USD | From a builder message, can cause mev-boost to go offline. | +| High | $12,500 USD | From a builder message, can cause a connected validator to go offline. | +| Critical | $25,000 USD | From a builder message, can remotely access arbitrary files on host. | + +### Scope + +Bugs that affect the security of the Ethereum protocol in the `mev-boost` +and `mev-boost-relay` repositories are in scope. Bugs in third-party +dependencies are not in scope unless they result in a bug in `mev-boost` with +demonstrable security impact. diff --git a/cli/main.go b/cli/main.go index 7521c290..de5e93f9 100644 --- a/cli/main.go +++ b/cli/main.go @@ -1,13 +1,14 @@ package cli import ( + "errors" "flag" "fmt" "os" - "strconv" "strings" "time" + "github.com/flashbots/mev-boost/common" "github.com/flashbots/mev-boost/config" "github.com/flashbots/mev-boost/server" "github.com/sirupsen/logrus" @@ -15,149 +16,243 @@ import ( const ( genesisForkVersionMainnet = "0x00000000" - genesisForkVersionKiln = "0x70000069" // https://github.com/eth-clients/merge-testnets/blob/main/kiln/config.yaml#L10 - genesisForkVersionRopsten = "0x80000069" genesisForkVersionSepolia = "0x90000069" genesisForkVersionGoerli = "0x00001020" + genesisForkVersionHolesky = "0x01017000" + + genesisTimeMainnet = 1606824023 + genesisTimeSepolia = 1655733600 + genesisTimeGoerli = 1614588812 + genesisTimeHolesky = 1695902400 ) var ( + // errors + errInvalidLoglevel = errors.New("invalid loglevel") + // defaults - defaultLogJSON = os.Getenv("LOG_JSON") != "" - defaultLogLevel = getEnv("LOG_LEVEL", "info") - defaultListenAddr = getEnv("BOOST_LISTEN_ADDR", "localhost:18550") - defaultRelayTimeoutMs = getEnvInt("RELAY_TIMEOUT_MS", 2000) // timeout for all the requests to the relay - defaultRelayCheck = os.Getenv("RELAY_STARTUP_CHECK") != "" - defaultGenesisForkVersion = getEnv("GENESIS_FORK_VERSION", "") + defaultLogJSON = os.Getenv("LOG_JSON") != "" + defaultLogLevel = common.GetEnv("LOG_LEVEL", "info") + defaultListenAddr = common.GetEnv("BOOST_LISTEN_ADDR", "localhost:18550") + defaultRelayCheck = os.Getenv("RELAY_STARTUP_CHECK") != "" + defaultRelayMinBidEth = common.GetEnvFloat64("MIN_BID_ETH", 0) + defaultDisableLogVersion = os.Getenv("DISABLE_LOG_VERSION") == "1" // disables adding the version to every log entry + defaultDebug = os.Getenv("DEBUG") != "" + defaultLogServiceTag = os.Getenv("LOG_SERVICE_TAG") + defaultRelays = os.Getenv("RELAYS") + defaultRelayMonitors = os.Getenv("RELAY_MONITORS") + defaultMaxRetries = common.GetEnvInt("REQUEST_MAX_RETRIES", 5) + + defaultGenesisForkVersion = common.GetEnv("GENESIS_FORK_VERSION", "") + defaultGenesisTime = common.GetEnvInt("GENESIS_TIMESTAMP", -1) + defaultUseSepolia = os.Getenv("SEPOLIA") != "" + defaultUseGoerli = os.Getenv("GOERLI") != "" + defaultUseHolesky = os.Getenv("HOLESKY") != "" + + // mev-boost relay request timeouts (see also https://github.com/flashbots/mev-boost/issues/287) + defaultTimeoutMsGetHeader = common.GetEnvInt("RELAY_TIMEOUT_MS_GETHEADER", 950) // timeout for getHeader requests + defaultTimeoutMsGetPayload = common.GetEnvInt("RELAY_TIMEOUT_MS_GETPAYLOAD", 4000) // timeout for getPayload requests + defaultTimeoutMsRegisterValidator = common.GetEnvInt("RELAY_TIMEOUT_MS_REGVAL", 3000) // timeout for registerValidator requests + + relays relayList + relayMonitors relayMonitorList // cli flags printVersion = flag.Bool("version", false, "only print version") logJSON = flag.Bool("json", defaultLogJSON, "log in JSON format instead of text") logLevel = flag.String("loglevel", defaultLogLevel, "minimum loglevel: trace, debug, info, warn/warning, error, fatal, panic") + logDebug = flag.Bool("debug", defaultDebug, "shorthand for '-loglevel debug'") + logService = flag.String("log-service", defaultLogServiceTag, "add a 'service=...' tag to all log messages") + logNoVersion = flag.Bool("log-no-version", defaultDisableLogVersion, "disables adding the version to every log entry") + + listenAddr = flag.String("addr", defaultListenAddr, "listen-address for mev-boost server") + relayURLs = flag.String("relays", defaultRelays, "relay urls - single entry or comma-separated list (scheme://pubkey@host)") + relayCheck = flag.Bool("relay-check", defaultRelayCheck, "check relay status on startup and on the status API call") + relayMinBidEth = flag.Float64("min-bid", defaultRelayMinBidEth, "minimum bid to accept from a relay [eth]") + relayMonitorURLs = flag.String("relay-monitors", defaultRelayMonitors, "relay monitor urls - single entry or comma-separated list (scheme://host)") - listenAddr = flag.String("addr", defaultListenAddr, "listen-address for mev-boost server") - relayURLs = flag.String("relays", "", "relay urls - single entry or comma-separated list (scheme://pubkey@host)") - relayTimeoutMs = flag.Int("request-timeout", defaultRelayTimeoutMs, "timeout for requests to a relay [ms]") - relayCheck = flag.Bool("relay-check", defaultRelayCheck, "check relay status on startup and on the status API call") + relayTimeoutMsGetHeader = flag.Int("request-timeout-getheader", defaultTimeoutMsGetHeader, "timeout for getHeader requests to the relay [ms]") + relayTimeoutMsGetPayload = flag.Int("request-timeout-getpayload", defaultTimeoutMsGetPayload, "timeout for getPayload requests to the relay [ms]") + relayTimeoutMsRegVal = flag.Int("request-timeout-regval", defaultTimeoutMsRegisterValidator, "timeout for registerValidator requests [ms]") + + relayRequestMaxRetries = flag.Int("request-max-retries", defaultMaxRetries, "maximum number of retries for a relay get payload request") // helpers - useGenesisForkVersionMainnet = flag.Bool("mainnet", false, "use Mainnet") - useGenesisForkVersionKiln = flag.Bool("kiln", false, "use Kiln") - useGenesisForkVersionRopsten = flag.Bool("ropsten", false, "use Ropsten") - useGenesisForkVersionSepolia = flag.Bool("sepolia", false, "use Sepolia") - useGenesisForkVersionGoerli = flag.Bool("goerli", false, "use Goerli") - useCustomGenesisForkVersion = flag.String("genesis-fork-version", defaultGenesisForkVersion, "use a custom genesis fork version") + mainnet = flag.Bool("mainnet", true, "use Mainnet") + sepolia = flag.Bool("sepolia", defaultUseSepolia, "use Sepolia") + goerli = flag.Bool("goerli", defaultUseGoerli, "use Goerli") + holesky = flag.Bool("holesky", defaultUseHolesky, "use Holesky") + + useCustomGenesisForkVersion = flag.String("genesis-fork-version", defaultGenesisForkVersion, "use a custom genesis fork version") + useCustomGenesisTime = flag.Int("genesis-timestamp", defaultGenesisTime, "use a custom genesis timestamp (unix seconds)") ) -var log = logrus.WithField("module", "cli") +var log = logrus.NewEntry(logrus.New()) // Main starts the mev-boost cli func Main() { + // process repeatable flags + flag.Var(&relays, "relay", "a single relay, can be specified multiple times") + flag.Var(&relayMonitors, "relay-monitor", "a single relay monitor, can be specified multiple times") + + // parse flags and get started flag.Parse() - logrus.SetOutput(os.Stdout) + // perhaps only print the version if *printVersion { - fmt.Printf("mev-boost %s\n", config.Version) + fmt.Printf("mev-boost %s\n", config.Version) //nolint return } - if *logJSON { - log.Logger.SetFormatter(&logrus.JSONFormatter{}) - } else { - log.Logger.SetFormatter(&logrus.TextFormatter{ - FullTimestamp: true, - }) - - } - - if *logLevel != "" { - lvl, err := logrus.ParseLevel(*logLevel) - if err != nil { - flag.Usage() - log.Fatalf("Invalid loglevel: %s", *logLevel) - } - logrus.SetLevel(lvl) + err := setupLogging() + if err != nil { + flag.Usage() + log.WithError(err).Fatal("failed setting up logging") } - log.Infof("mev-boost %s", config.Version) - genesisForkVersionHex := "" - if *useCustomGenesisForkVersion != "" { + var genesisTime uint64 + + switch { + case *useCustomGenesisForkVersion != "": genesisForkVersionHex = *useCustomGenesisForkVersion - } else if *useGenesisForkVersionMainnet { - genesisForkVersionHex = genesisForkVersionMainnet - } else if *useGenesisForkVersionKiln { - genesisForkVersionHex = genesisForkVersionKiln - } else if *useGenesisForkVersionRopsten { - genesisForkVersionHex = genesisForkVersionRopsten - } else if *useGenesisForkVersionSepolia { + case *sepolia: genesisForkVersionHex = genesisForkVersionSepolia - } else if *useGenesisForkVersionGoerli { + genesisTime = genesisTimeSepolia + case *goerli: genesisForkVersionHex = genesisForkVersionGoerli - } else { + genesisTime = genesisTimeGoerli + case *holesky: + genesisForkVersionHex = genesisForkVersionHolesky + genesisTime = genesisTimeHolesky + case *mainnet: + genesisForkVersionHex = genesisForkVersionMainnet + genesisTime = genesisTimeMainnet + default: flag.Usage() - log.Fatal("Please specify a genesis fork version (eg. -mainnet / -kiln / -ropsten / -sepolia / -goerli / -genesis-fork-version flags)") + log.Fatal("please specify a genesis fork version (eg. -mainnet / -sepolia / -goerli / -holesky / -genesis-fork-version flags)") + } + log.Infof("using genesis fork version: %s", genesisForkVersionHex) + + if *useCustomGenesisTime > -1 { + genesisTime = uint64(*useCustomGenesisTime) + } + + // For backwards compatibility with the -relays flag. + if *relayURLs != "" { + for _, relayURL := range strings.Split(*relayURLs, ",") { + err := relays.Set(strings.TrimSpace(relayURL)) + if err != nil { + log.WithError(err).WithField("relay", relayURL).Fatal("Invalid relay URL") + } + } } - log.Infof("Using genesis fork version: %s", genesisForkVersionHex) - relays := parseRelayURLs(*relayURLs) if len(relays) == 0 { flag.Usage() - log.Fatal("No relays specified") + log.Fatal("no relays specified") + } + log.Infof("using %d relays", len(relays)) + for index, relay := range relays { + log.Infof("relay #%d: %s", index+1, relay.String()) + } + + // For backwards compatibility with the -relay-monitors flag. + if *relayMonitorURLs != "" { + for _, relayMonitorURL := range strings.Split(*relayMonitorURLs, ",") { + err := relayMonitors.Set(strings.TrimSpace(relayMonitorURL)) + if err != nil { + log.WithError(err).WithField("relayMonitor", relayMonitorURL).Fatal("Invalid relay monitor URL") + } + } + } + + if len(relayMonitors) > 0 { + log.Infof("using %d relay monitors", len(relayMonitors)) + for index, relayMonitor := range relayMonitors { + log.Infof("relay-monitor #%d: %s", index+1, relayMonitor.String()) + } + } + + if *relayMinBidEth < 0.0 { + log.Fatal("Please specify a non-negative minimum bid") + } + + if *relayMinBidEth > 1000000.0 { + log.Fatal("Minimum bid is too large, please ensure min-bid is denominated in Ethers") + } + + if *relayMinBidEth > 0.0 { + log.Infof("minimum bid: %v eth", *relayMinBidEth) } - log.WithField("relays", relays).Infof("using %d relays", len(relays)) - relayTimeout := time.Duration(*relayTimeoutMs) * time.Millisecond - if relayTimeout <= 0 { - log.Fatal("Please specify a relay timeout greater than 0") + relayMinBidWei, err := common.FloatEthTo256Wei(*relayMinBidEth) + if err != nil { + log.WithError(err).Fatal("failed converting min bid") } opts := server.BoostServiceOpts{ - Log: log, - ListenAddr: *listenAddr, - Relays: relays, - GenesisForkVersionHex: genesisForkVersionHex, - RelayRequestTimeout: relayTimeout, - RelayCheck: *relayCheck, - } - server, err := server.NewBoostService(opts) + Log: log, + ListenAddr: *listenAddr, + Relays: relays, + RelayMonitors: relayMonitors, + GenesisForkVersionHex: genesisForkVersionHex, + GenesisTime: genesisTime, + RelayCheck: *relayCheck, + RelayMinBid: *relayMinBidWei, + RequestTimeoutGetHeader: time.Duration(*relayTimeoutMsGetHeader) * time.Millisecond, + RequestTimeoutGetPayload: time.Duration(*relayTimeoutMsGetPayload) * time.Millisecond, + RequestTimeoutRegVal: time.Duration(*relayTimeoutMsRegVal) * time.Millisecond, + RequestMaxRetries: *relayRequestMaxRetries, + } + service, err := server.NewBoostService(opts) if err != nil { log.WithError(err).Fatal("failed creating the server") } - if *relayCheck && !server.CheckRelays() { - log.Fatal("no relay available") + if *relayCheck && service.CheckRelays() == 0 { + log.Error("no relay passed the health-check!") } log.Println("listening on", *listenAddr) - log.Fatal(server.StartHTTPServer()) + log.Fatal(service.StartHTTPServer()) } -func getEnv(key string, defaultValue string) string { - if value, ok := os.LookupEnv(key); ok { - return value +func setupLogging() error { + // setup logging + log.Logger.SetOutput(os.Stdout) + if *logJSON { + log.Logger.SetFormatter(&logrus.JSONFormatter{ + TimestampFormat: config.RFC3339Milli, + }) + } else { + log.Logger.SetFormatter(&logrus.TextFormatter{ + FullTimestamp: true, + TimestampFormat: config.RFC3339Milli, + }) } - return defaultValue -} - -func getEnvInt(key string, defaultValue int) int { - if value, ok := os.LookupEnv(key); ok { - val, err := strconv.Atoi(value) - if err == nil { - return val - } + if *logDebug { + *logLevel = "debug" } - return defaultValue -} - -func parseRelayURLs(relayURLs string) []server.RelayEntry { - ret := []server.RelayEntry{} - for _, entry := range strings.Split(relayURLs, ",") { - relay, err := server.NewRelayEntry(entry) + if *logLevel != "" { + lvl, err := logrus.ParseLevel(*logLevel) if err != nil { - log.WithError(err).WithField("relayURL", entry).Fatal("Invalid relay URL") + return fmt.Errorf("%w: %s", errInvalidLoglevel, *logLevel) } - ret = append(ret, relay) + log.Logger.SetLevel(lvl) + } + if *logService != "" { + log = log.WithField("service", *logService) + } + + // Add version to logs and say hello + addVersionToLogs := !*logNoVersion + if addVersionToLogs { + log = log.WithField("version", config.Version) + log.Infof("starting mev-boost") + } else { + log.Infof("starting mev-boost %s", config.Version) } - return ret + log.Debug("debug logging enabled") + return nil } diff --git a/cli/main_test.go b/cli/main_test.go new file mode 100644 index 00000000..fddd7c15 --- /dev/null +++ b/cli/main_test.go @@ -0,0 +1,37 @@ +package cli + +import ( + "math/big" + "testing" + + "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/mev-boost/common" + "github.com/stretchr/testify/require" +) + +func TestFloatEthTo256Wei(t *testing.T) { + // test with small input + i := 0.000000000000012345 + weiU256, err := common.FloatEthTo256Wei(i) + require.NoError(t, err) + require.Equal(t, types.IntToU256(12345), *weiU256) + + // test with zero + i = 0 + weiU256, err = common.FloatEthTo256Wei(i) + require.NoError(t, err) + require.Equal(t, types.IntToU256(0), *weiU256) + + // test with large input + i = 987654.3 + weiU256, err = common.FloatEthTo256Wei(i) + require.NoError(t, err) + + r := big.NewInt(9876543) + r.Mul(r, big.NewInt(1e17)) + referenceWeiU256 := new(types.U256Str) + err = referenceWeiU256.FromBig(r) + require.NoError(t, err) + + require.Equal(t, *referenceWeiU256, *weiU256) +} diff --git a/cli/types.go b/cli/types.go new file mode 100644 index 00000000..41473700 --- /dev/null +++ b/cli/types.go @@ -0,0 +1,69 @@ +package cli + +import ( + "errors" + "net/url" + "strings" + + "github.com/flashbots/mev-boost/server" +) + +var errDuplicateEntry = errors.New("duplicate entry") + +type relayList []server.RelayEntry + +func (r *relayList) String() string { + return strings.Join(server.RelayEntriesToStrings(*r), ",") +} + +func (r *relayList) Contains(relay server.RelayEntry) bool { + for _, entry := range *r { + if relay.String() == entry.String() { + return true + } + } + return false +} + +func (r *relayList) Set(value string) error { + relay, err := server.NewRelayEntry(value) + if err != nil { + return err + } + if r.Contains(relay) { + return errDuplicateEntry + } + *r = append(*r, relay) + return nil +} + +type relayMonitorList []*url.URL + +func (rm *relayMonitorList) String() string { + relayMonitors := []string{} + for _, relayMonitor := range *rm { + relayMonitors = append(relayMonitors, relayMonitor.String()) + } + return strings.Join(relayMonitors, ",") +} + +func (rm *relayMonitorList) Contains(relayMonitor *url.URL) bool { + for _, entry := range *rm { + if relayMonitor.String() == entry.String() { + return true + } + } + return false +} + +func (rm *relayMonitorList) Set(value string) error { + relayMonitor, err := url.Parse(value) + if err != nil { + return err + } + if rm.Contains(relayMonitor) { + return errDuplicateEntry + } + *rm = append(*rm, relayMonitor) + return nil +} diff --git a/cmd/test-cli/README.md b/cmd/test-cli/README.md index 123442b3..9e314b17 100644 --- a/cmd/test-cli/README.md +++ b/cmd/test-cli/README.md @@ -27,8 +27,7 @@ Env & defaults: -vd-file VALIDATOR_DATA_FILE = ./validator_data.json -mev-boost MEV_BOOST_ENDPOINT = http://127.0.0.1:18550 -genesis-fork-version GENESIS_FORK_VERSION = "0x00000000" (mainnet) - network fork version - "0x70000069" (kiln) - "0x80000069" (ropsten) + "0x00001020" (goerli) "0x90000069" (sepolia) [getHeader] @@ -39,8 +38,7 @@ Env & defaults: -mm - mergemock mode, use mergemock defaults, only call execution and use fake slot in getHeader -genesis-fork-version GENESIS_FORK_VERSION = "0x00000000" (mainnet) - network fork version - "0x70000069" (kiln) - "0x80000069" (ropsten) + "0x00001020" (goerli) "0x90000069" (sepolia) [getPayload] @@ -51,25 +49,22 @@ Env & defaults: -mm - mergemock mode, use mergemock defaults, only call execution and use fake slot in getHeader -genesis-validators-root GENESIS_VALIDATORS_ROOT = "0x0000000000000000000000000000000000000000000000000000000000000000" (mainnet) - network genesis validators root - "0x99b09fcd43e5905236c370f184056bec6e6638cfc31a323b304fc4aa789cb4ad" (kiln) - "0x44f1e56283ca88b35c789f7f449e52339bc1fefe3a45913a43a6d16edcd33cf1" (ropsten) + "0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb" (goerli) "0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078" (sepolia) -genesis-fork-version GENESIS_FORK_VERSION = "0x00000000" (mainnet) - network fork version - "0x70000069" (kiln) - "0x80000069" (ropsten) + "0x00001020" (goerli) "0x90000069" (sepolia) -bellatrix-fork-version BELLATRIX_FORK_VERSION = "0x02000000" (mainnet) - network bellatrix fork version - "0x70000071" (kiln) - "0x80000071" (ropsten) + "0x02001020" (goerli) "0x90000071" (sepolia) ``` ### Run mev-boost ``` -./mev-boost -kiln -relays https://0xb5246e299aeb782fbc7c91b41b3284245b1ed5206134b0028b81dfb974e5900616c67847c2354479934fc4bb75519ee1@builder-relay-kiln.flashbots.net +./mev-boost -goerli -relay-check -relay URL-OF-TRUSTED-RELAY ``` ### Run beacon node diff --git a/cmd/test-cli/beacon.go b/cmd/test-cli/beacon.go index 9b211cb5..d454fb13 100644 --- a/cmd/test-cli/beacon.go +++ b/cmd/test-cli/beacon.go @@ -20,7 +20,7 @@ type beaconBlockData struct { BlockHash common.Hash } -func createBeacon(isMergemock bool, beaconEndpoint string, engineEndpoint string) Beacon { +func createBeacon(isMergemock bool, beaconEndpoint, engineEndpoint string) Beacon { if isMergemock { return &MergemockBeacon{engineEndpoint} } @@ -53,7 +53,7 @@ type partialSignedBeaconBlock struct { func getCurrentBeaconBlock(beaconEndpoint string) (beaconBlockData, error) { var blockResp partialSignedBeaconBlock - _, err := server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodGet, beaconEndpoint+"/eth/v2/beacon/blocks/head", "test-cli/beacon", nil, &blockResp) + _, err := server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodGet, beaconEndpoint+"/eth/v2/beacon/blocks/head", "test-cli/beacon", nil, nil, &blockResp) if err != nil { return beaconBlockData{}, err } diff --git a/cmd/test-cli/engine.go b/cmd/test-cli/engine.go index 94d5902a..a7fa0290 100644 --- a/cmd/test-cli/engine.go +++ b/cmd/test-cli/engine.go @@ -39,7 +39,7 @@ func sendForkchoiceUpdate(engineEndpoint string, block engineBlockData) error { log.WithField("hash", block.Hash).Info("sending FCU") params := []any{ forkchoiceState{block.Hash, block.Hash, block.Hash}, - payloadAttributes{hexutil.Uint64(block.Timestamp + 1), common.Hash{0x01}, common.Address{0x02}}, + payloadAttributes{block.Timestamp + 1, common.Hash{0x01}, common.Address{0x02}}, } payload := map[string]any{"id": 67, "jsonrpc": "2.0", "method": "engine_forkchoiceUpdatedV1", "params": params} return sendJSONRequest(engineEndpoint, payload, nil) diff --git a/cmd/test-cli/main.go b/cmd/test-cli/main.go index c01343e4..e249af91 100644 --- a/cmd/test-cli/main.go +++ b/cmd/test-cli/main.go @@ -8,14 +8,18 @@ import ( "os" "strconv" + builderApi "github.com/attestantio/go-builder-client/api" + builderSpec "github.com/attestantio/go-builder-client/spec" + eth2ApiV1Capella "github.com/attestantio/go-eth2-client/api/v1/capella" + "github.com/attestantio/go-eth2-client/spec/altair" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" - boostTypes "github.com/flashbots/go-boost-utils/types" - "github.com/sirupsen/logrus" - + "github.com/flashbots/go-boost-utils/ssz" "github.com/flashbots/mev-boost/server" + "github.com/sirupsen/logrus" ) -var log = logrus.WithField("service", "cmd/test-cli") +var log = logrus.NewEntry(logrus.New()) func doGenerateValidator(filePath string, gasLimit uint64, feeRecipient string) { v := newRandomValidator(gasLimit, feeRecipient) @@ -26,13 +30,12 @@ func doGenerateValidator(filePath string, gasLimit uint64, feeRecipient string) log.WithField("file", filePath).Info("Saved validator data") } -func doRegisterValidator(v validatorPrivateData, boostEndpoint string, builderSigningDomain boostTypes.Domain) { +func doRegisterValidator(v validatorPrivateData, boostEndpoint string, builderSigningDomain phase0.Domain) { message, err := v.PrepareRegistrationMessage(builderSigningDomain) if err != nil { log.WithError(err).Fatal("Could not prepare registration message") } - _, err = server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, boostEndpoint+"/eth/v1/builder/validators", "test-cli", message, nil) - + _, err = server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, boostEndpoint+"/eth/v1/builder/validators", "test-cli", nil, message, nil) if err != nil { log.WithError(err).Fatal("Validator registration not successful") } @@ -40,7 +43,7 @@ func doRegisterValidator(v validatorPrivateData, boostEndpoint string, builderSi log.WithError(err).Info("Registered validator") } -func doGetHeader(v validatorPrivateData, boostEndpoint string, beaconNode Beacon, engineEndpoint string, builderSigningDomain boostTypes.Domain) boostTypes.GetHeaderResponse { +func doGetHeader(v validatorPrivateData, boostEndpoint string, beaconNode Beacon, engineEndpoint string, builderSigningDomain phase0.Domain) builderSpec.VersionedSignedBuilderBid { // Mergemock needs to call forkchoice update before getHeader, for non-mergemock beacon node this is a no-op err := beaconNode.onGetHeader() if err != nil { @@ -67,17 +70,17 @@ func doGetHeader(v validatorPrivateData, boostEndpoint string, beaconNode Beacon uri := fmt.Sprintf("%s/eth/v1/builder/header/%d/%s/%s", boostEndpoint, currentBlock.Slot+1, currentBlockHash, v.Pk.String()) - var getHeaderResp boostTypes.GetHeaderResponse - if _, err := server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodGet, uri, "test-cli", nil, &getHeaderResp); err != nil { + var getHeaderResp builderSpec.VersionedSignedBuilderBid + if _, err := server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodGet, uri, "test-cli", nil, nil, &getHeaderResp); err != nil { log.WithError(err).WithField("currentBlockHash", currentBlockHash).Fatal("Could not get header") } - if getHeaderResp.Data.Message == nil { + if getHeaderResp.Capella.Message == nil { log.Fatal("Did not receive correct header") } - log.WithField("header", *getHeaderResp.Data.Message).Info("Got header from boost") + log.WithField("header", *getHeaderResp.Capella.Message).Info("Got header from boost") - ok, err := boostTypes.VerifySignature(getHeaderResp.Data.Message, builderSigningDomain, getHeaderResp.Data.Message.Pubkey[:], getHeaderResp.Data.Signature[:]) + ok, err := ssz.VerifySignature(getHeaderResp.Capella.Message, builderSigningDomain, getHeaderResp.Capella.Message.Pubkey[:], getHeaderResp.Capella.Signature[:]) if err != nil { log.WithError(err).Fatal("Could not verify builder bid signature") } @@ -88,25 +91,25 @@ func doGetHeader(v validatorPrivateData, boostEndpoint string, beaconNode Beacon return getHeaderResp } -func doGetPayload(v validatorPrivateData, boostEndpoint string, beaconNode Beacon, engineEndpoint string, builderSigningDomain boostTypes.Domain, proposerSigningDomain boostTypes.Domain) { +func doGetPayload(v validatorPrivateData, boostEndpoint string, beaconNode Beacon, engineEndpoint string, builderSigningDomain, proposerSigningDomain phase0.Domain) { header := doGetHeader(v, boostEndpoint, beaconNode, engineEndpoint, builderSigningDomain) - blindedBeaconBlock := boostTypes.BlindedBeaconBlock{ + blindedBeaconBlock := eth2ApiV1Capella.BlindedBeaconBlock{ Slot: 0, ProposerIndex: 0, - ParentRoot: boostTypes.Root{}, - StateRoot: boostTypes.Root{}, - Body: &boostTypes.BlindedBeaconBlockBody{ - RandaoReveal: boostTypes.Signature{}, - Eth1Data: &boostTypes.Eth1Data{}, - Graffiti: boostTypes.Hash{}, - ProposerSlashings: []*boostTypes.ProposerSlashing{}, - AttesterSlashings: []*boostTypes.AttesterSlashing{}, - Attestations: []*boostTypes.Attestation{}, - Deposits: []*boostTypes.Deposit{}, - VoluntaryExits: []*boostTypes.VoluntaryExit{}, - SyncAggregate: &boostTypes.SyncAggregate{}, - ExecutionPayloadHeader: header.Data.Message.Header, + ParentRoot: phase0.Root{}, + StateRoot: phase0.Root{}, + Body: ð2ApiV1Capella.BlindedBeaconBlockBody{ + RANDAOReveal: phase0.BLSSignature{}, + ETH1Data: &phase0.ETH1Data{}, + Graffiti: phase0.Hash32{}, + ProposerSlashings: []*phase0.ProposerSlashing{}, + AttesterSlashings: []*phase0.AttesterSlashing{}, + Attestations: []*phase0.Attestation{}, + Deposits: []*phase0.Deposit{}, + VoluntaryExits: []*phase0.SignedVoluntaryExit{}, + SyncAggregate: &altair.SyncAggregate{}, + ExecutionPayloadHeader: header.Capella.Message.Header, }, } @@ -115,19 +118,19 @@ func doGetPayload(v validatorPrivateData, boostEndpoint string, beaconNode Beaco log.WithError(err).Fatal("could not sign blinded beacon block") } - payload := boostTypes.SignedBlindedBeaconBlock{ + payload := eth2ApiV1Capella.SignedBlindedBeaconBlock{ Message: &blindedBeaconBlock, Signature: signature, } - var respPayload boostTypes.GetPayloadResponse - if _, err := server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, boostEndpoint+"/eth/v1/builder/blinded_blocks", "test-cli", payload, &respPayload); err != nil { + var respPayload builderApi.VersionedExecutionPayload + if _, err := server.SendHTTPRequest(context.TODO(), *http.DefaultClient, http.MethodPost, boostEndpoint+"/eth/v1/builder/blinded_blocks", "test-cli", nil, payload, &respPayload); err != nil { log.WithError(err).Fatal("could not get payload") } - if respPayload.Data == nil { + if respPayload.IsEmpty() { log.Fatal("Did not receive correct payload") } - log.WithField("payload", *respPayload.Data).Info("got payload from mev-boost") + log.WithField("payload", respPayload).Info("got payload from mev-boost") } func main() { @@ -211,7 +214,7 @@ func main() { if err := registerCommand.Parse(os.Args[2:]); err != nil { log.Fatal(err) } - builderSigningDomain, err := server.ComputeDomain(boostTypes.DomainTypeAppBuilder, genesisForkVersionStr, boostTypes.Root{}.String()) + builderSigningDomain, err := server.ComputeDomain(ssz.DomainTypeAppBuilder, genesisForkVersionStr, phase0.Root{}.String()) if err != nil { log.WithError(err).Fatal("computing signing domain failed") } @@ -220,7 +223,7 @@ func main() { if err := getHeaderCommand.Parse(os.Args[2:]); err != nil { log.Fatal(err) } - builderSigningDomain, err := server.ComputeDomain(boostTypes.DomainTypeAppBuilder, genesisForkVersionStr, boostTypes.Root{}.String()) + builderSigningDomain, err := server.ComputeDomain(ssz.DomainTypeAppBuilder, genesisForkVersionStr, phase0.Root{}.String()) if err != nil { log.WithError(err).Fatal("computing signing domain failed") } @@ -229,22 +232,22 @@ func main() { if err := getPayloadCommand.Parse(os.Args[2:]); err != nil { log.Fatal(err) } - builderSigningDomain, err := server.ComputeDomain(boostTypes.DomainTypeAppBuilder, genesisForkVersionStr, boostTypes.Root{}.String()) + builderSigningDomain, err := server.ComputeDomain(ssz.DomainTypeAppBuilder, genesisForkVersionStr, phase0.Root{}.String()) if err != nil { log.WithError(err).Fatal("computing signing domain failed") } - proposerSigningDomain, err := server.ComputeDomain(boostTypes.DomainTypeBeaconProposer, bellatrixForkVersionStr, genesisValidatorsRootStr) + proposerSigningDomain, err := server.ComputeDomain(ssz.DomainTypeBeaconProposer, bellatrixForkVersionStr, genesisValidatorsRootStr) if err != nil { log.WithError(err).Fatal("computing signing domain failed") } doGetPayload(mustLoadValidator(validatorDataFile), boostEndpoint, createBeacon(isMergemock, beaconEndpoint, engineEndpoint), engineEndpoint, builderSigningDomain, proposerSigningDomain) default: - fmt.Println("Expected generate|register|getHeader|getPayload subcommand") + log.Info("Expected generate|register|getHeader|getPayload subcommand") os.Exit(1) } } -func getEnv(key string, defaultValue string) string { +func getEnv(key, defaultValue string) string { if value, ok := os.LookupEnv(key); ok { return value } diff --git a/cmd/test-cli/requests.go b/cmd/test-cli/requests.go index fe8a4baf..503001e7 100644 --- a/cmd/test-cli/requests.go +++ b/cmd/test-cli/requests.go @@ -4,10 +4,13 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "io" "net/http" ) +var errRequestResponse = errors.New("request response error") + type jsonrpcMessage struct { Version string `json:"jsonrpc,omitempty"` ID json.RawMessage `json:"id,omitempty"` @@ -21,7 +24,7 @@ type jsonrpcMessage struct { Result json.RawMessage `json:"result,omitempty"` } -func sendJSONRequest(endpoint string, payload any, dst any) error { +func sendJSONRequest(endpoint string, payload, dst any) error { payloadBytes, err := json.Marshal(payload) if err != nil { return err @@ -30,7 +33,7 @@ func sendJSONRequest(endpoint string, payload any, dst any) error { body := bytes.NewReader(payloadBytes) fetchLog := log.WithField("endpoint", endpoint).WithField("method", "POST").WithField("payload", string(payloadBytes)) fetchLog.Info("sending request") - req, err := http.NewRequest("POST", endpoint, body) + req, err := http.NewRequest(http.MethodPost, endpoint, body) if err != nil { fetchLog.WithError(err).Error("could not prepare request") return err @@ -62,7 +65,7 @@ func sendJSONRequest(endpoint string, payload any, dst any) error { if jsonResp.Error != nil { fetchLog.WithField("code", jsonResp.Error.Code).WithField("err", jsonResp.Error.Message).Error("error response") - return errors.New(jsonResp.Error.Message) + return fmt.Errorf("%w: %s", errRequestResponse, jsonResp.Error.Message) } if dst != nil { diff --git a/cmd/test-cli/validator.go b/cmd/test-cli/validator.go index 9dd0fc16..ef4eb8a1 100644 --- a/cmd/test-cli/validator.go +++ b/cmd/test-cli/validator.go @@ -2,14 +2,20 @@ package main import ( "encoding/json" + "errors" "os" "time" + builderApiV1 "github.com/attestantio/go-builder-client/api/v1" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/flashbots/go-boost-utils/bls" - boostTypes "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/go-boost-utils/ssz" + "github.com/flashbots/go-boost-utils/utils" ) +var errInvalidLength = errors.New("invalid length") + type validatorPrivateData struct { Sk hexutil.Bytes Pk hexutil.Bytes @@ -22,7 +28,7 @@ func (v *validatorPrivateData) SaveValidator(filePath string) error { if err != nil { return err } - return os.WriteFile(filePath, validatorData, 0644) + return os.WriteFile(filePath, validatorData, 0o644) } func mustLoadValidator(filePath string) validatorPrivateData { @@ -43,41 +49,41 @@ func newRandomValidator(gasLimit uint64, feeRecipient string) validatorPrivateDa if err != nil { log.WithError(err).Fatal("unable to generate bls key pair") } - return validatorPrivateData{sk.Serialize(), pk.Compress(), hexutil.Uint64(gasLimit), feeRecipient} + return validatorPrivateData{bls.SecretKeyToBytes(sk), bls.PublicKeyToBytes(pk), hexutil.Uint64(gasLimit), feeRecipient} } -func (v *validatorPrivateData) PrepareRegistrationMessage(builderSigningDomain boostTypes.Domain) ([]boostTypes.SignedValidatorRegistration, error) { - pk := boostTypes.PublicKey{} - err := pk.FromSlice(v.Pk) - if err != nil { - return []boostTypes.SignedValidatorRegistration{}, err +func (v *validatorPrivateData) PrepareRegistrationMessage(builderSigningDomain phase0.Domain) ([]builderApiV1.SignedValidatorRegistration, error) { + pk := phase0.BLSPubKey{} + if len(v.Pk) != len(pk) { + return []builderApiV1.SignedValidatorRegistration{}, errInvalidLength } + copy(pk[:], v.Pk) - addr, err := boostTypes.HexToAddress(v.FeeRecipientHex) + addr, err := utils.HexToAddress(v.FeeRecipientHex) if err != nil { - return []boostTypes.SignedValidatorRegistration{}, err + return []builderApiV1.SignedValidatorRegistration{}, err } - msg := boostTypes.RegisterValidatorRequestMessage{ + msg := builderApiV1.ValidatorRegistration{ FeeRecipient: addr, - Timestamp: uint64(time.Now().Unix()), + Timestamp: time.Now(), Pubkey: pk, GasLimit: uint64(v.GasLimit), } signature, err := v.Sign(&msg, builderSigningDomain) if err != nil { - return []boostTypes.SignedValidatorRegistration{}, err + return []builderApiV1.SignedValidatorRegistration{}, err } - return []boostTypes.SignedValidatorRegistration{{ + return []builderApiV1.SignedValidatorRegistration{{ Message: &msg, Signature: signature, }}, nil } -func (v *validatorPrivateData) Sign(msg boostTypes.HashTreeRoot, domain boostTypes.Domain) (boostTypes.Signature, error) { +func (v *validatorPrivateData) Sign(msg ssz.ObjWithHashTreeRoot, domain phase0.Domain) (phase0.BLSSignature, error) { sk, err := bls.SecretKeyFromBytes(v.Sk) if err != nil { - return boostTypes.Signature{}, err + return phase0.BLSSignature{}, err } - return boostTypes.SignMessage(msg, domain, sk) + return ssz.SignMessage(msg, domain, sk) } diff --git a/common/common.go b/common/common.go new file mode 100644 index 00000000..c8c0a914 --- /dev/null +++ b/common/common.go @@ -0,0 +1,57 @@ +package common + +import ( + "math/big" + "os" + "strconv" + + "github.com/flashbots/go-boost-utils/types" +) + +const ( + SlotTimeSecMainnet = 12 +) + +func GetEnv(key, defaultValue string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return defaultValue +} + +func GetEnvInt(key string, defaultValue int) int { + if value, ok := os.LookupEnv(key); ok { + val, err := strconv.Atoi(value) + if err == nil { + return val + } + } + return defaultValue +} + +func GetEnvFloat64(key string, defaultValue float64) float64 { + if value, ok := os.LookupEnv(key); ok { + val, err := strconv.ParseFloat(value, 64) + if err == nil { + return val + } + } + return defaultValue +} + +// FloatEthTo256Wei converts a float (precision 10) denominated in eth to a U256Str denominated in wei +func FloatEthTo256Wei(val float64) (*types.U256Str, error) { + weiU256 := new(types.U256Str) + ethFloat := new(big.Float) + weiFloat := new(big.Float) + weiFloatLessPrecise := new(big.Float) + weiInt := new(big.Int) + + ethFloat.SetFloat64(val) + weiFloat.Mul(ethFloat, big.NewFloat(1e18)) + weiFloatLessPrecise.SetString(weiFloat.String()) + weiFloatLessPrecise.Int(weiInt) + + err := weiU256.FromBig(weiInt) + return weiU256, err +} diff --git a/config/vars.go b/config/vars.go index 42a2af9a..4f4b2732 100644 --- a/config/vars.go +++ b/config/vars.go @@ -1,31 +1,35 @@ package config import ( - "github.com/flashbots/go-utils/cli" + "os" + + "github.com/flashbots/mev-boost/common" ) -// Set during build var ( - // Version is the version of the software, set at build time - Version = "v0.8.3-dev" + // Version is set at build time (must be a var, not a const!) + Version = "v1.7.1" - // BuildTime is the output of `date` at build time. Eg. "Sat Jul 16 13:18:55 CEST 2022" - BuildTime string -) + // RFC3339Milli is a time format string based on time.RFC3339 but with millisecond precision + RFC3339Milli = "2006-01-02T15:04:05.999Z07:00" -// Other settings -var ( // ServerReadTimeoutMs sets the maximum duration for reading the entire request, including the body. A zero or negative value means there will be no timeout. - ServerReadTimeoutMs = cli.GetEnvInt("MEV_BOOST_SERVER_READ_TIMEOUT_MS", 1000) + ServerReadTimeoutMs = common.GetEnvInt("MEV_BOOST_SERVER_READ_TIMEOUT_MS", 1000) // ServerReadHeaderTimeoutMs sets the amount of time allowed to read request headers. - ServerReadHeaderTimeoutMs = cli.GetEnvInt("MEV_BOOST_SERVER_READ_HEADER_TIMEOUT_MS", 1000) + ServerReadHeaderTimeoutMs = common.GetEnvInt("MEV_BOOST_SERVER_READ_HEADER_TIMEOUT_MS", 1000) // ServerWriteTimeoutMs sets the maximum duration before timing out writes of the response. - ServerWriteTimeoutMs = cli.GetEnvInt("MEV_BOOST_SERVER_WRITE_TIMEOUT_MS", 0) + ServerWriteTimeoutMs = common.GetEnvInt("MEV_BOOST_SERVER_WRITE_TIMEOUT_MS", 0) // ServerIdleTimeoutMs sets the maximum amount of time to wait for the next request when keep-alives are enabled. - ServerIdleTimeoutMs = cli.GetEnvInt("MEV_BOOST_SERVER_IDLE_TIMEOUT_MS", 0) + ServerIdleTimeoutMs = common.GetEnvInt("MEV_BOOST_SERVER_IDLE_TIMEOUT_MS", 0) + + // ServerMaxHeaderBytes defines the max header byte size for requests (for dos prevention) + ServerMaxHeaderBytes = common.GetEnvInt("MAX_HEADER_BYTES", 4000) + + // SkipRelaySignatureCheck can be used to disable relay signature check + SkipRelaySignatureCheck = os.Getenv("SKIP_RELAY_SIGNATURE_CHECK") == "1" - ServerMaxHeaderBytes = cli.GetEnvInt("MAX_HEADER_BYTES", 4000) // max header byte size for requests for dos prevention + SlotTimeSec = uint64(common.GetEnvInt("SLOT_SEC", common.SlotTimeSecMainnet)) ) diff --git a/go.mod b/go.mod index 3cd22191..f6d36d87 100644 --- a/go.mod +++ b/go.mod @@ -1,27 +1,90 @@ module github.com/flashbots/mev-boost -go 1.18 +go 1.21 require ( - github.com/ethereum/go-ethereum v1.10.17 - github.com/flashbots/go-boost-utils v0.3.5 - github.com/flashbots/go-utils v0.4.5 - github.com/gorilla/mux v1.8.0 - github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.1 + github.com/ethereum/go-ethereum v1.13.10 + github.com/flashbots/go-boost-utils v1.8.0 + github.com/flashbots/go-utils v0.5.0 + github.com/google/uuid v1.6.0 + github.com/gorilla/mux v1.8.1 + github.com/holiman/uint256 v1.2.4 + github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.8.4 ) require ( + github.com/DataDog/zstd v1.5.2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/cockroachdb/errors v1.9.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect + github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect + github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/goccy/go-yaml v1.11.2 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/klauspost/compress v1.15.15 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.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 v1.0.4 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/supranational/blst v0.3.11 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/protobuf v1.30.0 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) + +require ( + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect + github.com/attestantio/go-builder-client v0.4.2 + github.com/attestantio/go-eth2-client v0.19.10 + github.com/btcsuite/btcd v0.22.0-beta // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/ferranbt/fastssz v0.1.2-0.20220723134332-b3d3034a4575 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/klauspost/cpuid/v2 v2.0.12 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/ferranbt/fastssz v0.1.3 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/supranational/blst v0.3.7 // indirect - golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 // indirect - golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/sys v0.16.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4ef13776..0aa577d7 100644 --- a/go.sum +++ b/go.sum @@ -1,412 +1,439 @@ 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.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= -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.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.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -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/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -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/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 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/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/attestantio/go-builder-client v0.4.2 h1:EycfAFqQV+ooc2z6hmTsbuH4TCLknr0aO0nHLHLMpJM= +github.com/attestantio/go-builder-client v0.4.2/go.mod h1:e02i/WO4fjs3/u9oIZEjiC8CK1Qyxy4cpiMMGKx4VqQ= +github.com/attestantio/go-eth2-client v0.19.10 h1:NLs9mcBvZpBTZ3du7Ey2NHQoj8d3UePY7pFBXX6C6qs= +github.com/attestantio/go-eth2-client v0.19.10/go.mod h1:TTz7YF6w4z6ahvxKiHuGPn6DbQn7gH6HPuWm/DEQeGE= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +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/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/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/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/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/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +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.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +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-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/cockroachdb/redact v1.1.3/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/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/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/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 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/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +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/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +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.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.17 h1:XEcumY+qSr1cZQaWsQs5Kck3FHB0V2RiMHPdTBJ+oT8= -github.com/ethereum/go-ethereum v1.10.17/go.mod h1:Lt5WzjM07XlXc95YzrhosmR4J9Ahd6X2wyEV2SvGhk0= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/ferranbt/fastssz v0.1.2-0.20220723134332-b3d3034a4575 h1:56lKKtcqQZ5sGjeuyBAeFwzcYuk32d8oqDvxQ9FUERA= -github.com/ferranbt/fastssz v0.1.2-0.20220723134332-b3d3034a4575/go.mod h1:U2ZsxlYyvGeQGmadhz8PlEqwkBzDIhHwd3xuKrg2JIs= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flashbots/go-boost-utils v0.3.5 h1:RApwo1+8SGKhEGWH/WFIhg21pkCCW+RpO7MkZwiMI6A= -github.com/flashbots/go-boost-utils v0.3.5/go.mod h1:vEfLpq6MLgdRtfWUG0fGDBbHXu0SyRInkw6ekLJeRIU= -github.com/flashbots/go-utils v0.4.5 h1:xTrVcfxQ+qpVVPyRBWUllwZAxbAijE06d9N7e7mmmlM= -github.com/flashbots/go-utils v0.4.5/go.mod h1:3YKyfbtetVIXuWKbZ9WmK8bSF20hSFXk0wCWDNHYFvE= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.10 h1:Ppdil79nN+Vc+mXfge0AuUgmKWuVv4eMqzoIVSdqZek= +github.com/ethereum/go-ethereum v1.13.10/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo= +github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE= +github.com/flashbots/go-boost-utils v1.8.0 h1:z3K1hw+Fbl9AGMNQKnK7Bvf0M/rKgjfruAEvra+Z8Mg= +github.com/flashbots/go-boost-utils v1.8.0/go.mod h1:Ry1Rw8Lx5v1rpAR0+IvR4sV10jYAeQaGVM3vRD8mYdM= +github.com/flashbots/go-utils v0.5.0 h1:ldjWta9B9//DJU2QcwRbErez3+1aKhSn6EoFc6d5kPY= +github.com/flashbots/go-utils v0.5.0/go.mod h1:LauDwifaRdSK0mS5X34GR59pJtUu1T/lOFNdff1BqtI= 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/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= -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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -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-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +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-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +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/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +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/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ= +github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.2.0/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/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -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/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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.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/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/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.2.0/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/google/go-cmp v0.5.0/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -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 v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -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.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.3-0.20220313090229-ca81a64b4204/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/huandu/go-clone v1.6.0 h1:HMo5uvg4wgfiy5FoGOqlFLQED/VGRm2D9Pi8g1FXPGc= +github.com/huandu/go-clone v1.6.0/go.mod h1:ReGivhG6op3GYr+UY3lS6mxjKp7MIGTknuU5TbTVaXE= +github.com/huandu/go-clone/generic v1.6.0 h1:Wgmt/fUZ28r16F2Y3APotFD59sHk1p78K0XLdbUYN5U= +github.com/huandu/go-clone/generic v1.6.0/go.mod h1:xgd9ZebcMsBWWcBx5mVMCoqMX24gLWr5lQicr+nVXNs= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +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/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/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/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +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.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-colorable v0.1.11/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.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +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.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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +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/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/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/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -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/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +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/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/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 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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.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/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= 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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw= +github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +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/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 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/testify v1.2.0/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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/supranational/blst v0.3.7 h1:QObqTzlW30Z947JMe0MH12mVhFOxgtDapuWvPvCEGDE= -github.com/supranational/blst v0.3.7/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +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/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +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/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c h1:4WU+p200eLYtBsx3M5CKXvkjVdf5SC3W9nMg37y0TFI= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/trailofbits/go-fuzz-utils v0.0.0-20210901195358-9657fcfd256c/go.mod h1:f3jBhpWvuZmue0HZK52GzRHJOYHYSILs/c8+K2S/J+o= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +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/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg= +github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -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.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +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.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/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-20190909091759-094676da4a83/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-20191227163750-53104e6ec876/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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-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/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 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/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/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/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.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-20181114220301-adae6a3d119a/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-20181220203305-927f97764cc3/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-20190327091125-710a502c58a2/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-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 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-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +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/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= @@ -415,161 +442,126 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-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-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-20181205085412-a5c9d58dba9a/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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/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-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-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/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-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM= -golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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/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/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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-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-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-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.3/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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -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= +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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 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/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -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-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 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= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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-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/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/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +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/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.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-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-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.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/server/backend.go b/server/backend.go index 3ce9025d..3309d541 100644 --- a/server/backend.go +++ b/server/backend.go @@ -1,9 +1,12 @@ package server -// Router paths -var ( +const ( + // Router paths pathStatus = "/eth/v1/builder/status" pathRegisterValidator = "/eth/v1/builder/validators" pathGetHeader = "/eth/v1/builder/header/{slot:[0-9]+}/{parent_hash:0x[a-fA-F0-9]+}/{pubkey:0x[a-fA-F0-9]+}" pathGetPayload = "/eth/v1/builder/blinded_blocks" + + // // Relay Monitor paths + // pathAuctionTranscript = "/monitor/v1/transcript" ) diff --git a/server/errors.go b/server/errors.go index b3ff008e..a9757b38 100644 --- a/server/errors.go +++ b/server/errors.go @@ -1,8 +1,9 @@ package server -import "fmt" +import "errors" -var ( - // ErrMissingRelayPubkey is returned if a new RelayEntry URL has no public key - ErrMissingRelayPubkey = fmt.Errorf("missing relay public key") -) +// ErrMissingRelayPubkey is returned if a new RelayEntry URL has no public key. +var ErrMissingRelayPubkey = errors.New("missing relay public key") + +// ErrPointAtInfinityPubkey is returned if a new RelayEntry URL has an all-zero public key. +var ErrPointAtInfinityPubkey = errors.New("relay public key cannot be the point-at-infinity") diff --git a/server/mock_relay.go b/server/mock_relay.go index 45a33570..fe6c6daa 100644 --- a/server/mock_relay.go +++ b/server/mock_relay.go @@ -10,19 +10,31 @@ import ( "testing" "time" + builderApi "github.com/attestantio/go-builder-client/api" + builderApiCapella "github.com/attestantio/go-builder-client/api/capella" + builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb" + builderApiV1 "github.com/attestantio/go-builder-client/api/v1" + builderSpec "github.com/attestantio/go-builder-client/spec" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/deneb" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/flashbots/go-boost-utils/bls" - "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/go-boost-utils/ssz" "github.com/gorilla/mux" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" ) -var ( +const ( mockRelaySecretKeyHex = "0x4e343a647c5a5c44d76c2c58b63f02cdf3a9a0ec40f102ebc26363b4b1b95033" - // mockRelayPublicKeyHex = "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249" +) + +var ( skBytes, _ = hexutil.Decode(mockRelaySecretKeyHex) - mockRelaySecretKey, _ = bls.SecretKeyFromBytes(skBytes[:]) - mockRelayPublicKey = bls.PublicKeyFromSecretKey(mockRelaySecretKey) + mockRelaySecretKey, _ = bls.SecretKeyFromBytes(skBytes) + mockRelayPublicKey, _ = bls.PublicKeyFromSecretKey(mockRelaySecretKey) ) // mockRelay is used to fake a relay's behavior. @@ -47,8 +59,8 @@ type mockRelay struct { handlerOverrideGetPayload func(w http.ResponseWriter, req *http.Request) // Default responses placeholders, used if overrider does not exist - GetHeaderResponse *types.GetHeaderResponse - GetPayloadResponse *types.GetPayloadResponse + GetHeaderResponse *builderSpec.VersionedSignedBuilderBid + GetPayloadResponse *builderApi.VersionedSubmitBlindedBlockResponse // Server section Server *httptest.Server @@ -58,6 +70,7 @@ type mockRelay struct { // newMockRelay creates a mocked relay which implements the backend.BoostBackend interface // A secret key must be provided to sign default and custom response messages func newMockRelay(t *testing.T) *mockRelay { + t.Helper() relay := &mockRelay{t: t, secretKey: mockRelaySecretKey, publicKey: mockRelayPublicKey, requestCount: make(map[string]int)} // Initialize server @@ -66,7 +79,7 @@ func newMockRelay(t *testing.T) *mockRelay { // Create the RelayEntry with correct pubkey url, err := url.Parse(relay.Server.URL) require.NoError(t, err) - urlWithKey := fmt.Sprintf("%s://%s@%s", url.Scheme, hexutil.Encode(mockRelayPublicKey.Compress()), url.Host) + urlWithKey := fmt.Sprintf("%s://%s@%s", url.Scheme, hexutil.Encode(bls.PublicKeyToBytes(mockRelayPublicKey)), url.Host) relay.RelayEntry, err = NewRelayEntry(urlWithKey) require.NoError(t, err) return relay @@ -92,7 +105,7 @@ func (m *mockRelay) newTestMiddleware(next http.Handler) http.Handler { ) } -// getRouter registers all methods from the backend, apply the test middleware a,nd return the configured router +// getRouter registers all methods from the backend, apply the test middleware and return the configured router func (m *mockRelay) getRouter() http.Handler { // Create router. r := mux.NewRouter() @@ -115,20 +128,20 @@ func (m *mockRelay) GetRequestCount(path string) int { } // By default, handleRoot returns the relay's status -func (m *mockRelay) handleRoot(w http.ResponseWriter, req *http.Request) { +func (m *mockRelay) handleRoot(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{}`) } // By default, handleStatus returns the relay's status as http.StatusOK -func (m *mockRelay) handleStatus(w http.ResponseWriter, req *http.Request) { +func (m *mockRelay) handleStatus(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) fmt.Fprintf(w, `{}`) } -// By default, handleRegisterValidator returns a default types.SignedValidatorRegistration +// By default, handleRegisterValidator returns a default builderApiV1.SignedValidatorRegistration func (m *mockRelay) handleRegisterValidator(w http.ResponseWriter, req *http.Request) { m.mu.Lock() defer m.mu.Unlock() @@ -136,8 +149,12 @@ func (m *mockRelay) handleRegisterValidator(w http.ResponseWriter, req *http.Req m.handlerOverrideRegisterValidator(w, req) return } + m.defaultHandleRegisterValidator(w, req) +} - payload := []types.SignedValidatorRegistration{} +// defaultHandleRegisterValidator returns the default handler for handleRegisterValidator +func (m *mockRelay) defaultHandleRegisterValidator(w http.ResponseWriter, req *http.Request) { + payload := []builderApiV1.SignedValidatorRegistration{} if err := DecodeJSON(req.Body, &payload); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return @@ -149,28 +166,59 @@ func (m *mockRelay) handleRegisterValidator(w http.ResponseWriter, req *http.Req // MakeGetHeaderResponse is used to create the default or can be used to create a custom response to the getHeader // method -func (m *mockRelay) MakeGetHeaderResponse(value uint64, hash, publicKey string) *types.GetHeaderResponse { - // Fill the payload with custom values. - message := &types.BuilderBid{ - Header: &types.ExecutionPayloadHeader{ - BlockHash: _HexToHash(hash), - ParentHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), - }, - Value: types.IntToU256(value), - Pubkey: _HexToPubkey(publicKey), - } - - // Sign the message. - signature, err := types.SignMessage(message, types.DomainBuilder, m.secretKey) - require.NoError(m.t, err) - - return &types.GetHeaderResponse{ - Version: "bellatrix", - Data: &types.SignedBuilderBid{ - Message: message, - Signature: signature, - }, +func (m *mockRelay) MakeGetHeaderResponse(value uint64, blockHash, parentHash, publicKey string, version spec.DataVersion) *builderSpec.VersionedSignedBuilderBid { + switch version { + case spec.DataVersionCapella: + // Fill the payload with custom values. + message := &builderApiCapella.BuilderBid{ + Header: &capella.ExecutionPayloadHeader{ + BlockHash: _HexToHash(blockHash), + ParentHash: _HexToHash(parentHash), + WithdrawalsRoot: phase0.Root{}, + }, + Value: uint256.NewInt(value), + Pubkey: _HexToPubkey(publicKey), + } + + // Sign the message. + signature, err := ssz.SignMessage(message, ssz.DomainBuilder, m.secretKey) + require.NoError(m.t, err) + + return &builderSpec.VersionedSignedBuilderBid{ + Version: spec.DataVersionCapella, + Capella: &builderApiCapella.SignedBuilderBid{ + Message: message, + Signature: signature, + }, + } + case spec.DataVersionDeneb: + message := &builderApiDeneb.BuilderBid{ + Header: &deneb.ExecutionPayloadHeader{ + BlockHash: _HexToHash(blockHash), + ParentHash: _HexToHash(parentHash), + WithdrawalsRoot: phase0.Root{}, + BaseFeePerGas: uint256.NewInt(0), + }, + BlobKZGCommitments: make([]deneb.KZGCommitment, 0), + Value: uint256.NewInt(value), + Pubkey: _HexToPubkey(publicKey), + } + + // Sign the message. + signature, err := ssz.SignMessage(message, ssz.DomainBuilder, m.secretKey) + require.NoError(m.t, err) + + return &builderSpec.VersionedSignedBuilderBid{ + Version: spec.DataVersionDeneb, + Deneb: &builderApiDeneb.SignedBuilderBid{ + Message: message, + Signature: signature, + }, + } + case spec.DataVersionUnknown, spec.DataVersionPhase0, spec.DataVersionAltair, spec.DataVersionBellatrix: + return nil } + return nil } // handleGetHeader handles incoming requests to server.pathGetHeader @@ -182,7 +230,11 @@ func (m *mockRelay) handleGetHeader(w http.ResponseWriter, req *http.Request) { m.handlerOverrideGetHeader(w, req) return } + m.defaultHandleGetHeader(w) +} +// defaultHandleGetHeader returns the default handler for handleGetHeader +func (m *mockRelay) defaultHandleGetHeader(w http.ResponseWriter) { // By default, everything will be ok. w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -191,7 +243,9 @@ func (m *mockRelay) handleGetHeader(w http.ResponseWriter, req *http.Request) { response := m.MakeGetHeaderResponse( 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) if m.GetHeaderResponse != nil { response = m.GetHeaderResponse @@ -205,14 +259,15 @@ func (m *mockRelay) handleGetHeader(w http.ResponseWriter, req *http.Request) { // MakeGetPayloadResponse is used to create the default or can be used to create a custom response to the getPayload // method -func (m *mockRelay) MakeGetPayloadResponse(parentHash, blockHash, feeRecipient string, blockNumber uint64) *types.GetPayloadResponse { - return &types.GetPayloadResponse{ - Version: "bellatrix", - Data: &types.ExecutionPayload{ +func (m *mockRelay) MakeGetPayloadResponse(parentHash, blockHash, feeRecipient string, blockNumber uint64, version spec.DataVersion) *builderApi.VersionedSubmitBlindedBlockResponse { + return &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: version, + Capella: &capella.ExecutionPayload{ ParentHash: _HexToHash(parentHash), BlockHash: _HexToHash(blockHash), BlockNumber: blockNumber, FeeRecipient: _HexToAddress(feeRecipient), + Withdrawals: make([]*capella.Withdrawal, 0), }, } } @@ -226,7 +281,11 @@ func (m *mockRelay) handleGetPayload(w http.ResponseWriter, req *http.Request) { m.handlerOverrideGetPayload(w, req) return } + m.defaultHandleGetPayload(w) +} +// defaultHandleGetPayload returns the default handler for handleGetPayload +func (m *mockRelay) defaultHandleGetPayload(w http.ResponseWriter) { // By default, everything will be ok. w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -234,10 +293,12 @@ func (m *mockRelay) handleGetPayload(w http.ResponseWriter, req *http.Request) { // Build the default response. response := m.MakeGetPayloadResponse( "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", - "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab1", + "0x534809bd2b6832edff8d8ce4cb0e50068804fd1ef432c8362ad708a74fdc0e46", "0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941", 12345, + spec.DataVersionCapella, ) + if m.GetPayloadResponse != nil { response = m.GetPayloadResponse } diff --git a/server/mock_relay_test.go b/server/mock_relay_test.go index 0dde3470..fce4bdd7 100644 --- a/server/mock_relay_test.go +++ b/server/mock_relay_test.go @@ -12,7 +12,7 @@ import ( func Test_mockRelay(t *testing.T) { t.Run("bad payload", func(t *testing.T) { relay := newMockRelay(t) - req, err := http.NewRequest("POST", pathRegisterValidator, bytes.NewReader([]byte("123"))) + req, err := http.NewRequest(http.MethodPost, pathRegisterValidator, bytes.NewReader([]byte("123"))) require.NoError(t, err) rr := httptest.NewRecorder() relay.getRouter().ServeHTTP(rr, req) diff --git a/server/mock_types.go b/server/mock_types.go index 0f6822bf..a956b94b 100644 --- a/server/mock_types.go +++ b/server/mock_types.go @@ -1,13 +1,15 @@ package server import ( + "github.com/attestantio/go-eth2-client/spec/bellatrix" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/go-boost-utils/utils" "github.com/sirupsen/logrus" ) // testLog is used to log information in the test methods -var testLog = logrus.WithField("testing", true) +var testLog = logrus.NewEntry(logrus.New()) // _HexToBytes converts a hexadecimal string to a byte array func _HexToBytes(hex string) []byte { @@ -19,8 +21,8 @@ func _HexToBytes(hex string) []byte { } // _HexToHash converts a hexadecimal string to an Ethereum hash -func _HexToHash(s string) (ret types.Hash) { - err := ret.UnmarshalText([]byte(s)) +func _HexToHash(s string) (ret phase0.Hash32) { + ret, err := utils.HexToHash(s) if err != nil { testLog.Error(err, " _HexToHash: ", s) panic(err) @@ -29,8 +31,8 @@ func _HexToHash(s string) (ret types.Hash) { } // _HexToAddress converts a hexadecimal string to an Ethereum address -func _HexToAddress(s string) (ret types.Address) { - err := ret.UnmarshalText([]byte(s)) +func _HexToAddress(s string) (ret bellatrix.ExecutionAddress) { + ret, err := utils.HexToAddress(s) if err != nil { testLog.Error(err, " _HexToAddress: ", s) panic(err) @@ -39,8 +41,8 @@ func _HexToAddress(s string) (ret types.Address) { } // _HexToPubkey converts a hexadecimal string to a BLS Public Key -func _HexToPubkey(s string) (ret types.PublicKey) { - err := ret.UnmarshalText([]byte(s)) +func _HexToPubkey(s string) (ret phase0.BLSPubKey) { + ret, err := utils.HexToPubkey(s) if err != nil { testLog.Error(err, " _HexToPubkey: ", s) panic(err) @@ -49,8 +51,8 @@ func _HexToPubkey(s string) (ret types.PublicKey) { } // _HexToSignature converts a hexadecimal string to a BLS Signature -func _HexToSignature(s string) (ret types.Signature) { - err := ret.UnmarshalText([]byte(s)) +func _HexToSignature(s string) (ret phase0.BLSSignature) { + ret, err := utils.HexToSignature(s) if err != nil { testLog.Error(err, " _HexToSignature: ", s) panic(err) diff --git a/server/mock_types_test.go b/server/mock_types_test.go index 51d78b10..adc156e5 100644 --- a/server/mock_types_test.go +++ b/server/mock_types_test.go @@ -3,10 +3,15 @@ package server import ( "testing" + builderApiCapella "github.com/attestantio/go-builder-client/api/capella" + "github.com/attestantio/go-eth2-client/spec/bellatrix" + "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/flashbots/go-boost-utils/bls" - "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/go-boost-utils/utils" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" ) @@ -55,7 +60,7 @@ func TestHexToHash(t *testing.T) { hex string expectedPanic bool - expectedHash *types.Hash + expectedHash *phase0.Hash32 }{ { name: "Should panic because of empty hexadecimal input", @@ -75,7 +80,7 @@ func TestHexToHash(t *testing.T) { name: "Should not panic and convert hexadecimal input to hash", hex: common.Hash{0x01}.String(), expectedPanic: false, - expectedHash: &types.Hash{0x01}, + expectedHash: &phase0.Hash32{0x01}, }, } @@ -102,7 +107,7 @@ func TestHexToAddress(t *testing.T) { hex string expectedPanic bool - expectedAddress *types.Address + expectedAddress *bellatrix.ExecutionAddress }{ { name: "Should panic because of empty hexadecimal input", @@ -122,7 +127,7 @@ func TestHexToAddress(t *testing.T) { name: "Should not panic and convert hexadecimal input to address", hex: common.Address{0x01}.String(), expectedPanic: false, - expectedAddress: &types.Address{0x01}, + expectedAddress: &bellatrix.ExecutionAddress{0x01}, }, } @@ -149,7 +154,7 @@ func TestHexToPublicKey(t *testing.T) { hex string expectedPanic bool - expectedPublicKey *types.PublicKey + expectedPublicKey *phase0.BLSPubKey }{ { name: "Should panic because of empty hexadecimal input", @@ -167,9 +172,9 @@ func TestHexToPublicKey(t *testing.T) { */ { name: "Should not panic and convert hexadecimal input to public key", - hex: types.PublicKey{0x01}.String(), + hex: phase0.BLSPubKey{0x01}.String(), expectedPanic: false, - expectedPublicKey: &types.PublicKey{0x01}, + expectedPublicKey: &phase0.BLSPubKey{0x01}, }, } @@ -195,24 +200,23 @@ func TestHexToSignature(t *testing.T) { privateKey, blsPublicKey, err := bls.GenerateNewKeypair() require.NoError(t, err) - publicKey := hexutil.Encode(blsPublicKey.Compress()) + publicKey := hexutil.Encode(bls.PublicKeyToBytes(blsPublicKey)) - message := &types.BuilderBid{ - Header: &types.ExecutionPayloadHeader{ + message := &builderApiCapella.BuilderBid{ + Header: &capella.ExecutionPayloadHeader{ BlockHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), }, - Value: types.IntToU256(12345), + Value: uint256.NewInt(12345), Pubkey: _HexToPubkey(publicKey), } ssz, err := message.MarshalSSZ() require.NoError(t, err) sig := bls.Sign(privateKey, ssz) - sigBytes := sig.Compress() + sigBytes := bls.SignatureToBytes(sig) - // Convert bls.Signature bytes to types.Signature - signature := &types.Signature{} - err = signature.FromSlice(sigBytes) + // Convert bls.Signature bytes to phase0.BLSSignature + signature, err := utils.BlsSignatureToSignature(sig) require.NoError(t, err) testCases := []struct { @@ -220,7 +224,7 @@ func TestHexToSignature(t *testing.T) { hex string expectedPanic bool - expectedSignature *types.Signature + expectedSignature *phase0.BLSSignature }{ { name: "Should panic because of empty hexadecimal input", @@ -240,7 +244,7 @@ func TestHexToSignature(t *testing.T) { name: "Should not panic and convert hexadecimal input to signature", hex: hexutil.Encode(sigBytes), expectedPanic: false, - expectedSignature: signature, + expectedSignature: &signature, }, } diff --git a/server/relay_entry.go b/server/relay_entry.go index b3329c38..162d98ca 100644 --- a/server/relay_entry.go +++ b/server/relay_entry.go @@ -1,15 +1,20 @@ package server import ( + "bytes" "net/url" "strings" - "github.com/flashbots/go-boost-utils/types" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/flashbots/go-boost-utils/utils" ) +// The point-at-infinity is 48 zero bytes. +var pointAtInfinityPubkey = [48]byte{} + // RelayEntry represents a relay that mev-boost connects to. type RelayEntry struct { - PublicKey types.PublicKey + PublicKey phase0.BLSPubKey URL *url.URL } @@ -17,12 +22,9 @@ func (r *RelayEntry) String() string { return r.URL.String() } -// GetURI returns the full request URI with scheme, host, path and args. +// GetURI returns the full request URI with scheme, host, path and args for the relay. func (r *RelayEntry) GetURI(path string) string { - u2 := *r.URL - u2.User = nil - u2.Path = path - return u2.String() + return GetURI(r.URL, path) } // NewRelayEntry creates a new instance based on an input string @@ -44,6 +46,25 @@ func NewRelayEntry(relayURL string) (entry RelayEntry, err error) { return entry, ErrMissingRelayPubkey } - err = entry.PublicKey.UnmarshalText([]byte(entry.URL.User.Username())) - return entry, err + // Convert the username string to a public key. + entry.PublicKey, err = utils.HexToPubkey(entry.URL.User.Username()) + if err != nil { + return entry, err + } + + // Check if the public key is the point-at-infinity. + if bytes.Equal(entry.PublicKey[:], pointAtInfinityPubkey[:]) { + return entry, ErrPointAtInfinityPubkey + } + + return entry, nil +} + +// RelayEntriesToStrings returns the string representation of a list of relay entries +func RelayEntriesToStrings(relays []RelayEntry) []string { + ret := make([]string, len(relays)) + for i, entry := range relays { + ret[i] = entry.String() + } + return ret } diff --git a/server/relay_entry_test.go b/server/relay_entry_test.go index 7c9bee08..ffb0d182 100644 --- a/server/relay_entry_test.go +++ b/server/relay_entry_test.go @@ -4,13 +4,14 @@ import ( "fmt" "testing" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/flashbots/go-boost-utils/types" "github.com/stretchr/testify/require" ) func TestParseRelaysURLs(t *testing.T) { // Used to fake a relay's public key. - publicKey := types.PublicKey{0x01} + publicKey := phase0.BLSPubKey{0x01} testCases := []struct { name string @@ -23,61 +24,58 @@ func TestParseRelaysURLs(t *testing.T) { expectedURL string }{ { - name: "Relay URL with protocol scheme", - relayURL: fmt.Sprintf("http://%s@foo.com", publicKey.String()), - + name: "Relay URL with protocol scheme", + relayURL: fmt.Sprintf("http://%s@foo.com", publicKey.String()), expectedURI: "http://foo.com", expectedPublicKey: publicKey.String(), expectedURL: fmt.Sprintf("http://%s@foo.com", publicKey.String()), }, { - name: "Relay URL without protocol scheme, without public key", - relayURL: "foo.com", - + name: "Relay URL without protocol scheme, without public key", + relayURL: "foo.com", expectedErr: ErrMissingRelayPubkey, }, { - name: "Relay URL without protocol scheme and with public key", - relayURL: publicKey.String() + "@foo.com", - + name: "Relay URL without protocol scheme and with public key", + relayURL: publicKey.String() + "@foo.com", expectedURI: "http://foo.com", expectedPublicKey: publicKey.String(), expectedURL: "http://" + publicKey.String() + "@foo.com", }, { - name: "Relay URL with public key host and port", - relayURL: publicKey.String() + "@foo.com:9999", - + name: "Relay URL with public key host and port", + relayURL: publicKey.String() + "@foo.com:9999", expectedURI: "http://foo.com:9999", expectedPublicKey: publicKey.String(), expectedURL: "http://" + publicKey.String() + "@foo.com:9999", }, { - name: "Relay URL with IP and port", - relayURL: publicKey.String() + "@12.345.678:9999", - + name: "Relay URL with IP and port", + relayURL: publicKey.String() + "@12.345.678:9999", expectedURI: "http://12.345.678:9999", expectedPublicKey: publicKey.String(), expectedURL: "http://" + publicKey.String() + "@12.345.678:9999", }, { - name: "Relay URL with https IP and port", - relayURL: "https://" + publicKey.String() + "@12.345.678:9999", - + name: "Relay URL with https IP and port", + relayURL: "https://" + publicKey.String() + "@12.345.678:9999", expectedURI: "https://12.345.678:9999", expectedPublicKey: publicKey.String(), expectedURL: "https://" + publicKey.String() + "@12.345.678:9999", }, { - name: "Invalid relay public key", - relayURL: "http://0x123456@foo.com", - + name: "Relay URL with invalid public key", + relayURL: "http://0x123456@foo.com", expectedErr: types.ErrLength, }, { - name: "Relay URL with query arg", - relayURL: fmt.Sprintf("http://%s@foo.com?id=foo&bar=1", publicKey.String()), - + name: "Relay URL with point-at-infinity public key", + relayURL: "http://0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000@foo.com", + expectedErr: ErrPointAtInfinityPubkey, + }, + { + name: "Relay URL with query arg", + relayURL: fmt.Sprintf("http://%s@foo.com?id=foo&bar=1", publicKey.String()), expectedURI: "http://foo.com?id=foo&bar=1", expectedPublicKey: publicKey.String(), expectedURL: fmt.Sprintf("http://%s@foo.com?id=foo&bar=1", publicKey.String()), diff --git a/server/service.go b/server/service.go index f3ea5df3..897d67ba 100644 --- a/server/service.go +++ b/server/service.go @@ -1,91 +1,144 @@ package server import ( + "bytes" "context" "encoding/json" "errors" "fmt" + "io" "net/http" + "net/url" "strconv" "strings" "sync" "sync/atomic" "time" + builderApi "github.com/attestantio/go-builder-client/api" + builderApiV1 "github.com/attestantio/go-builder-client/api/v1" + builderSpec "github.com/attestantio/go-builder-client/spec" + eth2ApiV1Bellatrix "github.com/attestantio/go-eth2-client/api/v1/bellatrix" + eth2ApiV1Capella "github.com/attestantio/go-eth2-client/api/v1/capella" + eth2ApiV1Deneb "github.com/attestantio/go-eth2-client/api/v1/deneb" + "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/flashbots/go-boost-utils/ssz" "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/go-boost-utils/utils" "github.com/flashbots/go-utils/httplogger" "github.com/flashbots/mev-boost/config" + "github.com/google/uuid" "github.com/gorilla/mux" "github.com/sirupsen/logrus" ) var ( + errNoRelays = errors.New("no relays") errInvalidSlot = errors.New("invalid slot") errInvalidHash = errors.New("invalid hash") errInvalidPubkey = errors.New("invalid pubkey") errNoSuccessfulRelayResponse = errors.New("no successful relay response") - - errServerAlreadyRunning = errors.New("server already running") + errServerAlreadyRunning = errors.New("server already running") ) -var nilHash = types.Hash{} -var nilResponse = struct{}{} +var ( + nilHash = phase0.Hash32{} + nilResponse = struct{}{} +) type httpErrorResp struct { Code int `json:"code"` Message string `json:"message"` } +// AuctionTranscript is the bid and blinded block received from the relay send to the relay monitor +type AuctionTranscript struct { + Bid *builderSpec.VersionedSignedBuilderBid // TODO: proper json marshalling and unmarshalling + Acceptance *eth2ApiV1Bellatrix.SignedBlindedBeaconBlock `json:"acceptance"` +} + +type slotUID struct { + slot uint64 + uid uuid.UUID +} + // BoostServiceOpts provides all available options for use with NewBoostService type BoostServiceOpts struct { Log *logrus.Entry ListenAddr string Relays []RelayEntry + RelayMonitors []*url.URL GenesisForkVersionHex string - RelayRequestTimeout time.Duration + GenesisTime uint64 RelayCheck bool + RelayMinBid types.U256Str + + RequestTimeoutGetHeader time.Duration + RequestTimeoutGetPayload time.Duration + RequestTimeoutRegVal time.Duration + RequestMaxRetries int } // BoostService - the mev-boost service type BoostService struct { - listenAddr string - relays []RelayEntry - log *logrus.Entry - srv *http.Server - relayCheck bool - - builderSigningDomain types.Domain - httpClient http.Client + listenAddr string + relays []RelayEntry + relayMonitors []*url.URL + log *logrus.Entry + srv *http.Server + relayCheck bool + relayMinBid types.U256Str + genesisTime uint64 + + builderSigningDomain phase0.Domain + httpClientGetHeader http.Client + httpClientGetPayload http.Client + httpClientRegVal http.Client + requestMaxRetries int - bidsLock sync.Mutex bids map[bidRespKey]bidResp // keeping track of bids, to log the originating relay on withholding + bidsLock sync.Mutex + + slotUID *slotUID + slotUIDLock sync.Mutex } // NewBoostService created a new BoostService func NewBoostService(opts BoostServiceOpts) (*BoostService, error) { if len(opts.Relays) == 0 { - return nil, errors.New("no relays") + return nil, errNoRelays } - builderSigningDomain, err := ComputeDomain(types.DomainTypeAppBuilder, opts.GenesisForkVersionHex, types.Root{}.String()) + builderSigningDomain, err := ComputeDomain(ssz.DomainTypeAppBuilder, opts.GenesisForkVersionHex, phase0.Root{}.String()) if err != nil { return nil, err } return &BoostService{ - listenAddr: opts.ListenAddr, - relays: opts.Relays, - log: opts.Log.WithField("module", "service"), - relayCheck: opts.RelayCheck, - bids: make(map[bidRespKey]bidResp), + listenAddr: opts.ListenAddr, + relays: opts.Relays, + relayMonitors: opts.RelayMonitors, + log: opts.Log, + relayCheck: opts.RelayCheck, + relayMinBid: opts.RelayMinBid, + genesisTime: opts.GenesisTime, + bids: make(map[bidRespKey]bidResp), + slotUID: &slotUID{}, builderSigningDomain: builderSigningDomain, - httpClient: http.Client{ - Timeout: opts.RelayRequestTimeout, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, + httpClientGetHeader: http.Client{ + Timeout: opts.RequestTimeoutGetHeader, + CheckRedirect: httpClientDisallowRedirects, + }, + httpClientGetPayload: http.Client{ + Timeout: opts.RequestTimeoutGetPayload, + CheckRedirect: httpClientDisallowRedirects, + }, + httpClientRegVal: http.Client{ + Timeout: opts.RequestTimeoutRegVal, + CheckRedirect: httpClientDisallowRedirects, }, + requestMaxRetries: opts.RequestMaxRetries, }, nil } @@ -143,7 +196,7 @@ func (m *BoostService) StartHTTPServer() error { } err := m.srv.ListenAndServe() - if err == http.ErrServerClosed { + if errors.Is(err, http.ErrServerClosed) { return nil } return err @@ -162,50 +215,47 @@ func (m *BoostService) startBidCacheCleanupTask() { } } -func (m *BoostService) handleRoot(w http.ResponseWriter, req *http.Request) { - m.respondOK(w, nilResponse) -} - -// handleStatus sends calls to the status endpoint of every relay. -// It returns OK if at least one returned OK, and returns error otherwise. -func (m *BoostService) handleStatus(w http.ResponseWriter, req *http.Request) { - if !m.relayCheck { - m.respondOK(w, nilResponse) - return - } - - // If relayCheck is enabled, make sure at least 1 relay returns success - var wg sync.WaitGroup - var numSuccessRequestsToRelay uint32 - ua := UserAgent(req.Header.Get("User-Agent")) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - for _, r := range m.relays { - wg.Add(1) - - go func(relay RelayEntry) { - defer wg.Done() - url := relay.GetURI(pathStatus) - log := m.log.WithField("url", url) - log.Debug("Checking relay status") - - _, err := SendHTTPRequest(ctx, m.httpClient, http.MethodGet, url, ua, nil, nil) - if err != nil && ctx.Err() != context.Canceled { - log.WithError(err).Error("failed to retrieve relay status") +func (m *BoostService) sendValidatorRegistrationsToRelayMonitors(payload []builderApiV1.SignedValidatorRegistration) { + log := m.log.WithField("method", "sendValidatorRegistrationsToRelayMonitors").WithField("numRegistrations", len(payload)) + for _, relayMonitor := range m.relayMonitors { + go func(relayMonitor *url.URL) { + url := GetURI(relayMonitor, pathRegisterValidator) + log = log.WithField("url", url) + _, err := SendHTTPRequest(context.Background(), m.httpClientRegVal, http.MethodPost, url, "", nil, payload, nil) + if err != nil { + log.WithError(err).Warn("error calling registerValidator on relay monitor") return } - - // Success: increase counter and cancel all pending requests to other relays - atomic.AddUint32(&numSuccessRequestsToRelay, 1) - cancel() - }(r) + log.Debug("sent validator registrations to relay monitor") + }(relayMonitor) } +} - // At the end, wait for every routine and return status according to relay's ones. - wg.Wait() +// func (m *BoostService) sendAuctionTranscriptToRelayMonitors(transcript *AuctionTranscript) { +// log := m.log.WithField("method", "sendAuctionTranscriptToRelayMonitors") +// for _, relayMonitor := range m.relayMonitors { +// go func(relayMonitor *url.URL) { +// url := GetURI(relayMonitor, pathAuctionTranscript) +// log := log.WithField("url", url) +// _, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodPost, url, UserAgent(""), nil, transcript, nil) +// if err != nil { +// log.WithError(err).Warn("error sending auction transcript to relay monitor") +// return +// } +// log.Debug("sent auction transcript to relay monitor") +// }(relayMonitor) +// } +// } + +func (m *BoostService) handleRoot(w http.ResponseWriter, _ *http.Request) { + m.respondOK(w, nilResponse) +} - if numSuccessRequestsToRelay > 0 { +// handleStatus sends calls to the status endpoint of every relay. +// It returns OK if at least one returned OK, and returns error otherwise. +func (m *BoostService) handleStatus(w http.ResponseWriter, _ *http.Request) { + w.Header().Set(HeaderKeyVersion, config.Version) + if !m.relayCheck || m.CheckRelays() > 0 { m.respondOK(w, nilResponse) } else { m.respondError(w, http.StatusServiceUnavailable, "all relays are unavailable") @@ -217,7 +267,7 @@ func (m *BoostService) handleRegisterValidator(w http.ResponseWriter, req *http. log := m.log.WithField("method", "registerValidator") log.Debug("registerValidator") - payload := []types.SignedValidatorRegistration{} + payload := []builderApiV1.SignedValidatorRegistration{} if err := DecodeJSON(req.Body, &payload); err != nil { m.respondError(w, http.StatusBadRequest, err.Error()) return @@ -236,7 +286,7 @@ func (m *BoostService) handleRegisterValidator(w http.ResponseWriter, req *http. url := relay.GetURI(pathRegisterValidator) log := log.WithField("url", url) - _, err := SendHTTPRequest(context.Background(), m.httpClient, http.MethodPost, url, ua, payload, nil) + _, err := SendHTTPRequest(context.Background(), m.httpClientRegVal, http.MethodPost, url, ua, nil, payload, nil) relayRespCh <- err if err != nil { log.WithError(err).Warn("error calling registerValidator on relay") @@ -245,6 +295,8 @@ func (m *BoostService) handleRegisterValidator(w http.ResponseWriter, req *http. }(relay) } + go m.sendValidatorRegistrationsToRelayMonitors(payload) + for i := 0; i < len(m.relays); i++ { respErr := <-relayRespCh if respErr == nil { @@ -262,11 +314,14 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) slot := vars["slot"] parentHashHex := vars["parent_hash"] pubkey := vars["pubkey"] + + ua := UserAgent(req.Header.Get("User-Agent")) log := m.log.WithFields(logrus.Fields{ "method": "getHeader", "slot": slot, "parentHash": parentHashHex, "pubkey": pubkey, + "ua": ua, }) log.Debug("getHeader") @@ -286,13 +341,36 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) return } - var mu sync.Mutex - relays := make(map[string][]string) // relays per blockHash - result := bidResp{} + // Make sure we have a uid for this slot + m.slotUIDLock.Lock() + if m.slotUID.slot < _slot { + m.slotUID.slot = _slot + m.slotUID.uid = uuid.New() + } + slotUID := m.slotUID.uid + m.slotUIDLock.Unlock() + log = log.WithField("slotUID", slotUID) - ua := UserAgent(req.Header.Get("User-Agent")) + // Log how late into the slot the request starts + slotStartTimestamp := m.genesisTime + _slot*config.SlotTimeSec + msIntoSlot := uint64(time.Now().UTC().UnixMilli()) - slotStartTimestamp*1000 + log.WithFields(logrus.Fields{ + "genesisTime": m.genesisTime, + "slotTimeSec": config.SlotTimeSec, + "msIntoSlot": msIntoSlot, + }).Infof("getHeader request start - %d milliseconds into slot %d", msIntoSlot, _slot) + + // Add request headers + headers := map[string]string{ + HeaderKeySlotUID: slotUID.String(), + } + + // Prepare relay responses + result := bidResp{} // the final response, containing the highest bid (if any) + relays := make(map[BlockHashHex][]RelayEntry) // relays that sent the bid for a specific blockHash // Call the relays + var mu sync.Mutex var wg sync.WaitGroup for _, relay := range m.relays { wg.Add(1) @@ -301,8 +379,8 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) path := fmt.Sprintf("/eth/v1/builder/header/%s/%s/%s", slot, parentHashHex, pubkey) url := relay.GetURI(path) log := log.WithField("url", url) - responsePayload := new(types.GetHeaderResponse) - code, err := SendHTTPRequest(context.Background(), m.httpClient, http.MethodGet, url, ua, nil, responsePayload) + responsePayload := new(builderSpec.VersionedSignedBuilderBid) + code, err := SendHTTPRequest(context.Background(), m.httpClientGetHeader, http.MethodGet, url, ua, headers, nil, responsePayload) if err != nil { log.WithError(err).Warn("error making request to relay") return @@ -313,79 +391,95 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) return } - // Skip if invalid payload - if responsePayload.Data == nil || responsePayload.Data.Message == nil || responsePayload.Data.Message.Header == nil || responsePayload.Data.Message.Header.BlockHash == nilHash { + // Skip if payload is empty + if responsePayload.IsEmpty() { + return + } + + // Getting the bid info will check if there are missing fields in the response + bidInfo, err := parseBidInfo(responsePayload) + if err != nil { + log.WithError(err).Warn("error parsing bid info") + return + } + + if bidInfo.blockHash == nilHash { + log.Warn("relay responded with empty block hash") return } - blockHash := responsePayload.Data.Message.Header.BlockHash.String() + valueEth := weiBigIntToEthBigFloat(bidInfo.value.ToBig()) log = log.WithFields(logrus.Fields{ - "blockNumber": responsePayload.Data.Message.Header.BlockNumber, - "blockHash": blockHash, - "txRoot": responsePayload.Data.Message.Header.TransactionsRoot.String(), - "value": responsePayload.Data.Message.Value.String(), + "blockNumber": bidInfo.blockNumber, + "blockHash": bidInfo.blockHash.String(), + "txRoot": bidInfo.txRoot.String(), + "value": valueEth.Text('f', 18), }) - if relay.PublicKey != responsePayload.Data.Message.Pubkey { - log.Errorf("bid pubkey mismatch. expected: %s - got: %s", relay.PublicKey.String(), responsePayload.Data.Message.Pubkey.String()) + if relay.PublicKey.String() != bidInfo.pubkey.String() { + log.Errorf("bid pubkey mismatch. expected: %s - got: %s", relay.PublicKey.String(), bidInfo.pubkey.String()) return } // Verify the relay signature in the relay response - ok, err := types.VerifySignature(responsePayload.Data.Message, m.builderSigningDomain, relay.PublicKey[:], responsePayload.Data.Signature[:]) - if err != nil { - log.WithError(err).Error("error verifying relay signature") - return - } - if !ok { - log.Error("failed to verify relay signature") - return + if !config.SkipRelaySignatureCheck { + ok, err := checkRelaySignature(responsePayload, m.builderSigningDomain, relay.PublicKey) + if err != nil { + log.WithError(err).Error("error verifying relay signature") + return + } + if !ok { + log.Error("failed to verify relay signature") + return + } } // Verify response coherence with proposer's input data - responseParentHash := responsePayload.Data.Message.Header.ParentHash.String() - if responseParentHash != parentHashHex { + if bidInfo.parentHash.String() != parentHashHex { log.WithFields(logrus.Fields{ "originalParentHash": parentHashHex, - "responseParentHash": responseParentHash, + "responseParentHash": bidInfo.parentHash.String(), }).Error("proposer and relay parent hashes are not the same") return } - isZeroValue := responsePayload.Data.Message.Value.String() == "0" - isEmptyListTxRoot := responsePayload.Data.Message.Header.TransactionsRoot.String() == "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1" + isZeroValue := bidInfo.value.IsZero() + isEmptyListTxRoot := bidInfo.txRoot.String() == "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1" if isZeroValue || isEmptyListTxRoot { log.Warn("ignoring bid with 0 value") return } + log.Debug("bid received") + + // Skip if value (fee) is lower than the minimum bid + if bidInfo.value.CmpBig(m.relayMinBid.BigInt()) == -1 { + log.Debug("ignoring bid below min-bid value") + return + } mu.Lock() defer mu.Unlock() // Remember which relays delivered which bids (multiple relays might deliver the top bid) - if _, ok := relays[blockHash]; !ok { - relays[blockHash] = []string{relay.String()} - } else { - relays[blockHash] = append(relays[blockHash], relay.String()) - } + relays[BlockHashHex(bidInfo.blockHash.String())] = append(relays[BlockHashHex(bidInfo.blockHash.String())], relay) // Compare the bid with already known top bid (if any) - if result.response.Data != nil { - valueDiff := responsePayload.Data.Message.Value.Cmp(&result.response.Data.Message.Value) + if !result.response.IsEmpty() { + valueDiff := bidInfo.value.Cmp(result.bidInfo.value) if valueDiff == -1 { // current bid is less profitable than already known one return } else if valueDiff == 0 { // current bid is equally profitable as already known one. Use hash as tiebreaker - previousBidBlockHash := result.response.Data.Message.Header.BlockHash.String() - if blockHash >= previousBidBlockHash { + previousBidBlockHash := result.bidInfo.blockHash + if bidInfo.blockHash.String() >= previousBidBlockHash.String() { return } } } // Use this relay's response as mev-boost response because it's most profitable - log.Debug("received a good bid") + log.Debug("new best bid") result.response = *responsePayload - result.blockHash = blockHash + result.bidInfo = bidInfo result.t = time.Now() }(relay) } @@ -393,52 +487,90 @@ func (m *BoostService) handleGetHeader(w http.ResponseWriter, req *http.Request) // Wait for all requests to complete... wg.Wait() - if result.blockHash == "" { + if result.response.IsEmpty() { log.Info("no bid received") w.WriteHeader(http.StatusNoContent) return } // Log result - result.relays = relays[result.blockHash] + valueEth := weiBigIntToEthBigFloat(result.bidInfo.value.ToBig()) + result.relays = relays[BlockHashHex(result.bidInfo.blockHash.String())] log.WithFields(logrus.Fields{ - "blockHash": result.blockHash, - "blockNumber": result.response.Data.Message.Header.BlockNumber, - "txRoot": result.response.Data.Message.Header.TransactionsRoot.String(), - "value": result.response.Data.Message.Value.String(), - "relays": strings.Join(result.relays, ", "), + "blockHash": result.bidInfo.blockHash.String(), + "blockNumber": result.bidInfo.blockNumber, + "txRoot": result.bidInfo.txRoot.String(), + "value": valueEth.Text('f', 18), + "relays": strings.Join(RelayEntriesToStrings(result.relays), ", "), }).Info("best bid") // Remember the bid, for future logging in case of withholding - bidKey := bidRespKey{slot: _slot, blockHash: result.blockHash} + bidKey := bidRespKey{slot: _slot, blockHash: result.bidInfo.blockHash.String()} m.bidsLock.Lock() m.bids[bidKey] = result m.bidsLock.Unlock() // Return the bid - m.respondOK(w, result.response) + m.respondOK(w, &result.response) } -func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request) { - log := m.log.WithField("method", "getPayload") - log.Debug("getPayload") - - payload := new(types.SignedBlindedBeaconBlock) - if err := DecodeJSON(req.Body, &payload); err != nil { - m.respondError(w, http.StatusBadRequest, err.Error()) - return - } - +func (m *BoostService) processCapellaPayload(w http.ResponseWriter, req *http.Request, log *logrus.Entry, payload *eth2ApiV1Capella.SignedBlindedBeaconBlock, body []byte) { if payload.Message == nil || payload.Message.Body == nil || payload.Message.Body.ExecutionPayloadHeader == nil { + log.WithField("body", string(body)).Error("missing parts of the request payload from the beacon-node") m.respondError(w, http.StatusBadRequest, "missing parts of the payload") return } - log = log.WithField("blockHash", payload.Message.Body.ExecutionPayloadHeader.BlockHash.String()) + // Get the slotUID for this slot + slotUID := "" + m.slotUIDLock.Lock() + if m.slotUID.slot == uint64(payload.Message.Slot) { + slotUID = m.slotUID.uid.String() + } else { + log.Warnf("latest slotUID is for slot %d rather than payload slot %d", m.slotUID.slot, payload.Message.Slot) + } + m.slotUIDLock.Unlock() + + // Prepare logger + ua := UserAgent(req.Header.Get("User-Agent")) + log = log.WithFields(logrus.Fields{ + "ua": ua, + "slot": payload.Message.Slot, + "blockHash": payload.Message.Body.ExecutionPayloadHeader.BlockHash.String(), + "parentHash": payload.Message.Body.ExecutionPayloadHeader.ParentHash.String(), + "slotUID": slotUID, + }) + + // Log how late into the slot the request starts + slotStartTimestamp := m.genesisTime + uint64(payload.Message.Slot)*config.SlotTimeSec + msIntoSlot := uint64(time.Now().UTC().UnixMilli()) - slotStartTimestamp*1000 + log.WithFields(logrus.Fields{ + "genesisTime": m.genesisTime, + "slotTimeSec": config.SlotTimeSec, + "msIntoSlot": msIntoSlot, + }).Infof("submitBlindedBlock request start - %d milliseconds into slot %d", msIntoSlot, payload.Message.Slot) + + // Get the bid! + bidKey := bidRespKey{slot: uint64(payload.Message.Slot), blockHash: payload.Message.Body.ExecutionPayloadHeader.BlockHash.String()} + m.bidsLock.Lock() + originalBid := m.bids[bidKey] + m.bidsLock.Unlock() + if originalBid.response.IsEmpty() { + log.Error("no bid for this getPayload payload found. was getHeader called before?") + } else if len(originalBid.relays) == 0 { + log.Warn("bid found but no associated relays") + } + + // send bid and signed block to relay monitor with eth2ApiV1Capella payload + // go m.sendAuctionTranscriptToRelayMonitors(&AuctionTranscript{Bid: originalBid.response.Data, Acceptance: payload}) + + // Add request headers + headers := map[string]string{HeaderKeySlotUID: slotUID} + + // Prepare for requests var wg sync.WaitGroup var mu sync.Mutex - result := new(types.GetPayloadResponse) - ua := UserAgent(req.Header.Get("User-Agent")) + result := new(builderApi.VersionedSubmitBlindedBlockResponse) // Prepare the request context, which will be cancelled after the first successful response from a relay requestCtx, requestCtxCancel := context.WithCancel(context.Background()) @@ -452,27 +584,44 @@ func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request log := log.WithField("url", url) log.Debug("calling getPayload") - responsePayload := new(types.GetPayloadResponse) - _, err := SendHTTPRequest(requestCtx, m.httpClient, http.MethodPost, url, ua, payload, responsePayload) - + responsePayload := new(builderApi.VersionedSubmitBlindedBlockResponse) + _, err := SendHTTPRequestWithRetries(requestCtx, m.httpClientGetPayload, http.MethodPost, url, ua, headers, payload, responsePayload, m.requestMaxRetries, log) if err != nil { - log.WithError(err).Error("error making request to relay") + if errors.Is(requestCtx.Err(), context.Canceled) { + log.Info("request was cancelled") // this is expected, if payload has already been received by another relay + } else { + log.WithError(err).Error("error making request to relay") + } return } - if responsePayload.Data == nil || responsePayload.Data.BlockHash == nilHash { + if getPayloadResponseIsEmpty(responsePayload) { log.Error("response with empty data!") return } // Ensure the response blockhash matches the request - if payload.Message.Body.ExecutionPayloadHeader.BlockHash != responsePayload.Data.BlockHash { + if payload.Message.Body.ExecutionPayloadHeader.BlockHash != responsePayload.Capella.BlockHash { log.WithFields(logrus.Fields{ - "responseBlockHash": responsePayload.Data.BlockHash.String(), + "responseBlockHash": responsePayload.Capella.BlockHash.String(), }).Error("requestBlockHash does not equal responseBlockHash") return } + // Ensure the response blockhash matches the response block + calculatedBlockHash, err := utils.ComputeBlockHash(&builderApi.VersionedExecutionPayload{ + Version: responsePayload.Version, + Capella: responsePayload.Capella, + }, nil) + if err != nil { + log.WithError(err).Error("could not calculate block hash") + } else if responsePayload.Capella.BlockHash != calculatedBlockHash { + log.WithFields(logrus.Fields{ + "calculatedBlockHash": calculatedBlockHash.String(), + "responseBlockHash": responsePayload.Capella.BlockHash.String(), + }).Error("responseBlockHash does not equal hash calculated from response block") + } + // Lock before accessing the shared payload mu.Lock() defer mu.Unlock() @@ -492,12 +641,9 @@ func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request wg.Wait() // If no payload has been received from relay, log loudly about withholding! - if result.Data == nil || result.Data.BlockHash == nilHash { - bidKey := bidRespKey{slot: payload.Message.Slot, blockHash: payload.Message.Body.ExecutionPayloadHeader.BlockHash.String()} - m.bidsLock.Lock() - originalResp := m.bids[bidKey] - m.bidsLock.Unlock() - log.WithField("relays", strings.Join(originalResp.relays, ", ")).Errorf("no payload received from relay -- withholding or network error --") + if result.Capella == nil || result.Capella.BlockHash == nilHash { + originRelays := RelayEntriesToStrings(originalBid.relays) + log.WithField("relaysWithBid", strings.Join(originRelays, ", ")).Error("no payload received from relay!") m.respondError(w, http.StatusBadGateway, errNoSuccessfulRelayResponse.Error()) return } @@ -505,18 +651,206 @@ func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request m.respondOK(w, result) } -// CheckRelays sends a request to each one of the relays previously registered to get their status -func (m *BoostService) CheckRelays() bool { +func (m *BoostService) processDenebPayload(w http.ResponseWriter, req *http.Request, log *logrus.Entry, blindedBlock *eth2ApiV1Deneb.SignedBlindedBeaconBlock) { + // Get the currentSlotUID for this slot + currentSlotUID := "" + m.slotUIDLock.Lock() + if m.slotUID.slot == uint64(blindedBlock.Message.Slot) { + currentSlotUID = m.slotUID.uid.String() + } else { + log.Warnf("latest slotUID is for slot %d rather than payload slot %d", m.slotUID.slot, blindedBlock.Message.Slot) + } + m.slotUIDLock.Unlock() + + // Prepare logger + ua := UserAgent(req.Header.Get("User-Agent")) + log = log.WithFields(logrus.Fields{ + "ua": ua, + "slot": blindedBlock.Message.Slot, + "blockHash": blindedBlock.Message.Body.ExecutionPayloadHeader.BlockHash.String(), + "parentHash": blindedBlock.Message.Body.ExecutionPayloadHeader.ParentHash.String(), + "slotUID": currentSlotUID, + }) + + // Log how late into the slot the request starts + slotStartTimestamp := m.genesisTime + uint64(blindedBlock.Message.Slot)*config.SlotTimeSec + msIntoSlot := uint64(time.Now().UTC().UnixMilli()) - slotStartTimestamp*1000 + log.WithFields(logrus.Fields{ + "genesisTime": m.genesisTime, + "slotTimeSec": config.SlotTimeSec, + "msIntoSlot": msIntoSlot, + }).Infof("submitBlindedBlock request start - %d milliseconds into slot %d", msIntoSlot, blindedBlock.Message.Slot) + + // Get the bid! + bidKey := bidRespKey{slot: uint64(blindedBlock.Message.Slot), blockHash: blindedBlock.Message.Body.ExecutionPayloadHeader.BlockHash.String()} + m.bidsLock.Lock() + originalBid := m.bids[bidKey] + m.bidsLock.Unlock() + if originalBid.response.IsEmpty() { + log.Error("no bid for this getPayload payload found, was getHeader called before?") + } else if len(originalBid.relays) == 0 { + log.Warn("bid found but no associated relays") + } + + // Add request headers + headers := map[string]string{HeaderKeySlotUID: currentSlotUID} + + // Prepare for requests + var wg sync.WaitGroup + var mu sync.Mutex + result := new(builderApi.VersionedSubmitBlindedBlockResponse) + + // Prepare the request context, which will be cancelled after the first successful response from a relay + requestCtx, requestCtxCancel := context.WithCancel(context.Background()) + defer requestCtxCancel() + for _, relay := range m.relays { - m.log.WithField("relay", relay.String()).Info("Checking relay") + wg.Add(1) + go func(relay RelayEntry) { + defer wg.Done() + url := relay.GetURI(pathGetPayload) + log := log.WithField("url", url) + log.Debug("calling getPayload") + + responsePayload := new(builderApi.VersionedSubmitBlindedBlockResponse) + _, err := SendHTTPRequestWithRetries(requestCtx, m.httpClientGetPayload, http.MethodPost, url, ua, headers, blindedBlock, responsePayload, m.requestMaxRetries, log) + if err != nil { + if errors.Is(requestCtx.Err(), context.Canceled) { + log.Info("request was cancelled") // this is expected, if payload has already been received by another relay + } else { + log.WithError(err).Error("error making request to relay") + } + return + } + + if getPayloadResponseIsEmpty(responsePayload) { + log.Error("response with empty data!") + return + } + + payload := responsePayload.Deneb.ExecutionPayload + blobs := responsePayload.Deneb.BlobsBundle + + // Ensure the response blockhash matches the request + if blindedBlock.Message.Body.ExecutionPayloadHeader.BlockHash != payload.BlockHash { + log.WithFields(logrus.Fields{ + "responseBlockHash": payload.BlockHash.String(), + }).Error("requestBlockHash does not equal responseBlockHash") + return + } + + commitments := blindedBlock.Message.Body.BlobKZGCommitments + // Ensure that blobs are valid and matches the request + if len(commitments) != len(blobs.Blobs) || len(commitments) != len(blobs.Commitments) || len(commitments) != len(blobs.Proofs) { + log.WithFields(logrus.Fields{ + "requestBlobCommitments": len(commitments), + "responseBlobs": len(blobs.Blobs), + "responseBlobCommitments": len(blobs.Commitments), + "responseBlobProofs": len(blobs.Proofs), + }).Error("block KZG commitment length does not equal responseBlobs length") + return + } + + for i, commitment := range commitments { + if commitment != blobs.Commitments[i] { + log.WithFields(logrus.Fields{ + "requestBlobCommitment": commitment.String(), + "responseBlobCommitment": blobs.Commitments[i].String(), + "index": i, + }).Error("requestBlobCommitment does not equal responseBlobCommitment") + return + } + } + + // Lock before accessing the shared payload + mu.Lock() + defer mu.Unlock() + + if requestCtx.Err() != nil { // request has been cancelled (or deadline exceeded) + return + } + + // Received successful response. Now cancel other requests and return immediately + requestCtxCancel() + *result = *responsePayload + log.Info("received payload from relay") + }(relay) + } + + // Wait for all requests to complete... + wg.Wait() + + // If no payload has been received from relay, log loudly about withholding! + if getPayloadResponseIsEmpty(result) { + originRelays := RelayEntriesToStrings(originalBid.relays) + log.WithField("relaysWithBid", strings.Join(originRelays, ", ")).Error("no payload received from relay!") + m.respondError(w, http.StatusBadGateway, errNoSuccessfulRelayResponse.Error()) + return + } + + m.respondOK(w, result) +} + +func (m *BoostService) handleGetPayload(w http.ResponseWriter, req *http.Request) { + log := m.log.WithField("method", "getPayload") + log.Debug("getPayload request starts") - url := relay.GetURI(pathStatus) - _, err := SendHTTPRequest(context.Background(), m.httpClient, http.MethodGet, url, "", nil, nil) - if err != nil { - m.log.WithError(err).WithField("relay", relay.String()).Error("relay check failed") - return false + // Read the body first, so we can log it later on error + body, err := io.ReadAll(req.Body) + if err != nil { + log.WithError(err).Error("could not read body of request from the beacon node") + m.respondError(w, http.StatusBadRequest, err.Error()) + return + } + + // Decode the body now + payload := new(eth2ApiV1Deneb.SignedBlindedBeaconBlock) + if err := DecodeJSON(bytes.NewReader(body), payload); err != nil { + log.Debug("could not decode Deneb request payload, attempting to decode body into Capella payload") + payload := new(eth2ApiV1Capella.SignedBlindedBeaconBlock) + if err := DecodeJSON(bytes.NewReader(body), payload); err != nil { + log.WithError(err).WithField("body", string(body)).Error("could not decode request payload from the beacon-node (signed blinded beacon block)") + m.respondError(w, http.StatusBadRequest, err.Error()) + return } + m.processCapellaPayload(w, req, log, payload, body) + return + } + m.processDenebPayload(w, req, log, payload) +} + +// CheckRelays sends a request to each one of the relays previously registered to get their status +func (m *BoostService) CheckRelays() int { + var wg sync.WaitGroup + var numSuccessRequestsToRelay uint32 + + for _, r := range m.relays { + wg.Add(1) + + go func(relay RelayEntry) { + defer wg.Done() + url := relay.GetURI(pathStatus) + log := m.log.WithField("url", url) + log.Debug("checking relay status") + + code, err := SendHTTPRequest(context.Background(), m.httpClientGetHeader, http.MethodGet, url, "", nil, nil, nil) + if err != nil { + log.WithError(err).Error("relay status error - request failed") + return + } + if code == http.StatusOK { + log.Debug("relay status OK") + } else { + log.Errorf("relay status error - unexpected status code %d", code) + return + } + + // Success: increase counter and cancel all pending requests to other relays + atomic.AddUint32(&numSuccessRequestsToRelay, 1) + }(r) } - return true + // At the end, wait for every routine and return status according to relay's ones. + wg.Wait() + return int(numSuccessRequestsToRelay) } diff --git a/server/service_test.go b/server/service_test.go index 89becfc1..33b2438e 100644 --- a/server/service_test.go +++ b/server/service_test.go @@ -9,11 +9,27 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" "strings" "testing" "time" + builderApi "github.com/attestantio/go-builder-client/api" + builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb" + builderApiV1 "github.com/attestantio/go-builder-client/api/v1" + builderSpec "github.com/attestantio/go-builder-client/spec" + eth2ApiV1Capella "github.com/attestantio/go-eth2-client/api/v1/capella" + eth2ApiV1Deneb "github.com/attestantio/go-eth2-client/api/v1/deneb" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/altair" + "github.com/attestantio/go-eth2-client/spec/bellatrix" + "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/deneb" + "github.com/attestantio/go-eth2-client/spec/phase0" + eth2UtilBellatrix "github.com/attestantio/go-eth2-client/util/bellatrix" "github.com/flashbots/go-boost-utils/types" + "github.com/holiman/uint256" + "github.com/prysmaticlabs/go-bitfield" "github.com/stretchr/testify/require" ) @@ -24,6 +40,7 @@ type testBackend struct { // newTestBackend creates a new backend, initializes mock relays, registers them and return the instance func newTestBackend(t *testing.T, numRelays int, relayTimeout time.Duration) *testBackend { + t.Helper() backend := testBackend{ relays: make([]*mockRelay, numRelays), } @@ -36,12 +53,16 @@ func newTestBackend(t *testing.T, numRelays int, relayTimeout time.Duration) *te } opts := BoostServiceOpts{ - Log: testLog, - ListenAddr: "localhost:12345", - Relays: relayEntries, - GenesisForkVersionHex: "0x00000000", - RelayRequestTimeout: relayTimeout, - RelayCheck: true, + Log: testLog, + ListenAddr: "localhost:12345", + Relays: relayEntries, + GenesisForkVersionHex: "0x00000000", + RelayCheck: true, + RelayMinBid: types.IntToU256(12345), + RequestTimeoutGetHeader: relayTimeout, + RequestTimeoutGetPayload: relayTimeout, + RequestTimeoutRegVal: relayTimeout, + RequestMaxRetries: 5, } service, err := NewBoostService(opts) require.NoError(t, err) @@ -50,7 +71,8 @@ func newTestBackend(t *testing.T, numRelays int, relayTimeout time.Duration) *te return &backend } -func (be *testBackend) request(t *testing.T, method string, path string, payload any) *httptest.ResponseRecorder { +func (be *testBackend) request(t *testing.T, method, path string, payload any) *httptest.ResponseRecorder { + t.Helper() var req *http.Request var err error @@ -68,9 +90,76 @@ func (be *testBackend) request(t *testing.T, method string, path string, payload return rr } +func blindedBlockToExecutionPayloadCapella(signedBlindedBeaconBlock *eth2ApiV1Capella.SignedBlindedBeaconBlock) *capella.ExecutionPayload { + header := signedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader + return &capella.ExecutionPayload{ + ParentHash: header.ParentHash, + FeeRecipient: header.FeeRecipient, + StateRoot: header.StateRoot, + ReceiptsRoot: header.ReceiptsRoot, + LogsBloom: header.LogsBloom, + PrevRandao: header.PrevRandao, + BlockNumber: header.BlockNumber, + GasLimit: header.GasLimit, + GasUsed: header.GasUsed, + Timestamp: header.Timestamp, + ExtraData: header.ExtraData, + BaseFeePerGas: header.BaseFeePerGas, + BlockHash: header.BlockHash, + Transactions: make([]bellatrix.Transaction, 0), + Withdrawals: make([]*capella.Withdrawal, 0), + } +} + +func blindedBlockContentsToPayloadDeneb(signedBlindedBlockContents *eth2ApiV1Deneb.SignedBlindedBeaconBlock) *builderApiDeneb.ExecutionPayloadAndBlobsBundle { + header := signedBlindedBlockContents.Message.Body.ExecutionPayloadHeader + numBlobs := len(signedBlindedBlockContents.Message.Body.BlobKZGCommitments) + commitments := make([]deneb.KZGCommitment, numBlobs) + copy(commitments, signedBlindedBlockContents.Message.Body.BlobKZGCommitments) + proofs := make([]deneb.KZGProof, numBlobs) + blobs := make([]deneb.Blob, numBlobs) + return &builderApiDeneb.ExecutionPayloadAndBlobsBundle{ + ExecutionPayload: &deneb.ExecutionPayload{ + ParentHash: header.ParentHash, + FeeRecipient: header.FeeRecipient, + StateRoot: header.StateRoot, + ReceiptsRoot: header.ReceiptsRoot, + LogsBloom: header.LogsBloom, + PrevRandao: header.PrevRandao, + BlockNumber: header.BlockNumber, + GasLimit: header.GasLimit, + GasUsed: header.GasUsed, + Timestamp: header.Timestamp, + ExtraData: header.ExtraData, + BaseFeePerGas: header.BaseFeePerGas, + BlockHash: header.BlockHash, + Transactions: make([]bellatrix.Transaction, 0), + Withdrawals: make([]*capella.Withdrawal, 0), + }, + BlobsBundle: &builderApiDeneb.BlobsBundle{ + Commitments: commitments, + Proofs: proofs, + Blobs: blobs, + }, + } +} + func TestNewBoostServiceErrors(t *testing.T) { t.Run("errors when no relays", func(t *testing.T) { - _, err := NewBoostService(BoostServiceOpts{testLog, ":123", []RelayEntry{}, "0x00000000", time.Second, true}) + _, err := NewBoostService(BoostServiceOpts{ + Log: testLog, + ListenAddr: ":123", + Relays: []RelayEntry{}, + RelayMonitors: []*url.URL{}, + GenesisForkVersionHex: "0x00000000", + GenesisTime: 0, + RelayCheck: true, + RelayMinBid: types.IntToU256(0), + RequestTimeoutGetHeader: time.Second, + RequestTimeoutGetPayload: time.Second, + RequestTimeoutRegVal: time.Second, + RequestMaxRetries: 1, + }) require.Error(t, err) }) } @@ -105,7 +194,7 @@ func TestWebserverRootHandler(t *testing.T) { backend := newTestBackend(t, 1, time.Second) // Check root handler - req, _ := http.NewRequest("GET", "/", nil) + req, _ := http.NewRequest(http.MethodGet, "/", nil) rr := httptest.NewRecorder() backend.boost.getRouter().ServeHTTP(rr, req) require.Equal(t, http.StatusOK, rr.Code) @@ -118,67 +207,54 @@ func TestWebserverMaxHeaderSize(t *testing.T) { backend.boost.listenAddr = addr go func() { err := backend.boost.StartHTTPServer() - require.NoError(t, err) + require.NoError(t, err) //nolint:testifylint }() time.Sleep(time.Millisecond * 100) path := "http://" + addr + "?" + strings.Repeat("abc", 4000) // path with characters of size over 4kb - code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, path, "test", nil, nil) + code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, path, "test", nil, nil, nil) require.Error(t, err) require.Equal(t, http.StatusRequestHeaderFieldsTooLarge, code) backend.boost.srv.Close() } -// Example good registerValidator payload -var payloadRegisterValidator = types.SignedValidatorRegistration{ - Message: &types.RegisterValidatorRequestMessage{ - FeeRecipient: _HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), - Timestamp: 1234356, - GasLimit: 278234191203, - Pubkey: _HexToPubkey( - "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249"), - }, - // Signed by 0x4e343a647c5a5c44d76c2c58b63f02cdf3a9a0ec40f102ebc26363b4b1b95033 - Signature: _HexToSignature( - "0x8209b5391cd69f392b1f02dbc03bab61f574bb6bb54bf87b59e2a85bdc0756f7db6a71ce1b41b727a1f46ccc77b213bf0df1426177b5b29926b39956114421eaa36ec4602969f6f6370a44de44a6bce6dae2136e5fb594cce2a476354264d1ea"), -} - func TestStatus(t *testing.T) { t.Run("At least one relay is available", func(t *testing.T) { - backend := newTestBackend(t, 2, time.Second) + backend := newTestBackend(t, 1, time.Second) + time.Sleep(time.Millisecond * 20) path := "/eth/v1/builder/status" - rr := backend.request(t, http.MethodGet, path, payloadRegisterValidator) + rr := backend.request(t, http.MethodGet, path, nil) require.Equal(t, http.StatusOK, rr.Code) + require.Greater(t, len(rr.Header().Get("X-MEVBoost-Version")), 0) //nolint:testifylint require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) }) t.Run("No relays available", func(t *testing.T) { backend := newTestBackend(t, 1, time.Second) - - // Make the relay unavailable. - backend.relays[0].Server.Close() + backend.relays[0].Server.Close() // makes the relay unavailable path := "/eth/v1/builder/status" - rr := backend.request(t, http.MethodGet, path, payloadRegisterValidator) + rr := backend.request(t, http.MethodGet, path, nil) require.Equal(t, http.StatusServiceUnavailable, rr.Code) + require.Greater(t, len(rr.Header().Get("X-MEVBoost-Version")), 0) //nolint:testifylint require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) }) } func TestRegisterValidator(t *testing.T) { path := "/eth/v1/builder/validators" - reg := types.SignedValidatorRegistration{ - Message: &types.RegisterValidatorRequestMessage{ + reg := builderApiV1.SignedValidatorRegistration{ + Message: &builderApiV1.ValidatorRegistration{ FeeRecipient: _HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), - Timestamp: 1234356, + Timestamp: time.Unix(1234356, 0), Pubkey: _HexToPubkey( "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249"), }, Signature: _HexToSignature( "0x81510b571e22f89d1697545aac01c9ad0c1e7a3e778b3078bef524efae14990e58a6e960a152abd49de2e18d7fd3081c15d5c25867ccfad3d47beef6b39ac24b6b9fbf2cfa91c88f67aff750438a6841ec9e4a06a94ae41410c4f97b75ab284c"), } - payload := []types.SignedValidatorRegistration{reg} + payload := []builderApiV1.SignedValidatorRegistration{reg} t.Run("Normal function", func(t *testing.T) { backend := newTestBackend(t, 1, time.Second) @@ -199,7 +275,7 @@ func TestRegisterValidator(t *testing.T) { require.Equal(t, 1, backend.relays[1].GetRequestCount(path)) // Now make one relay return an error - backend.relays[0].overrideHandleRegisterValidator(func(w http.ResponseWriter, r *http.Request) { + backend.relays[0].overrideHandleRegisterValidator(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusBadRequest) }) rr = backend.request(t, http.MethodPost, path, payload) @@ -208,7 +284,7 @@ func TestRegisterValidator(t *testing.T) { require.Equal(t, 2, backend.relays[1].GetRequestCount(path)) // Now make both relays return an error - which should cause the request to fail - backend.relays[1].overrideHandleRegisterValidator(func(w http.ResponseWriter, r *http.Request) { + backend.relays[1].overrideHandleRegisterValidator(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusBadRequest) }) rr = backend.request(t, http.MethodPost, path, payload) @@ -219,12 +295,12 @@ func TestRegisterValidator(t *testing.T) { }) t.Run("mev-boost relay timeout works with slow relay", func(t *testing.T) { - backend := newTestBackend(t, 1, 5*time.Millisecond) // 10ms max + backend := newTestBackend(t, 1, 150*time.Millisecond) // 10ms max rr := backend.request(t, http.MethodPost, path, payload) require.Equal(t, http.StatusOK, rr.Code) // Now make the relay return slowly, mev-boost should return an error - backend.relays[0].ResponseDelay = 10 * time.Millisecond + backend.relays[0].ResponseDelay = 180 * time.Millisecond rr = backend.request(t, http.MethodPost, path, payload) require.Equal(t, `{"code":502,"message":"no successful relay response"}`+"\n", rr.Body.String()) require.Equal(t, http.StatusBadGateway, rr.Code) @@ -232,15 +308,15 @@ func TestRegisterValidator(t *testing.T) { }) } -func TestGetHeader(t *testing.T) { - getPath := func(slot uint64, parentHash types.Hash, pubkey types.PublicKey) string { - return fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", slot, parentHash.String(), pubkey.String()) - } +func getHeaderPath(slot uint64, parentHash phase0.Hash32, pubkey phase0.BLSPubKey) string { + return fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", slot, parentHash.String(), pubkey.String()) +} +func TestGetHeader(t *testing.T) { hash := _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7") pubkey := _HexToPubkey( "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249") - path := getPath(1, hash, pubkey) + path := getHeaderPath(1, hash, pubkey) require.Equal(t, "/eth/v1/builder/header/1/0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", path) t.Run("Okay response from relay", func(t *testing.T) { @@ -250,14 +326,31 @@ func TestGetHeader(t *testing.T) { require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) }) + t.Run("Okay response from relay deneb", func(t *testing.T) { + backend := newTestBackend(t, 1, time.Second) + resp := backend.relays[0].MakeGetHeaderResponse( + 12345, + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionDeneb, + ) + backend.relays[0].GetHeaderResponse = resp + rr := backend.request(t, http.MethodGet, path, nil) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) + }) + t.Run("Bad response from relays", func(t *testing.T) { backend := newTestBackend(t, 2, time.Second) resp := backend.relays[0].MakeGetHeaderResponse( 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) - resp.Data.Message.Header.BlockHash = nilHash + resp.Capella.Message.Header.BlockHash = nilHash // 1/2 failing responses are okay backend.relays[0].GetHeaderResponse = resp @@ -274,6 +367,98 @@ func TestGetHeader(t *testing.T) { require.Equal(t, http.StatusNoContent, rr.Code) }) + t.Run("Invalid relay public key", func(t *testing.T) { + backend := newTestBackend(t, 1, time.Second) + + backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( + 12345, + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, + ) + + // Simulate a different public key registered to mev-boost + pk := phase0.BLSPubKey{} + backend.boost.relays[0].PublicKey = pk + + rr := backend.request(t, http.MethodGet, path, nil) + require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) + + // Request should have no content + require.Equal(t, http.StatusNoContent, rr.Code) + }) + + t.Run("Invalid relay signature", func(t *testing.T) { + backend := newTestBackend(t, 1, time.Second) + + backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( + 12345, + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, + ) + + // Scramble the signature + backend.relays[0].GetHeaderResponse.Capella.Signature = phase0.BLSSignature{} + + rr := backend.request(t, http.MethodGet, path, nil) + require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) + + // Request should have no content + require.Equal(t, http.StatusNoContent, rr.Code) + }) + + t.Run("Invalid slot number", func(t *testing.T) { + // Number larger than uint64 creates parsing error + slot := fmt.Sprintf("%d0", uint64(math.MaxUint64)) + invalidSlotPath := fmt.Sprintf("/eth/v1/builder/header/%s/%s/%s", slot, hash.String(), pubkey.String()) + + backend := newTestBackend(t, 1, time.Second) + rr := backend.request(t, http.MethodGet, invalidSlotPath, nil) + require.Equal(t, `{"code":400,"message":"invalid slot"}`+"\n", rr.Body.String()) + require.Equal(t, http.StatusBadRequest, rr.Code, rr.Body.String()) + require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) + }) + + t.Run("Invalid pubkey length", func(t *testing.T) { + invalidPubkeyPath := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 1, hash.String(), "0x1") + + backend := newTestBackend(t, 1, time.Second) + rr := backend.request(t, http.MethodGet, invalidPubkeyPath, nil) + require.Equal(t, `{"code":400,"message":"invalid pubkey"}`+"\n", rr.Body.String()) + require.Equal(t, http.StatusBadRequest, rr.Code, rr.Body.String()) + require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) + }) + + t.Run("Invalid hash length", func(t *testing.T) { + invalidSlotPath := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 1, "0x1", pubkey.String()) + + backend := newTestBackend(t, 1, time.Second) + rr := backend.request(t, http.MethodGet, invalidSlotPath, nil) + require.Equal(t, `{"code":400,"message":"invalid hash"}`+"\n", rr.Body.String()) + require.Equal(t, http.StatusBadRequest, rr.Code, rr.Body.String()) + require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) + }) + + t.Run("Invalid parent hash", func(t *testing.T) { + backend := newTestBackend(t, 1, time.Second) + + invalidParentHashPath := getHeaderPath(1, phase0.Hash32{}, pubkey) + rr := backend.request(t, http.MethodGet, invalidParentHashPath, nil) + require.Equal(t, http.StatusNoContent, rr.Code) + require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) + }) +} + +func TestGetHeaderBids(t *testing.T) { + hash := _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7") + pubkey := _HexToPubkey( + "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249") + path := getHeaderPath(2, hash, pubkey) + require.Equal(t, "/eth/v1/builder/header/2/0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", path) + t.Run("Use header with highest value", func(t *testing.T) { // Create backend and register 3 relays. backend := newTestBackend(t, 3, time.Second) @@ -282,21 +467,27 @@ func TestGetHeader(t *testing.T) { backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( 12345, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) // First relay will return signed response with value 12347. backend.relays[1].GetHeaderResponse = backend.relays[1].MakeGetHeaderResponse( 12347, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) // First relay will return signed response with value 12346. backend.relays[2].GetHeaderResponse = backend.relays[2].MakeGetHeaderResponse( 12346, "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) // Run the request. @@ -310,10 +501,12 @@ func TestGetHeader(t *testing.T) { require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) // Highest value should be 12347, i.e. second relay. - resp := new(types.GetHeaderResponse) + resp := new(builderSpec.VersionedSignedBuilderBid) err := json.Unmarshal(rr.Body.Bytes(), resp) require.NoError(t, err) - require.Equal(t, types.IntToU256(12347), resp.Data.Message.Value) + value, err := resp.Value() + require.NoError(t, err) + require.Equal(t, uint256.NewInt(12347), value) }) t.Run("Use header with lowest blockhash if same value", func(t *testing.T) { @@ -323,19 +516,25 @@ func TestGetHeader(t *testing.T) { backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( 12345, "0xa38385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) backend.relays[1].GetHeaderResponse = backend.relays[1].MakeGetHeaderResponse( 12345, "0xa18385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) backend.relays[2].GetHeaderResponse = backend.relays[2].MakeGetHeaderResponse( 12345, "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", + "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) // Run the request. @@ -349,113 +548,99 @@ func TestGetHeader(t *testing.T) { require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) // Highest value should be 12347, i.e. second relay. - resp := new(types.GetHeaderResponse) + resp := new(builderSpec.VersionedSignedBuilderBid) + err := json.Unmarshal(rr.Body.Bytes(), resp) require.NoError(t, err) - require.Equal(t, types.IntToU256(12345), resp.Data.Message.Value) - require.Equal(t, "0xa18385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", resp.Data.Message.Header.BlockHash.String()) + value, err := resp.Value() + require.NoError(t, err) + require.Equal(t, uint256.NewInt(12345), value) + blockHash, err := resp.BlockHash() + require.NoError(t, err) + require.Equal(t, "0xa18385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", blockHash.String()) }) - t.Run("Invalid relay public key", func(t *testing.T) { + t.Run("Respect minimum bid cutoff", func(t *testing.T) { + // Create backend and register relay. backend := newTestBackend(t, 1, time.Second) + // Relay will return signed response with value 12344. backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( - 12345, + 12344, + "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) - // Simulate a different public key registered to mev-boost - pk := types.PublicKey{} - backend.boost.relays[0].PublicKey = pk - + // Run the request. rr := backend.request(t, http.MethodGet, path, nil) + + // Each relay must have received the request. require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) - // Request should have no content + // Request should have no content (min bid is 12345) require.Equal(t, http.StatusNoContent, rr.Code) }) - t.Run("Invalid relay signature", func(t *testing.T) { + t.Run("Allow bids which meet minimum bid cutoff", func(t *testing.T) { + // Create backend and register relay. backend := newTestBackend(t, 1, time.Second) + // First relay will return signed response with value 12345. backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( 12345, + "0xa28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7", "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, ) - // Scramble the signature - backend.relays[0].GetHeaderResponse.Data.Signature = types.Signature{} - + // Run the request. rr := backend.request(t, http.MethodGet, path, nil) - require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) - - // Request should have no content - require.Equal(t, http.StatusNoContent, rr.Code) - }) - - t.Run("Invalid slot number", func(t *testing.T) { - // Number larger than uint64 creates parsing error - slot := fmt.Sprintf("%d0", uint64(math.MaxUint64)) - invalidSlotPath := fmt.Sprintf("/eth/v1/builder/header/%s/%s/%s", slot, hash.String(), pubkey.String()) - - backend := newTestBackend(t, 1, time.Second) - rr := backend.request(t, http.MethodGet, invalidSlotPath, nil) - require.Equal(t, `{"code":400,"message":"invalid slot"}`+"\n", rr.Body.String()) - require.Equal(t, http.StatusBadRequest, rr.Code, rr.Body.String()) - require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) - }) - t.Run("Invalid pubkey length", func(t *testing.T) { - invalidPubkeyPath := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 1, hash.String(), "0x1") - - backend := newTestBackend(t, 1, time.Second) - rr := backend.request(t, http.MethodGet, invalidPubkeyPath, nil) - require.Equal(t, `{"code":400,"message":"invalid pubkey"}`+"\n", rr.Body.String()) - require.Equal(t, http.StatusBadRequest, rr.Code, rr.Body.String()) - require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) - }) - - t.Run("Invalid hash length", func(t *testing.T) { - invalidSlotPath := fmt.Sprintf("/eth/v1/builder/header/%d/%s/%s", 1, "0x1", pubkey.String()) - - backend := newTestBackend(t, 1, time.Second) - rr := backend.request(t, http.MethodGet, invalidSlotPath, nil) - require.Equal(t, `{"code":400,"message":"invalid hash"}`+"\n", rr.Body.String()) - require.Equal(t, http.StatusBadRequest, rr.Code, rr.Body.String()) - require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) - }) - - t.Run("Invalid parent hash", func(t *testing.T) { - backend := newTestBackend(t, 1, time.Second) + // Each relay must have received the request. + require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) - invalidParentHashPath := getPath(1, types.Hash{}, pubkey) - rr := backend.request(t, http.MethodGet, invalidParentHashPath, nil) - require.Equal(t, http.StatusNoContent, rr.Code) - require.Equal(t, 0, backend.relays[0].GetRequestCount(path)) + // Value should be 12345 (min bid is 12345) + resp := new(builderSpec.VersionedSignedBuilderBid) + err := json.Unmarshal(rr.Body.Bytes(), resp) + require.NoError(t, err) + value, err := resp.Value() + require.NoError(t, err) + require.Equal(t, uint256.NewInt(12345), value) }) } func TestGetPayload(t *testing.T) { path := "/eth/v1/builder/blinded_blocks" - payload := types.SignedBlindedBeaconBlock{ + blockHash := _HexToHash("0x534809bd2b6832edff8d8ce4cb0e50068804fd1ef432c8362ad708a74fdc0e46") + payload := ð2ApiV1Capella.SignedBlindedBeaconBlock{ Signature: _HexToSignature( "0x8c795f751f812eabbabdee85100a06730a9904a4b53eedaa7f546fe0e23cd75125e293c6b0d007aa68a9da4441929d16072668abb4323bb04ac81862907357e09271fe414147b3669509d91d8ffae2ec9c789a5fcd4519629b8f2c7de8d0cce9"), - Message: &types.BlindedBeaconBlock{ + Message: ð2ApiV1Capella.BlindedBeaconBlock{ Slot: 1, ProposerIndex: 1, - ParentRoot: types.Root{0x01}, - StateRoot: types.Root{0x02}, - Body: &types.BlindedBeaconBlockBody{ - RandaoReveal: types.Signature{0xa1}, - Eth1Data: &types.Eth1Data{}, - Graffiti: types.Hash{0xa2}, - SyncAggregate: &types.SyncAggregate{}, - ExecutionPayloadHeader: &types.ExecutionPayloadHeader{ + ParentRoot: phase0.Root{0x01}, + StateRoot: phase0.Root{0x02}, + Body: ð2ApiV1Capella.BlindedBeaconBlockBody{ + RANDAOReveal: phase0.BLSSignature{0xa1}, + ETH1Data: &phase0.ETH1Data{ + BlockHash: blockHash[:], + }, + Graffiti: phase0.Hash32{0xa2}, + SyncAggregate: &altair.SyncAggregate{ + SyncCommitteeBits: bitfield.NewBitvector512(), + }, + ProposerSlashings: []*phase0.ProposerSlashing{}, + AttesterSlashings: []*phase0.AttesterSlashing{}, + Attestations: []*phase0.Attestation{}, + Deposits: []*phase0.Deposit{}, + VoluntaryExits: []*phase0.SignedVoluntaryExit{}, + ExecutionPayloadHeader: &capella.ExecutionPayloadHeader{ ParentHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab7"), - BlockHash: _HexToHash("0xe28385e7bd68df656cd0042b74b69c3104b5356ed1f20eb69f1f925df47a3ab1"), + BlockHash: blockHash, BlockNumber: 12345, FeeRecipient: _HexToAddress("0xdb65fEd33dc262Fe09D9a2Ba8F80b329BA25f941"), }, @@ -469,15 +654,18 @@ func TestGetPayload(t *testing.T) { require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) - resp := new(types.GetPayloadResponse) + resp := new(builderApi.VersionedSubmitBlindedBlockResponse) err := json.Unmarshal(rr.Body.Bytes(), resp) require.NoError(t, err) - require.Equal(t, payload.Message.Body.ExecutionPayloadHeader.BlockHash, resp.Data.BlockHash) + require.Equal(t, payload.Message.Body.ExecutionPayloadHeader.BlockHash, resp.Capella.BlockHash) }) t.Run("Bad response from relays", func(t *testing.T) { backend := newTestBackend(t, 2, time.Second) - resp := new(types.GetPayloadResponse) + resp := &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + Capella: &capella.ExecutionPayload{Withdrawals: []*capella.Withdrawal{}}, + } // 1/2 failing responses are okay backend.relays[0].GetPayloadResponse = resp @@ -495,21 +683,71 @@ func TestGetPayload(t *testing.T) { require.Equal(t, `{"code":502,"message":"no successful relay response"}`+"\n", rr.Body.String()) require.Equal(t, http.StatusBadGateway, rr.Code, rr.Body.String()) }) + + t.Run("Retries on error from relay", func(t *testing.T) { + backend := newTestBackend(t, 1, 2*time.Second) + + count := 0 + backend.relays[0].handlerOverrideGetPayload = func(w http.ResponseWriter, _ *http.Request) { + if count > 0 { + // success response on the second attempt + backend.relays[0].defaultHandleGetPayload(w) + } else { + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"code":500,"message":"internal server error"}`)) + require.NoError(t, err) + } + count++ + } + rr := backend.request(t, http.MethodPost, path, payload) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + }) + + t.Run("Error after max retries are reached", func(t *testing.T) { + backend := newTestBackend(t, 1, 0) + + count := 0 + maxRetries := 5 + + backend.relays[0].handlerOverrideGetPayload = func(w http.ResponseWriter, _ *http.Request) { + count++ + if count > maxRetries { + // success response after max retry attempts + backend.relays[0].defaultHandleGetPayload(w) + } else { + w.WriteHeader(http.StatusInternalServerError) + _, err := w.Write([]byte(`{"code":500,"message":"internal server error"}`)) + require.NoError(t, err) + } + } + rr := backend.request(t, http.MethodPost, path, payload) + require.Equal(t, 5, backend.relays[0].GetRequestCount(path)) + require.Equal(t, `{"code":502,"message":"no successful relay response"}`+"\n", rr.Body.String()) + require.Equal(t, http.StatusBadGateway, rr.Code, rr.Body.String()) + }) } func TestCheckRelays(t *testing.T) { - t.Run("At least one relay is okay", func(t *testing.T) { - backend := newTestBackend(t, 3, time.Second) - status := backend.boost.CheckRelays() - require.Equal(t, true, status) + t.Run("One relay is okay", func(t *testing.T) { + backend := newTestBackend(t, 1, time.Second) + numHealthyRelays := backend.boost.CheckRelays() + require.Equal(t, 1, numHealthyRelays) }) - t.Run("Every relays are down", func(t *testing.T) { + t.Run("One relay is down", func(t *testing.T) { backend := newTestBackend(t, 1, time.Second) backend.relays[0].Server.Close() - status := backend.boost.CheckRelays() - require.Equal(t, false, status) + numHealthyRelays := backend.boost.CheckRelays() + require.Equal(t, 0, numHealthyRelays) + }) + + t.Run("One relays is up, one down", func(t *testing.T) { + backend := newTestBackend(t, 2, time.Second) + backend.relays[0].Server.Close() + + numHealthyRelays := backend.boost.CheckRelays() + require.Equal(t, 1, numHealthyRelays) }) t.Run("Should not follow redirects", func(t *testing.T) { @@ -522,14 +760,146 @@ func TestCheckRelays(t *testing.T) { url, err := url.ParseRequestURI(backend.relays[0].Server.URL) require.NoError(t, err) backend.boost.relays[0].URL = url - status := backend.boost.CheckRelays() - require.Equal(t, false, status) + numHealthyRelays := backend.boost.CheckRelays() + require.Equal(t, 0, numHealthyRelays) }) } func TestEmptyTxRoot(t *testing.T) { - transactions := types.Transactions{} + transactions := eth2UtilBellatrix.ExecutionPayloadTransactions{Transactions: []bellatrix.Transaction{}} txroot, _ := transactions.HashTreeRoot() txRootHex := fmt.Sprintf("0x%x", txroot) require.Equal(t, "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1", txRootHex) } + +func TestGetPayloadWithTestdata(t *testing.T) { + path := "/eth/v1/builder/blinded_blocks" + + testPayloadsFiles := []string{ + "../testdata/signed-blinded-beacon-block-capella.json", + } + + for _, fn := range testPayloadsFiles { + t.Run(fn, func(t *testing.T) { + jsonFile, err := os.Open(fn) + require.NoError(t, err) + defer jsonFile.Close() + signedBlindedBeaconBlock := new(eth2ApiV1Capella.SignedBlindedBeaconBlock) + require.NoError(t, DecodeJSON(jsonFile, &signedBlindedBeaconBlock)) + + backend := newTestBackend(t, 1, time.Second) + mockResp := builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + Capella: &capella.ExecutionPayload{ + BlockHash: signedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash, + Withdrawals: make([]*capella.Withdrawal, 0), + }, + } + backend.relays[0].GetPayloadResponse = &mockResp + + rr := backend.request(t, http.MethodPost, path, signedBlindedBeaconBlock) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + require.Equal(t, 1, backend.relays[0].GetRequestCount(path)) + + resp := new(builderApi.VersionedSubmitBlindedBlockResponse) + err = json.Unmarshal(rr.Body.Bytes(), resp) + require.NoError(t, err) + require.Equal(t, signedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash, resp.Capella.BlockHash) + }) + } +} + +func TestGetPayloadCapella(t *testing.T) { + // Load the signed blinded beacon block used for getPayload + jsonFile, err := os.Open("../testdata/signed-blinded-beacon-block-capella.json") + require.NoError(t, err) + defer jsonFile.Close() + signedBlindedBeaconBlock := new(eth2ApiV1Capella.SignedBlindedBeaconBlock) + require.NoError(t, DecodeJSON(jsonFile, &signedBlindedBeaconBlock)) + + backend := newTestBackend(t, 1, time.Second) + + // Prepare getPayload response + backend.relays[0].GetPayloadResponse = &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + Capella: blindedBlockToExecutionPayloadCapella(signedBlindedBeaconBlock), + } + + // call getPayload, ensure it's only called on relay 0 (origin of the bid) + getPayloadPath := "/eth/v1/builder/blinded_blocks" + rr := backend.request(t, http.MethodPost, getPayloadPath, signedBlindedBeaconBlock) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + require.Equal(t, 1, backend.relays[0].GetRequestCount(getPayloadPath)) + + resp := new(builderApi.VersionedSubmitBlindedBlockResponse) + err = json.Unmarshal(rr.Body.Bytes(), resp) + require.NoError(t, err) + require.Equal(t, signedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash, resp.Capella.BlockHash) +} + +func TestGetPayloadDeneb(t *testing.T) { + // Load the signed blinded beacon block used for getPayload + jsonFile, err := os.Open("../testdata/signed-blinded-beacon-block-deneb.json") + require.NoError(t, err) + defer jsonFile.Close() + signedBlindedBlock := new(eth2ApiV1Deneb.SignedBlindedBeaconBlock) + require.NoError(t, DecodeJSON(jsonFile, &signedBlindedBlock)) + + backend := newTestBackend(t, 1, time.Second) + + // Prepare getPayload response + backend.relays[0].GetPayloadResponse = &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionDeneb, + Deneb: blindedBlockContentsToPayloadDeneb(signedBlindedBlock), + } + + // call getPayload, ensure it's only called on relay 0 (origin of the bid) + getPayloadPath := "/eth/v1/builder/blinded_blocks" + rr := backend.request(t, http.MethodPost, getPayloadPath, signedBlindedBlock) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + require.Equal(t, 1, backend.relays[0].GetRequestCount(getPayloadPath)) + + resp := new(builderApi.VersionedSubmitBlindedBlockResponse) + err = json.Unmarshal(rr.Body.Bytes(), resp) + require.NoError(t, err) + require.Equal(t, signedBlindedBlock.Message.Body.ExecutionPayloadHeader.BlockHash, resp.Deneb.ExecutionPayload.BlockHash) +} + +func TestGetPayloadToAllRelays(t *testing.T) { + // Load the signed blinded beacon block used for getPayload + jsonFile, err := os.Open("../testdata/signed-blinded-beacon-block-capella.json") + require.NoError(t, err) + defer jsonFile.Close() + signedBlindedBeaconBlock := new(eth2ApiV1Capella.SignedBlindedBeaconBlock) + require.NoError(t, DecodeJSON(jsonFile, &signedBlindedBeaconBlock)) + + // Create a test backend with 2 relays + backend := newTestBackend(t, 2, time.Second) + + // call getHeader, highest bid is returned by relay 0 + getHeaderPath := "/eth/v1/builder/header/12345/0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2/0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249" + backend.relays[0].GetHeaderResponse = backend.relays[0].MakeGetHeaderResponse( + 12345, + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0x8a1d7b8dd64e0aafe7ea7b6c95065c9364cf99d38470c12ee807d55f7de1529ad29ce2c422e0b65e3d5a05c02caca249", + spec.DataVersionCapella, + ) + rr := backend.request(t, http.MethodGet, getHeaderPath, nil) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + require.Equal(t, 1, backend.relays[0].GetRequestCount(getHeaderPath)) + require.Equal(t, 1, backend.relays[1].GetRequestCount(getHeaderPath)) + + // Prepare getPayload response + backend.relays[0].GetPayloadResponse = &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + Capella: blindedBlockToExecutionPayloadCapella(signedBlindedBeaconBlock), + } + + // call getPayload, ensure it's called to all relays + getPayloadPath := "/eth/v1/builder/blinded_blocks" + rr = backend.request(t, http.MethodPost, getPayloadPath, signedBlindedBeaconBlock) + require.Equal(t, http.StatusOK, rr.Code, rr.Body.String()) + require.Equal(t, 1, backend.relays[0].GetRequestCount(getPayloadPath)) + require.Equal(t, 1, backend.relays[1].GetRequestCount(getPayloadPath)) +} diff --git a/server/utils.go b/server/utils.go index 020b64dc..fb779d53 100644 --- a/server/utils.go +++ b/server/utils.go @@ -7,21 +7,44 @@ import ( "errors" "fmt" "io" + "math/big" "net/http" + "net/url" "strings" "time" + builderApi "github.com/attestantio/go-builder-client/api" + builderSpec "github.com/attestantio/go-builder-client/spec" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/flashbots/go-boost-utils/types" + "github.com/flashbots/go-boost-utils/bls" + "github.com/flashbots/go-boost-utils/ssz" "github.com/flashbots/mev-boost/config" + "github.com/holiman/uint256" + "github.com/sirupsen/logrus" +) + +const ( + HeaderKeySlotUID = "X-MEVBoost-SlotID" + HeaderKeyVersion = "X-MEVBoost-Version" +) + +var ( + errHTTPErrorResponse = errors.New("HTTP error response") + errInvalidForkVersion = errors.New("invalid fork version") + errMaxRetriesExceeded = errors.New("max retries exceeded") ) // UserAgent is a custom string type to avoid confusing url + userAgent parameters in SendHTTPRequest type UserAgent string +// BlockHashHex is a hex-string representation of a block hash +type BlockHashHex string + // SendHTTPRequest - prepare and send HTTP request, marshaling the payload if any, and decoding the response if dst is set -func SendHTTPRequest(ctx context.Context, client http.Client, method, url string, userAgent UserAgent, payload any, dst any) (code int, err error) { +func SendHTTPRequest(ctx context.Context, client http.Client, method, url string, userAgent UserAgent, headers map[string]string, payload, dst any) (code int, err error) { var req *http.Request if payload == nil { @@ -33,16 +56,21 @@ func SendHTTPRequest(ctx context.Context, client http.Client, method, url string } req, err = http.NewRequestWithContext(ctx, method, url, bytes.NewReader(payloadBytes)) - // Set content-type + // Set headers req.Header.Add("Content-Type", "application/json") } if err != nil { return 0, fmt.Errorf("could not prepare request: %w", err) } - // Set user agent + // Set user agent header req.Header.Set("User-Agent", strings.TrimSpace(fmt.Sprintf("mev-boost/%s %s", config.Version, userAgent))) + // Set other headers + for key, value := range headers { + req.Header.Set(key, value) + } + // Execute request resp, err := client.Do(req) if err != nil { @@ -59,7 +87,7 @@ func SendHTTPRequest(ctx context.Context, client http.Client, method, url string if err != nil { return resp.StatusCode, fmt.Errorf("could not read error response body for status code %d: %w", resp.StatusCode, err) } - return resp.StatusCode, fmt.Errorf("HTTP error response: %d / %s", resp.StatusCode, string(bodyBytes)) + return resp.StatusCode, fmt.Errorf("%w: %d / %s", errHTTPErrorResponse, resp.StatusCode, string(bodyBytes)) } if dst != nil { @@ -76,36 +104,71 @@ func SendHTTPRequest(ctx context.Context, client http.Client, method, url string return resp.StatusCode, nil } +// SendHTTPRequestWithRetries - prepare and send HTTP request, retrying the request if within the client timeout +func SendHTTPRequestWithRetries(ctx context.Context, client http.Client, method, url string, userAgent UserAgent, headers map[string]string, payload, dst any, maxRetries int, log *logrus.Entry) (code int, err error) { + var requestCtx context.Context + var cancel context.CancelFunc + if client.Timeout > 0 { + // Create a context with a timeout as configured in the http client + requestCtx, cancel = context.WithTimeout(context.Background(), client.Timeout) + } else { + requestCtx, cancel = context.WithCancel(context.Background()) + } + defer cancel() + + attempts := 0 + for { + attempts++ + if requestCtx.Err() != nil { + return 0, fmt.Errorf("request context error after %d attempts: %w", attempts, requestCtx.Err()) + } + if attempts > maxRetries { + return 0, errMaxRetriesExceeded + } + + code, err = SendHTTPRequest(ctx, client, method, url, userAgent, headers, payload, dst) + if err != nil { + log.WithError(err).Warn("error making request to relay, retrying") + time.Sleep(100 * time.Millisecond) // note: this timeout is only applied between retries, it does not delay the initial request! + continue + } + return code, nil + } +} + // ComputeDomain computes the signing domain -func ComputeDomain(domainType types.DomainType, forkVersionHex string, genesisValidatorsRootHex string) (domain types.Domain, err error) { - genesisValidatorsRoot := types.Root(common.HexToHash(genesisValidatorsRootHex)) +func ComputeDomain(domainType phase0.DomainType, forkVersionHex, genesisValidatorsRootHex string) (domain phase0.Domain, err error) { + genesisValidatorsRoot := phase0.Root(common.HexToHash(genesisValidatorsRootHex)) forkVersionBytes, err := hexutil.Decode(forkVersionHex) - if err != nil || len(forkVersionBytes) > 4 { - err = errors.New("invalid fork version passed") - return domain, err + if err != nil || len(forkVersionBytes) != 4 { + return domain, errInvalidForkVersion } var forkVersion [4]byte copy(forkVersion[:], forkVersionBytes[:4]) - return types.ComputeDomain(domainType, forkVersion, genesisValidatorsRoot), nil + return ssz.ComputeDomain(domainType, forkVersion, genesisValidatorsRoot), nil } // DecodeJSON reads JSON from io.Reader and decodes it into a struct func DecodeJSON(r io.Reader, dst any) error { decoder := json.NewDecoder(r) decoder.DisallowUnknownFields() + return decoder.Decode(dst) +} - if err := decoder.Decode(dst); err != nil { - return err - } - return nil +// GetURI returns the full request URI with scheme, host, path and args. +func GetURI(url *url.URL, path string) string { + u2 := *url + u2.User = nil + u2.Path = path + return u2.String() } // bidResp are entries in the bids cache type bidResp struct { - t time.Time - response types.GetHeaderResponse - blockHash string - relays []string + t time.Time + response builderSpec.VersionedSignedBuilderBid + bidInfo bidInfo + relays []RelayEntry } // bidRespKey is used as key for the bids cache @@ -113,3 +176,97 @@ type bidRespKey struct { slot uint64 blockHash string } + +// bidInfo is used to store bid response fields for logging and validation +type bidInfo struct { + blockHash phase0.Hash32 + parentHash phase0.Hash32 + pubkey phase0.BLSPubKey + blockNumber uint64 + txRoot phase0.Root + value *uint256.Int +} + +func httpClientDisallowRedirects(_ *http.Request, _ []*http.Request) error { + return http.ErrUseLastResponse +} + +func weiBigIntToEthBigFloat(wei *big.Int) (ethValue *big.Float) { + // wei / 10^18 + fbalance := new(big.Float) + fbalance.SetString(wei.String()) + ethValue = new(big.Float).Quo(fbalance, big.NewFloat(1e18)) + return +} + +func parseBidInfo(bid *builderSpec.VersionedSignedBuilderBid) (bidInfo, error) { + blockHash, err := bid.BlockHash() + if err != nil { + return bidInfo{}, err + } + parentHash, err := bid.ParentHash() + if err != nil { + return bidInfo{}, err + } + pubkey, err := bid.Builder() + if err != nil { + return bidInfo{}, err + } + blockNumber, err := bid.BlockNumber() + if err != nil { + return bidInfo{}, err + } + txRoot, err := bid.TransactionsRoot() + if err != nil { + return bidInfo{}, err + } + value, err := bid.Value() + if err != nil { + return bidInfo{}, err + } + bidInfo := bidInfo{ + blockHash: blockHash, + parentHash: parentHash, + pubkey: pubkey, + blockNumber: blockNumber, + txRoot: txRoot, + value: value, + } + return bidInfo, nil +} + +func checkRelaySignature(bid *builderSpec.VersionedSignedBuilderBid, domain phase0.Domain, pubKey phase0.BLSPubKey) (bool, error) { + root, err := bid.MessageHashTreeRoot() + if err != nil { + return false, err + } + sig, err := bid.Signature() + if err != nil { + return false, err + } + signingData := phase0.SigningData{ObjectRoot: root, Domain: domain} + msg, err := signingData.HashTreeRoot() + if err != nil { + return false, err + } + + return bls.VerifySignatureBytes(msg[:], sig[:], pubKey[:]) +} + +func getPayloadResponseIsEmpty(payload *builderApi.VersionedSubmitBlindedBlockResponse) bool { + switch payload.Version { + case spec.DataVersionCapella: + if payload.Capella == nil || payload.Capella.BlockHash == nilHash { + return true + } + case spec.DataVersionDeneb: + if payload.Deneb == nil || payload.Deneb.ExecutionPayload == nil || + payload.Deneb.ExecutionPayload.BlockHash == nilHash || + payload.Deneb.BlobsBundle == nil { + return true + } + case spec.DataVersionUnknown, spec.DataVersionPhase0, spec.DataVersionAltair, spec.DataVersionBellatrix: + return true + } + return false +} diff --git a/server/utils_test.go b/server/utils_test.go index eca8b7a3..3ae76f68 100644 --- a/server/utils_test.go +++ b/server/utils_test.go @@ -2,12 +2,20 @@ package server import ( "bytes" + "compress/gzip" "context" "fmt" + "math/big" "net/http" "net/http/httptest" "testing" + builderApi "github.com/attestantio/go-builder-client/api" + builderApiDeneb "github.com/attestantio/go-builder-client/api/deneb" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/capella" + "github.com/attestantio/go-eth2-client/spec/deneb" + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/flashbots/mev-boost/config" "github.com/stretchr/testify/require" ) @@ -15,7 +23,7 @@ import ( func TestMakePostRequest(t *testing.T) { // Test errors var x chan bool - code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, "", "test", x, nil) + code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, "", "test", nil, x, nil) require.Error(t, err) require.Equal(t, 0, code) } @@ -38,25 +46,166 @@ func TestSendHTTPRequestUserAgent(t *testing.T) { // Test with custom UA customUA := "test-user-agent" expectedUA := fmt.Sprintf("mev-boost/%s %s", config.Version, customUA) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { require.Equal(t, expectedUA, r.Header.Get("User-Agent")) done <- true })) - defer ts.Close() - code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, ts.URL, UserAgent(customUA), nil, nil) + code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, ts.URL, UserAgent(customUA), nil, nil, nil) + ts.Close() require.NoError(t, err) require.Equal(t, 200, code) <-done // Test without custom UA expectedUA = fmt.Sprintf("mev-boost/%s", config.Version) - ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts = httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { require.Equal(t, expectedUA, r.Header.Get("User-Agent")) done <- true })) - defer ts.Close() - code, err = SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, ts.URL, "", nil, nil) + code, err = SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, ts.URL, "", nil, nil, nil) + ts.Close() require.NoError(t, err) require.Equal(t, 200, code) <-done } + +func TestSendHTTPRequestGzip(t *testing.T) { + // Test with gzip response + var buf bytes.Buffer + zw := gzip.NewWriter(&buf) + _, err := zw.Write([]byte(`{ "msg": "test-message" }`)) + require.NoError(t, err) + require.NoError(t, zw.Close()) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, "gzip", r.Header.Get("Accept-Encoding")) + w.Header().Set("Content-Encoding", "gzip") + _, _ = w.Write(buf.Bytes()) + })) + resp := struct{ Msg string }{} + code, err := SendHTTPRequest(context.Background(), *http.DefaultClient, http.MethodGet, ts.URL, "", nil, nil, &resp) + ts.Close() + require.NoError(t, err) + require.Equal(t, 200, code) + require.Equal(t, "test-message", resp.Msg) +} + +func TestWeiBigIntToEthBigFloat(t *testing.T) { + // test with valid input + i := big.NewInt(1) + f := weiBigIntToEthBigFloat(i) + require.Equal(t, "0.000000000000000001", f.Text('f', 18)) + + // test with nil, which results on invalid big.Int input + f = weiBigIntToEthBigFloat(nil) + require.Equal(t, "0.000000000000000000", f.Text('f', 18)) +} + +func TestGetPayloadResponseIsEmpty(t *testing.T) { + testCases := []struct { + name string + payload *builderApi.VersionedSubmitBlindedBlockResponse + expected bool + }{ + { + name: "Non-empty capella payload response", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + Capella: &capella.ExecutionPayload{ + BlockHash: phase0.Hash32{0x1}, + }, + }, + expected: false, + }, + { + name: "Non-empty deneb payload response", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionDeneb, + Deneb: &builderApiDeneb.ExecutionPayloadAndBlobsBundle{ + ExecutionPayload: &deneb.ExecutionPayload{ + BlockHash: phase0.Hash32{0x1}, + }, + BlobsBundle: &builderApiDeneb.BlobsBundle{ + Blobs: make([]deneb.Blob, 0), + Commitments: make([]deneb.KZGCommitment, 0), + Proofs: make([]deneb.KZGProof, 0), + }, + }, + }, + expected: false, + }, + { + name: "Empty capella payload response", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + }, + expected: true, + }, + { + name: "Nil block hash for capella payload response", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionCapella, + Capella: &capella.ExecutionPayload{ + BlockHash: nilHash, + }, + }, + expected: true, + }, + { + name: "Empty deneb payload response", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionDeneb, + }, + expected: true, + }, + { + name: "Empty deneb execution payload", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionDeneb, + Deneb: &builderApiDeneb.ExecutionPayloadAndBlobsBundle{ + BlobsBundle: &builderApiDeneb.BlobsBundle{ + Blobs: make([]deneb.Blob, 0), + Commitments: make([]deneb.KZGCommitment, 0), + Proofs: make([]deneb.KZGProof, 0), + }, + }, + }, + expected: true, + }, + { + name: "Empty deneb blobs bundle", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Deneb: &builderApiDeneb.ExecutionPayloadAndBlobsBundle{ + ExecutionPayload: &deneb.ExecutionPayload{ + BlockHash: phase0.Hash32{0x1}, + }, + }, + }, + expected: true, + }, + { + name: "Nil block hash for deneb payload response", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Deneb: &builderApiDeneb.ExecutionPayloadAndBlobsBundle{ + ExecutionPayload: &deneb.ExecutionPayload{ + BlockHash: nilHash, + }, + }, + }, + expected: true, + }, + { + name: "Unsupported payload version", + payload: &builderApi.VersionedSubmitBlindedBlockResponse{ + Version: spec.DataVersionBellatrix, + }, + expected: true, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.expected, getPayloadResponseIsEmpty(tt.payload)) + }) + } +} diff --git a/testdata/signed-blinded-beacon-block-capella.json b/testdata/signed-blinded-beacon-block-capella.json new file mode 100644 index 00000000..4eb15d69 --- /dev/null +++ b/testdata/signed-blinded-beacon-block-capella.json @@ -0,0 +1,184 @@ +{ + "message": { + "slot": "1", + "proposer_index": "1", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "body": { + "randao_reveal": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "eth1_data": { + "deposit_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "deposit_count": "1", + "block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "graffiti": "0x035d6e107958843dc805e7094b9843a30a1afd56d0df2e2b8ad1a907d56961e3", + "proposer_slashings": [ + { + "signed_header_1": { + "message": { + "slot": "1", + "proposer_index": "1", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "signed_header_2": { + "message": { + "slot": "1", + "proposer_index": "1", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } + } + ], + "attester_slashings": [ + { + "attestation_1": { + "attesting_indices": ["1"], + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + }, + "attestation_2": { + "attesting_indices": ["1"], + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + } + } + ], + "attestations": [ + { + "aggregation_bits": "0x01", + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", + "data": { + "slot": "1", + "index": "1", + "beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "source": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "target": { + "epoch": "1", + "root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + } + } + } + ], + "deposits": [ + { + "proof": [ + "0xcd5a13caadcac56ae4acdabbe37bcd12348b3dbd728a703cc8c7fd641a765a23", + "0x11e283b8e45f0864817a125f630afa79e57a7093ccabb9a7dfec9b7cfc50b5b0", + "0x1c7dbcb2c3ff23e0867512b2f5cf4ff300fd95f6654c721a29e30a188d177acb", + "0x26777a9e610b55549f1d0fec6bc47049073ebd03a74c3d308dd4633ac156db43", + "0xd2253d9472594a826ac081ae7847d8ff776fccc8eccf044395fcdf4fe9357351", + "0x95faeee7cf2b494d0808fb6abcea11e0fb98e92d268f30bb0295f56ef624de48", + "0x128cb49cac29a390f096dd95b7ce97b0465bce89fd003f48e8b349cb7f70aeeb", + "0x9335cff8f4423727a09c097611113bd2a9b94af5bde89254163ff30cb448bd59", + "0x493e981118edb12e7fcb78ff0c90c8a365a813e272a96e648b2dd38a2424247e", + "0x8792492a88df0902cb407250b0149e8389a7b1d3f772094669bd0a75a054c5ff", + "0xbad3cf3f2f110d3af7ffd1157ca8d313f3564b208c854c24b9b8249371b00495", + "0x9aa7d4e7a9fc2c9ceaf6b475f2975c580b53f5c076988cdc2dfe20798ff85df3", + "0x67ee9b8565b885c424e7c0e17d4722e26baef4e086b863e7cb1a1eb50f95450e", + "0xe19199724ac00c727d30773c6d2e310de0ea5298be1af27f931ff265873c54ea", + "0x7ca1e29efdde8c316c55b06a4854df3f0e2c6061e9d37eaf59a5310b0fc63600", + "0xfb048c607eafa7ce08251b7bad7f4cdc9671fad7bd32b9a879c9705a25367a33", + "0xa545e0beb4b0e9f62b3e4718684ee7bb5b471ea1e6c00f4f39d464d819c789bb", + "0xac2587b5e376276c0fe6fec5b99538c64cac6fd7178495dc83f76cb71a4e04a8", + "0x7583c07a2c06f98d37f7e9f8c8a336a78814c96b85a5ca8e2b46d2ef42c3274a", + "0xa5ea145810ea5cb3af54f5022127d86190d1c3812037a7475b63d1da36d0deca", + "0x5e6ab651c5244c2701c655ab18a6098f6a6e0fab9c5ad68e8fd2fe40c4d74740", + "0xde9fd756dbfece4ef84d7491a405ae1a955adb3cf00465a6ac102fc273ae6c2c", + "0x2087c35f0207f9b2e2868314ad78b33b079e477b86b76ecbb007af9ac6f527a4", + "0xba70fa1d351ad45fff13e571827e9abd07c16928c097eaab7b465c9a2c3962d7", + "0x6b6ce93bbfa3c460dfb8d19feed44dfd8b27dc6f462161c341ce1a8e30c1e4f5", + "0xa274b195993d5b91c9f1bba9e0ebedf11a5558e03d4f8b90eef701e043f95d11", + "0xafc6eb3ff05c7254e544a201f7c47518c1cf0c7905e06c87648dab6a0cde515d", + "0xb4ab300a00ef3654766c11fe8ab92a3af548a652e7785e9b315d688ab72d0d2e", + "0xac00ad809a1a95d3585c776584f79949b556c87c894bb75dd57deac3da8dd786", + "0x683e1d65ed20b4bd6895e2d7e33fcac13e7a38316f4cdc0ac793e0c10e846a26", + "0xdaf19d17403f7067798652ef62326c928e7caae1dc65af119364cea9651847c5", + "0x2f68b4a365337404e1fc611bbfc3e841d603af623630a82586c7a26fbd2335d7", + "0x080cad38549e8609d5422fdf547c1dc12dccd045f369d8a4ca288bfeba35eeec" + ], + "data": { + "pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "withdrawal_credentials": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "amount": "1", + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } + } + ], + "voluntary_exits": [ + { + "message": { + "epoch": "1", + "validator_index": "1" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } + ], + "sync_aggregate": { + "sync_committee_bits": "0x41157a77520bb661f0763f70142fba246e4c46983310145ff9e0b417bbff29f3ede7f957eb00c50ef39dec4e9172f800b58e2b7d79e5757d47655699a19253d5", + "sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }, + "execution_payload_header": { + "parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "block_number": "1", + "gas_limit": "1", + "gas_used": "1", + "timestamp": "1", + "extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "base_fee_per_gas": "1", + "block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "transactions_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "withdrawals_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2" + }, + "bls_to_execution_changes": [ + { + "message": { + "validator_index": "1", + "from_bls_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", + "to_execution_address": "0xabcf8e0d4e9587369b2301d0790347320302cc09" + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + } + ] + } + }, + "signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" +} diff --git a/testdata/signed-blinded-beacon-block-deneb.json b/testdata/signed-blinded-beacon-block-deneb.json new file mode 100644 index 00000000..4a9d67b6 --- /dev/null +++ b/testdata/signed-blinded-beacon-block-deneb.json @@ -0,0 +1,307 @@ +{ + "message": { + "slot": "348241", + "proposer_index": "35822", + "parent_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "state_root": "0x4f6e0857501da4ab1d72f0c122869e1c084e16daa96613b64914aada28d0dc28", + "body": { + "randao_reveal": "0xb2b7d2e89bb4a4aa6a377972651bb9041cb59af8eedd19568d699fc0866189d3fd78cc93c0e63877b7e2bd6d34d1597c0afd4508aa99b6e882c2cb1ac6f424adba29afd46d1737124300ad72177715fcce8584dd25a06c45bfe9a8ccabd6175d", + "eth1_data": { + "deposit_root": "0x704964a5ad034a440f4f29ff1875986db66adbca45dc0014e439349c7e10194f", + "deposit_count": "4933", + "block_hash": "0x9c69de2814a7c3e3751654511372937627dacc3187bf457892789f3f5533c794" + }, + "graffiti": "0x74656b752d626573750000000000000000000000000000000000000000000000", + "proposer_slashings": [], + "attester_slashings": [], + "attestations": [ + { + "aggregation_bits": "0xffffffffffffffffffffffffffffff5fff", + "data": { + "slot": "348240", + "index": "7", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x92da7bf78eb364219f85af2388d7ac7ddbea1934786d75875486ec9fceb310eee131dcfea131bdf4593d3c431b31b2900bb48ebb7ab02e17524d86a4e132883246df8ce427e935dd9e20c422cdf8eb135b3cc45b86fe4c2f592fb4899eb22f7c" + }, + { + "aggregation_bits": "0xffdffffffffffff5fffffffffffffffffd", + "data": { + "slot": "348240", + "index": "3", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x88cce1f9fbf651e52a6ab71dea2f4025702021744aac90fb2997f82bac6c192e295ae39b2a430546cb102bf8b68f687e0f40a5179bc293e1424e37d694ef1ad6b3b8de72a0e7fbbe97aeafe6f47e949d415381fbbb090e3135224d5b324eefcb" + }, + { + "aggregation_bits": "0xffffffffffefffffffffbfffff7fffdfff", + "data": { + "slot": "348240", + "index": "11", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x82186d946dfde2ab3b5fcf5bd858fadeec7fa9729f28527e209d16a1d9b4d635558cad6f8de8cee12caa2a4fc5459fb911ca17cbbecfd22e83c82e244ad7a8c8c849a1e03ee88bf0d338c633c2acfefd142574897cd78f9076b69f6e370e3751" + }, + { + "aggregation_bits": "0xfffdffffffffffffffffffffffffffffff", + "data": { + "slot": "348240", + "index": "4", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0xb9c5ee50f800fe2619d1d0fe46a2fb00a64bcf2613e46a40b579ce6a39c4f6cd71a46790757ccc3df8f5f82e34c77c8d084f525ea8a4bd5bd10190496644be0740ace3d217e43af15229a8023d58e583cfec849fab10169225444f4f4ecc66a8" + }, + { + "aggregation_bits": "0xffffffffeffffffffffffdffffffffffff", + "data": { + "slot": "348240", + "index": "12", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x91e4ee5c0d6bb54d6856cee653c6859f635cebf9c51bef524d6f439cf6d1c69bea5fcb3e4c067c178cfa4518a76baba909b18189a864f38020f44b2cd5223a11e42d58aaedfa2f004710a72a704357874850842a1493017eca6e473d01395932" + }, + { + "aggregation_bits": "0xffffffffffffffffffffffffffffffffff", + "data": { + "slot": "348240", + "index": "13", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x8c376c5bb5ada745ba8cb8ce2aae103f4e3f85549ceaacaf312b1fa8e6d2ee5232149a926dcfd58ffa1f50f710eb4edc10943bbd40a601f2fb4d53104a59c0663a397744b59f1fa0744bba49f22afc3bab47045ebb42e61dac41ad44c6bf28f4" + }, + { + "aggregation_bits": "0xfffffffffffffffffffffffffffeffffff", + "data": { + "slot": "348240", + "index": "1", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0xaf11c64ce957f2a1686d12b07d0fbc170d89e48490e326cd73ef761ba042bddc01e48e5fc39953c6113df0a989d75e750d5b9d75155259508c2bbdd53903967f893e24f2f7f751f4a05b0fb1cb2b9084ce8543690a8a623599308d6c190fca4a" + }, + { + "aggregation_bits": "0xfffffff5fffffffffdfffbbffffff7ffff", + "data": { + "slot": "348240", + "index": "6", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x91075da401796a4341ab9a850ff330c9b0d996ca12b9970ec15a4b40fee652edd043e0c9f9d81529621b3a7970e676f619d7a39af67bf193af4441b5447f199f02d75a26c32181569cddc0a237b7064971539f80811fe40e9362d4d9242404ed" + }, + { + "aggregation_bits": "0xfffffffdfffffffffffffdfffffeffffff", + "data": { + "slot": "348240", + "index": "0", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0xad9aa5aa9b9c022036fbb81a0aca626b19a2ccd7c7ee6efa5b2a454f5ffb5d75d00e5563b31319b3a0ad1e0ef6f512be00fb8c39243004a1133610344473953dfcf06c3bd53f00255de6983927acd8624b0131fe9d8a085062747d70972b4713" + }, + { + "aggregation_bits": "0xffffffffffff7fdfffffffffffffffffff", + "data": { + "slot": "348240", + "index": "2", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x89dde857bc31a4cc5e71d6cc440c00b2b1ee85b758722aadc5c4da0a939523de7532aabcfef4e80f84094924bb69d80d0a3d702b85859c5fce0433b6d0f7bc302af866ef7a9234a75be7bbd91b32256126808ffdf65ac0ce07a33afbaa16c575" + }, + { + "aggregation_bits": "0xffffffffffffffffffffffffffffffffff", + "data": { + "slot": "348240", + "index": "8", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x891fbba500eee2cf2f5734d3bf8445e8684376e47469692d44e87fc8a295616d9f29410afc2d6ff2bc649618b33b417e13de4e152099aac054f4d35df4cd79234b6df1edcf2393b7ebc0f2ecf61f4604232b96830e0dbff9311408dad4479667" + }, + { + "aggregation_bits": "0xfffffffffffbffffffffefffffffff5fff", + "data": { + "slot": "348240", + "index": "14", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0xb9ab0354d0d61eb6b5f2184dc3bd0c8416cca74f2c913c6aaca653a87dd2c4b8ba2471aa450e0fa170573637c49dc8920eb84970fea4230d7b3c3c8c8152c782e912b29bc19a6de05dc36c1b44db2f649f31673b4751e1b22f17021833ca9cc8" + }, + { + "aggregation_bits": "0xfffffebffffbf7ffeffffbffffffffffff", + "data": { + "slot": "348240", + "index": "5", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x8e17aa835e6343f708e73e432b7268741f60565f5bb6ef62b5fba892438ca5474a14c0382609e14624058f3fab120e8902ad3f667cf14418836ce120f3bbf50ea3c15923c881e599227cc3a2758ef9a2cd08bd3b862bd711a875e27477ac347c" + }, + { + "aggregation_bits": "0xfffffffffffefffffffffffeffffffffff", + "data": { + "slot": "348240", + "index": "10", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0xa0909f67a7745300cee6278605e3cb79e5a9564cd6e81ac379b285e0eb6e1849537326b546079d7bf676c8e33a166cad00ab74a396f12c9f4851fb64612f6aeb911db550e0aeae88e1b90831a5a858ae64f9262f67403327d85fcb345df9fca4" + }, + { + "aggregation_bits": "0xffffffffffffffffffffffffffffffffff", + "data": { + "slot": "348240", + "index": "9", + "beacon_block_root": "0x15bd2273ad32344e34f842fc77ad8acb2a2eaedafa6e5328ef799babfe81113d", + "source": { + "epoch": "10881", + "root": "0x12a21e7bb91e09dac76d5d3f170db6358785032f10b9130a1e92e6f4409f2ecf" + }, + "target": { + "epoch": "10882", + "root": "0x1c8a9a3a0d4c9d72a93b9ff2ea442a986f4d6dfde52953e48a146206393e7708" + } + }, + "signature": "0x886d038ddd7598cfda720dfe1caf83e030e24b207bbc0c97d012fbf5accbaa2f63366f32fe643aa1fdf6c8282480cd51165710bb786d77ecfb72ef7cc9d55e342c94fb57f5a75d50a0d486ecdf014bb08e0195f24202911c86efb5b46b2167ab" + } + ], + "deposits": [], + "voluntary_exits": [], + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffff7edfffffffff7ffffffffffffffffffffffffffff7ffdf7ffffffff7fffffefffffffffffffbfffffffffdffffffffffffffffffffff", + "sync_committee_signature": "0x877de19f5fff89de5af36954d4c7f7f5c2ccf6f8dc39fe7e3eb87c3357fca26f0af747dffd0f992c8844a20763e9f8a51858d0be70ce610055c6421d340160adec0ddcb706a7d7a5c45edff7b5f9b9a039fce093cea41c7a5699834f9de48b94" + }, + "execution_payload_header": { + "parent_hash": "0xa330251430b91a6fb5342f30a1f527dc76499c03a411464235951dbd51b94d9f", + "fee_recipient": "0xf97e180c050e5Ab072211Ad2C213Eb5AEE4DF134", + "state_root": "0x079f2cc22a29388fd4fc20f451cbaa3ff39845d68b2c368ff7be314617418e38", + "receipts_root": "0xed980a4cf6df8ba330c14ed9fe0597ec20515f44e5a9adfd2f7b72aa14890996", + "logs_bloom": "0x0000000400000008000008000040000000000000000000001000104880000200000004000000400000000204000020002000000000000000000000000022000800000004000000000002000c000000000000000000000100000000000000000000000000000000000000000000000040000000000040000001000014000000010002104000000000000000000000000000000000000000000000000000000080020000000000000000002400000000000001000000000002000200102000000040100002000000000000000000000000000000000000000800000000000000000010000000000000000000000000000000000400002000000000000000200000", + "prev_randao": "0x86cc02ef030b0c147321a7f94158c1b33cb730f8baac3c59955b983fda3ae39b", + "block_number": "330714", + "gas_limit": "30000000", + "gas_used": "369098", + "timestamp": "1679442492", + "extra_data": "0x", + "base_fee_per_gas": "7", + "block_hash": "0x4ab1ced57222819bf6a6b6c1456715011585599a1cef18b060eb364811bbb14e", + "transactions_root": "0x6d47bae3b4963cbde00ec39bbd6442540afe26f8005e73722489904836008bfc", + "withdrawals_root": "0x5dc5f3ff8bade8e1dd04e5cf56292b2a194a2829e1c8e8b4a627d95e08296ba3", + "blob_gas_used": "4438756708366371443", + "excess_blob_gas": "12504111653614393862" + }, + "bls_to_execution_changes": [], + "blob_kzg_commitments": [ + "0x95cc5099bbd8420d8ebade383c00a2346dace60a7604f768cd71501757b4d72eeb7d5474a6b615af10379d69aa9f478f", + "0xae9f2d2217013ef61f995f9074faead9ec24e8048440164ec3d6029b87d43686dd0c97c2df9554fc997d0d66c3a78929" + ] + } + }, + "signature": "0x8c3095fd9d3a18e43ceeb7648281e16bb03044839dffea796432c4e5a1372bef22c11a98a31e0c1c5389b98cc6d45917170a0f1634bcf152d896f360dc599fabba2ec4de77898b5dff080fa1628482bdbad5b37d2e64fea3d8721095186cfe50" +} diff --git a/testdata/signed-builder-bid-bellatrix.json b/testdata/signed-builder-bid-bellatrix.json new file mode 100644 index 00000000..5cc79f21 --- /dev/null +++ b/testdata/signed-builder-bid-bellatrix.json @@ -0,0 +1,26 @@ +{ + "version": "bellatrix", + "data": { + "message": { + "header": { + "parent_hash": "0x17f4eeae822cc81533016678413443b95e34517e67f12b4a3a92ff6b66f972ef", + "fee_recipient": "0x58e809c71e4885cb7b3f1d5c793ab04ed239d779", + "state_root": "0x3d6e230e6eceb8f3db582777b1500b8b31b9d268339e7b32bba8d6f1311b211d", + "receipts_root": "0xea760203509bdde017a506b12c825976d12b04db7bce9eca9e1ed007056a3f36", + "logs_bloom": "0x0c803a8d3c6642adee3185bd914c599317d96487831dabda82461f65700b2528781bdadf785664f9d8b11c4ee1139dfeb056125d2abd67e379cabc6d58f1c3ea304b97cf17fcd8a4c53f4dedeaa041acce062fc8fbc88ffc111577db4a936378749f2fd82b4bfcb880821dd5cbefee984bc1ad116096a64a44a2aac8a1791a7ad3a53d91c584ac69a8973daed6daee4432a198c9935fa0e5c2a4a6ca78b821a5b046e571a5c0961f469d40e429066755fec611afe25b560db07f989933556ce0cea4070ca47677b007b4b9857fc092625f82c84526737dc98e173e34fe6e4d0f1a400fd994298b7c2fa8187331c333c415f0499836ff0eed5c762bf570e67b44", + "prev_randao": "0x76ff751467270668df463600d26dba58297a986e649bac84ea856712d4779c00", + "block_number": "2983837628677007840", + "gas_limit": "6738255228996962210", + "gas_used": "5573520557770513197", + "timestamp": "1744720080366521389", + "extra_data": "0xc648", + "base_fee_per_gas": "88770397543877639215846057887940126737648744594802753726778414602657613619599", + "block_hash": "0x42c294e902bfc9884c1ce5fef156d4661bb8f0ff488bface37f18c3e7be64b0f", + "transactions_root": "0x8457d0eb7611a621e7a094059f087415ffcfc91714fc184a1f3c48db06b4d08b" + }, + "value": "12345", + "pubkey": "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" + }, + "signature": "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" + } +} diff --git a/testdata/signed-builder-bid-capella.json b/testdata/signed-builder-bid-capella.json new file mode 100644 index 00000000..7019af51 --- /dev/null +++ b/testdata/signed-builder-bid-capella.json @@ -0,0 +1,27 @@ +{ + "version": "capella", + "data": { + "message": { + "header": { + "parent_hash": "0x17f4eeae822cc81533016678413443b95e34517e67f12b4a3a92ff6b66f972ef", + "fee_recipient": "0x58e809c71e4885cb7b3f1d5c793ab04ed239d779", + "state_root": "0x3d6e230e6eceb8f3db582777b1500b8b31b9d268339e7b32bba8d6f1311b211d", + "receipts_root": "0xea760203509bdde017a506b12c825976d12b04db7bce9eca9e1ed007056a3f36", + "logs_bloom": "0x0c803a8d3c6642adee3185bd914c599317d96487831dabda82461f65700b2528781bdadf785664f9d8b11c4ee1139dfeb056125d2abd67e379cabc6d58f1c3ea304b97cf17fcd8a4c53f4dedeaa041acce062fc8fbc88ffc111577db4a936378749f2fd82b4bfcb880821dd5cbefee984bc1ad116096a64a44a2aac8a1791a7ad3a53d91c584ac69a8973daed6daee4432a198c9935fa0e5c2a4a6ca78b821a5b046e571a5c0961f469d40e429066755fec611afe25b560db07f989933556ce0cea4070ca47677b007b4b9857fc092625f82c84526737dc98e173e34fe6e4d0f1a400fd994298b7c2fa8187331c333c415f0499836ff0eed5c762bf570e67b44", + "prev_randao": "0x76ff751467270668df463600d26dba58297a986e649bac84ea856712d4779c00", + "block_number": "2983837628677007840", + "gas_limit": "6738255228996962210", + "gas_used": "5573520557770513197", + "timestamp": "1744720080366521389", + "extra_data": "0xc648", + "base_fee_per_gas": "88770397543877639215846057887940126737648744594802753726778414602657613619599", + "block_hash": "0x42c294e902bfc9884c1ce5fef156d4661bb8f0ff488bface37f18c3e7be64b0f", + "transactions_root": "0x8457d0eb7611a621e7a094059f087415ffcfc91714fc184a1f3c48db06b4d08b", + "withdrawals_root": "0x5c1a7e3e0eab917a7c0d677c6b692ed55ce05f07d572e09b1c06d558f474ea7a" + }, + "value": "12345", + "pubkey": "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" + }, + "signature": "0x010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101" + } +} diff --git a/testdata/zhejiang-execution-payload-capella.json b/testdata/zhejiang-execution-payload-capella.json new file mode 100644 index 00000000..3618a63c --- /dev/null +++ b/testdata/zhejiang-execution-payload-capella.json @@ -0,0 +1,132 @@ +{ + "parent_hash": "0x40d276c614b86013c95bbe59698628940b6c62ef63d50a528c24e9eaab591198", + "fee_recipient": "0x13cb6ae34a13a0977f4d7101ebc24b87bb23f0d5", + "state_root": "0x0e5a097fa89d985392014b37b23e7f69286ed8dc8c06e2e55dada849d331c2e1", + "receipts_root": "0x1ddeeef0faf8c796762027d9ac55de825894985b18d6640cac1cc29041d50b7d", + "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prev_randao": "0xf5af8152556568891bc536bdd0ccfa2e37b7c951dd57af0e9f8bd552b7426c53", + "block_number": "139225", + "gas_limit": "30000000", + "gas_used": "551506", + "timestamp": "1677021756", + "extra_data": "0x496c6c756d696e61746520446d6f63726174697a6520447374726962757465", + "base_fee_per_gas": "7", + "block_hash": "0x08751ea2076d3ecc606231495a90ba91a66a9b8fb1a2b76c333f1957a1c667c3", + "transactions": [ + "0xf875821054851010b87200830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd8872386f26fc10000841998aeef8328d3b9a0ac092d4e961e6382b8b3a80e60b4d55a52157ebda53a9183326f4dc3f1d1302ea062e966f7c072a22699fff9eaffe4893fa41d1fb72cd9382fc43fb083d299d8a7", + "0xf86e821055850dbcac8e00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd880844e71d92d8328d3b9a03cb8c728f77b35a07507b70aa922b6e31bae0fc870b0de7fba0d50dc6eb9ceefa0513ea444fb4133e923fc530c3a34a3de84623fa1a860761b2275e94a4801d757", + "0xf875821054850ee6b28000830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd8872386f399c6f200841998aeef8328d3baa005822bca0d7104540eae5db2b6b1c6269bca65510f44f769b2a9a1458be6397da04ea1f2d874c9effa336099ae5f2bd626887675a5ce9fb8847ecc2722131a0130", + "0xf86e821055850dbcac8e00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd880844e71d92d8328d3baa07925e78c3ac04877525a917beee6e24128356a63f3cd7c6ec221771cb405be8da05be8386defe14eef922fbba7c949f6404882a0d123e467ff11a55d6a9e4c18c1", + "0xf875821054850eab17b600830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd8872386f3d561bc00841998aeef8328d3b9a0cd6b92b536ec40391285420feb8248bc129b80345a9ba19fc33f5653823a7706a0646f8ce3664772820240cabd6f6f9c1248e3017fdf4d5c5fb72763e61f78d446", + "0xf86e821055850dbcac8e00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd880844e71d92d8328d3baa05bf1ba6fac6f621ec8528d3f6b5abbae72e3f9c8a212ddaad551f29427c30a49a05a33b94218db9e44ffbdf068d63be62475719a0029dcd652437344608d583d04", + "0xf875821054850e6f7cec00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd8872386f410fc8600841998aeef8328d3baa00358efcdf94fee3b7736a1acde54bdaba1dcde70d028acb26e8a658a3a4e8395a00a24ea2c5d9f398ff9747e43c896096ea770fb9ecd09c0979229198cd08dbde8", + "0xf86e821055850dbcac8e00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd880844e71d92d8328d3b9a0d03940e5d6f837663e31647492cbdb78d6682838d88522f939c8f67737ff435fa07747a35e473163a7fa8ddeae5a1c04b7ff93881170940758dc2e09eb4a704d4e", + "0xf875821054850e33e22200830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd8872386f44c975000841998aeef8328d3baa070381fcd0adb9c49e68fe861c7badc5d644d7140db3e2235b61e22eb9a023e1ba02fe84ed44efd953759c6c4892077acc571a8798d0afe84552e70be2298e717bb", + "0xf86e821055850dbcac8e00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd880844e71d92d8328d3b9a07a1e1c837de4d9e5797c0d76fd1b8dd4b768893e6990d4320063636230cc9b47a018d81eb0e8a82de9e5ee9eb886c8b0a6fcde6d52ee5d2f1fe9e472499d8d7828", + "0xf875821054850df8475800830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd8872386f488321a00841998aeef8328d3b9a0cff9e433b2df74270efe3bed7b19e9eb845c0b51da98cf235db283b05a4c52eea066302c9e70d910b3b97caa4d9f4e7d884af346f26e7ef25925009e93844da676", + "0xf86e821055850dbcac8e00830186a094f9aec40f2a2b2effe691491d7cfe3c90ddf6bbd880844e71d92d8328d3baa02aa3cb1e8b51a9080ec3b92f8978995f0b794fd8db0614d5a384711cc1db3259a012351f30cffed3adc0d2c89b71d2fecbdcc2133cfeb7b1058532bbb0e53e45e9", + "0x02f877831469cb82cbe48405f5e1008405f5e10e825208948aa2b8622c1223a62fbc1119fa2b483ab41b250b88016345785d8a000080c080a0600fd64f4b97683e7b1130f5bc9e9860e0bb9f6dc6c3bf13ecdb6554cc6596c1a02682d446b692d0d8e2a7e4d15ba9fd259230bea211fc40ee056629ede5093811", + "0x02f877831469cb82cbe58405f5e1008405f5e10e82520894388979a907fee4c8083aaffb9f66bc33001c1d4d88016345785d8a000080c001a0b242a130a0e7976a70766124772c6bc7a01c11e08bb1af134ae7d077b45c1528a03adc5e74af9304312fa53bf858babe795bcfe06ab5188873b6fe7623c08b7b57", + "0x02f877831469cb82cbe58405f5e1008405f5e10e825208948aa2b8622c1223a62fbc1119fa2b483ab41b250b88016345785d8a000080c001a05defd8d136d031e2c9afcc1bd590dc6e3fda7863a641771f6d0d168cbf610f02a03b2a491394fc5702ce911a312324660018e4421084f53b965d8b3f238143ad63", + "0x02f877831469cb82cbe68405f5e1008405f5e10e82520894388979a907fee4c8083aaffb9f66bc33001c1d4d88016345785d8a000080c001a0dc74824467ca640cdeae147a837e867b92548ea2e1ca739a865e022dc27b8a7aa062f6d55d06030a6a90e57b2086336cf33a00fa4ac1a08ba9a14b7c1b83e51647", + "0x02f86e831469cb8209aa800782520894f97e180c050e5ab072211ad2c213eb5aee4df13487614fca865658a280c080a011f98172aa018c80ff382d7d4c51534fd78fa29254dac91746e3d0c6ef7010dea04cbc906ab1c898206d3b084cd38066d9d1e671bf11cc98d41ceb93a20cc8d5e3" + ], + "withdrawals": [ + { + "index": "1530173", + "validator_index": "48240", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530174", + "validator_index": "48241", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530175", + "validator_index": "48242", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530176", + "validator_index": "48243", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530177", + "validator_index": "48244", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530178", + "validator_index": "48245", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530179", + "validator_index": "48246", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530180", + "validator_index": "48247", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "178988" + }, + { + "index": "1530181", + "validator_index": "48248", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530182", + "validator_index": "48249", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "178988" + }, + { + "index": "1530183", + "validator_index": "48250", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530184", + "validator_index": "48251", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530185", + "validator_index": "48252", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530186", + "validator_index": "48253", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530187", + "validator_index": "48254", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + }, + { + "index": "1530188", + "validator_index": "48255", + "address": "0xf97e180c050e5ab072211ad2c213eb5aee4df134", + "amount": "188209" + } + ] +}