diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index 1de973a959..4f0209b80a 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -6,6 +6,7 @@ on: push: branches: - main + - dev paths-ignore: - "docs/**" - "infrastructure/**" @@ -192,6 +193,21 @@ jobs: path: coverage/coverage.out if-no-files-found: warn + # Coverage gate: 14% minimum total. Baseline measured at PR #4256 + # (2026-08-19); no automated baseline-update mechanism exists. + - name: Check coverage gate + run: | + docker run --rm \ + -v "${{ github.workspace }}/coverage:/coverage" \ + go-build-env \ + go tool cover -func /coverage/coverage.out > /tmp/cover-func.txt + TOTAL=$(grep '^total:' /tmp/cover-func.txt | awk '{print $3}' | tr -d '%') + echo "Total coverage: ${TOTAL}%" + PASS=$(awk -v t="$TOTAL" 'BEGIN { print (t+0 >= 14) ? "yes" : "no" }') + if [ "$PASS" != "yes" ]; then + echo "::error::Coverage ${TOTAL}% is below the 14% minimum threshold" + exit 1 + fi - name: Build Docker Runtime Image if: github.event_name != 'workflow_dispatch' @@ -358,6 +374,93 @@ jobs: install-go: false checks: "-SA1019" + client-bench: + needs: [client-build-test-publish] + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + actions: read + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.24" + cache: false + + - name: Download Docker Build Image + uses: actions/download-artifact@v4 + with: + name: go-build-env-image + path: /tmp + + - name: Load Docker Build Image + run: | + docker load --input /tmp/go-build-env-image.tar + + - name: Download previous benchmark results + id: download-prev + uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6 + continue-on-error: true + with: + name: go-bench + path: bench-prev + workflow: client.yml + branch: main + if_no_artifact_found: warn + + - name: Run benchmarks + run: | + docker run \ + --workdir /go/src/github.com/keep-network/keep-core \ + go-build-env \ + go test -bench=. -benchmem -count=10 -run='^$' ./pkg/... \ + > bench.txt + cat bench.txt + + - name: Install benchstat + run: go install golang.org/x/perf/cmd/benchstat@v0.0.0-20260813145340-fd4a688df892 + + # Benchmark regression gate: >12% slower than main's last `go-bench` + # artifact. Baseline measured at PR #4256 (2026-08-19). This job only + # runs on manual `workflow_dispatch` - no automatic push/PR trigger. + # GAP: benchmarks added in this PR have no main-side baseline, so + # benchstat silently skips them on first run; they only become + # gated after shipping to main and someone manually dispatches this + # workflow again on main to record a baseline artifact. + # No automated baseline-record mechanism exists in this workflow. + - name: Compare benchmarks + if: steps.download-prev.outcome == 'success' && hashFiles('bench-prev/**') != '' + run: | + benchstat bench-prev/*.txt bench.txt | tee benchstat-results.txt + python3 - <<'EOF' + import sys, re + content = open('benchstat-results.txt').read() + regressions = [] + for line in content.splitlines(): + if '~' in line or not line.strip(): + continue + m = re.search(r'\+(\d+\.\d+)%', line) + if m and float(m.group(1)) > 12: + regressions.append(line) + if regressions: + print('Performance regressions >12% detected:') + for r in regressions: + print(' ', r) + sys.exit(1) + EOF + + - name: Upload benchmark results + if: always() + uses: actions/upload-artifact@v4 + with: + name: go-bench + path: bench.txt + overwrite: true + if-no-files-found: warn + client-integration-test: needs: [client-detect-changes, electrum-integration-detect-changes, client-build-test-publish] if: | @@ -379,8 +482,11 @@ jobs: docker load --input /tmp/go-build-env-image.tar - name: Run Go Integration Tests + env: + ETHEREUM_MAINNET_RPC_URL: ${{ secrets.ETHEREUM_MAINNET_RPC_URL }} run: | docker run \ + -e ETHEREUM_MAINNET_RPC_URL \ --workdir /go/src/github.com/keep-network/keep-core \ go-build-env \ gotestsum -- -timeout 20m -tags=integration ./... diff --git a/.gitignore b/.gitignore index 0c2c04268b..dc42dcb3f6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,6 @@ *.swp *.swo -# Infrastructure -/infrastructure/gcp/service-accounts* - # Secret directory used in Kubernetes configurations /infrastructure/kube/**/.secret/ /infrastructure/kube/**/*.secret diff --git a/Makefile b/Makefile index ab468ae08f..6dc591d2f3 100644 --- a/Makefile +++ b/Makefile @@ -146,4 +146,7 @@ cmd-help: build @echo '$$ $(app_name) start --help' > docs/resources/client-start-help ./$(app_name) start --help >> docs/resources/client-start-help -.PHONY: all development sepolia download_artifacts generate gen_proto build cmd-help release build_multi +bench: + go test -bench=. -benchmem -count=10 -run='^$$' ./pkg/... + +.PHONY: all development sepolia mainnet local get_artifacts generate gen_proto build cmd-help release build_multi bench diff --git a/cmd/flags.go b/cmd/flags.go index 7a67ad5df8..302be9e408 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -310,6 +310,25 @@ func initTbtcFlags(cmd *cobra.Command, cfg *config.Config) { tbtc.DefaultKeyGenerationConcurrency, "tECDSA key generation concurrency.", ) + + cmd.Flags().IntVar( + &cfg.Tbtc.WalletTxSatPerVByteFloor, + "tbtc.walletTxSatPerVByteFloor", + tbtc.DefaultWalletTxSatPerVByteFloor, + "Minimum fee rate (sat/vByte) applied to wallet Bitcoin transactions "+ + "(deposit sweeps, redemptions, moving funds, moved funds sweeps). "+ + "Applies to both the leader-side floor in tbtcpg and the "+ + "follower-side soft check; 0 means use the default.", + ) + + cmd.Flags().IntVar( + &cfg.Tbtc.WalletTxFeeBufferPercent, + "tbtc.walletTxFeeBufferPercent", + tbtc.DefaultWalletTxFeeBufferPercent, + "Safety-buffer percentage applied over the per-vByte fee rate "+ + "(bufferedRate = ceil(rawRate * (100+Percent) / 100)); "+ + "0 means use the default.", + ) } // Initialize flags for Maintainer configuration. @@ -373,6 +392,15 @@ func initMaintainerFlags(command *cobra.Command, cfg *config.Config) { "The wait time which should be applied when there are no more "+ "transaction proofs to submit.", ) + command.Flags().UintVar( + &cfg.Maintainer.Spv.MaxProofHeaders, + "spv.maxProofHeaders", + spv.DefaultMaxProofHeaders, + "The maximum number of block headers allowed when assembling an SPV "+ + "proof. Bounds the forward walk over headers and so the number of "+ + "consecutive leading minimum-difficulty (DIFF1) headers a proof "+ + "can absorb before it becomes unprovable.", + ) } // Initialize flags for Developer configuration. diff --git a/cmd/flags_test.go b/cmd/flags_test.go index bb313cf50c..d490b9558e 100644 --- a/cmd/flags_test.go +++ b/cmd/flags_test.go @@ -22,6 +22,7 @@ import ( ethereumEcdsa "github.com/keep-network/keep-core/pkg/chain/ethereum/ecdsa/gen" ethereumTbtc "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen" ethereumThreshold "github.com/keep-network/keep-core/pkg/chain/ethereum/threshold/gen" + "github.com/keep-network/keep-core/pkg/tbtc" ) var cmdFlagsTests = map[string]struct { @@ -225,6 +226,20 @@ var cmdFlagsTests = map[string]struct { expectedValueFromFlag: 101, defaultValue: runtime.GOMAXPROCS(0), }, + "tbtc.walletTxSatPerVByteFloor": { + readValueFunc: func(c *config.Config) interface{} { return c.Tbtc.WalletTxSatPerVByteFloor }, + flagName: "--tbtc.walletTxSatPerVByteFloor", + flagValue: "7", + expectedValueFromFlag: 7, + defaultValue: tbtc.DefaultWalletTxSatPerVByteFloor, + }, + "tbtc.walletTxFeeBufferPercent": { + readValueFunc: func(c *config.Config) interface{} { return c.Tbtc.WalletTxFeeBufferPercent }, + flagName: "--tbtc.walletTxFeeBufferPercent", + flagValue: "30", + expectedValueFromFlag: 30, + defaultValue: tbtc.DefaultWalletTxFeeBufferPercent, + }, "maintainer.bitcoinDifficulty": { readValueFunc: func(c *config.Config) interface{} { return c.Maintainer.BitcoinDifficulty.Enabled }, flagName: "--bitcoinDifficulty", diff --git a/cmd/start.go b/cmd/start.go index c5bc8902f2..95e1ebc2bb 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -231,7 +231,7 @@ func initializeClientInfo( signing chain.Signing, blockCounter chain.BlockCounter, ) *clientinfo.Registry { - registry, isConfigured := clientinfo.Initialize(ctx, config.ClientInfo.Port) + registry, isConfigured := clientinfo.Initialize(ctx, config.ClientInfo) if !isConfigured { logger.Infof("client info endpoint not configured") return nil diff --git a/docs/dev-ops.adoc b/docs/dev-ops.adoc index 1908b4312e..60e182ece1 100644 --- a/docs/dev-ops.adoc +++ b/docs/dev-ops.adoc @@ -7,7 +7,7 @@ = Kubernetes -At Keep we run on GCP + Kubernetes. To accommodate the aforementioned +At Keep we run on GCP and Kubernetes. To accommodate the xref:./run-keep-node.adoc#system-considerations[System Considerations] we use the following pattern for each of our environments: @@ -16,4 +16,3 @@ we use the following pattern for each of our environments: - A LoadBalancer Service for each client. - A StatefulSet for each client. -You can see our Testnet Kubernetes configurations link:https://github.com/threshold-network/keep-core/tree/main/infrastructure/kube/keep-test[here]. diff --git a/docs/index.adoc b/docs/index.adoc index c54fe111c2..4d8622438c 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -4,5 +4,6 @@ * xref:./registration.adoc[Registration] * xref:./run-keep-node.adoc[Run Keep Client Node] +* xref:./profiling.md[Profiling & pprof runbook] * xref:./development/README.adoc[Developers] * xref:./dev-ops.adoc[DevOps] \ No newline at end of file diff --git a/docs/profiling.md b/docs/profiling.md new file mode 100644 index 0000000000..fdc0d6c077 --- /dev/null +++ b/docs/profiling.md @@ -0,0 +1,149 @@ +# Go Profiling Runbook + +## Overview + +The keep-core binary exposes Go runtime profiling endpoints via the +`clientinfo` HTTP server. The `/debug/pprof/...` endpoints are gated by +`EnablePprof` in configuration: they are only registered when +`EnablePprof: true` is set explicitly under `[ClientInfo]`, and are +not served otherwise. Profiles are served at `/debug/pprof/` on the +same port as metrics and diagnostics (`ClientInfo.Port`). + +## Security Warning + +The clientinfo HTTP server binds to all interfaces (`0.0.0.0`). The +`EnablePprof` flag is the only thing that prevents the `/debug/pprof/` +endpoints from being reachable on that port: **leave `EnablePprof` +unset or `false` on any node whose `ClientInfo.Port` is reachable +beyond a trusted network.** Leaving the flag at its default (off) is +the secure posture; flipping it on a node exposed to untrusted +networks exposes CPU profiles, heap dumps, and goroutine traces that +can leak sensitive runtime state. + +Safe access patterns when profiling is genuinely needed: +- Run on a private/firewalled network +- Use an SSH tunnel: `ssh -L 9601:localhost:9601 node-host` +- Restrict at the network layer (security group, firewall rule) +- Profile briefly (e.g. `-seconds=` on the CPU profile endpoint) and + set `EnablePprof: false` again when finished + +## Enabling Profiling + +Profiling is disabled by default. To enable it, set `EnablePprof: true` +in your config (TOML example): + +```toml +[ClientInfo] + Port = 9601 + EnablePprof = true +``` + +Or pass via environment / flag if your deployment uses those overrides. +Set `EnablePprof` back to `false` (or remove it) as soon as you are +finished profiling so the endpoints stop being served. + +## Standard Commands + +Replace `9601` with your configured `ClientInfo.Port`. + +### CPU profile (30 seconds) + +```sh +go tool pprof http://localhost:9601/debug/pprof/profile?seconds=30 +``` + +### Heap profile + +```sh +go tool pprof http://localhost:9601/debug/pprof/heap +``` + +### Goroutine dump (text) + +```sh +curl -s http://localhost:9601/debug/pprof/goroutine?debug=2 +``` + +### Trace (5 seconds) + +```sh +curl -o /tmp/trace.out http://localhost:9601/debug/pprof/trace?seconds=5 +go tool trace /tmp/trace.out +``` + +### Mutex contention + +```sh +# Enable mutex profiling first (runtime call or startup flag): +# runtime.SetMutexProfileFraction(1) +go tool pprof http://localhost:9601/debug/pprof/mutex +``` + +## Benchmark + Profile Workflow + +To identify hot paths found by benchmarks: + +Note: `-bench=` accepts a Go regular expression that substring-matches +benchmark names; the patterns below intentionally match every +size-suffixed variant of the named benchmark (e.g. +`BenchmarkGetRecentWindows_100Windows`, `BenchmarkComputeSignatureHashes_5Inputs`). + +```sh +# Run benchmark and write CPU profile +go test ./pkg/tbtc/... -run=^$ -bench=BenchmarkGetRecentWindows \ + -cpuprofile=/tmp/cpu.pprof -benchtime=5s + +# Inspect interactively +go tool pprof /tmp/cpu.pprof +(pprof) top10 +(pprof) web # requires graphviz +``` + +For memory allocation hot paths: + +```sh +go test ./pkg/bitcoin/... -run=^$ -bench=BenchmarkComputeSignatureHashes \ + -memprofile=/tmp/mem.pprof -benchtime=5s +go tool pprof /tmp/mem.pprof +(pprof) alloc_space +(pprof) top10 +``` + +## Comparing Benchmarks Across Commits + +```sh +# Baseline (main branch) +git stash +go test ./pkg/... -run=^$ -bench=. -count=6 | tee /tmp/baseline.txt + +# Candidate (your branch) +git stash pop +go test ./pkg/... -run=^$ -bench=. -count=6 | tee /tmp/candidate.txt + +benchstat /tmp/baseline.txt /tmp/candidate.txt +``` + +Install `benchstat`: `go install golang.org/x/perf/cmd/benchstat@latest` + +## Available Endpoints + +| Endpoint | Description | +|----------|-------------| +| `/debug/pprof/` | Index of available profiles | +| `/debug/pprof/cmdline` | Process command line | +| `/debug/pprof/profile` | CPU profile (30s default) | +| `/debug/pprof/symbol` | Symbol lookup | +| `/debug/pprof/trace` | Execution trace | +| `/debug/pprof/goroutine` | Goroutine stacks | +| `/debug/pprof/heap` | Heap allocations | +| `/debug/pprof/allocs` | Allocation samples | +| `/debug/pprof/block` | Goroutine blocking events | +| `/debug/pprof/mutex` | Mutex contention | + +## Notes + +- CPU profiling adds ~5% overhead to the profiled binary during the sampling + window. It is safe to run against a live node for short durations. +- Heap and goroutine profiles are sampled snapshots; a single sample may + miss transient allocations. Take multiple profiles under load. + diff --git a/docs/release-process.md b/docs/release-process.md index 3d1ed5b74a..78b3eb5da5 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -9,6 +9,26 @@ Keep Core now supports fully automated releases through GitHub Actions. When you 3. Creates a GitHub release with artifacts 4. Generates release notes +## Release Tracking PR (dev → main) + +When a release cycle accumulates a large or interconnected set of +changes, the project uses a long-lived aggregation PR instead of +landing everything via normal `feature → main` PRs: + +- **Base:** `main` +- **Head:** a moving `dev` branch that tracks `main` by merging each + sub-PR into `dev` (and `main`) before the sub-PR closes +- **State:** the PR stays open across the whole cycle. Its diff + against `main` is the live view of "what is still queued for the + next release." + +Sub-PRs are still reviewed and CI'd independently — the aggregation +PR is just the place to watch the cumulative state. When the cycle is +ready to ship, fast-forward `dev` to the latest `main`, resolve any +final conflicts, and merge the aggregation PR into `main` as a single +merge commit. The version tag is then cut from `main` per "Creating +a Release" below. + ## Creating a Release ### 1. Prepare the Release diff --git a/docs/retired-components.md b/docs/retired-components.md index 3f524cd8cf..caf174088a 100644 --- a/docs/retired-components.md +++ b/docs/retired-components.md @@ -12,7 +12,7 @@ below are the original locations under the now-extracted v1 tree (formerly - `token-stakedrop/` - `solidity-v1/scripts/withdraw-old-rewards.js` - `solidity-v1/dashboard/` -- KEEP token dashboard Kubernetes manifests under `infrastructure/kube/keep-*` +- the `./infrastructure/` tree, with the exceptions noted below: KEEP-era GKE manifests under `kube/{keep-test,keep-dev,keep-prd,lcl}`, Terraform modules sourcing from the now-defunct `thesis/infrastructure` repository, the `provision-keep-client` initcontainer that consumed `solidity-v1/` contract JSONs (since extracted to `keep-core-v1`), and other private-testnet / Goerli-era assets - `scripts/start_dashboard.sh` These components were removed because they are no longer part of supported @@ -20,7 +20,35 @@ operations, were tied to deprecated KEEP-token workflows, and had accumulated unmaintained security risk. In particular, the old rewards withdrawal helper contained a committed mainnet private key (since rotated and no longer active), and the retired staking escrow had no remaining ETH, KEEP, or T balance on -Ethereum mainnet when checked before removal. +Ethereum mainnet when checked before removal. The removed `infrastructure/` +tree also contained low-sensitivity testnet/dev credential material now +recoverable only via git history: a private Ethereum testnet keystore +passphrase and a hardcoded local-dev dashboard `WS_SECRET`. Neither is a +production credential. + +**Exceptions: three Kubernetes overlays under `infrastructure/kube/` were +kept.** Unlike the rest of the tree, these overlays are actively deployed +(`kubectl apply -k ./`, independent of the retired Terraform) and remain in +the repository at their original paths: + +- `infrastructure/kube/keep-test/tbtc-v2-maintainer/`: the tBTC v2 testnet + maintainer, last patched to fix its Electrum endpoint shortly before this + cleanup +- `infrastructure/kube/keep-prd/tbtc-v2-monitoring/`: tBTC v2 mainnet + monitoring +- `infrastructure/kube/keep-prd/keep-maintainer/`: the keep-client + maintainer StatefulSet on mainnet + +The two `keep-prd/` overlays build on shared bases under +`infrastructure/kube/templates/{keep-maintainer,tbtc-v2-monitoring}/`, which +were kept with them. + +**GCP projects referenced by the retired Terraform remain live.** +`keep-test-f3e0` and `keep-prd-210b` (see `.github/workflows/client.yml`, +`docs/run-keep-node.adoc`, and `docs/registration.adoc`) are still used for +CI image publishing and client-binary distribution. They are managed +out-of-band from the removed Terraform, which had not been applied since +2020 and sourced from the same now-defunct `thesis/infrastructure` remote. Historical documents under the `docs/` tree of `keep-core-v1` (formerly `docs-v1/` here) may still mention these components for release history and diff --git a/infrastructure/docker/ethereum/dashboard-node/Dockerfile b/infrastructure/docker/ethereum/dashboard-node/Dockerfile deleted file mode 100644 index 7f97a1f7a8..0000000000 --- a/infrastructure/docker/ethereum/dashboard-node/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -FROM ubuntu -MAINTAINER "Markus Fix - -RUN apt-get update && apt-get upgrade -y -RUN apt-get install -y build-essential -RUN apt-get install -y nodejs npm git curl - -RUN npm install -g grunt -RUN npm install -g pm2 - -RUN git clone https://github.com/lispmeister/eth-netstats.git /var/lib/eth-netstats -WORKDIR /var/lib/eth-netstats -RUN npm install -RUN grunt all - -RUN git clone https://github.com/lispmeister/bootnode-registrar.git /var/lib/bootnode -WORKDIR /var/lib/bootnode -RUN npm install - -RUN useradd -ms /bin/bash dashboard -USER dashboard - -WORKDIR /home/dashboard -COPY app.json /home/dashboard/app.json -COPY run.sh /home/dashboard/run.sh - -COPY updateNode.sh /home/dashboard/updateNode.sh -RUN /bin/bash /home/dashboard/updateNode.sh - -ENTRYPOINT ["/bin/bash", "run.sh"] diff --git a/infrastructure/docker/ethereum/dashboard-node/README.adoc b/infrastructure/docker/ethereum/dashboard-node/README.adoc deleted file mode 100644 index bcf3c034b1..0000000000 --- a/infrastructure/docker/ethereum/dashboard-node/README.adoc +++ /dev/null @@ -1,22 +0,0 @@ -= Build Dashboard Node image - -To build the docker image -``` -docker build --pull --squash --no-cache --rm -t $DOCKER_ID_USER/eth-stats-dashboard . -``` - -You can list your new image with this command: -``` -docker images |grep eth-stats-dashboard -``` - -Push the image to Docker Hub: -``` -docker push $DOCKER_ID_USER/eth-stats-dashboard -``` - -Start a single node as a Docker container without Kubernetes -mapping the HTTP interface to `localhost:3000`: -``` -docker run -it -p 3000:3000 $DOCKER_ID_USER/eth-stats-dashboard -``` diff --git a/infrastructure/docker/ethereum/dashboard-node/app.json b/infrastructure/docker/ethereum/dashboard-node/app.json deleted file mode 100644 index 6b1cef986c..0000000000 --- a/infrastructure/docker/ethereum/dashboard-node/app.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "name" : "bootNodeRegistrar", - "script" : "/var/lib/bootnode/app.js", - "log_date_format" : "YYYY-MM-DD HH:mm Z", - "merge_logs" : false, - "watch" : true, - "max_restarts" : 0, - "exec_interpreter" : "node", - "exec_mode" : "fork_mode", - "env": - { - "PORT" : 3001, - "NODE_ENV" : "production", - "VERBOSITY" : 2 - } - } -] diff --git a/infrastructure/docker/ethereum/dashboard-node/run.sh b/infrastructure/docker/ethereum/dashboard-node/run.sh deleted file mode 100644 index e31faef6cb..0000000000 --- a/infrastructure/docker/ethereum/dashboard-node/run.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -export NVM_DIR="$HOME/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - -cd /home/dashboard -pm2 start app.json -cd /var/lib/eth-netstats -npm start diff --git a/infrastructure/docker/ethereum/dashboard-node/updateNode.sh b/infrastructure/docker/ethereum/dashboard-node/updateNode.sh deleted file mode 100644 index 0d2af792be..0000000000 --- a/infrastructure/docker/ethereum/dashboard-node/updateNode.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# install Node Version Manager -curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash - -export NVM_DIR="$HOME/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" -# install long-term support version of node -nvm install --lts -nvm use --lts diff --git a/infrastructure/docker/ethereum/geth-node/Dockerfile b/infrastructure/docker/ethereum/geth-node/Dockerfile deleted file mode 100644 index 11b0951562..0000000000 --- a/infrastructure/docker/ethereum/geth-node/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# Be explicit about the ethereum go client version installed -# This version should be used to tag the resulting image that's pushed -# to Keeps container registry -FROM ethereum/client-go:v1.9.6 -MAINTAINER "Thesis.co" - -# Install dependencies required for downstream commands -# These dependencies can be used here, in geth-init.sh, or run-geth.sh - -RUN apk add --no-cache --update python -RUN apk add --no-cache --update build-base -RUN apk add --no-cache --update nodejs npm -RUN apk add --no-cache --update bash -RUN apk add --no-cache --update jq -RUN apk add --no-cache --update curl -RUN apk add --no-cache --update git - -# Configure log rotation - -RUN npm install pm2 -g -RUN pm2 install pm2-logrotate -RUN pm2 set pm2-logrotate:max_size 100M -RUN pm2 set pm2-logrotate:compress true -RUN pm2 set pm2-logrotate:rotateInterval '23 * * *' - -# Install code to report in at the registry of the bootnode (dashboard) -RUN git clone https://github.com/lispmeister/bootnode-registrar.git /root/lib/bootnode -WORKDIR /root/lib/bootnode -RUN npm install - -# Install ethStatsApi to report local stats to dashboard -RUN git clone https://github.com/lispmeister/eth-net-intelligence-api.git /root/lib/ethStatsApi -WORKDIR /root/lib/ethStatsApi -RUN npm install - -# Change to /root before provisioning our services -WORKDIR /root - -# Setup target dir for geth data -RUN mkdir .geth - -# Copy passphrase file -COPY testnet-account-passphrase.txt passphrase - -# Copy keystore -# If you need a copy of the keystore it's in /keep-core/private-testnet/keyfles -ADD keystore .geth/keystore - -# Create genesis file -COPY genesis-template.json genesis-template.json -COPY geth-init.sh geth-init.sh -RUN /root/geth-init.sh - -# Provision our three services (check app.json for details) -COPY app.json app.json -COPY run-geth.sh run-geth.sh - -ENTRYPOINT ["pm2", "start", "--no-daemon", "app.json"] diff --git a/infrastructure/docker/ethereum/geth-node/README.adoc b/infrastructure/docker/ethereum/geth-node/README.adoc deleted file mode 100644 index 7642f248f5..0000000000 --- a/infrastructure/docker/ethereum/geth-node/README.adoc +++ /dev/null @@ -1,37 +0,0 @@ -= Build Geth Node Image - -== WARNING == - -We are currently storing the passphrase for all accounts that we create on -the testnet in the file `passphrase` that lives in the same directory as this -README file. This is HORRIBLY INSECURE and only OK for the internal testnet. - -== Build -To build the docker image: -``` -docker build --pull --squash --no-cache --rm -t $DOCKER_ID_USER/geth-node . -``` - -Build an image with five Keep client accounts: -``` -docker build --build-arg KEEP_ACCOUNTS=5 --pull --squash --no-cache --rm -t $DOCKER_ID_USER/geth-node . -``` - -== List -You can list your new image with this command: -``` -docker images |grep geth-node -``` - -== Copy Keystore Files -You can copy the keystore files for the accounts created during the Docker run -with the following commands: -``` -docker run --entrypoint="" --rm -v `pwd`:/out $DOCKER_ID_USER/geth-node cp -rv /root/.geth/keystore /out -``` - -== Push Image -Push the image to Docker Hub: -``` -docker push $DOCKER_ID_USER/geth-node -``` diff --git a/infrastructure/docker/ethereum/geth-node/app.json b/infrastructure/docker/ethereum/geth-node/app.json deleted file mode 100644 index b47e831150..0000000000 --- a/infrastructure/docker/ethereum/geth-node/app.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "name" : "gethNode", - "script" : "/root/run-geth.sh", - "log_date_format" : "YYYY-MM-DD HH:mm Z", - "merge_logs" : false, - "watch" : true, - "max_restarts" : 0, - "exec_interpreter" : "/bin/bash", - "exec_mode" : "fork_mode", - "env": - { - "VERBOSITY" : 3 - } - }, - { - "name" : "ethStatsApi", - "script" : "/root/lib/ethStatsApi/app.js", - "log_date_format" : "YYYY-MM-DD HH:mm Z", - "merge_logs" : false, - "watch" : true, - "max_restarts" : 0, - "exec_interpreter" : "node", - "exec_mode" : "fork_mode", - "env": - { - "NODE_ENV" : "production", - "RPC_HOST" : "localhost", - "RPC_PORT" : "8545", - "LISTENING_PORT" : "30303", - "VERBOSITY" : 1 - } - }, - { - "name" : "bootNodeReporter", - "script" : "/root/lib/bootnode/client.js", - "log_date_format" : "YYYY-MM-DD HH:mm Z", - "merge_logs" : false, - "watch" : true, - "max_restarts" : 10, - "restart_delay" : 4000, - "exec_interpreter" : "node", - "exec_mode" : "fork_mode", - "env": - { - "NODE_ENV" : "production", - "VERBOSITY" : 1 - } - } -] diff --git a/infrastructure/docker/ethereum/geth-node/docker-entrypoint.sh b/infrastructure/docker/ethereum/geth-node/docker-entrypoint.sh deleted file mode 100755 index bec45a1308..0000000000 --- a/infrastructure/docker/ethereum/geth-node/docker-entrypoint.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh -set -e - -# generate a random node id -export RANDOM_ID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) -echo "-- RANDOM_ID: $RANDOM_ID" -echo "" - -# create new account for this Keep client -/geth account new --password /root/passphrase | \ - cut -d "{" -f2 | cut -d "}" -f1 > /root/account0 -export GETH_ETH_ACCOUNT0=`cat /root/account0` -echo "-- GETH_ETH_ACCOUNT0: $GETH_ETH_ACCOUNT0" -echo "" - -# create new account for Keep peers -/geth account new --password /root/passphrase | \ - cut -d "{" -f2 | cut -d "}" -f1 > /root/account1 -export GETH_ETH_ACCOUNT1=`cat /root/account1` -echo "-- GETH_ETH_ACCOUNT1: $GETH_ETH_ACCOUNT1" -echo "" - -# Generate genesis.json and issue tokens to Keep peers account1 -cat <> /root/genesis.json -{ - "config": { - "chainId": 1101, - "homesteadBlock": 0, - "eip155Block": 0, - "eip158Block": 0 - }, - "difficulty" : "0x20000", - "gasLimit" : "0x493E00", - "alloc": { -EOF - -echo " \"0x$GETH_ETH_ACCOUNT1\": {" >> /root/genesis.json -echo " \"balance\": \"1000000000000000000000\"" >> /root/genesis.json -cat <> /root/genesis.json - } - } -} -EOF - -# dump genesis file -echo "-- Dump genesis.json:" -cat /root/genesis.json -echo "" - -# initialize chain with our genesis.json parameters -echo "-- Initialize geth" -/geth init /root/genesis.json -echo "" - -# start miner and allocate rewards to account0 -echo "-- Start geth mining for account0: $GETH_ETH_ACCOUNT0" -echo "" - -exec "/geth" --port 30303 --networkid 1101 \ - --ws --wsaddr "0.0.0.0" --wsport 8546 --wsorigins "*" \ - --rpc --rpcport 8545 --rpcaddr 0.0.0.0 --rpccorsdomain "" \ - --rpcapi "db,ssh,miner,admin,eth,net,web3,personal" \ - --syncmode "fast" \ - --mine --miner.threads=1 \ - --identity $RANDOM_ID \ - --miner.etherbase=$GETH_ETH_ACCOUNT0 diff --git a/infrastructure/docker/ethereum/geth-node/genesis-template.json b/infrastructure/docker/ethereum/geth-node/genesis-template.json deleted file mode 100644 index 207883e6c1..0000000000 --- a/infrastructure/docker/ethereum/geth-node/genesis-template.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "config": { - "chainId": 1101, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "homesteadBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "daoForkBlock": 0, - "istanbulBlock": 0, - "daoForkSupport": true - }, - "coinbase": "0x0000000000000000000000000000000000000000", - "difficulty": "0x20", - "extraData": "", - "gasLimit": "0x7A1200", - "nonce": "0x90F0050060078460", - "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "0x00", - "alloc": { - } -} \ No newline at end of file diff --git a/infrastructure/docker/ethereum/geth-node/geth-init.sh b/infrastructure/docker/ethereum/geth-node/geth-init.sh deleted file mode 100755 index 82f175a760..0000000000 --- a/infrastructure/docker/ethereum/geth-node/geth-init.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -DATADIR_DEFAULT=/root/.geth - -# feed keep ETH accounts into genesis -for keyfile in ${DATADIR_DEFAULT}/keystore/*; -do - ACCOUNT=`cat $keyfile | jq .address | tr -d '"'` - echo "0x${ACCOUNT}" >> /root/keep_accounts -done -echo "-- Keep client accounts populated:" -cat /root/keep_accounts - -# Generate genesis.json and issue tokens to Keep peers. -# We are setting mining difficulty to zero. -# Start with the preamble -# Generate genesis.json and issue tokens to Keep peers. -# We are setting mining difficulty to zero. -GENESIS=/root/genesis.json -GENESIS_TEMPLATE=/root/genesis-template.json -RESULT=`cat $GENESIS_TEMPLATE` -# add Keep client accounts and fund them -while read -r ACCOUNT; do - LINE=".alloc += {\"${ACCOUNT}\": {"balance": \"1000000000000000000000\"}}" - RESULT=`echo $RESULT | jq "$LINE"` -done < /root/keep_accounts -echo $RESULT | jq . > $GENESIS - -## genesis.json generation done ------- - -## set miner account -echo "-- Setting miner account:" -head -1 /root/keep_accounts > /root/mining_account -cat /root/mining_account - -# dump genesis file -echo "-- Dump genesis.json:" -cat $GENESIS -echo "" - -# List the keystore directory -echo "-- KEYSTORE directory" -ls -la ${DATADIR_DEFAULT}/keystore - -# List the .geth directory -echo "-- .geth directory" -ls -la $DATADIR_DEFAULT - -# List the /root directory -echo "-- /root directory" -ls -la /root diff --git a/infrastructure/docker/ethereum/geth-node/run-geth.sh b/infrastructure/docker/ethereum/geth-node/run-geth.sh deleted file mode 100755 index e89f76cf34..0000000000 --- a/infrastructure/docker/ethereum/geth-node/run-geth.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -DATADIR_DEFAULT=/root/.geth -ETH_IPC_PATH_DEFAULT=/root/.geth/geth.ipc - -RPCPORT=8545 -RPCHOST=0.0.0.0 -RPCAPI=db,ssh,miner,admin,eth,net,web3,personal -WSPORT=8546 -#WSHOST=0.0.0.0 -#WSORIGINS="*" -GETHPORT=30303 -GETHARGS= -BOOTNODE_URL="$BOOTNODE_URL/staticenodes?network=$BOOTNODE_NETWORK" -BOOTNODES=$(curl --connect-timeout 1 --retry 10 --retry-max-time 10 -f -s $BOOTNODE_URL) - -# fetch accounts -export GETH_ETH_MINING_ACCOUNT=`cat /root/mining_account` -echo "-- GETH_ETH_MINING_ACCOUNT: $GETH_ETH_MINING_ACCOUNT" - -# dump genesis file -echo "-- Dump genesis.json:" -GENESIS=/root/genesis.json -cat $GENESIS -echo "" - -if [ -z "$HOSTVOLUME" ]; then - DATADIR="$DATADIR_DEFAULT" - echo "-- No HOSTVOLUME was supplied. Using default DATADIR: $DATADIR" -else - DATADIR="$HOSTVOLUME" # GCP: each pod has a private volume attached - echo "-- Setting DATADIR to: $DATADIR" - # check if we need to create the directory - if [ ! -d "$DATADIR" ]; then - echo "-- Creating $DATADIR" - mkdir -p $DATADIR - fi - echo "-- Copying keystore to DATADIR" - cp -rv $DATADIR_DEFAULT/keystore $DATADIR - echo "-- List DATADIR/keystore:" - ls -la $DATADIR/keystore -fi - -if [ -z "$ETH_IPC_PATH" ]; then - ETH_IPC_PATH="$ETH_IPC_PATH_DEFAULT" - echo "-- No ETH_IPC_PATH was supplied. Using default ETH_IPC_PATH: $ETH_IPC_PATH" -fi - -if [ -z "$NETWORKID" ]; then - echo "-- No NETWORKID was supplied" - exit 1 -fi - -if [ -z "$GENESIS" ]; then - echo "-- No GENESIS was supplied" - exit 1 -fi - -if [ -z "$NODE_NAME" ]; then - echo "-- No NODE_NAME was supplied" - exit 1 -fi - -if [ "$ENABLE_MINER" ]; then - MINER_ADDRESS=$GETH_ETH_MINING_ACCOUNT - echo "-- MINER_ADDRESS: $MINER_ADDRESS" - - while [ -z "$BOOTNODES" ] - do - BOOTNODES=$(curl --connect-timeout 1 --retry 10 --retry-delay 0 --retry-max-time 10 -f -s $BOOTNODE_URL) - done - - GETHARGS="--mine --miner.etherbase=$MINER_ADDRESS" - - if [ "$MINER_THREADS" ]; then - GETHARGS="$GETHARGS --minerthreads $MINER_THREADS" - fi -else - GETHARGS="" -fi - - -if [ "$BOOTNODES" ]; then - echo "-- Adding bootnodes:" - mkdir -p $DATADIR - echo $BOOTNODES > $DATADIR/static-nodes.json - cat $DATADIR/static-nodes.json -fi - -# TODO: only initialize if DATADIR has no chain data -if [ ! -d "$DATADIR/geth/chaindata" ]; then - echo "-- No chaindata directory. Neet to Initialize. Writing genesis block..." - geth --datadir $DATADIR init $GENESIS -fi - -echo "-- BOOTNODES: $BOOTNODES" -echo "-- GETHARGS: $GETHARGS" - -echo "-- Starting geth..." - -geth --datadir $DATADIR --ethash.dagdir $DATADIR --ipcpath $ETH_IPC_PATH \ - --nodiscover \ - --port $GETHPORT --networkid $NETWORKID \ - --ws --wsaddr "0.0.0.0" --wsport $WSPORT --wsorigins "*" \ - --rpc --rpcport $RPCPORT --rpcaddr $RPCHOST --rpccorsdomain "*" --rpcvhosts "*" \ - --rpcapi $RPCAPI \ - --identity $NODE_NAME \ - --syncmode "fast" \ - --allow-insecure-unlock \ - --targetgaslimit "7000000" \ - $GETHARGS diff --git a/infrastructure/docker/ethereum/geth-node/testnet-account-passphrase.txt b/infrastructure/docker/ethereum/geth-node/testnet-account-passphrase.txt deleted file mode 100644 index ce79aaf6a2..0000000000 --- a/infrastructure/docker/ethereum/geth-node/testnet-account-passphrase.txt +++ /dev/null @@ -1 +0,0 @@ -doughnut_armenian_parallel_firework_backbite_employer_singlet diff --git a/infrastructure/eth-networks/private-testnet/.gitignore b/infrastructure/eth-networks/private-testnet/.gitignore deleted file mode 100644 index 17ce0d765e..0000000000 --- a/infrastructure/eth-networks/private-testnet/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Local NPM Command Installation -package.json -package-lock.json -node_modules/ - -# Secrets -bundles/*/secret/ - -# Generated Documentation -bundles/*/index.html - -# Bundles -*.tgz diff --git a/infrastructure/eth-networks/private-testnet/README.adoc b/infrastructure/eth-networks/private-testnet/README.adoc deleted file mode 100644 index 8cfa31a1ef..0000000000 --- a/infrastructure/eth-networks/private-testnet/README.adoc +++ /dev/null @@ -1,38 +0,0 @@ -= Keep Network Private Testnet - -We set up a Keep Network Private Testnet that is accessible by the permitted parties. -Here we hold the code helping us to create bundles for the parties. - -The network runs against the link:https://goerli.net/[Ethereum Görli Testnet]. - -The generated bundles contain a preinitialized Ethereum Account details. The account -receives a stake delegation to the Staking Provider Account with authorized `beacon` and -`tbtc` application. This is a production-like experience for the Staking Providers, -where they will receive stakes from their customers. - -== Scripts - -=== Prerequisites - -The scripts require the following tools to be installed: - -- `npx` - link:https://nodejs.org/en/download/package-manager/#macos[macOS install] -- `geth` - link:https://geth.ethereum.org/docs/install-and-build/installing-geth#macos-via-homebrew[macOS install] -- `asciidoctor` - link:https://asciidoctor.org/docs/install-asciidoctor-macos/#homebrew-procedure[macOS install] -- `docker` - link:https://docs.docker.com/desktop/install/mac-install/[macOS install] - -=== Create New Bundle - -To create a new bundle run: - -```bash -./scripts/new-bundle.sh -``` - -=== Initialize Staking Provider - -To simulate a Staker delegation to a Staking Provider and authorize the applications run: - -```bash -./scripts/init-provider.sh -``` diff --git a/infrastructure/eth-networks/private-testnet/bundles/bundle-guide.adoc b/infrastructure/eth-networks/private-testnet/bundles/bundle-guide.adoc deleted file mode 100644 index 0ef60af252..0000000000 --- a/infrastructure/eth-networks/private-testnet/bundles/bundle-guide.adoc +++ /dev/null @@ -1,98 +0,0 @@ -:toc: left -:toclevels: 3 -:sectanchors: true -:sectids: true -:source-highlighter: rouge -:icons: font - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -= Keep Network Private Testnet Bundle - -Use this bundle to setup Keep Network node running on Keep Network Private Testnet. -The Keep Network Private Testnet is running against the -link:https://goerli.net/[Ethereum Görli Testnet]. - -This is a quickstart guide, for the full documentation please visit -link:https://docs.keep.network/run-keep-node.html[Run Keep Node documentation]. - -== Stake and Register - -This bundle comes with a Staking Provider Ethereum Account that was already staked and -authorized, similar to what a Staker would do in the production. - -IMPORTANT: Delivering the Staking Provider Account details in the bundle is a -simplification for testnet. On mainnet, the Staking Provider will have -to provide the address for the Staker. See <<#staking-provider-account>> section. - -[#staking-provider-account] -=== Staking Provider Account - -A Staking Provider is responsible for providing a Staker with a Staking Provider -Account address where the stake should be delegated to. - -The Staking Provider Account is controlled by the Staking Provider. - -The Staking Provider Account can be an Ethereum account managed by any kind of -a wallet that can sign transactions (i.e. it doesn't have to be a Key File). - -=== Operator Account - -The Operator Account is an Ethereum account that the Keep Client runs with. The -client requires an encrypted Ethereum Key File along with the Password for the -Operator Account to run. - -The Operator Account is controlled by the Staking Provider. - -The Staking Provider has to register an Operator Account address for the stake delegation -received to the Staking Provider Account. - -To generate an Ethereum Account Key File you can use `geth account new` command. - -[source,shell] ----- -geth account new --keystore ./keystore ----- - -Keep the password used for the Key File encryption as it will -have to be passed to the Keep Client start command. - -Once the Operator Account address is known it should be registered with a transaction -submitted from the Staking Provider Account, please refer to -link:https://docs.keep.network/registration.html#register-operator[Register Operator] -documentation. - -TIP: When starting the client, remember about running the `keep-client start` -command with the `--goerli` flag. - -IMPORTANT: The Operator Account has to be funded with Goerli ETH (GöETH) so the -client can submit transactions to the Ethereum chain. This bundle doesn't fund -the account, please do it on your own. - -== Configuration - -For details on the Keep Client Node configuration visit -link:https://docs.keep.network/run-keep-node.html#configuration[Configuration documentation]. - -== Running - -For details on running the Keep Client Node on Testnet visit -link:https://docs.keep.network/run-keep-node.html#testnet[Testnet documentation]. - -=== Validate - -To validate the running client check the metrics for the number of connected peers -(`connected_peers_count`). - -The client should connect to the bootstrap nodes (at least 2) and other nodes that -are working in the network. There should be at least 10 connections. - -``` -curl localhost:9601/metrics -``` diff --git a/infrastructure/eth-networks/private-testnet/scripts/init-provider.sh b/infrastructure/eth-networks/private-testnet/scripts/init-provider.sh deleted file mode 100755 index 981717eefc..0000000000 --- a/infrastructure/eth-networks/private-testnet/scripts/init-provider.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash -set -eou pipefail - -ROOT_DIR="$(realpath "$(dirname $0)/../bundles")" - -if [ -z "${CHAIN_API_URL+x}" ]; then - read -p "Provide Ethereum API URL: " CHAIN_API_URL -fi - -if [ -z "${PURSE_PRIVATE_KEY+x}" ]; then - read -p "Provide ETH Purse Private Key: " PURSE_PRIVATE_KEY -fi - -if [ -z "${GOERLI_DEPLOYER_PRIVATE_KEY+x}" ]; then - read -p "Provide GOERLI_DEPLOYER_PRIVATE_KEY: " GOERLI_DEPLOYER_PRIVATE_KEY -fi - -STAKING_PROVIDER=${1-} -if [ -z "$STAKING_PROVIDER" ]; then - read -p "Provide Staking Provider name: " STAKING_PROVIDER -fi - -STAKING_PROVIDER_DIR="$(realpath "$ROOT_DIR/$STAKING_PROVIDER")" - -if [ ! -d "$STAKING_PROVIDER_DIR" ]; then - echo "Directory for $STAKING_PROVIDER does not exists." - exit 1 -fi - -CONFIG_DIR="$STAKING_PROVIDER_DIR/config" -SECRETS_DIR="$STAKING_PROVIDER_DIR/secret" - -KEY_FILE_PATH="$CONFIG_DIR/staking-provider-eth-account-key-file.json" -KEY_FILE_PASSWORD_PATH="$SECRETS_DIR/staking-provider-eth-account-password" -PRIVATE_KEY_FILE_PATH="$SECRETS_DIR/staking-provider-eth-account-private-key" - -ACCOUNT_ADDRESS=$(jq -jr .address $KEY_FILE_PATH) -ACCOUNT_PRIVATE_KEY=$(cat $PRIVATE_KEY_FILE_PATH) - -[[ $ACCOUNT_ADDRESS == 0x* ]] || ACCOUNT_ADDRESS="0x$ACCOUNT_ADDRESS" - -printf "Staking Provider Account Address: $ACCOUNT_ADDRESS\n" - -printf "Pull the latest images...\n" - -docker pull gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest -docker pull gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - -printf "Fund Staking Provider Account Address with ether from purse...\n" - -docker run \ - --rm \ - --env "CHAIN_API_URL=$CHAIN_API_URL" \ - --env "ACCOUNTS_PRIVATE_KEYS=$PURSE_PRIVATE_KEY" \ - --platform linux/amd64 \ - gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest \ - ensure-eth-balance \ - --network goerli \ - --target-balance "0.1 ether" \ - $ACCOUNT_ADDRESS - -printf "Initialize staking...\n" - -docker run \ - --rm \ - --env "CHAIN_API_URL=$CHAIN_API_URL" \ - --env "ACCOUNTS_PRIVATE_KEYS=$GOERLI_DEPLOYER_PRIVATE_KEY,$ACCOUNT_PRIVATE_KEY" \ - --platform linux/amd64 \ - gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest \ - initialize:staking \ - --network goerli \ - --owner $ACCOUNT_ADDRESS \ - --provider $ACCOUNT_ADDRESS - -printf "Authorize the Random Beacon...\n" - -docker run \ - --rm \ - --env "CHAIN_API_URL=$CHAIN_API_URL" \ - --env "ACCOUNTS_PRIVATE_KEYS=$GOERLI_DEPLOYER_PRIVATE_KEY,$ACCOUNT_PRIVATE_KEY" \ - --platform linux/amd64 \ - gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest \ - authorize:beacon \ - --network goerli \ - --owner $ACCOUNT_ADDRESS \ - --provider $ACCOUNT_ADDRESS - -printf "Authorize the ECDSA...\n" - -docker run \ - --rm \ - --env "CHAIN_API_URL=$CHAIN_API_URL" \ - --env "ACCOUNTS_PRIVATE_KEYS=$GOERLI_DEPLOYER_PRIVATE_KEY,$ACCOUNT_PRIVATE_KEY" \ - --platform linux/amd64 \ - gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest \ - authorize:ecdsa \ - --network goerli \ - --owner $ACCOUNT_ADDRESS \ - --provider $ACCOUNT_ADDRESS - -printf "\n\e[1;32mDONE!\n\n\e[0m" diff --git a/infrastructure/eth-networks/private-testnet/scripts/new-bundle.sh b/infrastructure/eth-networks/private-testnet/scripts/new-bundle.sh deleted file mode 100755 index 8f34594f09..0000000000 --- a/infrastructure/eth-networks/private-testnet/scripts/new-bundle.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -set -eou pipefail - -ROOT_DIR="$(realpath "$(dirname $0)/../bundles")" - -if ! npx eth-helper --version &>/dev/null; then - printf "eth-helper could not be found; installing... \n" - npm install nkuba/eth-helper -fi - -STAKING_PROVIDER=${1-} -if [ -z "${STAKING_PROVIDER}" ]; then - read -p "Provide Staking Provider name: " STAKING_PROVIDER -fi - -STAKING_PROVIDER_DIR="$(realpath "$ROOT_DIR/$STAKING_PROVIDER")" - -if [ -d "$STAKING_PROVIDER_DIR" ]; then - echo "Directory for $STAKING_PROVIDER already exists." - exit 1 -fi - -if [ -z "${KEYFILE_PASSWORD+x}" ]; then - read -s -r -p "Provide password for key file encryption: " KEYFILE_PASSWORD - if [ -z "$KEYFILE_PASSWORD" ]; then - printf "KEYFILE_PASSWORD not set\n" - exit 1 - fi - printf "\n" -fi - -CONFIG_DIR="$STAKING_PROVIDER_DIR/config" -SECRETS_DIR="$STAKING_PROVIDER_DIR/secret" - -KEY_FILE_PATH="$CONFIG_DIR/staking-provider-eth-account-key-file.json" -KEY_FILE_PASSWORD_PATH="$SECRETS_DIR/staking-provider-eth-account-password" -PRIVATE_KEY_FILE_PATH="$SECRETS_DIR/staking-provider-eth-account-private-key" - -mkdir $STAKING_PROVIDER_DIR -mkdir $SECRETS_DIR -mkdir $CONFIG_DIR - -cd $STAKING_PROVIDER_DIR - -echo -n "$KEYFILE_PASSWORD" >"$KEY_FILE_PASSWORD_PATH" - -geth account new \ - --keystore ./ \ - --password "$KEY_FILE_PASSWORD_PATH" - -mv UTC-* $KEY_FILE_PATH - -npx eth-helper extract-private-key \ - -k "$KEY_FILE_PATH" \ - -p "$KEY_FILE_PASSWORD_PATH" \ - -o "$PRIVATE_KEY_FILE_PATH" - -asciidoctor ../bundle-guide.adoc -o index.html --doctype book - -tar -zcvf keep-test-bundle-$STAKING_PROVIDER.tgz --exclude *.tgz . - -printf "A bundle was saved: keep-test-bundle-$STAKING_PROVIDER.tgz" - -printf "\n\e[1;32mDONE!\n\n\e[0m" diff --git a/infrastructure/kube/keep-dev/.gitignore b/infrastructure/kube/keep-dev/.gitignore deleted file mode 100644 index ce71aabd5c..0000000000 --- a/infrastructure/kube/keep-dev/.gitignore +++ /dev/null @@ -1 +0,0 @@ -secrets/* diff --git a/infrastructure/kube/keep-dev/atlantis-ingress-https.yaml b/infrastructure/kube/keep-dev/atlantis-ingress-https.yaml deleted file mode 100644 index b9aa18e455..0000000000 --- a/infrastructure/kube/keep-dev/atlantis-ingress-https.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: atlantis-https - annotations: - kubernetes.io/ingress.class: "gce" - kubernetes.io/ingress.allow-http: "false" - kubernetes.io/ingress.global-static-ip-name: "keep-dev-atlantis-external-ip-0" -spec: - tls: - - hosts: - # This assumes tls-secret exists and the SSL - # certificate contains a CN for foo.bar.com - secretName: atlantis-tls - backend: - # This assumes http-svc exists and routes to healthy endpoints - serviceName: atlantis-https - servicePort: 8443 diff --git a/infrastructure/kube/keep-dev/atlantis-service-https.yaml b/infrastructure/kube/keep-dev/atlantis-service-https.yaml deleted file mode 100644 index 5d3ee762f3..0000000000 --- a/infrastructure/kube/keep-dev/atlantis-service-https.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: atlantis-https - annotations: - service.alpha.kubernetes.io/app-protocols: '{"atlantis-https-port":"HTTPS"}' -spec: - type: NodePort - ports: - - name: atlantis-https-port - port: 8443 - targetPort: 8443 - selector: - app: atlantis \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/atlantis-statefulset.yaml b/infrastructure/kube/keep-dev/atlantis-statefulset.yaml deleted file mode 100644 index c087d10249..0000000000 --- a/infrastructure/kube/keep-dev/atlantis-statefulset.yaml +++ /dev/null @@ -1,114 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: atlantis -spec: - serviceName: atlantis - replicas: 1 - updateStrategy: - type: RollingUpdate - rollingUpdate: - partition: 0 - selector: - matchLabels: - app: atlantis - template: - metadata: - labels: - app: atlantis - spec: - securityContext: - fsGroup: 1000 # Atlantis group (1000) read/write access to volumes. - containers: - - name: atlantis - image: runatlantis/atlantis:latest - env: - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /mnt/terraform-admin-service-account/thesis-terraform-admin-service-account-creds.json - - name: ATLANTIS_ALLOW_REPO_CONFIG - value: "true" - - name: ATLANTIS_ATLANTIS_URL - value: https://atlantis.keep-dev.com - - name: ATLANTIS_SSL_CERT_FILE - value: /atlantis/tls/tls.crt - - name: ATLANTIS_SSL_KEY_FILE - value: /atlantis/tls/tls.key - - name: ATLANTIS_REPO_WHITELIST - value: github.com/keep-network/keep-core - - name: ATLANTIS_GH_USER - value: thesis-heimdall - - name: ATLANTIS_GH_TOKEN - valueFrom: - secretKeyRef: - name: atlantis-git - key: gh-access-token - - name: ATLANTIS_GH_WEBHOOK_SECRET - valueFrom: - secretKeyRef: - name: atlantis-git - key: gh-webhook-secret - - name: ATLANTIS_DATA_DIR - value: /atlantis - - name: ATLANTIS_PORT - value: "8443" - - name: TF_VAR_gcp_thesis_org_id - valueFrom: - secretKeyRef: - name: terraform-env-vars - key: org-id - - name: TF_VAR_gcp_thesis_billing_account - valueFrom: - secretKeyRef: - name: terraform-env-vars - key: billing-account - volumeMounts: - - name: atlantis-data - mountPath: /atlantis - - name: atlantis-tls-files - mountPath: /atlantis/tls - - name: atlantis-gitconfig - mountPath: /home/atlantis/ - - name: terraform-admin-service-account - mountPath: /mnt/terraform-admin-service-account - ports: - - name: atlantis - containerPort: 8443 - resources: - requests: - memory: 256Mi - cpu: 100m - limits: - memory: 256Mi - cpu: 100m - livenessProbe: - periodSeconds: 60 - httpGet: - path: /healthz - port: 8443 - scheme: HTTPS - readinessProbe: - periodSeconds: 60 - httpGet: - path: /healthz - port: 8443 - scheme: HTTPS - volumes: - - name: atlantis-tls-files - secret: - secretName: atlantis-tls - - name: atlantis-gitconfig - secret: - secretName: atlantis-gitconfig - - name: terraform-admin-service-account - secret: - secretName: terraform-admin-service-account - volumeClaimTemplates: - - metadata: - name: atlantis-data - spec: - accessModes: ["ReadWriteOnce"] # Volume should not be shared by multiple nodes. - resources: - requests: - # The biggest thing Atlantis stores is the Git repo when it checks it out. - # It deletes the repo after the pull request is merged. - storage: 1Gi diff --git a/infrastructure/kube/keep-dev/eth-account-info-configmap.yaml b/infrastructure/kube/keep-dev/eth-account-info-configmap.yaml deleted file mode 100644 index 0b9fc8c22a..0000000000 --- a/infrastructure/kube/keep-dev/eth-account-info-configmap.yaml +++ /dev/null @@ -1,20 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: eth-account-info - namespace: default -data: - account-0-keyfile: | - {"address":"0ec14bc7cca82c942cf276f6bbd0413216ddb2be","crypto":{"cipher":"aes-128-ctr","ciphertext":"d1e1885d30a2c25a54664487db4d69da496951733de6ceb4d5f565fe62eaba79","cipherparams":{"iv":"8cacad8a1b79982f568948b7f97b3dd3"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"00bfb9f49e54e6dba5b1b0c5b09904998fcfa10d0381b90bf26d45904a4e2636"},"mac":"9038c2a02d7837e448088fb19fc76e9d6c5063e8f1cb0addb40dc9df061b4928"},"id":"afb99070-073f-4dc6-b0d7-92b41fcf0afb","version":3} - - account-1-keyfile: | - {"address":"cab2a402bac470686d14956fb310d51bbef9fa31","crypto":{"cipher":"aes-128-ctr","ciphertext":"50193ab419aa322ceb556d4c073d1727763e5d873cce4e0735e6690194432665","cipherparams":{"iv":"6f869f3bd192d80981435016cc19afff"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"b72fe3dfd4d7419baa4d9a8ed7e27bf509fb9e1557a5d73c9c4879bf3a19abe9"},"mac":"5d3093a7b0160a8c2187efd4ab7ec168226e561ff7a0d714886c5dff28c405e7"},"id":"d43da5de-511f-4a1d-8ba8-0e0c24bf33e6","version":3} - - account-2-keyfile: | - {"address":"ac049223397e2f25ea9fe56d5ee0896f6d8e8cb7","crypto":{"cipher":"aes-128-ctr","ciphertext":"42f6463f021f631ffbaf04989c107d784f0e1ba3a3b469073af4cc928d90bd5b","cipherparams":{"iv":"a533352b5ceb005cd730153f26e2f710"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"5716b94200ea2500fd257d28c1fd58e92ea991a4642813c91f66ae81fbc088bd"},"mac":"09b1e0857562a20ef0d25f41687095cdf246295a9dba82fee7e46756b0437bdc"},"id":"3819c68b-bc9d-4f54-867a-1ea7955c3cff","version":3} - - account-3-keyfile: | - {"address":"3ff855895ef4ac833c32ab6a0d6c7fbfa137e26e","crypto":{"cipher":"aes-128-ctr","ciphertext":"3cb866a0a1c0db6ca8accfc3c3036d9ee93b5dbca98f89dcf8f293e8b0134146","cipherparams":{"iv":"50e06549568b995a76190673e1643635"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ee2164b8525571024704eaa976c3ac80fb41b74bcaa7d7788f7ac94dd1b6878b"},"mac":"408bf7c097e905019e25b4c82b4c988b03a607469f77bdbe0e9ca6d870fa9055"},"id":"93a1dc32-f80a-400a-99be-c478f72a6630","version":3} - - account-4-keyfile: | - {"address":"0954efefeb970d317a51736201b4eb2de75ff5de","crypto":{"cipher":"aes-128-ctr","ciphertext":"ad2d8baa3626a7ffd0040a09dbbe73e179aa125e1677987524e1c5593f03c645","cipherparams":{"iv":"856e9d869aaa40e994bda72f969505ac"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a83d0582c376744e44cb982a90a5512a6570e046ea7ee0fd571e2fbda0cb762b"},"mac":"83ea2018f56bdd4967e8bd32c60cc7b3021bab59c72e4292c2e0ff20fc3b37e6"},"id":"666be636-2a15-4563-b78f-1ab704ec606c","version":3} diff --git a/infrastructure/kube/keep-dev/eth-dashboard-internal-deployment.yaml b/infrastructure/kube/keep-dev/eth-dashboard-internal-deployment.yaml deleted file mode 100644 index e6b71b220b..0000000000 --- a/infrastructure/kube/keep-dev/eth-dashboard-internal-deployment.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: eth-dashboard - namespace: default -spec: - replicas: 1 - strategy: - type: RollingUpdate - selector: - matchLabels: - app: eth-dashboard - template: - metadata: - labels: - app: eth-dashboard - spec: - securityContext: - fsGroup: 1000 - containers: - - name: eth-dashboard - image: gcr.io/keep-dev-fe24/eth-dashboard-node - ports: - - containerPort: 3000 - - containerPort: 3001 - env: - - name: WS_SECRET - value: BANZAI!!!! - - name: BOOTNODE_URL - value: http://eth-dashboard.default.svc.cluster.local:3001 diff --git a/infrastructure/kube/keep-dev/eth-dashboard-internal-ingress.yaml b/infrastructure/kube/keep-dev/eth-dashboard-internal-ingress.yaml deleted file mode 100644 index 2b2fbd33b1..0000000000 --- a/infrastructure/kube/keep-dev/eth-dashboard-internal-ingress.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: eth-dashboard-http - annotations: - kubernetes.io/ingress.class: "gce" - kubernetes.io/ingress.allow-http: "true" -spec: - backend: - # This assumes service eth-dashboard-http exists and routes to healthy endpoints - serviceName: eth-dashboard-http - servicePort: 8080 diff --git a/infrastructure/kube/keep-dev/eth-dashboard-internal-service.yaml b/infrastructure/kube/keep-dev/eth-dashboard-internal-service.yaml deleted file mode 100644 index 009bef4d1e..0000000000 --- a/infrastructure/kube/keep-dev/eth-dashboard-internal-service.yaml +++ /dev/null @@ -1,30 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: eth-dashboard-http -spec: - type: NodePort - ports: - - name: eth-dashboard-http-port - port: 8080 - targetPort: 3000 - selector: - app: eth-dashboard ---- -apiVersion: v1 -kind: Service -metadata: - name: eth-dashboard - labels: - app: eth-dashboard -spec: - ports: - - port: 3000 - targetPort: 3000 - name: tcp-3000 - - port: 3001 - targetPort: 3001 - name: tcp-3001 - selector: - app: eth-dashboard diff --git a/infrastructure/kube/keep-dev/eth-miner-internal-daemonset.yaml b/infrastructure/kube/keep-dev/eth-miner-internal-daemonset.yaml deleted file mode 100644 index 8ca5e1abd1..0000000000 --- a/infrastructure/kube/keep-dev/eth-miner-internal-daemonset.yaml +++ /dev/null @@ -1,56 +0,0 @@ ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: eth-miner-node - namespace: default -spec: - selector: - matchLabels: - app: geth - type: miner - template: - metadata: - labels: - app: geth - type: miner - spec: - securityContext: - fsGroup: 1000 - containers: - - name: miner - image: gcr.io/keep-dev-fe24/eth-geth-node:1.9.6 - ports: - - containerPort: 8545 - - containerPort: 8546 - - containerPort: 30303 - volumeMounts: - env: - - name: INSTANCE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: NETWORKID - value: "1101" - - name: WS_SERVER - value: ws://eth-dashboard.default.svc.cluster.local:3000 - - name: WS_SECRET - value: BANZAI!!!! - - name: BOOTNODE_URL - value: http://eth-dashboard.default.svc.cluster.local:3001 - - name: BOOTNODE_NETWORK - value: network_1 - - name: ENABLE_MINER - value: "1" - - name: MINER_THREADS - value: "1" - - name: ETH_IPC_PATH - value: /tmp/geth.ipc diff --git a/infrastructure/kube/keep-dev/eth-miner-internal-service.yaml b/infrastructure/kube/keep-dev/eth-miner-internal-service.yaml deleted file mode 100644 index 9fbdb5e690..0000000000 --- a/infrastructure/kube/keep-dev/eth-miner-internal-service.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: eth-miner-node - labels: - app: geth - type: miner -spec: - ports: - - port: 8545 - targetPort: 8545 - name: tcp-8545 - - port: 8546 - targetPort: 8546 - name: tcp-8546 - - port: 30303 - targetPort: 30303 - name: tcp-30303 - - port: 30303 - targetPort: 30303 - name: udp-30303 - protocol: UDP - selector: - app: geth - type: miner diff --git a/infrastructure/kube/keep-dev/eth-network-internal-configmap.yaml b/infrastructure/kube/keep-dev/eth-network-internal-configmap.yaml deleted file mode 100644 index 50511759c4..0000000000 --- a/infrastructure/kube/keep-dev/eth-network-internal-configmap.yaml +++ /dev/null @@ -1,10 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: eth-network-internal - namespace: default -data: - rpc-url: http://eth-tx-node.default.svc.cluster.local:8545 - ws-url: ws://eth-tx-node.default.svc.cluster.local:8546 - network-id: '1101' - contract-owner-eth-account-address: '0x923c5dbf353e99394a21aa7b67f3327ca111c67d' diff --git a/infrastructure/kube/keep-dev/eth-tx-internal-deployment.yaml b/infrastructure/kube/keep-dev/eth-tx-internal-deployment.yaml deleted file mode 100644 index 147c0b313f..0000000000 --- a/infrastructure/kube/keep-dev/eth-tx-internal-deployment.yaml +++ /dev/null @@ -1,56 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: eth-tx-node - namespace: default -spec: - replicas: 1 - strategy: - type: RollingUpdate - selector: - matchLabels: - app: geth - type: tx - template: - metadata: - labels: - app: geth - type: tx - spec: - securityContext: - fsGroup: 1000 - containers: - - name: tx - image: gcr.io/keep-dev-fe24/eth-geth-node:1.9.6 - ports: - - containerPort: 8545 - - containerPort: 8546 - - containerPort: 30303 - env: - - name: INSTANCE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: NETWORKID - value: "1101" - - name: WS_SERVER - value: ws://eth-dashboard.default.svc.cluster.local:3000 - - name: WS_SECRET - value: BANZAI!!!! - - name: BOOTNODE_URL - value: http://eth-dashboard.default.svc.cluster.local:3001 - - name: BOOTNODE_NETWORK - value: network_1 - - name: BOOTNODE_PUBLIC_IP - value: eth-dashboard.default.svc.cluster.local - - name: ETH_IPC_PATH - value: /tmp/geth.ipc diff --git a/infrastructure/kube/keep-dev/eth-tx-internal-service.yaml b/infrastructure/kube/keep-dev/eth-tx-internal-service.yaml deleted file mode 100644 index 9c8bef2e16..0000000000 --- a/infrastructure/kube/keep-dev/eth-tx-internal-service.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: eth-tx-node - labels: - app: geth - type: tx -spec: - ports: - - port: 8545 - targetPort: 8545 - name: tcp-8545 - - port: 8546 - targetPort: 8546 - name: tcp-8546 - - port: 30303 - targetPort: 30303 - name: tcp-30303 - - port: 30303 - targetPort: 30303 - name: udp-30303 - protocol: UDP - selector: - app: geth - type: tx \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/keep-client-0-service.yaml b/infrastructure/kube/keep-dev/keep-client-0-service.yaml deleted file mode 100644 index 44b23b5734..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-0-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-0 - namespace: default - labels: - app: keep - type: beacon - id: '0' -spec: - ports: - - port: 3919 - targetPort: 3919 - name: tcp-3919 - selector: - app: keep - type: beacon - id: '0' \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/keep-client-0-statefulset.yaml b/infrastructure/kube/keep-dev/keep-client-0-statefulset.yaml deleted file mode 100644 index 2ca4df8b06..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-0-statefulset.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-0 - namespace: default - labels: - keel.sh/policy: all - app: keep - type: beacon - id: '0' -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: beacon - id: '0' - serviceName: keep-client-0 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 512Mi - - metadata: - name: keep-client-config - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 5Mi - template: - metadata: - labels: - app: keep - type: beacon - id: '0' - spec: - volumes: - - name: keep-client-config - persistentVolumeClaim: - claimName: keep-client-config - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-0-keyfile - path: account-0-keyfile - containers: - - name: keep-client-0 - image: gcr.io/keep-dev-fe24/keep-client - imagePullPolicy: Always - ports: - - containerPort: 3919 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-0 - - name: LOG_LEVEL - value: debug - - name: IPFS_LOGGING_FMT - value: nocolor - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["keep-client", "-config", "/mnt/keep-client/config/keep-client-config.toml", "start"] - initContainers: - - name: initcontainer-provision-keep-client - image: gcr.io/keep-dev-fe24/initcontainer-provision-keep-client-ethereum - imagePullPolicy: Always - env: - - name: ETH_RPC_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: rpc-url - - name: ETH_WS_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: ws-url - - name: ETH_NETWORK_ID - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: network-id - - name: CONTRACT_OWNER_ETH_ACCOUNT_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-address - - name: CONTRACT_OWNER_ETH_ACCOUNT_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_KEYFILE_PATH - value: /mnt/keep-client/keyfile/account-0-keyfile - - name: KEEP_CLIENT_PEERS - value: /ip4/10.102.100.40/tcp/3919/ipfs/16Uiu2HAm3eJtyFKAttzJ85NLMromHuRg4yyum3CREMf6CHBBV6KY - - name: KEEP_CLIENT_ANNOUNCED_ADDRESSES - value: '' - - name: KEEP_CLIENT_PORT - value: '3919' - - name: KEEP_CLIENT_DATA_DIR - value: /mnt/keep-client/data - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["node", "/tmp/provision-keep-client.js"] diff --git a/infrastructure/kube/keep-dev/keep-client-1-service.yaml b/infrastructure/kube/keep-dev/keep-client-1-service.yaml deleted file mode 100644 index d9fd231f70..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-1-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-1 - namespace: default - labels: - app: keep - type: beacon - id: '1' -spec: - ports: - - port: 3919 - targetPort: 3919 - name: tcp-3919 - selector: - app: keep - type: beacon - id: '1' \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/keep-client-1-statefulset.yaml b/infrastructure/kube/keep-dev/keep-client-1-statefulset.yaml deleted file mode 100644 index 57b6e662b5..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-1-statefulset.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-1 - namespace: default - labels: - keel.sh/policy: all - app: keep - type: beacon - id: '1' -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: beacon - id: '1' - serviceName: keep-client-1 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 512Mi - - metadata: - name: keep-client-config - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 5Mi - template: - metadata: - labels: - app: keep - type: beacon - id: '1' - spec: - volumes: - - name: keep-client-config - persistentVolumeClaim: - claimName: keep-client-config - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-1-keyfile - path: account-1-keyfile - containers: - - name: keep-client-1 - image: gcr.io/keep-dev-fe24/keep-client - imagePullPolicy: Always - ports: - - containerPort: 3919 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-1 - - name: LOG_LEVEL - value: debug - - name: IPFS_LOGGING_FMT - value: nocolor - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["keep-client", "-config", "/mnt/keep-client/config/keep-client-config.toml", "start"] - initContainers: - - name: initcontainer-provision-keep-client - image: gcr.io/keep-dev-fe24/initcontainer-provision-keep-client-ethereum - imagePullPolicy: Always - env: - - name: ETH_RPC_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: rpc-url - - name: ETH_WS_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: ws-url - - name: ETH_NETWORK_ID - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: network-id - - name: CONTRACT_OWNER_ETH_ACCOUNT_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-address - - name: CONTRACT_OWNER_ETH_ACCOUNT_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_KEYFILE_PATH - value: /mnt/keep-client/keyfile/account-1-keyfile - - name: KEEP_CLIENT_PEERS - value: /ip4/10.102.100.165/tcp/3919/ipfs/16Uiu2HAmCcfVpHwfBKNFbQuhvGuFXHVLQ65gB4sJm7HyrcZuLttH - - name: KEEP_CLIENT_ANNOUNCED_ADDRESSES - value: '' - - name: KEEP_CLIENT_PORT - value: '3919' - - name: KEEP_CLIENT_DATA_DIR - value: /mnt/keep-client/data - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["node", "/tmp/provision-keep-client.js"] diff --git a/infrastructure/kube/keep-dev/keep-client-2-service.yaml b/infrastructure/kube/keep-dev/keep-client-2-service.yaml deleted file mode 100644 index 06e2672748..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-2-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-2 - namespace: default - labels: - app: keep - type: beacon - id: '2' -spec: - ports: - - port: 3919 - targetPort: 3919 - name: tcp-3919 - selector: - app: keep - type: beacon - id: '2' \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/keep-client-2-statefulset.yaml b/infrastructure/kube/keep-dev/keep-client-2-statefulset.yaml deleted file mode 100644 index 92827cffe6..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-2-statefulset.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-2 - namespace: default - labels: - keel.sh/policy: all - app: keep - type: beacon - id: '2' -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: beacon - id: '2' - serviceName: keep-client-0 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 512Mi - - metadata: - name: keep-client-config - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 5Mi - template: - metadata: - labels: - app: keep - type: beacon - id: '2' - spec: - volumes: - - name: keep-client-config - persistentVolumeClaim: - claimName: keep-client-config - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-2-keyfile - path: account-2-keyfile - containers: - - name: keep-client-0 - image: gcr.io/keep-dev-fe24/keep-client - imagePullPolicy: Always - ports: - - containerPort: 3919 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-2 - - name: LOG_LEVEL - value: debug - - name: IPFS_LOGGING_FMT - value: nocolor - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["keep-client", "-config", "/mnt/keep-client/config/keep-client-config.toml", "start"] - initContainers: - - name: initcontainer-provision-keep-client - image: gcr.io/keep-dev-fe24/initcontainer-provision-keep-client-ethereum - imagePullPolicy: Always - env: - - name: ETH_RPC_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: rpc-url - - name: ETH_WS_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: ws-url - - name: ETH_NETWORK_ID - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: network-id - - name: CONTRACT_OWNER_ETH_ACCOUNT_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-address - - name: CONTRACT_OWNER_ETH_ACCOUNT_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_KEYFILE_PATH - value: /mnt/keep-client/keyfile/account-2-keyfile - - name: KEEP_CLIENT_PEERS - value: /ip4/10.102.100.40/tcp/3919/ipfs/16Uiu2HAm3eJtyFKAttzJ85NLMromHuRg4yyum3CREMf6CHBBV6KY - - name: KEEP_CLIENT_ANNOUNCED_ADDRESSES - value: '' - - name: KEEP_CLIENT_PORT - value: '3919' - - name: KEEP_CLIENT_DATA_DIR - value: /mnt/keep-client/data - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["node", "/tmp/provision-keep-client.js"] diff --git a/infrastructure/kube/keep-dev/keep-client-3-service.yaml b/infrastructure/kube/keep-dev/keep-client-3-service.yaml deleted file mode 100644 index 19e8050a9e..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-3-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-3 - namespace: default - labels: - app: keep - type: beacon - id: '3' -spec: - ports: - - port: 3919 - targetPort: 3919 - name: tcp-3919 - selector: - app: keep - type: beacon - id: '3' \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/keep-client-3-statefulset.yaml b/infrastructure/kube/keep-dev/keep-client-3-statefulset.yaml deleted file mode 100644 index e5a88dfaeb..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-3-statefulset.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-3 - namespace: default - labels: - keel.sh/policy: all - app: keep - type: beacon - id: '3' -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: beacon - id: '3' - serviceName: keep-client-0 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 512Mi - - metadata: - name: keep-client-config - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 5Mi - template: - metadata: - labels: - app: keep - type: beacon - id: '3' - spec: - volumes: - - name: keep-client-config - persistentVolumeClaim: - claimName: keep-client-config - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-3-keyfile - path: account-3-keyfile - containers: - - name: keep-client-0 - image: gcr.io/keep-dev-fe24/keep-client - imagePullPolicy: Always - ports: - - containerPort: 3919 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-3 - - name: LOG_LEVEL - value: debug - - name: IPFS_LOGGING_FMT - value: nocolor - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["keep-client", "-config", "/mnt/keep-client/config/keep-client-config.toml", "start"] - initContainers: - - name: initcontainer-provision-keep-client - image: gcr.io/keep-dev-fe24/initcontainer-provision-keep-client-ethereum - imagePullPolicy: Always - env: - - name: ETH_RPC_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: rpc-url - - name: ETH_WS_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: ws-url - - name: ETH_NETWORK_ID - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: network-id - - name: CONTRACT_OWNER_ETH_ACCOUNT_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-address - - name: CONTRACT_OWNER_ETH_ACCOUNT_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_KEYFILE_PATH - value: /mnt/keep-client/keyfile/account-3-keyfile - - name: KEEP_CLIENT_PEERS - value: /ip4/10.102.100.149/tcp/3919/ipfs/16Uiu2HAmNNuCp45z5bgB8KiTHv1vHTNAVbBgxxtTFGAndageo9Dp - - name: KEEP_CLIENT_ANNOUNCED_ADDRESSES - value: '' - - name: KEEP_CLIENT_PORT - value: '3919' - - name: KEEP_CLIENT_DATA_DIR - value: /mnt/keep-client/data - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["node", "/tmp/provision-keep-client.js"] diff --git a/infrastructure/kube/keep-dev/keep-client-4-service.yaml b/infrastructure/kube/keep-dev/keep-client-4-service.yaml deleted file mode 100644 index 8b232d9507..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-4-service.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-4 - namespace: default - labels: - app: keep - type: beacon - id: '4' -spec: - ports: - - port: 3919 - targetPort: 3919 - name: tcp-3919 - selector: - app: keep - type: beacon - id: '4' \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/keep-client-4-statefulset.yaml b/infrastructure/kube/keep-dev/keep-client-4-statefulset.yaml deleted file mode 100644 index ef6ea6b042..0000000000 --- a/infrastructure/kube/keep-dev/keep-client-4-statefulset.yaml +++ /dev/null @@ -1,124 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-4 - namespace: default - labels: - keel.sh/policy: all - app: keep - type: beacon - id: '4' -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: beacon - id: '4' - serviceName: keep-client-0 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 512Mi - - metadata: - name: keep-client-config - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 5Mi - template: - metadata: - labels: - app: keep - type: beacon - id: '4' - spec: - volumes: - - name: keep-client-config - persistentVolumeClaim: - claimName: keep-client-config - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-4-keyfile - path: account-4-keyfile - containers: - - name: keep-client-0 - image: gcr.io/keep-dev-fe24/keep-client - imagePullPolicy: Always - ports: - - containerPort: 3919 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-4 - - name: LOG_LEVEL - value: debug - - name: IPFS_LOGGING_FMT - value: nocolor - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["keep-client", "-config", "/mnt/keep-client/config/keep-client-config.toml", "start"] - initContainers: - - name: initcontainer-provision-keep-client - image: gcr.io/keep-dev-fe24/initcontainer-provision-keep-client-ethereum - imagePullPolicy: Always - env: - - name: ETH_RPC_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: rpc-url - - name: ETH_WS_URL - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: ws-url - - name: ETH_NETWORK_ID - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: network-id - - name: CONTRACT_OWNER_ETH_ACCOUNT_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-address - - name: CONTRACT_OWNER_ETH_ACCOUNT_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-internal - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_KEYFILE_PATH - value: /mnt/keep-client/keyfile/account-4-keyfile - - name: KEEP_CLIENT_PEERS - value: /ip4/10.102.100.66/tcp/3919/ipfs/16Uiu2HAm8KJX32kr3eYUhDuzwTucSfAfspnjnXNf9veVhB12t6Vf - - name: KEEP_CLIENT_ANNOUNCED_ADDRESSES - value: '' - - name: KEEP_CLIENT_PORT - value: '3919' - - name: KEEP_CLIENT_DATA_DIR - value: /mnt/keep-client/data - volumeMounts: - - name: keep-client-config - mountPath: /mnt/keep-client/config - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["node", "/tmp/provision-keep-client.js"] diff --git a/infrastructure/kube/keep-dev/tenderly-agent-configmap.yaml b/infrastructure/kube/keep-dev/tenderly-agent-configmap.yaml deleted file mode 100644 index 65a228cfc2..0000000000 --- a/infrastructure/kube/keep-dev/tenderly-agent-configmap.yaml +++ /dev/null @@ -1,55 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: tenderly-agent - namespace: default -data: - config.yaml: | - agent: - database_path: .db - networks: - 1101: - name: keep-dev - address: 0.0.0.0:8555 - # the address and port of your local node - # note: this is the address from the perspective of the container - rpc_server: eth-tx-node.default.svc.cluster.local:8545 - node_type: geth - chain_config: - chainId: 1101 - homesteadBlock: 0 - eip150Block: 0 - eip155Block: 0 - eip158Block: 0 - byzantiumBlock: 0 - constantinopleBlock: 0 - petersburgBlock: 0 - istanbulBlock: 0 - clique: - period: 1 - epoch: 30000 - cert_file: ./.tenderly/cert/tenderly-agent.crt # optional - key_file: ./.tenderly/private/tenderly-agent.key # optional - tenderly-agent.crt: | - -----BEGIN CERTIFICATE----- - MIIDSTCCAjECCQDOwN/Y2oFDPDANBgkqhkiG9w0BAQsFADBfMRkwFwYDVQQDDBBk - ZXYua2VlcC5uZXR3b3JrMRAwDgYDVQQIDAdHZW9yZ2lhMRAwDgYDVQQHDAdBdGxh - bnRhMQ0wCwYDVQQKDARLZWVwMQ8wDQYDVQQLDAZEZXZPcHMwHhcNMjAwMzA5MjA0 - NzQ3WhcNMzAwMzA3MjA0NzQ3WjBuMSgwJgYDVQQDDB90ZW5kZXJseS1hZ2VudC5k - ZXYua2VlcC5uZXR3b3JrMRAwDgYDVQQIDAdHZW9yZ2lhMRAwDgYDVQQHDAdBdGxh - bnRhMQ0wCwYDVQQKDARLZWVwMQ8wDQYDVQQLDAZEZXZPcHMwggEiMA0GCSqGSIb3 - DQEBAQUAA4IBDwAwggEKAoIBAQDaJCPohw0cQXyzUinOW8cmGKpRtrwlvf/8pyUA - 1UPLTQ0h0QGFyba1ErceF3TAQLTmvoW5nmaQBkVlR++JynQIm4ZKQXlKNkBYM1qN - 5ce2sZpzIzJuatKA6BgFPh2R/p9YY9o+lMpeJCJ7wDnMuG5LrGk52g4Jb3zUu2XD - CdO9eZfUFnATlnBQ3UX5cbdyKmkTBPUijXezAevcFmdyoGCp/W0zdS1Slu25nRNd - EYKKBEfob/73aGWUuVdbnE01q9fguzzFAN5LEWewXDFCQ/sm8OdpvN62LvKmEXP7 - Dl4GHtkVq69bzQ1gGDwWr8GRkPKMnSALUETgQx8qBtA4JNQfAgMBAAEwDQYJKoZI - hvcNAQELBQADggEBAGEbIPTdTv6/LLf1y/rbFd/mYy2EbB5s7OcGXEDlUO00P7X3 - PcFZ88rVEWRc4eZxSFmPmwiDId5kEHXarsyM1yl2mG2Z08hNkTvq822GrgW+0dXy - EGuA512oQ491CLv+rIz0l/Cv0pMfICJXZsyiPArU2CPdA9JAfVEqQbGyd/TNr20p - p6fM4nqzd/m2gD7tFj9r3TJYNk5m1eiNsVV82SszaCgTK+ZagugWwXXd5snY4Zck - W1gG7eVF0RAMFjpAaquWGwAUtoqs2Wmx1w8cIp7Kw+3a8GEEUKxfNIXoWzs9tSEX - ApE5j9Uc3rETemy0x812OBR6iWj80TBPaaYyPzc= - -----END CERTIFICATE----- - - diff --git a/infrastructure/kube/keep-dev/tenderly-agent-deployment.yaml b/infrastructure/kube/keep-dev/tenderly-agent-deployment.yaml deleted file mode 100644 index 778aab8d33..0000000000 --- a/infrastructure/kube/keep-dev/tenderly-agent-deployment.yaml +++ /dev/null @@ -1,49 +0,0 @@ ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: tenderly-agent - labels: - app: tenderly - type: agent -spec: - replicas: 1 - selector: - matchLabels: - app: tenderly - type: agent - template: - metadata: - labels: - app: tenderly - type: agent - spec: - containers: - - name: tenderly-agent - image: gcr.io/tenderly-public/tenderly-agent:latest - volumeMounts: - - name: tenderly-agent-config - mountPath: /tenderly/config - - name: tenderly-agent-cert - mountPath: /tenderly/.tenderly/cert - - name: tenderly-agent-cert-key - mountPath: /tenderly/.tenderly/private - volumes: - - name: tenderly-agent-config - configMap: - name: tenderly-agent - items: - - key: config.yaml - path: config.yaml - - name: tenderly-agent-cert - configMap: - name: tenderly-agent - items: - - key: tenderly-agent.crt - path: tenderly-agent.crt - - name: tenderly-agent-cert-key - secret: - secretName: tenderly-agent - items: - - key: tenderly-agent.key - path: tenderly-agent.key \ No newline at end of file diff --git a/infrastructure/kube/keep-dev/tenderly-agent-service.yaml b/infrastructure/kube/keep-dev/tenderly-agent-service.yaml deleted file mode 100644 index 15f9574a2b..0000000000 --- a/infrastructure/kube/keep-dev/tenderly-agent-service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: tenderly-agent - namespace: default - labels: - app: tenderly - type: agent -spec: - type: LoadBalancer - ports: - - name: agent-port - port: 8555 - targetPort: 8555 - selector: - app: tenderly - type: agent diff --git a/infrastructure/kube/keep-prd/.envrc b/infrastructure/kube/keep-prd/.envrc deleted file mode 100644 index 1f94e4483f..0000000000 --- a/infrastructure/kube/keep-prd/.envrc +++ /dev/null @@ -1 +0,0 @@ -export CLOUDSDK_ACTIVE_CONFIG_NAME=keep-prd diff --git a/infrastructure/kube/keep-prd/bitcoin/bitcoin-namespace.yaml b/infrastructure/kube/keep-prd/bitcoin/bitcoin-namespace.yaml deleted file mode 100644 index aa47d7b9e4..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/bitcoin-namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bitcoin diff --git a/infrastructure/kube/keep-prd/bitcoin/bitcoind/bitcoind-data-bitcoind-1-pvc.yaml b/infrastructure/kube/keep-prd/bitcoin/bitcoind/bitcoind-data-bitcoind-1-pvc.yaml deleted file mode 100644 index 639281f2bc..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/bitcoind/bitcoind-data-bitcoind-1-pvc.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: bitcoind-data-bitcoind-1 - namespace: bitcoin - labels: - app: bitcoind - chain: bitcoin - network: mainnet -spec: - storageClassName: bitcoind - dataSource: - name: bitcoind-snapshot - kind: VolumeSnapshot - apiGroup: snapshot.storage.k8s.io - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 650Gi diff --git a/infrastructure/kube/keep-prd/bitcoin/bitcoind/bitcoind-volumesnapshot.yaml b/infrastructure/kube/keep-prd/bitcoin/bitcoind/bitcoind-volumesnapshot.yaml deleted file mode 100644 index 5182f14be1..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/bitcoind/bitcoind-volumesnapshot.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: snapshot.storage.k8s.io/v1 -kind: VolumeSnapshot -metadata: - name: bitcoind-snapshot -spec: - volumeSnapshotClassName: bitcoind - source: - persistentVolumeClaimName: bitcoind-data-bitcoind-0 diff --git a/infrastructure/kube/keep-prd/bitcoin/bitcoind/kustomization.yaml b/infrastructure/kube/keep-prd/bitcoin/bitcoind/kustomization.yaml deleted file mode 100644 index 726fc3cddf..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/bitcoind/kustomization.yaml +++ /dev/null @@ -1,31 +0,0 @@ -resources: - - ../../../templates/bitcoin/bitcoind - -namespace: bitcoin - -commonLabels: - network: mainnet - -configMapGenerator: - - name: bitcoind - behavior: merge - literals: - - chain=main - -secretGenerator: - - name: bitcoind - behavior: merge - envs: - - .env.secret - -patches: - - target: - kind: StatefulSet - name: bitcoind - patch: |- - apiVersion: apps/v1 - kind: StatefulSet - metadata: - name: bitcoind - spec: - replicas: 2 diff --git a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-compact-history-job.yaml b/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-compact-history-job.yaml deleted file mode 100644 index f6f79363b9..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-compact-history-job.yaml +++ /dev/null @@ -1,67 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: electrumx-compact-history - namespace: bitcoin - labels: - chain: bitcoin - app: electrumx - network: mainnet -spec: - backoffLimit: 0 - completions: 1 - parallelism: 1 - template: - metadata: - labels: - chain: bitcoin - app: electrumx - network: mainnet - job-name: electrumx-compact-history - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - # https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods - fsGroupChangePolicy: "OnRootMismatch" - containers: - - name: electrumx - image: lukechilds/electrumx:v1.16.0 # TODO: switch to our image - imagePullPolicy: Always - command: - - /electrumx/electrumx_compact_history - env: - - name: COIN - value: BitcoinSegwit - - name: NET - value: mainnet - - name: DB_DIRECTORY - value: /mnt/electrum/data - - name: DAEMON_TOKEN - valueFrom: - secretKeyRef: - name: bitcoind - key: rpc-password - - name: DAEMON_HOST - valueFrom: - configMapKeyRef: - name: electrumx - key: daemon-host - - name: DAEMON_URL - value: http://$(DAEMON_USER):$(DAEMON_TOKEN)@$(DAEMON_HOST) - - name: COST_SOFT_LIMIT - value: "0" - - name: COST_HARD_LIMIT - value: "0" - - name: LOG_LEVEL - value: debug - volumeMounts: - - name: electrumx-data - mountPath: /mnt/electrum/data - restartPolicy: Never - volumes: - - name: electrumx-data - persistentVolumeClaim: - # Update to the desired replica's volume index. - claimName: electrumx-data-electrumx-2 diff --git a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-data-electrumx-1-pvc.yaml b/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-data-electrumx-1-pvc.yaml deleted file mode 100644 index 9898a901a8..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-data-electrumx-1-pvc.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: electrumx-data-electrumx-1 - namespace: bitcoin - labels: - app: electrumx - chain: bitcoin - network: mainnet -spec: - storageClassName: electrumx-v2 - dataSource: - name: electrumx-snapshot - kind: VolumeSnapshot - apiGroup: snapshot.storage.k8s.io - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 450Gi diff --git a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-data-electrumx-2-pvc.yaml b/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-data-electrumx-2-pvc.yaml deleted file mode 100644 index 4d311f6981..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-data-electrumx-2-pvc.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: electrumx-data-electrumx-2 - namespace: bitcoin - labels: - app: electrumx - chain: bitcoin - network: mainnet -spec: - storageClassName: electrumx-v2 - dataSource: - name: electrumx-snapshot - kind: VolumeSnapshot - apiGroup: snapshot.storage.k8s.io - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 450Gi diff --git a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-volumesnapshot.yaml b/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-volumesnapshot.yaml deleted file mode 100644 index 36242a68ca..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/electrumx/electrumx-volumesnapshot.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: snapshot.storage.k8s.io/v1 -kind: VolumeSnapshot -metadata: - name: electrumx-snapshot -spec: - volumeSnapshotClassName: electrumx - source: - persistentVolumeClaimName: electrumx-data-electrumx-0 diff --git a/infrastructure/kube/keep-prd/bitcoin/electrumx/kustomization.yaml b/infrastructure/kube/keep-prd/bitcoin/electrumx/kustomization.yaml deleted file mode 100644 index d20f7bd6c5..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/electrumx/kustomization.yaml +++ /dev/null @@ -1,43 +0,0 @@ -resources: - - ../../../templates/bitcoin/electrumx - -namespace: bitcoin - -commonLabels: - network: mainnet - -secretGenerator: - - name: tbtc-network-cloudflare-origin-cert - type: kubernetes.io/tls - files: - - .secret/ca.crt - - .secret/tls.crt - - .secret/tls.key - -patches: - - target: - kind: Service - name: electrumx - patch: |- - apiVersion: v1 - kind: Service - metadata: - name: electrumx - spec: - type: LoadBalancer - loadBalancerIP: 35.223.16.19 - - target: - kind: StatefulSet - name: electrumx - patch: |- - apiVersion: apps/v1 - kind: StatefulSet - metadata: - name: electrumx - spec: - replicas: 3 - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-prd/bitcoin/kustomization.yaml b/infrastructure/kube/keep-prd/bitcoin/kustomization.yaml deleted file mode 100644 index 03ec75721c..0000000000 --- a/infrastructure/kube/keep-prd/bitcoin/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: - - bitcoin-namespace.yaml diff --git a/infrastructure/kube/keep-prd/keep-maintainer/kustomization.yaml b/infrastructure/kube/keep-prd/keep-maintainer/kustomization.yaml index a74c65a959..33424f8422 100644 --- a/infrastructure/kube/keep-prd/keep-maintainer/kustomization.yaml +++ b/infrastructure/kube/keep-prd/keep-maintainer/kustomization.yaml @@ -10,7 +10,7 @@ commonLabels: images: - name: keep-maintainer - newName: thresholdnetwork/keep-client + newName: keepnetwork/keep-client newTag: v2.1.0 configMapGenerator: diff --git a/infrastructure/kube/keep-prd/monitoring/README.adoc b/infrastructure/kube/keep-prd/monitoring/README.adoc deleted file mode 100644 index bc9f79b764..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/README.adoc +++ /dev/null @@ -1,37 +0,0 @@ -:icons: font - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -# Monitoring - -The monitoring stack has the following components: - -1. Prometheus -2. Trickster -3. Grafana - -The production monitoring is based on the configuration described in the link:../../keep-test/monitoring/README.adoc[keep-test monitoring documentation]. - -Resources are exposed publicly under the following URLs: - -[cols="^1s,2m"] -|=== -^h|Service -^h|Address - -|Public Dashboard -|link:https://public.monitoring.threshold.network[] - -|Grafana -|link:https://monitoring.threshold.network/grafana[] - -|Prometheus -|link:https://monitoring.threshold.network/prometheus[] - -|=== diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/config/dashboards.yaml b/infrastructure/kube/keep-prd/monitoring/grafana/config/dashboards.yaml deleted file mode 100644 index 54bf65f56f..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/config/dashboards.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: 1 -providers: - - name: dashboards-provider - type: file - disableDeletion: true - updateIntervalSeconds: 10 - allowUiUpdates: true - options: - path: "/var/lib/grafana/dashboards" - foldersFromFilesStructure: true diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/config/datasources.yaml b/infrastructure/kube/keep-prd/monitoring/grafana/config/datasources.yaml deleted file mode 100644 index ef00731e62..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/config/datasources.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: 1 -datasources: - - name: Trickster - type: prometheus - access: proxy - editable: true - orgId: 1 - url: http://trickster:8480/prometheus - version: 1 - isDefault: true diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/config/grafana.ini b/infrastructure/kube/keep-prd/monitoring/grafana/config/grafana.ini deleted file mode 100644 index 66e6511968..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/config/grafana.ini +++ /dev/null @@ -1,9 +0,0 @@ -[auth.google] -enabled = true -scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email -auth_url = https://accounts.google.com/o/oauth2/auth -token_url = https://accounts.google.com/o/oauth2/token -allow_sign_up = true - -[feature_toggles] -publicDashboards = true diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/keep-nodes-public.json b/infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/keep-nodes-public.json deleted file mode 100644 index eb91d379db..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/keep-nodes-public.json +++ /dev/null @@ -1,911 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 2, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 30 - }, - { - "color": "green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 11, - "x": 0, - "y": 0 - }, - "id": 8, - "interval": "1m", - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "sum by(job) (sum by(chain_address) (up{job=\"keep-discovered-nodes\"}))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Nodes Up", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "super-light-red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 30 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 16, - "w": 13, - "x": 11, - "y": 0 - }, - "id": 3, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (connected_wellknown_peers_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "Connected Bootstraps", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "log": 2, - "type": "log" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "area" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "light-yellow", - "value": 300 - }, - { - "color": "green", - "value": 900 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 28, - "w": 11, - "x": 0, - "y": 8 - }, - "id": 4, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "asc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (tbtc_pre_params_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "TBTC PreParams Count", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 16 - }, - "id": 2, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (connected_peers_count{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "Connected Peers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 11, - "x": 0, - "y": 36 - }, - "id": 10, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": false, - "displayName": "chain_address" - } - ] - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "up{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "__auto", - "range": false, - "refId": "Nodes" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "client_info{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "legendFormat": "", - "range": false, - "refId": "Client Info" - } - ], - "title": "Client Versions (experimental)", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "byField": "chain_address" - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "Time 1": false, - "Time 2": true, - "Value": true, - "Value #A": true, - "Value #B": true, - "Value #Client Info": true, - "Value #Nodes": true, - "__name__": true, - "__name__ 1": true, - "__name__ 2": true, - "app": true, - "controller_revision_hash": true, - "id": true, - "instance": false, - "instance 1": false, - "instance 2": true, - "job": true, - "job 1": true, - "job 2": true, - "kubernetes_namespace": true, - "kubernetes_pod_name": true, - "kubernetes_pod_name_monitoring": true, - "network": true, - "network_id": true, - "network_id 1": true, - "network_id 2": true, - "statefulset_kubernetes_io_pod_name": true, - "type": true - }, - "indexByName": { - "Time 1": 3, - "Time 2": 8, - "Value #Client Info": 13, - "Value #Nodes": 7, - "__name__ 1": 4, - "__name__ 2": 9, - "chain_address": 0, - "instance 1": 1, - "instance 2": 10, - "job 1": 5, - "job 2": 11, - "network_id 1": 6, - "network_id 2": 12, - "version": 2 - }, - "renameByName": { - "chain_address": "Chain Address", - "instance 1": "Instance", - "version": "Client Version" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information on whether the node is connected to the Bitcoin network.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 36 - }, - "id": 11, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (btc_connectivity{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "BTC Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information on whether the node is connected to the Ethereum network.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 56 - }, - "id": 12, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (eth_connectivity{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "ETH Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 70, - "lineWidth": 0, - "spanNulls": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 13, - "x": 11, - "y": 76 - }, - "id": 6, - "options": { - "alignValue": "left", - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "mergeValues": true, - "rowHeight": 0.9, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (up{job=\"keep-discovered-nodes\"})", - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "A" - } - ], - "title": "Uptime (experimental)", - "type": "state-timeline" - } - ], - "refresh": false, - "schemaVersion": 37, - "style": "dark", - "tags": [ - "tbtc", - "keep", - "public" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-2d", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "Keep Nodes (Public)", - "uid": "hhDyYDI4z", - "version": 17, - "weekStart": "" -} \ No newline at end of file diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/keep-nodes.json b/infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/keep-nodes.json deleted file mode 100644 index e794fd0650..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/dashboards/keep/keep-nodes.json +++ /dev/null @@ -1,1223 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 3, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 30 - }, - { - "color": "green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 11, - "x": 0, - "y": 0 - }, - "id": 8, - "interval": "1m", - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "sum by(job) (sum by(chain_address) (up{job=\"keep-discovered-nodes\"}))", - "format": "time_series", - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Nodes Up", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "super-light-red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 30 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 16, - "w": 13, - "x": 11, - "y": 0 - }, - "id": 3, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (connected_wellknown_peers_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "Connected Bootstraps", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "log": 2, - "type": "log" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "area" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "light-yellow", - "value": 300 - }, - { - "color": "green", - "value": 900 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 28, - "w": 11, - "x": 0, - "y": 8 - }, - "id": 4, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "asc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (tbtc_pre_params_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "TBTC PreParams Count", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 16 - }, - "id": 2, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (connected_peers_count{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "Connected Peers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 11, - "x": 0, - "y": 36 - }, - "id": 10, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": false, - "displayName": "chain_address" - } - ] - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "up{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "__auto", - "range": false, - "refId": "Nodes" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "client_info{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "legendFormat": "", - "range": false, - "refId": "Client Info" - } - ], - "title": "Client Versions", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "byField": "chain_address" - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "Time 1": false, - "Time 2": true, - "Value": true, - "Value #A": true, - "Value #B": true, - "Value #Client Info": true, - "Value #Nodes": true, - "__name__": true, - "__name__ 1": true, - "__name__ 2": true, - "app": true, - "controller_revision_hash": true, - "id": true, - "instance": false, - "instance 1": false, - "instance 2": true, - "job": true, - "job 1": true, - "job 2": true, - "kubernetes_namespace": true, - "kubernetes_pod_name": true, - "kubernetes_pod_name_monitoring": true, - "network": true, - "network_id": true, - "network_id 1": true, - "network_id 2": true, - "statefulset_kubernetes_io_pod_name": true, - "type": true - }, - "indexByName": { - "Time 1": 3, - "Time 2": 8, - "Value #Client Info": 13, - "Value #Nodes": 7, - "__name__ 1": 4, - "__name__ 2": 9, - "chain_address": 0, - "instance 1": 1, - "instance 2": 10, - "job 1": 5, - "job 2": 11, - "network_id 1": 6, - "network_id 2": 12, - "version": 2 - }, - "renameByName": { - "chain_address": "Chain Address", - "instance 1": "Instance", - "version": "Client Version" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information on whether the node is connected to the Bitcoin network", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "0x0f115091c3909048BA336C76Fd30ca616c1A2bB8" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 36 - }, - "id": 13, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (btc_connectivity{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "BTC Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information on whether the node is connected to the Ethereum network", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red" - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 56 - }, - "id": 14, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (eth_connectivity{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "ETH Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "A number of running instances for each operator address.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 60, - "lineWidth": 0, - "spanNulls": false - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "super-light-orange" - }, - { - "color": "super-light-green", - "value": 1 - }, - { - "color": "super-light-red", - "value": 2 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 11, - "x": 0, - "y": 61 - }, - "id": 12, - "options": { - "alignValue": "center", - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "mergeValues": true, - "rowHeight": 0.9, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "count by(chain_address) (up{job=\"keep-discovered-nodes\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Node Instances", - "type": "state-timeline" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 70, - "lineWidth": 0, - "spanNulls": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 13, - "x": 11, - "y": 76 - }, - "id": 6, - "options": { - "alignValue": "left", - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "mergeValues": true, - "rowHeight": 0.9, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (up{job=\"keep-discovered-nodes\"})", - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "A" - } - ], - "title": "Uptime (experimental)", - "type": "state-timeline" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Inbound network join requests across all monitored nodes, with the failure-reason breakdown. A high failure share is expected: unrecognized peers probing the network are rejected by the on-chain firewall check. Investigate when the mix shifts (e.g. firewall rpc error or timeout growth) or when bursts coincide with peer loss.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 11, - "x": 0, - "y": 81 - }, - "id": 15, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "total", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_success_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "success", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_timeout_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: timeout", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_eof_reset_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: eof/reset", - "range": true, - "refId": "E" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_protocol_crypto_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: protocol/crypto", - "range": true, - "refId": "F" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_firewall_unrecognized_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: firewall unrecognized", - "range": true, - "refId": "G" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_firewall_rpc_error_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: firewall rpc error", - "range": true, - "refId": "H" - } - ], - "title": "Network Join Requests (per 10m)", - "type": "timeseries" - } - ], - "refresh": false, - "schemaVersion": 37, - "style": "dark", - "tags": [ - "tbtc", - "keep" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-7d", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "Keep Nodes", - "uid": "tMgEvbnVk", - "version": 13, - "weekStart": "" -} \ No newline at end of file diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/grafana-deployment.yaml b/infrastructure/kube/keep-prd/monitoring/grafana/grafana-deployment.yaml deleted file mode 100644 index d9d39b4acd..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/grafana-deployment.yaml +++ /dev/null @@ -1,99 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grafana -spec: - replicas: 1 - selector: - matchLabels: - app: grafana - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - runAsNonRoot: true - containers: - - name: grafana - image: grafana/grafana:9.2.5 - env: - - name: GF_SERVER_DOMAIN - value: monitoring.threshold.network - - name: GF_SERVER_ROOT_URL - value: "https://%(domain)s/grafana/" - - name: GF_SERVER_SERVE_FROM_SUB_PATH - value: "true" - - name: GF_FEATURE_TOGGLES_PUBLICDASHBOARDS - value: "true" - - name: GF_AUTH_GOOGLE_CLIENT_ID - valueFrom: - secretKeyRef: - name: grafana-auth-google - key: client_id - - name: GF_AUTH_GOOGLE_CLIENT_SECRET - valueFrom: - secretKeyRef: - name: grafana-auth-google - key: client_secret - ports: - - name: grafana - containerPort: 3000 - readinessProbe: - httpGet: - path: /api/health - port: grafana - initialDelaySeconds: 10 - periodSeconds: 30 - timeoutSeconds: 2 - livenessProbe: - initialDelaySeconds: 30 - tcpSocket: - port: grafana - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 250m - memory: 512Mi - volumeMounts: - - name: grafana-grafana-ini - mountPath: /etc/grafana/grafana.ini - subPath: grafana.ini - - name: grafana-config-datasources - mountPath: /etc/grafana/provisioning/datasources - - name: grafana-config-dashboards - mountPath: /etc/grafana/provisioning/dashboards - - name: grafana-storage - mountPath: /var/lib/grafana - - name: grafana-dashboards-keep - mountPath: /var/lib/grafana/dashboards/keep - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: grafana-storage - persistentVolumeClaim: - claimName: grafana-pvc - - name: grafana-dashboards-keep - configMap: - name: grafana-dashboards-keep - - name: grafana-config-datasources - configMap: - name: grafana-config - items: - - key: datasources.yaml - path: datasources.yaml - - name: grafana-config-dashboards - configMap: - name: grafana-config - items: - - key: dashboards.yaml - path: dashboards.yaml - - name: grafana-grafana-ini - configMap: - name: grafana-config - items: - - key: grafana.ini - path: grafana.ini diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/grafana-pvc.yaml b/infrastructure/kube/keep-prd/monitoring/grafana/grafana-pvc.yaml deleted file mode 100644 index 46b9de4205..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/grafana-pvc.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: grafana-pvc - namespace: monitoring - labels: - app: grafana -spec: - storageClassName: monitoring-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/grafana-service.yaml b/infrastructure/kube/keep-prd/monitoring/grafana/grafana-service.yaml deleted file mode 100644 index 2db62dbeda..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/grafana-service.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: grafana -spec: - selector: - app: grafana - type: NodePort - ports: - - port: 3000 - targetPort: grafana diff --git a/infrastructure/kube/keep-prd/monitoring/grafana/kustomization.yaml b/infrastructure/kube/keep-prd/monitoring/grafana/kustomization.yaml deleted file mode 100644 index e1ca15444f..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/grafana/kustomization.yaml +++ /dev/null @@ -1,26 +0,0 @@ -resources: - - grafana-deployment.yaml - - grafana-pvc.yaml - - grafana-service.yaml - -namespace: monitoring - -commonLabels: - app: grafana - type: monitoring - -configMapGenerator: - - name: grafana-config - files: - - config/grafana.ini - - config/dashboards.yaml - - config/datasources.yaml - - name: grafana-dashboards-keep - files: - - dashboards/keep/keep-nodes-public.json - - dashboards/keep/keep-nodes.json - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-prd/monitoring/monitoring-ingress.yaml b/infrastructure/kube/keep-prd/monitoring/monitoring-ingress.yaml deleted file mode 100644 index bfa25808cb..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/monitoring-ingress.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: monitoring - namespace: monitoring - annotations: - kubernetes.io/ingress.class: "gce" - # The static IP has to be created with the following command: - # `gcloud compute addresses create keep-monitoring-ingress --global` - kubernetes.io/ingress.global-static-ip-name: "keep-monitoring-ingress" - networking.gke.io/managed-certificates: monitoring-cert -spec: - defaultBackend: - service: - name: grafana - port: - number: 3000 - rules: - - http: - paths: - - path: "/grafana" - pathType: Prefix - backend: - service: - name: grafana - port: - number: 3000 - - path: "/prometheus" - pathType: Prefix - backend: - service: - name: trickster - port: - number: 8480 - - path: "/trickster" - pathType: Prefix - backend: - service: - name: trickster - port: - number: 8480 ---- -apiVersion: networking.gke.io/v1 -kind: ManagedCertificate -metadata: - name: monitoring-cert - namespace: monitoring -spec: - domains: - - monitoring.threshold.network diff --git a/infrastructure/kube/keep-prd/monitoring/prometheus/config/config.yaml b/infrastructure/kube/keep-prd/monitoring/prometheus/config/config.yaml deleted file mode 100644 index caafb7470f..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/prometheus/config/config.yaml +++ /dev/null @@ -1,30 +0,0 @@ -global: - scrape_interval: 1m - scrape_timeout: 10s - evaluation_interval: 1m -rule_files: - - /etc/prometheus/rules.yaml -scrape_configs: - - job_name: keep-discovered-nodes - honor_timestamps: true - metrics_path: /metrics - scheme: http - follow_redirects: true - enable_http2: true - relabel_configs: - - source_labels: [__meta_chain_address] - separator: ; - regex: (.*) - target_label: chain_address - replacement: $1 - action: replace - - source_labels: [__meta_network_id] - separator: ; - regex: (.*) - target_label: network_id - replacement: $1 - action: replace - file_sd_configs: - - files: - - /etc/prometheus/sd/keep-sd.json - refresh_interval: 5m diff --git a/infrastructure/kube/keep-prd/monitoring/prometheus/config/rules.yaml b/infrastructure/kube/keep-prd/monitoring/prometheus/config/rules.yaml deleted file mode 100644 index 668044bd92..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/prometheus/config/rules.yaml +++ /dev/null @@ -1,52 +0,0 @@ -groups: - - name: keep-network-join-requests - rules: - # Fires only when an abnormal burst of inbound join-request failures - # coincides with peer loss or coordination degradation on the same - # node. A high failure ratio alone is expected behavior (unrecognized - # peers probing the network are rejected by the on-chain firewall - # check) and intentionally does not fire this alert. - - alert: KeepNodeJoinFailureBurstWithConnectivityDegradation - expr: | - ( - sum by (chain_address) ( - rate(performance_network_join_requests_failed_total{job="keep-discovered-nodes"}[30m]) - ) - > - 4 * sum by (chain_address) ( - rate(performance_network_join_requests_failed_total{job="keep-discovered-nodes"}[6h] offset 30m) - ) + 0.05 - ) - and on (chain_address) - ( - min by (chain_address) ( - connected_wellknown_peers_count{job="keep-discovered-nodes"} - ) == 0 - or - min by (chain_address) ( - delta(connected_peers_count{job="keep-discovered-nodes"}[30m]) - ) < -5 - or - sum by (chain_address) ( - increase(performance_coordination_failed_total{job="keep-discovered-nodes"}[1h]) - ) > 0 - or - sum by (chain_address) ( - increase(performance_coordination_leader_timeout_total{job="keep-discovered-nodes"}[1h]) - ) > 2 - ) - for: 15m - labels: - severity: warning - annotations: - summary: >- - Join-request failure burst with connectivity degradation on - {{ $labels.chain_address }} - description: >- - Inbound network join-request failures on node - {{ $labels.chain_address }} spiked to more than 4x their 6h - baseline while the node also shows well-known peer isolation, - peer loss, or coordination degradation. Check the per-reason - breakdown (performance_network_join_requests_failed_*_total) - to tell genuine non-recognition (firewall_unrecognized) apart - from firewall RPC errors, timeouts, and connection resets. diff --git a/infrastructure/kube/keep-prd/monitoring/prometheus/kustomization.yaml b/infrastructure/kube/keep-prd/monitoring/prometheus/kustomization.yaml deleted file mode 100644 index c70e76bef9..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/prometheus/kustomization.yaml +++ /dev/null @@ -1,21 +0,0 @@ -resources: - - prometheus-deployment.yaml - - prometheus-pvc.yaml - - prometheus-service.yaml - -namespace: monitoring - -commonLabels: - app: prometheus - type: monitoring - -configMapGenerator: - - name: prometheus-config - files: - - config/config.yaml - - config/rules.yaml - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-deployment.yaml b/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-deployment.yaml deleted file mode 100644 index 227e85b42f..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-deployment.yaml +++ /dev/null @@ -1,91 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: prometheus -spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: prometheus - type: monitoring - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - runAsNonRoot: true - containers: - - name: prometheus - image: prom/prometheus:v2.43.1 - args: - - --config.file=/etc/prometheus/config.yaml - - --storage.tsdb.path=/etc/prometheus/data - - --storage.tsdb.retention.time=1y - - --web.external-url=/prometheus/ - ports: - - name: prometheus - containerPort: 9090 - readinessProbe: - httpGet: - path: "/prometheus/-/ready" - port: prometheus - initialDelaySeconds: 10 - periodSeconds: 30 - timeoutSeconds: 2 - livenessProbe: - httpGet: - path: "/prometheus/-/healthy" - port: prometheus - initialDelaySeconds: 10 - periodSeconds: 30 - timeoutSeconds: 2 - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 500m - memory: 512Mi - volumeMounts: - - name: prometheus-config-volume - mountPath: /etc/prometheus/ - - name: prometheus-storage-volume - mountPath: /etc/prometheus/data/ - - name: prometheus-sd-volume - mountPath: /etc/prometheus/sd/ - securityContext: - readOnlyRootFilesystem: true - - name: keep-sd - image: keepnetwork/keep-prometheus-sd - args: - - --output.file=/etc/prometheus/sd/keep-sd.json - - --source.address=bst-a01.tbtc.boar.network:9601 - - --source.address=bst-b01.tbtc.boar.network:9601 - - --refresh.interval=5m - - --scan.timeout=3s - - --log.json - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 250m - memory: 256Mi - volumeMounts: - - name: prometheus-sd-volume - mountPath: /etc/prometheus/sd/ - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: prometheus-config-volume - configMap: - name: prometheus-config - - name: prometheus-storage-volume - persistentVolumeClaim: - claimName: prometheus-pvc - - name: prometheus-sd-volume - emptyDir: {} diff --git a/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-pvc.yaml b/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-pvc.yaml deleted file mode 100644 index 6ca54ca443..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-pvc.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: prometheus-pvc -spec: - storageClassName: monitoring-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi diff --git a/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-service.yaml b/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-service.yaml deleted file mode 100644 index ef83e37517..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/prometheus/prometheus-service.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: prometheus -spec: - selector: - app: prometheus - ports: - - port: 9090 - targetPort: prometheus diff --git a/infrastructure/kube/keep-prd/monitoring/storage-class.yaml b/infrastructure/kube/keep-prd/monitoring/storage-class.yaml deleted file mode 100644 index bf375bd8c0..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/storage-class.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: monitoring-storage -provisioner: kubernetes.io/gce-pd -parameters: - type: pd-ssd - replication-type: none -reclaimPolicy: Retain -allowVolumeExpansion: true -mountOptions: - - debug -volumeBindingMode: Immediate diff --git a/infrastructure/kube/keep-prd/monitoring/trickster/config/trickster.yaml b/infrastructure/kube/keep-prd/monitoring/trickster/config/trickster.yaml deleted file mode 100644 index 0c4b5797c5..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/trickster/config/trickster.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Trickster Configuration File. -# -# A full configuration file example can be found here: -# https://github.com/trickstercache/trickster/blob/main/examples/conf/example.full.yaml - -frontend: - listen_port: 8480 - -backends: - default: - provider: prometheus - origin_url: http://prometheus:9090 - is_default: true - healthcheck: - path: /prometheus/-/ready - upstream_path: /prometheus/-/ready - interval_ms: 5000 - expected_body: "Prometheus Server is Ready.\n" - -metrics: - listen_port: 8481 - listen_address: "" - -logging: - log_level: info diff --git a/infrastructure/kube/keep-prd/monitoring/trickster/kustomization.yaml b/infrastructure/kube/keep-prd/monitoring/trickster/kustomization.yaml deleted file mode 100644 index 0ca82fb0a6..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/trickster/kustomization.yaml +++ /dev/null @@ -1,19 +0,0 @@ -resources: - - trickster-deployment.yaml - - trickster-service.yaml - -namespace: monitoring - -commonLabels: - app: trickster - type: monitoring - -configMapGenerator: - - name: trickster-config - files: - - config/trickster.yaml - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-prd/monitoring/trickster/trickster-deployment.yaml b/infrastructure/kube/keep-prd/monitoring/trickster/trickster-deployment.yaml deleted file mode 100644 index f63c615dad..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/trickster/trickster-deployment.yaml +++ /dev/null @@ -1,58 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: trickster -spec: - replicas: 1 - selector: - matchLabels: - app: trickster - type: monitoring - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - runAsNonRoot: true - containers: - - name: trickster - image: trickstercache/trickster:2 - ports: - - name: trickster - containerPort: 8480 - - name: metrics - containerPort: 8481 - readinessProbe: - httpGet: - path: "/trickster/health/default" - port: metrics - livenessProbe: - httpGet: - path: "/trickster/ping" - port: trickster - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 500m - memory: 512Mi - volumeMounts: - - name: trickster-config - mountPath: /etc/trickster - env: - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: trickster-config - configMap: - name: trickster-config - items: - - key: trickster.yaml - path: trickster.yaml diff --git a/infrastructure/kube/keep-prd/monitoring/trickster/trickster-service.yaml b/infrastructure/kube/keep-prd/monitoring/trickster/trickster-service.yaml deleted file mode 100644 index cdcb0f9030..0000000000 --- a/infrastructure/kube/keep-prd/monitoring/trickster/trickster-service.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: trickster -spec: - selector: - app: trickster - ports: - - name: trickster - port: 8480 - targetPort: trickster - - name: metrics - port: 8481 - targetPort: metrics diff --git a/infrastructure/kube/keep-test/.envrc b/infrastructure/kube/keep-test/.envrc deleted file mode 100644 index 4d732771d6..0000000000 --- a/infrastructure/kube/keep-test/.envrc +++ /dev/null @@ -1 +0,0 @@ -export CLOUDSDK_ACTIVE_CONFIG_NAME=keep-test diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoin-namespace.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/bitcoin-namespace.yaml deleted file mode 100644 index 28b32f048a..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoin-namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bitcoin-testnet diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/bitcoind-data-bitcoind-1-pvc.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/bitcoind-data-bitcoind-1-pvc.yaml deleted file mode 100644 index d1e4bbb76e..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/bitcoind-data-bitcoind-1-pvc.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: bitcoind-data-bitcoind-1 - namespace: bitcoin-testnet - labels: - app: bitcoind - chain: bitcoin - network: testnet -spec: - storageClassName: bitcoind - dataSource: - name: bitcoind-snapshot - kind: VolumeSnapshot - apiGroup: snapshot.storage.k8s.io - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 50Gi diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/bitcoind-volumesnapshot.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/bitcoind-volumesnapshot.yaml deleted file mode 100644 index 5182f14be1..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/bitcoind-volumesnapshot.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: snapshot.storage.k8s.io/v1 -kind: VolumeSnapshot -metadata: - name: bitcoind-snapshot -spec: - volumeSnapshotClassName: bitcoind - source: - persistentVolumeClaimName: bitcoind-data-bitcoind-0 diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/kustomization.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/kustomization.yaml deleted file mode 100644 index 5e140ca5cd..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/bitcoind/kustomization.yaml +++ /dev/null @@ -1,46 +0,0 @@ -resources: - - ../../../../templates/bitcoin/bitcoind - -namespace: bitcoin-testnet - -commonLabels: - network: testnet - -configMapGenerator: - - name: bitcoind - behavior: merge - literals: - - chain=test - -secretGenerator: - - name: bitcoind - behavior: merge - envs: - - .env.secret - -patches: - # Patch bitcoind StatefulSet by setting a storage request specific for testnet. - - target: - kind: StatefulSet - name: bitcoind - patch: |- - apiVersion: apps/v1 - kind: StatefulSet - metadata: - name: bitcoind - spec: - replicas: 2 - volumeClaimTemplates: - - metadata: - name: bitcoind-data - labels: - chain: bitcoin - app: bitcoind - network: testnet - spec: - storageClassName: bitcoind - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 50Gi diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/electrumx-data-electrumx-1-pvc.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/electrumx-data-electrumx-1-pvc.yaml deleted file mode 100644 index 8db8802fed..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/electrumx-data-electrumx-1-pvc.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: electrumx-data-electrumx-1 - namespace: bitcoin-testnet - labels: - app: electrumx - chain: bitcoin - network: testnet -spec: - storageClassName: electrumx-v2 - dataSource: - name: electrumx-snapshot - kind: VolumeSnapshot - apiGroup: snapshot.storage.k8s.io - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 40Gi diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/electrumx-volumesnapshot.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/electrumx-volumesnapshot.yaml deleted file mode 100644 index 36242a68ca..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/electrumx-volumesnapshot.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: snapshot.storage.k8s.io/v1 -kind: VolumeSnapshot -metadata: - name: electrumx-snapshot -spec: - volumeSnapshotClassName: electrumx - source: - persistentVolumeClaimName: electrumx-data-electrumx-0 diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/kustomization.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/kustomization.yaml deleted file mode 100644 index 55063a72d5..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/electrumx/kustomization.yaml +++ /dev/null @@ -1,67 +0,0 @@ -resources: - - ../../../../templates/bitcoin/electrumx - -namespace: bitcoin-testnet - -commonLabels: - network: testnet - -secretGenerator: - - name: test-tbtc-network-cloudflare-origin-cert - type: kubernetes.io/tls - files: - - .secret/tls.crt - - .secret/tls.key - -patches: - - target: - kind: Service - name: electrumx - patch: |- - apiVersion: v1 - kind: Service - metadata: - name: electrumx - spec: - type: LoadBalancer - loadBalancerIP: 34.70.22.39 - - target: - kind: StatefulSet - name: electrumx - patch: |- - apiVersion: apps/v1 - kind: StatefulSet - metadata: - name: electrumx - spec: - replicas: 2 - template: - spec: - containers: - - name: electrumx - env: - - name: NET - value: testnet - volumes: - - name: tbtc-network-cloudflare-origin-cert - secret: - secretName: test-tbtc-network-cloudflare-origin-cert - volumeClaimTemplates: - - metadata: - name: electrumx-data - labels: - chain: bitcoin - app: electrumx - network: testnet - spec: - storageClassName: electrumx-v2 - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 40Gi - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-test/bitcoin/testnet/kustomization.yaml b/infrastructure/kube/keep-test/bitcoin/testnet/kustomization.yaml deleted file mode 100644 index 03ec75721c..0000000000 --- a/infrastructure/kube/keep-test/bitcoin/testnet/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: - - bitcoin-namespace.yaml diff --git a/infrastructure/kube/keep-test/eth-account-info-configmap.yaml b/infrastructure/kube/keep-test/eth-account-info-configmap.yaml deleted file mode 100644 index 2bf17899d1..0000000000 --- a/infrastructure/kube/keep-test/eth-account-info-configmap.yaml +++ /dev/null @@ -1,309 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: eth-account-info - namespace: default -data: - relay-requester-address: "0xcd5524a79afd81f1a25c1298d41a8e9271a759e5" - relay-requester-keyfile: | - {"address":"cd5524a79afd81f1a25c1298d41a8e9271a759e5","crypto":{"cipher":"aes-128-ctr","ciphertext":"8218af1cb5da7eccd70ac1b7eae3a21df2130bf76e34ce146efe33e68c3f0984","cipherparams":{"iv":"bafa5af5602116398d8dc3c394b8460d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"2cf504c3b85067d793c1e278dc51cf66d162fb485904b3c1536b5b30e9e73580"},"mac":"9e414c00af2dbf82c6cd2ab05f71b7525cae0b76a463cd0f82b0d8d6404c726d"},"id":"e4cb5dc2-82db-4577-b7ca-b196ab1d2264","version":3} - account-0-address: "0x0ec14bc7cca82c942cf276f6bbd0413216ddb2be" - account-0-keyfile: | - {"address":"0ec14bc7cca82c942cf276f6bbd0413216ddb2be","crypto":{"cipher":"aes-128-ctr","ciphertext":"d1e1885d30a2c25a54664487db4d69da496951733de6ceb4d5f565fe62eaba79","cipherparams":{"iv":"8cacad8a1b79982f568948b7f97b3dd3"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"00bfb9f49e54e6dba5b1b0c5b09904998fcfa10d0381b90bf26d45904a4e2636"},"mac":"9038c2a02d7837e448088fb19fc76e9d6c5063e8f1cb0addb40dc9df061b4928"},"id":"afb99070-073f-4dc6-b0d7-92b41fcf0afb","version":3} - account-1-address: "0xcab2a402bac470686d14956fb310d51bbef9fa31" - account-1-keyfile: | - {"address":"cab2a402bac470686d14956fb310d51bbef9fa31","crypto":{"cipher":"aes-128-ctr","ciphertext":"50193ab419aa322ceb556d4c073d1727763e5d873cce4e0735e6690194432665","cipherparams":{"iv":"6f869f3bd192d80981435016cc19afff"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"b72fe3dfd4d7419baa4d9a8ed7e27bf509fb9e1557a5d73c9c4879bf3a19abe9"},"mac":"5d3093a7b0160a8c2187efd4ab7ec168226e561ff7a0d714886c5dff28c405e7"},"id":"d43da5de-511f-4a1d-8ba8-0e0c24bf33e6","version":3} - account-2-address: "0xac049223397e2f25ea9fe56d5ee0896f6d8e8cb7" - account-2-keyfile: | - {"address":"ac049223397e2f25ea9fe56d5ee0896f6d8e8cb7","crypto":{"cipher":"aes-128-ctr","ciphertext":"42f6463f021f631ffbaf04989c107d784f0e1ba3a3b469073af4cc928d90bd5b","cipherparams":{"iv":"a533352b5ceb005cd730153f26e2f710"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"5716b94200ea2500fd257d28c1fd58e92ea991a4642813c91f66ae81fbc088bd"},"mac":"09b1e0857562a20ef0d25f41687095cdf246295a9dba82fee7e46756b0437bdc"},"id":"3819c68b-bc9d-4f54-867a-1ea7955c3cff","version":3} - account-3-address: "0x3ff855895ef4ac833c32ab6a0d6c7fbfa137e26e" - account-3-keyfile: | - {"address":"3ff855895ef4ac833c32ab6a0d6c7fbfa137e26e","crypto":{"cipher":"aes-128-ctr","ciphertext":"3cb866a0a1c0db6ca8accfc3c3036d9ee93b5dbca98f89dcf8f293e8b0134146","cipherparams":{"iv":"50e06549568b995a76190673e1643635"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ee2164b8525571024704eaa976c3ac80fb41b74bcaa7d7788f7ac94dd1b6878b"},"mac":"408bf7c097e905019e25b4c82b4c988b03a607469f77bdbe0e9ca6d870fa9055"},"id":"93a1dc32-f80a-400a-99be-c478f72a6630","version":3} - account-4-address: "0x0954efefeb970d317a51736201b4eb2de75ff5de" - account-4-keyfile: | - {"address":"0954efefeb970d317a51736201b4eb2de75ff5de","crypto":{"cipher":"aes-128-ctr","ciphertext":"ad2d8baa3626a7ffd0040a09dbbe73e179aa125e1677987524e1c5593f03c645","cipherparams":{"iv":"856e9d869aaa40e994bda72f969505ac"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a83d0582c376744e44cb982a90a5512a6570e046ea7ee0fd571e2fbda0cb762b"},"mac":"83ea2018f56bdd4967e8bd32c60cc7b3021bab59c72e4292c2e0ff20fc3b37e6"},"id":"666be636-2a15-4563-b78f-1ab704ec606c","version":3} - account-5-address: "0xd12a53056b74d96f89910ad3485da69a662f7930" - account-5-keyfile: | - {"address":"d12a53056b74d96f89910ad3485da69a662f7930","crypto":{"cipher":"aes-128-ctr","ciphertext":"02569d09ce9bd7371844dc60117bfd3ce97829a28d4c812cd7b30187047abd39","cipherparams":{"iv":"58b6a3e2cbc560323bebb81ebff1ca2c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8c28d3ece5b9e268ef73a148158dfe89f963ecf96e76fae41bda3ed2917b1331"},"mac":"9a9e29ebfd8712d70791df38b14e9de7aecdffdb858405f961966d65de843012"},"id":"0fdef51c-dd68-40e0-80d1-c038e800e511","version":3} - account-6-address: "0x677753a3cb8f3575be626f6a1f26e5c027c0af29" - account-6-keyfile: | - {"address":"677753a3cb8f3575be626f6a1f26e5c027c0af29","crypto":{"cipher":"aes-128-ctr","ciphertext":"cca77c25f7ea03abc65f154ef56bc712f4f3c4e21734e3ffc979615bc3d4b430","cipherparams":{"iv":"a94134c6c64db813aedb193d8d27c08e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9f9722abaf81affdbe6dc1573e97305a234cf4f3cba7dde985f45acff7a5b4f0"},"mac":"472e17c5d4660f669a09d724dbe0dbafb1cb8dc272e422e31654a300d2fc89ac"},"id":"66f810fc-7e95-4f1d-a490-792e0b8452ec","version":3} - account-7-address: "0x1aa7a9de6bd5a5802a98be50ff12f5a024a5abe0" - account-7-keyfile: | - {"address":"1aa7a9de6bd5a5802a98be50ff12f5a024a5abe0","crypto":{"cipher":"aes-128-ctr","ciphertext":"86279536ed5efaeb02b3a689cab7f7bb1a1d0554a36097164e499792d5d2b1fb","cipherparams":{"iv":"ee76fa919405a1fcd22311c181da4f70"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"3791fe112eab42db3721293556ca8a70431143462bedf0a320019c1626ac4a89"},"mac":"489181ff7b99ff1f1e40feef41f7099f4390da1064c4377e2e0946d705696f80"},"id":"0ae13709-5f10-4f42-a474-903353474732","version":3} - account-8-address: "0x76bc6bad38728329fe1c0e57d2555726f26a0399" - account-8-keyfile: | - {"address":"76bc6bad38728329fe1c0e57d2555726f26a0399","crypto":{"cipher":"aes-128-ctr","ciphertext":"40b3d9aae76bad5d8c61651adbad20c8b39a86e465e3e49b228ede63a615f549","cipherparams":{"iv":"10e8d87e8a6740fb1424fce23cee9fd7"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"db64fc334131708c0501b0fa5c90a41f10a47c1fb485e1b1c9cf3c625ade0781"},"mac":"19261b5cb3c73747a50884f4987a3ce31373af19e5970b19ea5da0ae17cbc8d1"},"id":"ff1f7c67-520a-45d5-90e4-f99d9303f327","version":3} - account-9-address: "0x5cd847903bb7f29de77eecc135628ca5b104a355" - account-9-keyfile: | - {"address":"5cd847903bb7f29de77eecc135628ca5b104a355","crypto":{"cipher":"aes-128-ctr","ciphertext":"306a0fa382c0f7a27dced0ca467a9664638a87ac1757f25819f4c7da45a9542b","cipherparams":{"iv":"606ff921f2474b0fcf53bdb3a2de5e14"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"85fa0683ece0df0e763956816220287b7d902b51157b74f2e05ea342d5a44ff7"},"mac":"c53de6511e7baccbbf0e99ab68743d09008c90af6ffd4a8087ea350e44640935"},"id":"821565f0-f8d0-4508-8409-89b0a19c9bfa","version":3} - account-10-address: "0xcc0123cb642ab7d24c1de153418cc7a1b42f8595" - account-10-keyfile: | - {"address":"cc0123cb642ab7d24c1de153418cc7a1b42f8595","crypto":{"cipher":"aes-128-ctr","ciphertext":"7eb604aa36c47222a0aab1e6591770789c5427d092290a2269e653d20b734bb6","cipherparams":{"iv":"f2e295eef926b71042b9ed08734c5968"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9d30f2896d1f6ee7bd19576c0f922dc4b4db98715ffb27b90e0ad200993ce74e"},"mac":"7d0275617c128819569d0062d579dea1afe9afe94f13ba39e5677564af51da70"},"id":"6d52a7aa-c2a2-46b4-ad2d-f145e805836e","version":3} - account-11-address: "0x2150a36177fced7a5b6d8840eb76a8c09cba1601" - account-11-keyfile: | - {"address":"2150a36177fced7a5b6d8840eb76a8c09cba1601","crypto":{"cipher":"aes-128-ctr","ciphertext":"b6f9a6cf0815e83bf11120d8dc51df0cc8ece3c0434ac56353e34d40a457703b","cipherparams":{"iv":"9a929dd1183f151bdf19a5d685fb2075"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"66299b29b303e63324a2fa3c365f87f3a51b7ec3a02d109a34c70f798dbca819"},"mac":"64ad5823a83652f987fd188a70b790b6d5f03829d9f604c2a0b17fc455a309ab"},"id":"983b30f6-1920-43c5-8e83-08c72c68174c","version":3} - account-12-address: "0x6213cacb40c83447503e8e177137cffdacc59ee6" - account-12-keyfile: | - {"address":"6213cacb40c83447503e8e177137cffdacc59ee6","crypto":{"cipher":"aes-128-ctr","ciphertext":"4426d345bd619655ad2138847bc88c0a72afe4faa4ac38f69a099b62101f17c5","cipherparams":{"iv":"0cf40b99e00a65aa6325a07fd69b916e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"3472ea2351b6236af54434e510e76cb9b50bf639898522a6c420c31e3e17e342"},"mac":"5595eba3025437d54a9e04613ee3dc219a0baba947bdac5b046a08b5df79835a"},"id":"7c487ef0-806e-40f9-a6d8-cf25aa44a38b","version":3} - account-13-address: "0xef52aada7c474d67aaede102c030522547001a46" - account-13-keyfile: | - {"address":"ef52aada7c474d67aaede102c030522547001a46","crypto":{"cipher":"aes-128-ctr","ciphertext":"9db579c5a39279dc91fab604a08a8c160b99fc92b694c0190c3fd2713d952b28","cipherparams":{"iv":"83428b6dd0673a1bca03009ecc0b7fcf"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"b9b5d319ec98dff0f783bb17bfa23b62a1e6bfe3f6e66bd42fa37a48b952b55e"},"mac":"3c87298445dd58d7c11196099f1cfdc1a6e7bb7055eb72f1abedc93cca7fab65"},"id":"0b7e9d2e-8a0d-4d9a-95b6-6a7e1f828f4a","version":3} - account-14-address: "0xbbfd3adc60e4d82a063442adf21294f16f0ae4a8" - account-14-keyfile: | - {"address":"bbfd3adc60e4d82a063442adf21294f16f0ae4a8","crypto":{"cipher":"aes-128-ctr","ciphertext":"63eb6083de9db291d86f4baa30265e12b21c5e99f5c6379a44b7fab382f5d848","cipherparams":{"iv":"cc4e44f79b3ce1fa703c7d6f2fbf8975"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c7ae81ea0de51ad5b5856c16502db06a79596d27d077e6a778b1b2cf3956811a"},"mac":"f91f2016c57a245207013eac5484b62dfec5bfe19573198187a6057427541889"},"id":"731ff860-6c6f-445d-9fdd-a93c7a54fbcc","version":3} - account-15-address: "0xf8b07ea64379845bb172b1bfb5064c2f6e73faf7" - account-15-keyfile: | - {"address":"f8b07ea64379845bb172b1bfb5064c2f6e73faf7","crypto":{"cipher":"aes-128-ctr","ciphertext":"b3e15d0c80adce83b36af2da24c297d041efcd799389781cc873fdfc3eba8af1","cipherparams":{"iv":"5a4c0cad0c9178f4a4dc65e5065cb9b3"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ec6abac88520b87bc398c11e589fb982b6c9b4aa3f8716148ae9f6a9c43e853c"},"mac":"3e2e69a1ca4c5cf1b7b5ab4932d8e6bbf46c69f66837505c968c61768a079285"},"id":"20662e3f-25f6-436f-afc7-396f78c98073","version":3} - account-16-address: "0x22c109baa3f47bae309211195d9a5c79fd32f6c5" - account-16-keyfile: | - {"address":"22c109baa3f47bae309211195d9a5c79fd32f6c5","crypto":{"cipher":"aes-128-ctr","ciphertext":"daba6da04153932b3821e423549c7679643b6ac87cf5399b3249d89a2bf6ff67","cipherparams":{"iv":"ced83eabfec9dcc1a77231e17780410a"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"fb209482da530eba29d3a81a51a794872125e9a6b6625afac5c7498ed31e781f"},"mac":"49ea6834a65a917f3cc924926870f1b7e3c0cf6948202da254422c5bd5e1f1f5"},"id":"13904fe7-4a16-49f5-8163-1d572d112afb","version":3} - account-17-address: "0x5fe83ab703a7e3bcc2b8c9c86be71ccd7cdadeb3" - account-17-keyfile: | - {"address":"5fe83ab703a7e3bcc2b8c9c86be71ccd7cdadeb3","crypto":{"cipher":"aes-128-ctr","ciphertext":"56918dfd8e2bd4cc34a7d2adb38ef4d6739b45798a6f2a27212be3a335427d9b","cipherparams":{"iv":"4b246c6cf8e68a5962899ed8bf4dd43f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"bc7460e1a7e18b3252ddd1a3ca05f398bbe72fe3a1b4d10f8e4f4228f8a75653"},"mac":"4f85c40f459a44cb3a1106187dc7b79678b7d0f4277ae54405b7cfdbe88c5f50"},"id":"91e45287-832e-47a4-92bb-085a586ba1e4","version":3} - account-18-address: "0xaf3bcf9c3fbba388200cd8d098f8b73461e08c5f" - account-18-keyfile: | - {"address":"af3bcf9c3fbba388200cd8d098f8b73461e08c5f","crypto":{"cipher":"aes-128-ctr","ciphertext":"d26f9a376b9a55d3cb972afe741500110123f2a8c40335b12dc9ee0f214aa1c8","cipherparams":{"iv":"4d1db4ace91e9b1e60eef37c6b694953"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e08f620cc2fda7cb6b681385652dbec47c34d278803753bdce2ddfa69a3e48ff"},"mac":"b5d22b4064db5c1eab8f6475bd485fa341df72a6e7919e57f8bd00665d608958"},"id":"79ebcb1d-2c59-4e52-baf3-a77c82e7fd56","version":3} - account-19-address: "0xf33153e1020881d52cbe6db06b801824480c325b" - account-19-keyfile: | - {"address":"f33153e1020881d52cbe6db06b801824480c325b","crypto":{"cipher":"aes-128-ctr","ciphertext":"5a4f702509eea6ff4630fcaefb7f8d7971094344435a7841d29ee7f6baaaa821","cipherparams":{"iv":"6d2c082a8071371ec16683d6da07a296"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ccba1ef481ea7fd6cb2286cd6c163354bdc649f00deb7f342876c8977b6041b5"},"mac":"6cff490e45eb00e415414a4976ce8b147791c03648088f74917739a234abe351"},"id":"8ec0519c-890e-400c-84d9-38519659b1e3","version":3} - account-20-address: "0xb4a78b27007cee374403681ffacdace76909f913" - account-20-keyfile: | - {"address":"b4a78b27007cee374403681ffacdace76909f913","crypto":{"cipher":"aes-128-ctr","ciphertext":"825634adfa65bb79da672fb19377456d06264519c409d532199e2bffc99a7aa5","cipherparams":{"iv":"d882cd05d6e0f96c7d6b93083864d2a5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"293029c5a27de4a6f33fae8b459568c2d2e93d3b7a1ac49122256f670f9acbd9"},"mac":"da7318ee1893afd857d36071d2e0e01fbbe9f75fe9adb4265ceb7f02c49a922f"},"id":"732154e6-145b-4700-bddd-797ac11d9f25","version":3} - account-21-address: "0xe44391df208629cc4f42a6e4cb17ba8c1fbbf0e3" - account-21-keyfile: | - {"address":"e44391df208629cc4f42a6e4cb17ba8c1fbbf0e3","crypto":{"cipher":"aes-128-ctr","ciphertext":"8840bf60888c27149f8ae59c862a35095a1b112e69aaa474be5d2820e9b9732e","cipherparams":{"iv":"b07626a490f2c649bc1a9ce20ea9931b"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f750e37fc4fc5c5d5d6116c074c666c88e2c71bc73383c68f9f7368036c3feca"},"mac":"0788c27a8d49a81c576a3832f3272b015e8caad1f9b418064a6085e95723b7de"},"id":"014399a3-b14b-4dba-89d6-f36cc325ffb7","version":3} - account-22-address: "0xdb7af39b6d8754b5dadad29bef43945bdd487806" - account-22-keyfile: | - {"address":"db7af39b6d8754b5dadad29bef43945bdd487806","crypto":{"cipher":"aes-128-ctr","ciphertext":"76cfb216f5b40d78f1f70f28210f48846c86f6c8204245f4e415e32da30bda88","cipherparams":{"iv":"47bf995d1dbd2a0cd070df2f23fe3de7"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1ddc490e6e9a6b249e0bdb753dffe8f377c09ce7ed365c1cce72b5a674b39d63"},"mac":"8f8cf27fa11523db97f5342fac2d92edb6962f21eaaf7d011aa30a26eeccab60"},"id":"101c8d5d-3f3f-477c-a0bf-991b4b868a48","version":3} - account-23-address: "0x2c1e150bed83ecea9cb111ba9bf485f0ac19c683" - account-23-keyfile: | - {"address":"2c1e150bed83ecea9cb111ba9bf485f0ac19c683","crypto":{"cipher":"aes-128-ctr","ciphertext":"9bddac5144b118ead6a8fb6b9c1087a81bfbe356a98df3b9a6658db3468291e8","cipherparams":{"iv":"0fb3a7d72b64243b08c2fbcb8744f85e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ef2155aafc128413f3f45738a2159bab308a7773332435f4b45fbe862d8d9c72"},"mac":"8d860f9c01e4676dd9d4c448a1dbca27e00fe5241cdec070f0422edddcac54ad"},"id":"bbc3d7c0-9905-4115-bebf-cfd01d18f54e","version":3} - account-24-address: "0xd7b1b5e78efcb2bf425bb109c2bed6d14b8009fb" - account-24-keyfile: | - {"address":"d7b1b5e78efcb2bf425bb109c2bed6d14b8009fb","crypto":{"cipher":"aes-128-ctr","ciphertext":"a9c9110f71175e690f0af51fe3bf9a7d9cfcaaa776ac6207a0b0448dded399dc","cipherparams":{"iv":"6fac01bf5e006dc2bf158044976f4bfa"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"623fd2b3010bea5e678caae3f4d9fc1ae54787b98753b4a7619285456551ca2d"},"mac":"54e9247d5631e0b30f1801e72b46c615af08810d64761f73e5383c65d79a9076"},"id":"d86b436d-3066-4cc1-bea0-0effc2c2aa6f","version":3} - account-25-address: "0xe1165cef25bfbebe98b534ef6224223cf4580570" - account-25-keyfile: | - {"address":"e1165cef25bfbebe98b534ef6224223cf4580570","crypto":{"cipher":"aes-128-ctr","ciphertext":"30b40293db73af288d8c79c2b7afb89e8660c1837ef11f159d01a3537f678170","cipherparams":{"iv":"43936e17120eb83a7c8313bc535460d0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c56314526991328040ffa9c2bee8a360f320394ec7b31d2426f24c9addc8376a"},"mac":"d7cc65ff7f720a1a26e6038645491b300454930228a37507d7565c84e326c3e7"},"id":"534d81be-108d-4e30-b18a-a83607773be2","version":3} - account-26-address: "0xf86ab3c084912d4cd57982cad97fbb22d74f3c98" - account-26-keyfile: | - {"address":"f86ab3c084912d4cd57982cad97fbb22d74f3c98","crypto":{"cipher":"aes-128-ctr","ciphertext":"ebd249f20090df75b746b81401afb53960c5a07ce069b6286dd8d553bdef5cd9","cipherparams":{"iv":"616a7201eadfd59909581b17dc0384bd"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ad0fb8882696c7c1c19f4a206ac0939ad3a66fef288e23df2dd28d9d2dca138b"},"mac":"48c2c3b1daaa353115995ff38755bba48bf97851a1068bf7ac18ecc019891f57"},"id":"ae93dee1-32ba-4d30-b684-b0a116e3e8f4","version":3} - account-27-address: "0x0a556970d79d924f5532507b8f4a899f26d16e90" - account-27-keyfile: | - {"address":"0a556970d79d924f5532507b8f4a899f26d16e90","crypto":{"cipher":"aes-128-ctr","ciphertext":"9be0f38d053e69f28d5bbfa1a8e7f25c434016edd5fd48f8eea15a5c00101714","cipherparams":{"iv":"da2f69448d5f5b5fb46c849e25c9374e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f8a38e4cec22a03a3fed5f008953a55d664005b0296f057b520d107d65c90583"},"mac":"4e6d0ef26b309b905d621329c8b850bcb21032d8d9e48916b27f1b22aa0243ad"},"id":"46a6bd1a-5a53-4cce-a48a-15d8964a4bd8","version":3} - account-28-address: "0x06915e6471f7d12ebe317bed11c4c9e6afa8faf1" - account-28-keyfile: | - {"address":"06915e6471f7d12ebe317bed11c4c9e6afa8faf1","crypto":{"cipher":"aes-128-ctr","ciphertext":"233737bb511b7d9069e48735491636a91cd2edd5bd9819bd39eaa56e51ccbafb","cipherparams":{"iv":"4333f03889f922bcc0b49149e5c85e3f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"6f1065459f74d4e4b1cabf1bbd017e36d87327eea7b53dba3273a242badd1b95"},"mac":"c6d30d39783a92a4d39b939da073e2dfcfb26faf39160e47be10e0c197a95504"},"id":"e20615d7-df15-4f82-a284-099a53c73648","version":3} - account-29-address: "0x12a9d4579c2cf9daedb3d8b6a844dc3a878222fb" - account-29-keyfile: | - {"address":"12a9d4579c2cf9daedb3d8b6a844dc3a878222fb","crypto":{"cipher":"aes-128-ctr","ciphertext":"7b9b686e539226dd9d85d33aa51cd87ff7dd5eb5c062a1954e5150e7bb5553c3","cipherparams":{"iv":"8f842bf10d11953f108ae88df527b4ba"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"21167fbc4d30c2da5253fd47ade4b94b1f66036f1e6edd4b3deaf5213c723d9e"},"mac":"40386f30dc04f60c682609ce8be44ec05a6a62e81f10884055fa173a60e305fe"},"id":"d3ee8030-2b45-4c4a-8360-f4455c73873e","version":3} - account-30-address: "0xc9f6a78167fcc9c1867b50c77c2a0aa003fd489c" - account-30-keyfile: | - {"address":"c9f6a78167fcc9c1867b50c77c2a0aa003fd489c","crypto":{"cipher":"aes-128-ctr","ciphertext":"2b5a72531a7394379c828704716aead63cbdf57010408552846e88be47d5c4ef","cipherparams":{"iv":"7755004c8c2caaecc57a4ff85fd03322"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"100ffdc13ffb773168d435758c0291fe38d7bb70f4fbd46da796ba63e3e41104"},"mac":"eb9e7aeb7aa237b3a525641165a52b67727265f2a9de172025aee152bc31c414"},"id":"1312b41c-d0be-4c82-ac8b-4ef772fa7b56","version":3} - account-31-address: "0x127d48d2536a85a97085d12eeff74ec244b153d6" - account-31-keyfile: | - {"address":"127d48d2536a85a97085d12eeff74ec244b153d6","crypto":{"cipher":"aes-128-ctr","ciphertext":"fff1243cab851041d84d00e778e21c92f218233ff12983e5c68c4ee49c30a47e","cipherparams":{"iv":"3f27900280b09de16a6494f515a1e5e4"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e2fd43ee53400803f960875717f93658597492f161dd985921b13a8e97071b9c"},"mac":"c2ac84b499830d14855886b5f780dd5f4d9f64dbd131e9ac8b64a47fcedcb874"},"id":"248b7f5a-21cf-4568-b940-fb5ec4039cd4","version":3} - account-32-address: "0x765fbe861a8be3e3377047301dca87dcce3b7291" - account-32-keyfile: | - {"address":"765fbe861a8be3e3377047301dca87dcce3b7291","crypto":{"cipher":"aes-128-ctr","ciphertext":"183e8e2c81afb396893315ae29660a94aa85698a2ab4eefa6d23c4a21fc0a108","cipherparams":{"iv":"993966a6ab9078aa194839dd8a77d158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f59680509daf3e8313a4108cbe0378e559859bffbceeb583153fab044a764ab3"},"mac":"482d76539ee8b14878264594ffd8ad21690a3f72faaf91087ab7353604d718ac"},"id":"436c66e6-ef23-4892-bf61-fc3a5595665b","version":3} - account-33-address: "0xa723c7d91c3070a80e43f39b10d2e9d082bb4bdd" - account-33-keyfile: | - {"address":"a723c7d91c3070a80e43f39b10d2e9d082bb4bdd","crypto":{"cipher":"aes-128-ctr","ciphertext":"d88a20dcefa779c4f3b3b2e994a6212bf8a6e772c921709458348c503698780a","cipherparams":{"iv":"6d88802070f7d853ff520b33dcdb67a5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"2b2fae61be6361748626ec6679a27d198d284123de8db4448abd4a4a95d8abd9"},"mac":"3ddd54dd23759fabf2d9154aeb8cab64f21cbb2091f5e17955d0506e05977e5e"},"id":"92dc2fd7-582e-4f70-b9a6-800525964589","version":3} - account-34-address: "0x98787a33e399361f2d6189c227b86a3025ee5688" - account-34-keyfile: | - {"address":"98787a33e399361f2d6189c227b86a3025ee5688","crypto":{"cipher":"aes-128-ctr","ciphertext":"1cac6f9371710d5bd3227d4da0983842f7a55ef54012cecacce5881c7e96357b","cipherparams":{"iv":"eb71eaa83c3e4cb73bc85380f5cfab83"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ce8275197567fcf999ef1d28237a87229a6d308df3b012c439a0a757be5f6225"},"mac":"c9596d85010336bcb8197e0b51521b26f5507b18cf57fe3e5c8d541cfd24b91b"},"id":"fe9cc520-238e-454d-acba-e11838d5ee3d","version":3} - account-35-address: "0xbd38bbbde29fc5ffadaa40e339eda2abc51c11e8" - account-35-keyfile: | - {"address":"bd38bbbde29fc5ffadaa40e339eda2abc51c11e8","crypto":{"cipher":"aes-128-ctr","ciphertext":"18381ac7daa0b1b221f9e182ee9958fd88330ecddfc34b173687b150e19bb262","cipherparams":{"iv":"aa1823a585f545d86f3e9578599a369c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"6409c0de3648e9f79c8205e129f20b361763c0368d08c6d133dc584962b522e9"},"mac":"4e84123df5933568cb5e4b048c1a15b44ac1762f91990f6a93d20e5b8e1364d2"},"id":"31d5c73e-3d2e-4e90-b875-a6566986197c","version":3} - account-36-address: "0x6eeabbb3bf02bbd51779994ee0a16b0a27118041" - account-36-keyfile: | - {"address":"6eeabbb3bf02bbd51779994ee0a16b0a27118041","crypto":{"cipher":"aes-128-ctr","ciphertext":"e3dbcf0bc5c9e1aa651e0d7ff0927d4b9861ceecc9405d52b667c172c3b6aa73","cipherparams":{"iv":"66eeddfa89cbe9bfc298c7b33dab5a82"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"0db71cf1e3f9ec8cd7071e0fee80efb5c7c845254b1a11bd19f83e22196980d3"},"mac":"15ae5bafb285e6e860ff32f0324af74c6bb5213b6ff7633d0260da45bf381e9e"},"id":"6616b3a0-eccf-4c29-9448-709a27b5a573","version":3} - account-37-address: "0xb325305f4dc4018982838b7599589a4acc82e348" - account-37-keyfile: | - {"address":"b325305f4dc4018982838b7599589a4acc82e348","crypto":{"cipher":"aes-128-ctr","ciphertext":"a0af3a150f30878f7909b0a97baf408b188a39ef86a1759c07a33da687752e9c","cipherparams":{"iv":"4e6e7a6708fca5c8131897a17e411e07"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ca7cf158d65221f5d0941002e1e5afe81431fd3c442c61baa24a949fabd22c97"},"mac":"d74bc7a434e625f0d35247ec1ff7d6fd58f21bd9a41f2b536ef1bf9ec3d468cc"},"id":"793089b8-ef61-4564-922e-48a23fb74269","version":3} - account-38-address: "0x3f069271b7279d6d5384dd3bc3fede1855518d4c" - account-38-keyfile: | - {"address":"3f069271b7279d6d5384dd3bc3fede1855518d4c","crypto":{"cipher":"aes-128-ctr","ciphertext":"9097759452619cc7d27cfa80f3b85a32d1da54105af41397b08f20ef3b1bbdf3","cipherparams":{"iv":"d0eb8a436fbe1a0bc597af430a5ab0fb"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"856bd3545142f569033c234f2f536e4bf973dcfa947df194cf7eac8559bd9d7a"},"mac":"66198225fc42b75bd84b84d84872ac1e65851a457f33fdf356483b34418c6d54"},"id":"ca591401-d92a-4438-ad95-d2b421dd8b76","version":3} - account-39-address: "0x9d95667b6e9bf6e84019f6514c6cef11bfa6cbdf" - account-39-keyfile: | - {"address":"9d95667b6e9bf6e84019f6514c6cef11bfa6cbdf","crypto":{"cipher":"aes-128-ctr","ciphertext":"f5a9f90b9505c7c534bc84ae1d56a3abc8b9b32275e3f2b3880f8c9c172d863f","cipherparams":{"iv":"868c49fd8e8dff254b241a143fa6eeb1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"fc37d202bc12bfbd13dfd1e68fb3e7119cfe8d6a9e8d85babf683e3f9658ba33"},"mac":"cecdf8d872aca5f436104b7973deff7b600cc3e9815a9f676de1fe61821c5b3e"},"id":"43beb89f-3780-4886-9c82-920efa074ad1","version":3} - account-40-address: "0x06ecd74b4b949e32ec2378515d9ae278c8bf2b43" - account-40-keyfile: | - {"address":"06ecd74b4b949e32ec2378515d9ae278c8bf2b43","crypto":{"cipher":"aes-128-ctr","ciphertext":"8dc38dab7acef366f26600ae4cd143db08d0f13603e84e23cf19e7c5ba10a384","cipherparams":{"iv":"c9bfff41b4abd1b2fc5815000a014d3d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f6d4125128fcab3ba1b60f698ee29949814030dae82726bc074a8d7a114e66f4"},"mac":"be98f18689d3a64273bca6f9a9af8e89b855c7239bce696469344783c07fd20f"},"id":"4dcd8016-f2f4-4719-b7bb-0e73df8e84a9","version":3} - account-41-address: "0x1363223b0acfa2fb58e1c7f374f011be2333db96" - account-41-keyfile: | - {"address":"1363223b0acfa2fb58e1c7f374f011be2333db96","crypto":{"cipher":"aes-128-ctr","ciphertext":"95b8832967d4fc453e3287bc51ba6bec8e3a11e84e2b8dc54bc501e9dc222b7c","cipherparams":{"iv":"b9ed108524d25630798e56583dae4131"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1ebc1c5df10650592179e3e19a6133750ca9f225fa54e33d99f82065bd986494"},"mac":"6267013080fa2ceb42964fa60e1041096ede40d1eb06aa4838e3d0cd5d5bfbf9"},"id":"c8633238-28a2-4208-a5f1-b477e459d203","version":3} - account-42-address: "0x5a6e4ed97aa97924e415ff22d42d2a0acc04f0d5" - account-42-keyfile: | - {"address":"5a6e4ed97aa97924e415ff22d42d2a0acc04f0d5","crypto":{"cipher":"aes-128-ctr","ciphertext":"f05ddf65790ae143e772fd9da7f26392b62937c30ba7716a3c8bef7c5dfa8fed","cipherparams":{"iv":"f4a9f62f81d9d56d3d5e2417ba3740ef"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"15bb60f1b5f4b33ff7ceadbfa5da132e7dda7d8c6ddb0c26ab77264ff217eaf9"},"mac":"fa8cb34ee8512d8fc72b387fa69b4419bf0531c76e61589980cc8e9100e8dca7"},"id":"ea463da1-f94b-47b9-90fd-c1eff5ebad80","version":3} - account-43-address: "0xb4b48cb7338bfd72bb92f314b8af0737b15de529" - account-43-keyfile: | - {"address":"b4b48cb7338bfd72bb92f314b8af0737b15de529","crypto":{"cipher":"aes-128-ctr","ciphertext":"bd87427c816f7727510c984d6d55ada88d5dfa0393210dbbbd5bbfc3028ea885","cipherparams":{"iv":"fd95be87ef3e48e80c7e8535c72e4a35"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"65ebb3e27a6438246fb17f121e3af89ffaaff4edbabe2bc251b044aeed4f1d3b"},"mac":"772ae2000d0f67d6a189ae9b3c32339091f8f03bfb1df527f9717d66500d58de"},"id":"787f0ee0-9b04-4950-ae9a-180b9f2e1140","version":3} - account-44-address: "0xb182da6013ffa83ede34c0f621f08ec1dc11fabc" - account-44-keyfile: | - {"address":"b182da6013ffa83ede34c0f621f08ec1dc11fabc","crypto":{"cipher":"aes-128-ctr","ciphertext":"321a2e23557b8ab33c60a669e8049edf7270dcfc2057956ec7ffd9f6ae0037fa","cipherparams":{"iv":"242584dd8c87bc8036a25c059f7dc52c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"b59c0f963083ef979db83b224df6b0b0adf43a2ca286425376b12f980a1f94df"},"mac":"c88c7dee2166996f05da9f0c9fd21ff5ee9f6b9be8c084b5dc91a2bd487187d8"},"id":"aa9ece81-9eb2-44a1-a296-3a1b0d2a461d","version":3} - account-45-address: "0x1e3cc42656ba98ddec729c1cbea514dae25d0de9" - account-45-keyfile: | - {"address":"1e3cc42656ba98ddec729c1cbea514dae25d0de9","crypto":{"cipher":"aes-128-ctr","ciphertext":"de26281b1b2eb37366ad2868579d877e14f11a82197a2ee446db35e89093d299","cipherparams":{"iv":"b1d3c177190435a731b064cac5cd3739"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d18e18b322f2a8eb4acacc36749ba897ae0a99dccbd56240a2ae4d32d0cb5c02"},"mac":"dbaf7a8886d2469516e13c933f17139d42f270955417bc56d9709e4934e9503a"},"id":"e7a4264a-ecdc-450b-b0ff-2a155f9bf5ea","version":3} - account-46-address: "0x881edb9fa7bb70ad6adeb903bb5bb960c981ec95" - account-46-keyfile: | - {"address":"881edb9fa7bb70ad6adeb903bb5bb960c981ec95","crypto":{"cipher":"aes-128-ctr","ciphertext":"e192dfefae980cccb0dcd5215559b5b5cf06647b1ceeded1603775d3a3ce94d6","cipherparams":{"iv":"2a566c350912f29768164cdad722716d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f80bf34d97a61a815e1d8388619bddf1483bf9ef4162e7515d15b7de77d9a454"},"mac":"874cb0f7cb033f246d01d38fb1c997a4e30e9e627edaebb4d3d47f5611fea35c"},"id":"089277a2-1ffa-4658-931c-4a4c29a8219e","version":3} - account-47-address: "0x0f1ffbafc315df3bb0ff566d880baeb5757fa12b" - account-47-keyfile: | - {"address":"0f1ffbafc315df3bb0ff566d880baeb5757fa12b","crypto":{"cipher":"aes-128-ctr","ciphertext":"a2bbb40b28bb7b90ceed72de064738d76fcba2b76e0c7a2f9281c89c71f38a77","cipherparams":{"iv":"02590a460b9784c3335b00ad57a48eb9"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d66acc05e36af2d5aae8feb9dff59641c9a021d3fe0c877dd762b86244128473"},"mac":"6490ca9077f686057033a1562a28911c3c5a669bdddc41d0912e7976138af592"},"id":"55c33815-007b-4664-b590-146afdd954e1","version":3} - account-48-address: "0xc9b138bfcae72cf69dbeb3d418aede58fdb7cede" - account-48-keyfile: | - {"address":"c9b138bfcae72cf69dbeb3d418aede58fdb7cede","crypto":{"cipher":"aes-128-ctr","ciphertext":"73ab913054e91994f92b9701e4e6b43fb591dfcee603c76056909f85f368a6a9","cipherparams":{"iv":"5ba2015007fd54b523952fde2e39f7a1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"49b1f83026d38e0faa35075d932c41ebcb6854668e0f1e0806d0dd000ab339ee"},"mac":"09feff10b41cb0fd72bcb28b5c390e97d6a07bb5b554318bf6de8d453b2cc3f4"},"id":"881646d9-2ee3-4adb-bf87-5d91c88de35f","version":3} - account-49-address: "0xa78c127157b6aa89079e5e3666d51856a3553bdd" - account-49-keyfile: | - {"address":"a78c127157b6aa89079e5e3666d51856a3553bdd","crypto":{"cipher":"aes-128-ctr","ciphertext":"11ebb4684cc5534471ca651fa97044b48fcccbe01771688c36ea019bf5632f6b","cipherparams":{"iv":"f0646bf3be80088f001a50f1d23d1393"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"bca01ec05d2fe9d92af877403ee2fe1dde27cef4db4913055d775b5f03c820ad"},"mac":"4355a10abba4f468af039e0b4d7362078bb9808cb08400e70db986706d4f9122"},"id":"7bb60055-b602-409a-9c00-ff486d4c3290","version":3} - account-50-address: "0xdd21e3d887d923667e84b71ec9244338f0882022" - account-50-keyfile: | - {"address":"dd21e3d887d923667e84b71ec9244338f0882022","crypto":{"cipher":"aes-128-ctr","ciphertext":"3b20224bd8cb497dfd425eed28ac44748c268e9bc9dcbbb3074e5068540a49fa","cipherparams":{"iv":"c025c17a11e41d10940f47798982f668"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ce57b9219c492ed09b61e14af0f4e3ca0d0c28aeba08425bc91bb01a444d5de9"},"mac":"7423adb79573c618f840da8b7e3bf31d8d254b9d4c0ad1948dcfa68c599ac7f9"},"id":"fe7f813b-b187-4dff-a914-ebe0b24812d5","version":3} - account-51-address: "0x7f7633e1b86c54c94f25b5ec8d1fd11ece4b1181" - account-51-keyfile: | - {"address":"7f7633e1b86c54c94f25b5ec8d1fd11ece4b1181","crypto":{"cipher":"aes-128-ctr","ciphertext":"5fd706557deec406a46f063d5bfdc15ca144a0af5ef96d1a9135c7fd71d32b8a","cipherparams":{"iv":"1cda57ca99bc0927354c00be0d9e1508"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"87a481ede6f72d9350cd4b7ab4f0455df0c35e70b5847b2b03b1cec84ca46eb4"},"mac":"e65e1d1fec514527d884813e6ef7cb4939c743088e7b0d8aac504d884f64e31b"},"id":"7bd051ed-cd22-4779-9aa7-a7a45ff2c977","version":3} - account-52-address: "0x713a114a5e620938f93cef33ca6362bbd7be7aa2" - account-52-keyfile: | - {"address":"713a114a5e620938f93cef33ca6362bbd7be7aa2","crypto":{"cipher":"aes-128-ctr","ciphertext":"e8997ad7140debe133a1b573b0a3e4c511bfcaeac8cb3b4f59d57db0403fe7ff","cipherparams":{"iv":"fd9dd64b6fa425057bbff040ba2c596e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"bf0833cb883d6ecc04a404f6f22be2e6db6d4ac871585b54f0578ffe315db55a"},"mac":"63393a31a1571954d440028cb709676285b5e964205aa5f4049ac12900615846"},"id":"37c558de-2d07-46df-bd43-4e1d3bd3f791","version":3} - account-53-address: "0xe7f76f6eeae7dc754280d4d5a8e15426138525d9" - account-53-keyfile: | - {"address":"e7f76f6eeae7dc754280d4d5a8e15426138525d9","crypto":{"cipher":"aes-128-ctr","ciphertext":"3c76c5c2a2b497a849cdcf43abcf376ffae2d6a971025deb21aff845136f6a78","cipherparams":{"iv":"53b016b728dc2f38e3ea73521751a166"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a203d92e0c77ce2c3d334da0ae6c3c245ba857ae02e1a16db41d4bcf6460fcc0"},"mac":"394a20572ad42635c282fa063ad20097f20ea41212915bef3cdfd1e6d9f32f18"},"id":"4b1aa6b0-d115-44f5-be1d-0bd6c7cc7df4","version":3} - account-54-address: "0xb3a06e0ef0c16899abb0ea95a5171ea2cb06f4d7" - account-54-keyfile: | - {"address":"b3a06e0ef0c16899abb0ea95a5171ea2cb06f4d7","crypto":{"cipher":"aes-128-ctr","ciphertext":"87c3bd7c66cf6d5c0fef009a67b6c6663d63869ba72fd75752c1234ff846a2a1","cipherparams":{"iv":"024f6cc468f249a9cebb02819d732cbc"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"936b0e4854ac3a4622cf120fbc24154a6fdceddb723dab27c16a0088a9f2f8e0"},"mac":"0ae8b566bc73e4bfadb936972bb5e529aa450c209a2864f83d3add34f9e890df"},"id":"ed566df4-9990-48ec-b489-ac56ff0140c9","version":3} - account-55-address: "0xb702d04773d6fd3fcc066cf130717d681cdd8c5b" - account-55-keyfile: | - {"address":"b702d04773d6fd3fcc066cf130717d681cdd8c5b","crypto":{"cipher":"aes-128-ctr","ciphertext":"2684f7913301c183e9ebaea9c6b22a8458dc379230828b1f8e058f5f0d0538e8","cipherparams":{"iv":"58a5a5f10bb9de8b2bcc44d4a592e742"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c538a8c893ac897c86e7d52346939d8870d7d7b4dcff07472584e28925607002"},"mac":"0cd5e986f11b77c221de5a7eae6b53a8677c3f99e10bc37b78aa730c878ef251"},"id":"accc72a3-94c5-4387-bca4-20b4604e282e","version":3} - account-56-address: "0x5b53745ce2f533aa05e4c14e33daec411d9576fe" - account-56-keyfile: | - {"address":"5b53745ce2f533aa05e4c14e33daec411d9576fe","crypto":{"cipher":"aes-128-ctr","ciphertext":"30bce9457eeaf58caad23ef9b8927fa0642d134b646d1af68543719297d4b69e","cipherparams":{"iv":"be232415bf3d22445c62209b80bddaec"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a62b09281a0dca289794b5a2f4c66eaec82f662a595642fb9cb4c0eb4eb4a882"},"mac":"d6e3415a1cedfbb30149da5fff5efc4bf2b9dab3a806ec1713696a6ef674af42"},"id":"46dbb35d-8f94-48aa-90b0-e753e5ba8d1a","version":3} - account-57-address: "0x3bd569b9a3172991bf15ceb318a170ca7923b737" - account-57-keyfile: | - {"address":"3bd569b9a3172991bf15ceb318a170ca7923b737","crypto":{"cipher":"aes-128-ctr","ciphertext":"9703c10e4dea5629caedfce60402754aabbca973620cf97e5d25d585ef8ad760","cipherparams":{"iv":"0b38e6aa34aa407caa6f1953ae0044af"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8ba3e8a4aec494c64d64f7a2ec079ff0fdd6a8df037ddb48be4653e83b1eaeba"},"mac":"ded691b6da2d227521f957b1ce28334ffd0b26f31048f3eaf6387d47103f6143"},"id":"103a3df5-c613-49ed-9f0f-e0d1ee2a970d","version":3} - account-58-address: "0x4ae4ff81fbdb6ff6aa7a71ade1fe735023bf55cb" - account-58-keyfile: | - {"address":"4ae4ff81fbdb6ff6aa7a71ade1fe735023bf55cb","crypto":{"cipher":"aes-128-ctr","ciphertext":"2ba7fab603a489fa2bc1c0d071935506c2ce9fd1601eeea6c22f741a8648188c","cipherparams":{"iv":"f009814e69b32dfd2371bb1d6e225a56"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"240a234eb77fd58b3c3c4fad0353c5ccf585b0e71ebd1686de765b09e432ffcf"},"mac":"84eace34590bc58240e43b5d9f49bcffc08001a298decb0d68b1d89442a6ed34"},"id":"c59c8874-3af8-4471-aef4-11c5cf78937e","version":3} - account-59-address: "0x215c9275417fc94ad2d1ef368e126b968772fb27" - account-59-keyfile: | - {"address":"215c9275417fc94ad2d1ef368e126b968772fb27","crypto":{"cipher":"aes-128-ctr","ciphertext":"6b2ec8e75ee8d241efbee1f9cfee8b6f74b8b63149dd5e929e7a978cb8c04423","cipherparams":{"iv":"c7efc204d57096631de9a5bda34ef976"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c2f7467c65836dfad77a24b7fb165b02eb000b482b82bccd1f1ff1ae3123db5b"},"mac":"bb6ae1c04e14809f959e9fdb7ae687a253bb5c5b35b23108211f05334a5d25e3"},"id":"f7dd8579-b8fe-41a9-b9d7-ef5e0dfffa7e","version":3} - account-60-address: "0xc2a93865f2451455174e4af0757be4ae0eb4efad" - account-60-keyfile: | - {"address":"c2a93865f2451455174e4af0757be4ae0eb4efad","crypto":{"cipher":"aes-128-ctr","ciphertext":"8f7483ccb88887aa5655a8c096d2d59de1f777519c5626b0eefbabc4f5803993","cipherparams":{"iv":"e55fadab5162533fe0ebd5ac1d08f910"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"85fa26e6465a2ab8ab86372e03bf79fc1b59ed43374788cba88250260a04bc0e"},"mac":"1483b790a0980058e0e355aa34a6351aa36cd3a0fb4f276096524fbe9505ac2a"},"id":"38acae56-9673-4039-ad75-bf30116c1904","version":3} - account-61-address: "0x5d3f80feed09f0013afd5e2e77f0c96884c4cbaf" - account-61-keyfile: | - {"address":"5d3f80feed09f0013afd5e2e77f0c96884c4cbaf","crypto":{"cipher":"aes-128-ctr","ciphertext":"7e78e1bf39a76911137ab59dbb7a560227e26341c2f8e8a1fa5a67992247d75c","cipherparams":{"iv":"b1d41597e2fe5c24b167e4714d4c7934"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c638f717664c2c466bf1f2c7a4f1c46afe65ba2738413791c2682da3291f21f3"},"mac":"db20b371940937f885eb4af4dc825a6c77846a43f8440ce11be2712937e1033c"},"id":"58bedcfc-42d0-4f49-8a7e-26407e029d5a","version":3} - account-62-address: "0x5fc57afc4779bfc73cf9df11e243c50c8cbaf2a4" - account-62-keyfile: | - {"address":"5fc57afc4779bfc73cf9df11e243c50c8cbaf2a4","crypto":{"cipher":"aes-128-ctr","ciphertext":"fb91c015451108783ef7dac55abbe937aa96d2487dc26d358cefdb72700b4075","cipherparams":{"iv":"8669788d98dbf78c65a6aeec3efe62aa"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"56f5c472a98dbdbef2710f7738f7d589bc636e307088e8f294f93f2567b7f5fd"},"mac":"8021f4f71c984e53638b167a0e7c7306aec581a079fe62995562957f6b9e8b1c"},"id":"ab4f6cb5-8c46-4534-bfde-363e5792226f","version":3} - account-63-address: "0xec4e9ccc33a28d3c6adbdf80200fa97a38f4d3ce" - account-63-keyfile: | - {"address":"ec4e9ccc33a28d3c6adbdf80200fa97a38f4d3ce","crypto":{"cipher":"aes-128-ctr","ciphertext":"c331c31c1f538db1a6350818c957ae041bef5aabfdff7ea85e72188b73bee9ed","cipherparams":{"iv":"b4f93453327bcfb6c3bb6c2b75abea62"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"2bc9b74d70786be2faf2212d14088876ab1407e89823ff471ca2c8fd038e4e57"},"mac":"99fd049b79450f467a7683f15ab6ecd0d1b371f06f95adc8e398db25109cc7c2"},"id":"2b7bbce4-5ecf-4206-a10f-5f7f544e5051","version":3} - account-64-address: "0x8d516f40ab30bb6c20df8feffdb888349234f015" - account-64-keyfile: | - {"address":"8d516f40ab30bb6c20df8feffdb888349234f015","crypto":{"cipher":"aes-128-ctr","ciphertext":"9fa794a83d6e3bd164b5edcdb1237db7bbac00e1540567568f90de825752b318","cipherparams":{"iv":"0fefa2313ecca5184cfce818ce99ecb1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"70d30be31c3d4b303087a72a25a065aa36ae779da9d8b24a38764f26adadd6e5"},"mac":"f157e4aa072a2fe454ed02f626397b190aba6a62b1c5e170d1c10962ae5c2b32"},"id":"ea7dea45-6fa9-4d24-83ea-5fd5ba4877e3","version":3} - account-65-address: "0x894242f10b55a0c397cd76cf37913c5ab24ab7b3" - account-65-keyfile: | - {"address":"894242f10b55a0c397cd76cf37913c5ab24ab7b3","crypto":{"cipher":"aes-128-ctr","ciphertext":"6511b0c6b9979996f647a19cbb9f3ce1235416cf54b7c8e1ce53d4e901e09c23","cipherparams":{"iv":"363216dcfe40c53563d4bdb95a1d77e8"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c30dc6044e39e5c4bd50a3bcf3442a24c5da2f4390906bb5f9f9a88f4c95e999"},"mac":"6c7b2ea9e4fe84ba0fe2683e44e04cee7904cdb6b3a539fa254e7a47e78be80b"},"id":"9b45b951-9d89-4df5-8641-b248a45e47e3","version":3} - account-66-address: "0xbb78ce192add31a8335e381c52a88175bcd0798d" - account-66-keyfile: | - {"address":"bb78ce192add31a8335e381c52a88175bcd0798d","crypto":{"cipher":"aes-128-ctr","ciphertext":"8118b9ed9cb3abafd22cc246b69b64ac2f8b59dd2176269953573dba386bfd5d","cipherparams":{"iv":"b5c98ba464b6fde0c2d0d3bb66a59252"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"429360476c31d58f17cfb3967521950307fba6ad7977cf95b0cfb125e8339c9b"},"mac":"ac94c759481ad7f08d3641466606d0fd79bc670267eb3d567f0228bfa874bb0b"},"id":"16ce236b-f8dd-4c4a-a9ef-3ca23602e3bb","version":3} - account-67-address: "0x64a7d3084d8ae4c6d3a20758bcb1248d5293dfed" - account-67-keyfile: | - {"address":"64a7d3084d8ae4c6d3a20758bcb1248d5293dfed","crypto":{"cipher":"aes-128-ctr","ciphertext":"dc87d6983081339a5ecba864fb13fbf4edc9f639a52cedd224f76b21d7ffb430","cipherparams":{"iv":"5cd51cdaec7461eaf965e39ae0afbb7c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9415ce9074d492bf5fabe91cd23abb3511ec60da442ed43b0e99935d4d00762f"},"mac":"622809350d55d7477c9b69ae695ff75c9c5218a5c251790855fdb152a15007d8"},"id":"c4f0c5b1-cd42-47b7-87d6-8a7d587d8a46","version":3} - account-68-address: "0x21382f74a6ada375682a4282614d6004a167de7c" - account-68-keyfile: | - {"address":"21382f74a6ada375682a4282614d6004a167de7c","crypto":{"cipher":"aes-128-ctr","ciphertext":"86e3a1609dcab1c0b6680f21370d73b007a92b11fed8f4217183238275829d48","cipherparams":{"iv":"0584f079c8304bc93436e74b956de051"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9a7e5ff576c26d892f0860ce3453b51b398f470b1003f044eaa15c0e27e1107f"},"mac":"9456e2c7a6b871bd96721adcc9251ac019a82d9b01482fabe797b580ed27837a"},"id":"70a6e86d-3317-49af-adc1-e4fd808a2220","version":3} - account-69-address: "0x9bed51d95f77b29eb07e2a720679c48849f1df0a" - account-69-keyfile: | - {"address":"9bed51d95f77b29eb07e2a720679c48849f1df0a","crypto":{"cipher":"aes-128-ctr","ciphertext":"c59e3729680aec16cf52f04adcd7f94fbdadc492b60fcdfec77d23e8725284f8","cipherparams":{"iv":"4f93a8d6746444fa34040ff1aeebed38"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d9e4a4dbffa3f0dc36848615f23a00cfd8c894d4d961a3a3db12d2cd2cf1c6ca"},"mac":"40f0bc17eee3907e387e8a9c0a4bfd0e28438e02788c1cb8fd56a5558dfdaba1"},"id":"da3a8d8e-0895-4ca5-a44d-15696b2861da","version":3} - account-70-address: "0xccb77a8dacab6b6e1fd26dfc0b893561e05ff73b" - account-70-keyfile: | - {"address":"ccb77a8dacab6b6e1fd26dfc0b893561e05ff73b","crypto":{"cipher":"aes-128-ctr","ciphertext":"c0a1a71dd1e46b1e1bc71c3d8b34d7f8823276b2af31d807ff435a234f5e9209","cipherparams":{"iv":"7cee0d70fc20292617ad2788f5920452"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c1979e98bd2d9b05d5537e76b231ab7ac76fbbe594f21f6d8a3fc6873a4cc747"},"mac":"47558de5c3b6ed74e810f1b79e557fe96f3fc6d3ade0c9598d43f01aedfdd021"},"id":"8eb31a8b-efb4-4f50-a54b-fd2083e57b4f","version":3} - account-71-address: "0x223f7abce664d1b2619c8684341ee276272dca40" - account-71-keyfile: | - {"address":"223f7abce664d1b2619c8684341ee276272dca40","crypto":{"cipher":"aes-128-ctr","ciphertext":"f886f710a91fe2bd208fab8c3685513a014573f18a65ac516f42b25102d70f67","cipherparams":{"iv":"381155b50b2011d01382a54735806b0e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e6026f95bf36ae607c5cdb0981a108f1d8854f7ce043917c882f17c4c9825b5a"},"mac":"269e86b5811341c572919ac18b09da22869400febe987fccb399232413a22fac"},"id":"f343ae86-3e9a-46b2-b5ec-9dce41aea78e","version":3} - account-72-address: "0x92a50483402886dd2d3e6bb07b77940560779c49" - account-72-keyfile: | - {"address":"92a50483402886dd2d3e6bb07b77940560779c49","crypto":{"cipher":"aes-128-ctr","ciphertext":"66ab4264c34151cb6a179cb2e4d580226fbf75b11a667aa19adca07d1d91810c","cipherparams":{"iv":"1d635cdb883e16b078f1ec1c27b1d115"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"19183df4866fd2127d274458629dd1f927d72a81f017d8854a57c08abff65425"},"mac":"384722f9a9d0a0a5dd03bff95dee16b43a0254dd8f2f6dbe0cfe0d0b09846b2d"},"id":"071a8cd4-d071-417a-a31f-58e4f4306552","version":3} - account-73-address: "0xce7ef6140350acb104950540286fd082e25ca806" - account-73-keyfile: | - {"address":"ce7ef6140350acb104950540286fd082e25ca806","crypto":{"cipher":"aes-128-ctr","ciphertext":"02d2406eeec8bcbbce92d4ce275fc48fb7ec71ae96a573f566197ba561f0722e","cipherparams":{"iv":"3742490d6e33b3512fa63e9edd1bb017"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"41f95626f96902d191ec2f8098b2297190df996979df04f1ad589bfe9b3dba6a"},"mac":"b456ef0f326271aefd674142333828725ec12726e97fd407118792a9554e8e08"},"id":"69fe139a-c451-450f-8197-f1a6a8a0ab24","version":3} - account-74-address: "0x89c46eafe8f81bd3a804f6d8534e1771a69baaed" - account-74-keyfile: | - {"address":"89c46eafe8f81bd3a804f6d8534e1771a69baaed","crypto":{"cipher":"aes-128-ctr","ciphertext":"d4d513750fcd7ecbd54a00cd8dc6d87eda8b5fc381901723998b62002d5f4c3d","cipherparams":{"iv":"80b11dd856e9fcca928468ae85da4935"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"90a712a2db8546175b1fe41725ba5d4db25cfec87a61c42b9b84f7c50cb95844"},"mac":"e149a029453e0300e8f7b0682030e6ee7c079f1b782f4f7af5d165bf7775a466"},"id":"b28aeec7-1ad6-4547-a03d-f820a3e1416b","version":3} - account-75-address: "0x8569ec5e73c66b40b43b9f39cc35f258aa74b06f" - account-75-keyfile: | - {"address":"8569ec5e73c66b40b43b9f39cc35f258aa74b06f","crypto":{"cipher":"aes-128-ctr","ciphertext":"0b4a4bf5b62906bbbf8e843e1d13e95a0877a949ad504be9bdee179a01aacf67","cipherparams":{"iv":"99b66d57aeadafd2aa0d9f77c7a1042c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f556f7b630ec1cd981600e251ab25fed30cb268ab237c21159a820d44490fca5"},"mac":"96e6539d139d18cb16d47cdd2cd54be6b51fbd360eb113e53d0de82ef1efa207"},"id":"c5412766-de1c-411a-9282-0594849c3167","version":3} - account-76-address: "0x71dae2f3a3f58dd58d4687d1d1d94f6947571f08" - account-76-keyfile: | - {"address":"71dae2f3a3f58dd58d4687d1d1d94f6947571f08","crypto":{"cipher":"aes-128-ctr","ciphertext":"de99d17c98decc84f9470dd1bf569a01f3d5ae1d07dfc76f52ed3ce671f8f934","cipherparams":{"iv":"55c327788f1c997f94f770bc282f769a"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"41f38e89e3a8f9530f0d0e050b3e8d3e76de1cbe9946f7017f1ec80f26932276"},"mac":"07feb0ea3b25ee62c53ee49eb4efa34de82a4aa0968a80341ba24c0b891a5269"},"id":"84350a8b-e90b-48c5-8283-1d386eb0a056","version":3} - account-77-address: "0x8914c585b4f825a7a4ee67208e34d525fa3b90e4" - account-77-keyfile: | - {"address":"8914c585b4f825a7a4ee67208e34d525fa3b90e4","crypto":{"cipher":"aes-128-ctr","ciphertext":"0f75741ed9782d1fe9e08621a07476f7347bfc6e9cff579e40f9475f1554c8f7","cipherparams":{"iv":"bd59462bae3702004f98a58cb2e994bc"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"61dfcadb00239f8c6550f1169309cd3a76eee047b342b228ccb54a3bb25369d2"},"mac":"1e610cbd9a19dfeb86f13fa46e925b9933af65a7658e74f468307840c36284ef"},"id":"3f6e9bee-17c4-4b0a-b9cd-eeb253883dff","version":3} - account-78-address: "0xf9b978d9c0b253638368230a0a0efa1c811df371" - account-78-keyfile: | - {"address":"f9b978d9c0b253638368230a0a0efa1c811df371","crypto":{"cipher":"aes-128-ctr","ciphertext":"ea5a9a95453b2a750422a70fc043cbc15c8f9a300d1f9cfd217a9fda2ae7e83d","cipherparams":{"iv":"b40075a44e73a49629c26bd10e0fd214"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"6a596deb5b2b038823d1d5afa822bfecef01cbc61a975bddc71dbf403e0a7926"},"mac":"bf7bf1426186753fe6ab77ddfb2d18f4a972b1d37387a47107188494fa74a1d0"},"id":"7ae256fb-cc81-454d-a618-f2d01812285d","version":3} - account-79-address: "0x696738ce105743a2950a7729bad66c8cda78552d" - account-79-keyfile: | - {"address":"696738ce105743a2950a7729bad66c8cda78552d","crypto":{"cipher":"aes-128-ctr","ciphertext":"6b8951356ca1da0823cc99eb71178e24075d825b6c29a271353950014fddd04a","cipherparams":{"iv":"7c78bc250d7e55ea9898b82d1addbaa9"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a21c7658e0c8bbec363b5f3c06c589bfb6d3072a4570ce415d16093803d221ea"},"mac":"9f68670f36de4c9f6e3bfc4a6518c0c1583cd2121d9a84bbafab06d29b6d572b"},"id":"85c9ce46-1873-4269-bc61-2f664077a25c","version":3} - account-80-address: "0x5c266973ee88a23ca96cc4d429d8d386f32d7f23" - account-80-keyfile: | - {"address":"5c266973ee88a23ca96cc4d429d8d386f32d7f23","crypto":{"cipher":"aes-128-ctr","ciphertext":"69a85a32347d603e868609a7a7ad12dee2075f93e66061da38d13aded91579ae","cipherparams":{"iv":"32f071510a15e09b2e3556f4c871cfdd"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"23ede4763eaf0e76509b7c7b5360cd59ffe6b93f07ed56db6263694faf01f275"},"mac":"548c1cb13b0ba43684d3514216b9a5ce56aa46b170a23067079687def80b73ed"},"id":"a2527fe2-a54b-4838-bca4-886bf552fa5e","version":3} - account-81-address: "0x86022171aaf9657766d543b06bc30e7f2086acb9" - account-81-keyfile: | - {"address":"86022171aaf9657766d543b06bc30e7f2086acb9","crypto":{"cipher":"aes-128-ctr","ciphertext":"a61c1f7f95b85e0dc57d868c290755b3ac53f6a24bebe9621aabcfa68155329a","cipherparams":{"iv":"d8020d609f33cfe284c1804421adefcb"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"69371f68c0ac65c5ba4348afea6f56a9ecf346055d1dcc7d7251bef0fab107f9"},"mac":"150680f2207235d67d507255aaa5c648794ce22d3b910657c27a4c8b422271de"},"id":"17f7d6d1-3640-4165-ad95-728bfa8490d8","version":3} - account-82-address: "0xb5e467d166cd1d1da2f9345c5ef3e81f07b73b53" - account-82-keyfile: | - {"address":"b5e467d166cd1d1da2f9345c5ef3e81f07b73b53","crypto":{"cipher":"aes-128-ctr","ciphertext":"1842ee9cc83ba9d21fd78873a60be88e869e9b24558c6e03ae64effe8dfe07e6","cipherparams":{"iv":"d6f643913e8431d142c4913efeac1d23"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"5d527570bfcd94a8bc10786e8993aab069327f5974632cadf15e5a087d42bfa4"},"mac":"4546d24f7b0f8b685b05c710f5c081102156faa5ed9b0f14990e76bbd25d1d87"},"id":"a6694e0a-8353-4579-b654-7b8f03a2ee0d","version":3} - account-83-address: "0x84d7da05d5dc1e47b0c5075c7ff456d0d3be1424" - account-83-keyfile: | - {"address":"84d7da05d5dc1e47b0c5075c7ff456d0d3be1424","crypto":{"cipher":"aes-128-ctr","ciphertext":"9580c625b60f77e502dbf594cf6efd96b3de9acd4e9ca914fb01c8ddca40fcbe","cipherparams":{"iv":"08fad98bb032eff831db7364ae4b24b0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ce77b1999c25b29fea7bed481899d7e7cc884fad7d2a830eaf9fd435c3b26b09"},"mac":"366b7834b51e9fc651cc19e76411289e8045e588ee750b63b49b69f92b8ca034"},"id":"a7a45c88-f8bc-4f5a-bc10-c11f506bc88a","version":3} - account-84-address: "0xbf4d20f0a40aa1627c4dc03e2eca26dc76cdcb2f" - account-84-keyfile: | - {"address":"bf4d20f0a40aa1627c4dc03e2eca26dc76cdcb2f","crypto":{"cipher":"aes-128-ctr","ciphertext":"2819591308a416302380fd0757d50075acacdee56fd76cdeb3227a678ea5623b","cipherparams":{"iv":"9a32860b243a185d2d7bd537227231bf"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ca6c84dc59a9f6678630ef67761ea1be3429234e06e4094a163b36b22a3745f2"},"mac":"589e1ef0daea168f8c1d50ae26ffb6a9245e90d43480773304cf101a679cc24e"},"id":"c6f5974a-cf96-4277-8f4d-7e52fbd29f0d","version":3} - account-85-address: "0x891b55a7559147f2a141d34ff9e002d9e93dc519" - account-85-keyfile: | - {"address":"891b55a7559147f2a141d34ff9e002d9e93dc519","crypto":{"cipher":"aes-128-ctr","ciphertext":"8854e8ea8f8c1b766f528c6acfea778c0f1f571396a181dba5d1644d1986a3a1","cipherparams":{"iv":"91c4ad32acbb3edd8a597aceea0576f1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e5060339906fd09e283793007c56997da5123f948f799d0501f35d1c9cdabb65"},"mac":"8e6b22ef3e505930e16521436cb51e21199a4318fc915608798384d6b9ae6377"},"id":"ccf81ae0-0991-4dba-acd9-120f27b24d92","version":3} - account-86-address: "0x14fcea2a1305a4c71c02c3d57fe1e8f77a6b57d3" - account-86-keyfile: | - {"address":"14fcea2a1305a4c71c02c3d57fe1e8f77a6b57d3","crypto":{"cipher":"aes-128-ctr","ciphertext":"1736a5db71803485def5be896784ac57d3c27c0d911be68440a36339fc0f4045","cipherparams":{"iv":"231f64338fb2a9fbf896a7a117b23bf3"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"26eb1e24f4c5259fbf7ffff00f6a750c6a59baa6e9ee5be2df01b2782b4554e3"},"mac":"0ef57e90c814f380a897d04ae2c884f0798ba3d96af73194d17b30f7d1c19ee4"},"id":"84ea004f-5c50-4cf5-b99d-dc8c88f66500","version":3} - account-87-address: "0xfd7e16d89be981db1db54e2605ac59552f7ef5f2" - account-87-keyfile: | - {"address":"fd7e16d89be981db1db54e2605ac59552f7ef5f2","crypto":{"cipher":"aes-128-ctr","ciphertext":"0775c7eeec8767f4a9f12fcb74edf333ac7fc6875f936b6b4bd61611418261ef","cipherparams":{"iv":"4237ca78789d4576970e40ac71ef157c"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"50ca92eacfbdc9148c787b9254fd29ef23afa6f0b617f6a38a051e902983aecb"},"mac":"393ce842535a3e3195d000a0ddc888e1b97e67477d7775fdb46a028d491d893c"},"id":"8f262b20-3f77-442b-8b47-29747730696b","version":3} - account-88-address: "0xe4034b30e70643f6216cd7f6ffec1f5bab818bae" - account-88-keyfile: | - {"address":"e4034b30e70643f6216cd7f6ffec1f5bab818bae","crypto":{"cipher":"aes-128-ctr","ciphertext":"3e651dd0f290f0a4620651d27b592f7db22b1ee2de832fba84f20bbe0feacb31","cipherparams":{"iv":"c405b5d329f7447160a8539ec61b7fb3"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"eae27b86b0180f25ad07fb2dbd973ed01ba1ccf4e4a2315304fb7d87dd31f4f1"},"mac":"35eaa69de5f9747ed83022f8628c6df8c8f63d13a8e963895f821c986f969317"},"id":"8a89a93b-fe62-454f-b0cc-7c94ca104a54","version":3} - account-89-address: "0xc1fd92ca4cd3634441b36966a7bb767fad88a1c8" - account-89-keyfile: | - {"address":"c1fd92ca4cd3634441b36966a7bb767fad88a1c8","crypto":{"cipher":"aes-128-ctr","ciphertext":"a14f021f73bee0479513d6c2781cceb982338921cc55bc460f61ded42bc4c42d","cipherparams":{"iv":"fd3c6f7c6eb730cde485ce772dddf6b8"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"43aab3b1b44288edfeabbd3d6c166d839dcaa98d4c00cd04006745c07cc81025"},"mac":"32c1ce100f68e525c44c22c1fc49bf3ea8af2459d946694bd9f3648f34e17bed"},"id":"8b8ed472-33d6-4d8e-8f5c-1085a4bf8dab","version":3} - account-90-address: "0x5d41ff2b042c89dcb79570f02971d3e7b449d7c0" - account-90-keyfile: | - {"address":"5d41ff2b042c89dcb79570f02971d3e7b449d7c0","crypto":{"cipher":"aes-128-ctr","ciphertext":"980613ae4416039559a23859fa6c25bd6db15543616642273358e0c044b19aeb","cipherparams":{"iv":"04163f5d2bb38534cc45242677354596"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a348a527122195899075b512dda50e6372f55f2db341b399b6a845eccda3be9d"},"mac":"3c8b5f1c76f6f9f673f6ca33251a2e48e1c43f7e30f80d0ca523f7a7374a0593"},"id":"b495206d-5491-44db-aba1-f2de9aa2a88f","version":3} - account-91-address: "0x730c8670a01faf3a70a2788e16b2815c2b34db37" - account-91-keyfile: | - {"address":"730c8670a01faf3a70a2788e16b2815c2b34db37","crypto":{"cipher":"aes-128-ctr","ciphertext":"ddfdd1ad75e07c6d64208c5a30d982c1d6bfb3083dd6ebbbe27fd441630f5052","cipherparams":{"iv":"c5db1b2ac5992f0b71a4e54d48b590e0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"7f5ff78423b37352388fa4c350222efac03c971ea0f5777d4f9f18aca66fa9dd"},"mac":"9e6d432edcac88054a714bb01b417635310308f0411d4a5897358bf79e12dfc5"},"id":"fdcf0d0f-c526-4d56-a2b9-894fbd80ec34","version":3} - account-92-address: "0x285fc27d49de57755e8040bbfcd141c13c5eb25a" - account-92-keyfile: | - {"address":"285fc27d49de57755e8040bbfcd141c13c5eb25a","crypto":{"cipher":"aes-128-ctr","ciphertext":"ca6d748e1277b16bcea91ffd4856434d27391b29910feb395e7d8fae41563b8d","cipherparams":{"iv":"07452b57375de5777f5921e72b129b6d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"3d50f3d80f23051bf47b9504363c2b00347895198ce340b72340765dc0da7797"},"mac":"e998499876157511e7f94e8d239b10e2d7c90c5b0b517388b5bacb94265011d0"},"id":"1a239bdd-40df-4d2a-9aa9-00d648a10b76","version":3} - account-93-address: "0xf68e5f768d607280f1eab153ae6f27a021e33140" - account-93-keyfile: | - {"address":"f68e5f768d607280f1eab153ae6f27a021e33140","crypto":{"cipher":"aes-128-ctr","ciphertext":"6db5e25c05a2f28d6f5dabb16d51490b624d88e500cb5875e5378159a0a42fd0","cipherparams":{"iv":"2fc32736a73dc0c478897d97ed945ae9"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"0089d0a3c0699b76895a933c9e7c0ed03a1d31c2a0ab6c60b54bb0d60f204246"},"mac":"387163a78e8ca8b3ce953a11d990e0f58e850d834f058c90e1dcb7802c2214a6"},"id":"6cab197f-f523-4303-a8df-9d65bdb24625","version":3} - account-94-address: "0x1045581987377137a4be69c90744ad7cc486515d" - account-94-keyfile: | - {"address":"1045581987377137a4be69c90744ad7cc486515d","crypto":{"cipher":"aes-128-ctr","ciphertext":"5f726d6e6db0749e710a9fab3fe6c9a99d7e6a07fc83798d874611dc97952bb5","cipherparams":{"iv":"428c9bdd8da8fb0a2a703e8e9fc53ec0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f4c95b732c6b56ec9a388d8b974114bfeb5458e7c69ade4c742124e7e739783e"},"mac":"9996afcfe2ae5aec92f67045d0aa9acbd3847bb6fb20e1c59d36d5f0202b1001"},"id":"12533915-5e94-432d-9810-f508dd4af6cd","version":3} - account-95-address: "0x4fd6f76407a7f85eebca41d3e4571cf7414a0b5f" - account-95-keyfile: | - {"address":"4fd6f76407a7f85eebca41d3e4571cf7414a0b5f","crypto":{"cipher":"aes-128-ctr","ciphertext":"e0982a2226cdb00e7e3444446370b9a7a7c2e45d6313088c532ee53f077b4f46","cipherparams":{"iv":"4250e985fe85284dc522fef832b7f3dd"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e883148a3b6bf773340f50329f62d9020e63422b6431d4ba951680b3590ede4e"},"mac":"e451c909197fd5b1774de330f4c54c68d2c0822bae6a0a9b1cf61464774caf0e"},"id":"b398cf6a-2fff-4ddc-a02e-9845f4f58845","version":3} - account-96-address: "0xbf0f3c029a8d7e57be721364bb0a43b61713918f" - account-96-keyfile: | - {"address":"bf0f3c029a8d7e57be721364bb0a43b61713918f","crypto":{"cipher":"aes-128-ctr","ciphertext":"b06bb050b83d2255339880e34305c57500ea834ce5179575b52fa46ef918dd2f","cipherparams":{"iv":"72a45f7738ad0e0d960e97a16b6331c6"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a2d78db2608ec7081f373f99b2e2687efd4b90b9a2bad5ca13f22b5953b2d53c"},"mac":"c332f10204f770dd919dc2f0213c423730713855e8922b1e49626f3fa78ef54d"},"id":"f1e14e97-aa69-412e-9c1e-32ec189b75a0","version":3} - account-97-address: "0x28cc0f2b7398680b436a12ceca5cfef220640879" - account-97-keyfile: | - {"address":"28cc0f2b7398680b436a12ceca5cfef220640879","crypto":{"cipher":"aes-128-ctr","ciphertext":"aa6221589a8ad6254689726f3faa9ec7125bc0b8a26341591e4b86b16252c54c","cipherparams":{"iv":"3c655a548f02e27b42229673df37dc75"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"3515fa07e956be86a5090d579b1665d7ad74e06fedadc182dc9828e4b778887f"},"mac":"0b88b449867167af54414d6816b84009856e211f6551e9353c5ebb7d16d47e28"},"id":"89f4adf5-28f1-4dfc-bf98-064134c74c5a","version":3} - account-98-address: "0xf3254b90a0a771447037b627e355284bf0f4788f" - account-98-keyfile: | - {"address":"f3254b90a0a771447037b627e355284bf0f4788f","crypto":{"cipher":"aes-128-ctr","ciphertext":"7267877cece6c3efa2b7a7345184f36f4d7d1b2644eb48a541698296a3382cb9","cipherparams":{"iv":"382714a5e993f2daa6fea11c2974b159"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f0c6ea1559ac59892c607daeba0c0caf8c68076b88e418177f58127200003448"},"mac":"a9ab2bdaf63cd8b6e3100e847bae33b01902bb1141579b8b1725dbc6e3ed33f1"},"id":"03ba879c-49ff-4056-84ce-890bd4ab20c5","version":3} - account-99-address: "0xd237c76e6902f71da0a66a8f5583d25cc64add6f" - account-99-keyfile: | - {"address":"d237c76e6902f71da0a66a8f5583d25cc64add6f","crypto":{"cipher":"aes-128-ctr","ciphertext":"3564f11313280f9b98a4f3256e19e0bff4c5d92a195a01204d33def1b372855b","cipherparams":{"iv":"a36914ce8d6028ed92450e9ff7ef69ce"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1cd4244a2f8e96b67e62e81c83ee23e351427393950e509082138d7afb2f31e1"},"mac":"3ab600b5195143af4966f07f96ae4f96d99cc991b6d8538267c91fe6b171a188"},"id":"c86ab6c0-12d4-4e10-ab28-30e2af708571","version":3} diff --git a/infrastructure/kube/keep-test/geth-node/eth-goerli-node.yaml b/infrastructure/kube/keep-test/geth-node/eth-goerli-node.yaml deleted file mode 100644 index 289b360a12..0000000000 --- a/infrastructure/kube/keep-test/geth-node/eth-goerli-node.yaml +++ /dev/null @@ -1,89 +0,0 @@ ---- -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: geth-goerli -provisioner: kubernetes.io/gce-pd -parameters: - type: pd-ssd - replication-type: none -reclaimPolicy: Retain -allowVolumeExpansion: true -mountOptions: - - debug -volumeBindingMode: Immediate ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: geth-goerli - labels: - app: geth - network: goerli -spec: - replicas: 1 - serviceName: geth-goerli - volumeClaimTemplates: - - metadata: - name: geth-goerli - spec: - accessModes: [ReadWriteOnce] - storageClassName: geth-goerli - resources: - requests: - storage: 200Gi - selector: - matchLabels: - app: geth - network: goerli - template: - metadata: - labels: - app: geth - network: goerli - spec: - containers: - - name: geth-goerli - image: ethereum/client-go:v1.10.20 - ports: - - containerPort: 8545 - - containerPort: 8546 - - containerPort: 30303 - volumeMounts: - - name: geth-goerli - mountPath: /root/.ethereum - args: - - "--http" - - "--http.addr=0.0.0.0" - - "--ws" - - "--ws.addr=0.0.0.0" - - "--goerli" - - "--syncmode=snap" ---- -apiVersion: v1 -kind: Service -metadata: - name: geth-goerli - labels: - app: geth - network: goerli -spec: - selector: - app: geth - network: goerli - ports: - - port: 8545 - targetPort: 8545 - name: tcp-8545 - - port: 8546 - targetPort: 8546 - name: tcp-8546 - - port: 30303 - targetPort: 30303 - name: tcp-30303 - - port: 30303 - targetPort: 30303 - name: udp-30303 - protocol: UDP - type: LoadBalancer - loadBalancerIP: "35.238.111.174" diff --git a/infrastructure/kube/keep-test/keep-client/README.md b/infrastructure/kube/keep-test/keep-client/README.md deleted file mode 100644 index 9ece10b201..0000000000 --- a/infrastructure/kube/keep-test/keep-client/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Keep Client - -## Configuration - -### Generation - -Keep Client Nodes manifests are generated with [`ytt`](https://carvel.dev/ytt/). - -To generate the YAML configuration for nodes run `./gen.sh`. - -ytt configuration consists of 3 files: - -- [`template.yaml`](.gen/template.yaml) - template for kubernetes manifest -- [`schema.yaml`](./gen/schema.yaml) - properties with default values -- [`data.yaml`](./gen/data.yaml) - values for generation - -### Resources - -Manifests for `StatefulSet` and `Service` for all the nodes are generated into the [`keep-clients.yaml`](./keep-clients.yaml) file. - -A node manifest reads values from following resources: - -Config Maps: - -- [`keep-client-config`](./keep-client-config.yaml) -- [`eth-account-info`](../eth-account-info-configmap.yaml) - -Secrets: - -- `eth-network-sepolia` -- `eth-account-passphrases` -- `eth-account-privatekeys` diff --git a/infrastructure/kube/keep-test/keep-client/gen.sh b/infrastructure/kube/keep-test/keep-client/gen.sh deleted file mode 100755 index 5f9cdeca85..0000000000 --- a/infrastructure/kube/keep-test/keep-client/gen.sh +++ /dev/null @@ -1,17 +0,0 @@ -#! /bin/bash - -if ! command -v ytt &> /dev/null -then - echo "ytt could not be found; for installation instruction visit https://carvel.dev/ytt/docs/latest/install" - exit -fi - - -ytt \ - -f gen/template.yaml \ - -f gen/data.yaml \ - -f gen/schema.yaml \ - --file-mark 'template.yaml:path=keep-clients.yaml' \ - --output-files . - -echo '# File generated with gen.sh - DO NOT EDIT' | cat - keep-clients.yaml > keep-clients.yaml.tmp && mv keep-clients.yaml.tmp keep-clients.yaml diff --git a/infrastructure/kube/keep-test/keep-client/gen/data.yaml b/infrastructure/kube/keep-test/keep-client/gen/data.yaml deleted file mode 100644 index 47dc4919f1..0000000000 --- a/infrastructure/kube/keep-test/keep-client/gen/data.yaml +++ /dev/null @@ -1,22 +0,0 @@ -#@data/values ---- -clients: - - id: 0 - publicAnnouncedAddress: "bootstrap-0.test.keep.network" - staticIP: "104.154.61.116" - - id: 1 - publicAnnouncedAddress: "bootstrap-1.test.keep.network" - staticIP: "35.223.100.87" - - id: 2 - - id: 3 - - id: 4 - - id: 5 - - id: 6 - - id: 7 - - id: 8 - - id: 9 -initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest diff --git a/infrastructure/kube/keep-test/keep-client/gen/schema.yaml b/infrastructure/kube/keep-test/keep-client/gen/schema.yaml deleted file mode 100644 index 6660088a9a..0000000000 --- a/infrastructure/kube/keep-test/keep-client/gen/schema.yaml +++ /dev/null @@ -1,14 +0,0 @@ -#@data/values-schema ---- -clients: - - id: 0 - #@schema/nullable - networkPeers: "" - #@schema/nullable - publicAnnouncedAddress: "" - #@schema/nullable - staticIP: "" - stakeAmount: 800_000 -initContainers: - - name: "" - image: "" diff --git a/infrastructure/kube/keep-test/keep-client/gen/template.yaml b/infrastructure/kube/keep-test/keep-client/gen/template.yaml deleted file mode 100644 index 2f5a569566..0000000000 --- a/infrastructure/kube/keep-test/keep-client/gen/template.yaml +++ /dev/null @@ -1,173 +0,0 @@ -#@ load("@ytt:data", "data") - -#@ for client in data.values.clients: - -#@ def labels(): -app: keep -type: client -id: #@ str(client.id) -network: sepolia -#@ end - -#@ def name(): -#@ return "keep-client-" + str(client.id) -#@ end - -#@ def account(): -#@ return "account-" + str(client.id) -#@ end ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: #@ name() - namespace: default - labels: #@ labels() -spec: - replicas: 1 - selector: - matchLabels: #@ labels() - serviceName: #@ name() - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: #@ labels() - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: #@ account() + "-keyfile" - path: #@ account() + "-keyfile" - containers: - - name: keep-client - image: "gcr.io/keep-test-f3e0/keep-client:latest" - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: #@ account() - #! Read secret to env variable to use it as arg. - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: ["keep-client", "start"] - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - "--ethereum.keyFile" - - #@ "/mnt/keep-client/keyfile/" + account() + "-keyfile" - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - "--storage.dir" - - "/mnt/keep-client/data" - - "--network.port" - - "3919" - #@ if client.publicAnnouncedAddress: - - "--network.announcedAddresses" - - #@ "/dns4/" + client.publicAnnouncedAddress + "/tcp/3919" - #@ end - #@ if client.networkPeers: - - "--network.peers" - - #@ client.networkPeers - #@ end - - "--clientInfo.port" - - "9601" - - "--tbtc.keyGenerationConcurrency" - - "2" - initContainers: - #@ for/end initcontainer in data.values.initContainers: - - name: #@ initcontainer.name - image: #@ initcontainer.image - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: #@ account() - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: #@ account() + "-address" - args: - - "initialize" - - "--network" - - "sepolia" - - "--owner" - - "$(KEEP_CLIENT_ETH_ADDRESS)" - - "--provider" - - "$(KEEP_CLIENT_ETH_ADDRESS)" - - "--operator" - - "$(KEEP_CLIENT_ETH_ADDRESS)" - #@ if client.stakeAmount: - - "--amount" - - #@ str(client.stakeAmount) - - "--authorization" - - #@ str(client.stakeAmount) - #@ end ---- -apiVersion: v1 -kind: Service -metadata: - name: #@ name() - namespace: default - labels: #@ labels() -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: #@ labels() - loadBalancerIP: #@ client.staticIP -#@ end diff --git a/infrastructure/kube/keep-test/keep-client/keep-client-config.yaml b/infrastructure/kube/keep-test/keep-client/keep-client-config.yaml deleted file mode 100644 index dde3321712..0000000000 --- a/infrastructure/kube/keep-test/keep-client/keep-client-config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: keep-client-config - namespace: default -data: - LOG_LEVEL: "keep*=info tss-lib=warn" - GOLOG_LOG_FMT: json - # GOLOG_OUTPUT: stdout - ELECTRUM_TCP_URL: tcp://electrumx.bitcoin-testnet:80 diff --git a/infrastructure/kube/keep-test/keep-client/keep-clients.yaml b/infrastructure/kube/keep-test/keep-client/keep-clients.yaml deleted file mode 100644 index d3f284bb8c..0000000000 --- a/infrastructure/kube/keep-test/keep-client/keep-clients.yaml +++ /dev/null @@ -1,2054 +0,0 @@ -# File generated with gen.sh - DO NOT EDIT -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-0 - namespace: default - labels: - app: keep - type: client - id: "0" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "0" - network: sepolia - serviceName: keep-client-0 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "0" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-0-keyfile - path: account-0-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-0 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-0-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --network.announcedAddresses - - /dns4/bootstrap-0.test.keep.network/tcp/3919 - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-0 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-0-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-0 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-0-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-0 - namespace: default - labels: - app: keep - type: client - id: "0" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "0" - network: sepolia - loadBalancerIP: 104.154.61.116 ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-1 - namespace: default - labels: - app: keep - type: client - id: "1" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "1" - network: sepolia - serviceName: keep-client-1 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "1" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-1-keyfile - path: account-1-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-1 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-1-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --network.announcedAddresses - - /dns4/bootstrap-1.test.keep.network/tcp/3919 - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-1 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-1-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-1 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-1-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-1 - namespace: default - labels: - app: keep - type: client - id: "1" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "1" - network: sepolia - loadBalancerIP: 35.223.100.87 ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-2 - namespace: default - labels: - app: keep - type: client - id: "2" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "2" - network: sepolia - serviceName: keep-client-2 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "2" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-2-keyfile - path: account-2-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-2 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-2-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-2 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-2-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-2 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-2-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-2 - namespace: default - labels: - app: keep - type: client - id: "2" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "2" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-3 - namespace: default - labels: - app: keep - type: client - id: "3" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "3" - network: sepolia - serviceName: keep-client-3 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "3" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-3-keyfile - path: account-3-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-3 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-3-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-3 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-3-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-3 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-3-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-3 - namespace: default - labels: - app: keep - type: client - id: "3" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "3" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-4 - namespace: default - labels: - app: keep - type: client - id: "4" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "4" - network: sepolia - serviceName: keep-client-4 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "4" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-4-keyfile - path: account-4-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-4 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-4-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-4 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-4-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-4 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-4-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-4 - namespace: default - labels: - app: keep - type: client - id: "4" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "4" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-5 - namespace: default - labels: - app: keep - type: client - id: "5" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "5" - network: sepolia - serviceName: keep-client-5 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "5" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-5-keyfile - path: account-5-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-5 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-5-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-5 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-5-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-5 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-5-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-5 - namespace: default - labels: - app: keep - type: client - id: "5" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "5" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-6 - namespace: default - labels: - app: keep - type: client - id: "6" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "6" - network: sepolia - serviceName: keep-client-6 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "6" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-6-keyfile - path: account-6-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-6 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-6-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-6 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-6-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-6 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-6-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-6 - namespace: default - labels: - app: keep - type: client - id: "6" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "6" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-7 - namespace: default - labels: - app: keep - type: client - id: "7" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "7" - network: sepolia - serviceName: keep-client-7 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "7" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-7-keyfile - path: account-7-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-7 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-7-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-7 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-7-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-7 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-7-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-7 - namespace: default - labels: - app: keep - type: client - id: "7" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "7" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-8 - namespace: default - labels: - app: keep - type: client - id: "8" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "8" - network: sepolia - serviceName: keep-client-8 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "8" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-8-keyfile - path: account-8-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-8 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-8-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-8 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-8-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-8 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-8-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-8 - namespace: default - labels: - app: keep - type: client - id: "8" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "8" - network: sepolia - loadBalancerIP: null ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: keep-client-9 - namespace: default - labels: - app: keep - type: client - id: "9" - network: sepolia -spec: - replicas: 1 - selector: - matchLabels: - app: keep - type: client - id: "9" - network: sepolia - serviceName: keep-client-9 - volumeClaimTemplates: - - metadata: - name: keep-client-data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 4096Mi - template: - metadata: - labels: - app: keep - type: client - id: "9" - network: sepolia - spec: - volumes: - - name: keep-client-data - persistentVolumeClaim: - claimName: keep-client-data - - name: eth-account-keyfile - configMap: - name: eth-account-info - items: - - key: account-9-keyfile - path: account-9-keyfile - containers: - - name: keep-client - image: gcr.io/keep-test-f3e0/keep-client:latest - imagePullPolicy: Always - resources: - requests: - cpu: 1500m - memory: 512M - ports: - - name: network - containerPort: 3919 - - name: client-info - containerPort: 9601 - env: - - name: KEEP_ETHEREUM_PASSWORD - valueFrom: - secretKeyRef: - name: eth-account-passphrases - key: account-9 - - name: ETH_WS_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: ws-url - envFrom: - - configMapRef: - name: keep-client-config - volumeMounts: - - name: keep-client-data - mountPath: /mnt/keep-client/data - - name: eth-account-keyfile - mountPath: /mnt/keep-client/keyfile - command: - - keep-client - - start - args: - - --testnet - - --ethereum.url - - $(ETH_WS_URL) - - --ethereum.keyFile - - /mnt/keep-client/keyfile/account-9-keyfile - - --bitcoin.electrum.url - - $(ELECTRUM_TCP_URL) - - --storage.dir - - /mnt/keep-client/data - - --network.port - - "3919" - - --clientInfo.port - - "9601" - - --tbtc.keyGenerationConcurrency - - "2" - initContainers: - - name: initcontainer-beacon - image: gcr.io/keep-test-f3e0/keep-random-beacon-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-9 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-9-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" - - name: initcontainer-ecdsa - image: gcr.io/keep-test-f3e0/keep-ecdsa-hardhat:latest - imagePullPolicy: Always - env: - - name: CHAIN_API_URL - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: http-url - - name: CONTRACT_OWNER_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-network-sepolia - key: contract-owner-eth-account-private-key - - name: KEEP_CLIENT_ETH_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: eth-account-privatekeys - key: account-9 - - name: ACCOUNTS_PRIVATE_KEYS - value: $(CONTRACT_OWNER_ETH_PRIVATE_KEY),$(KEEP_CLIENT_ETH_PRIVATE_KEY) - - name: KEEP_CLIENT_ETH_ADDRESS - valueFrom: - configMapKeyRef: - name: eth-account-info - key: account-9-address - args: - - initialize - - --network - - sepolia - - --owner - - $(KEEP_CLIENT_ETH_ADDRESS) - - --provider - - $(KEEP_CLIENT_ETH_ADDRESS) - - --operator - - $(KEEP_CLIENT_ETH_ADDRESS) - - --amount - - "800000" - - --authorization - - "800000" ---- -apiVersion: v1 -kind: Service -metadata: - name: keep-client-9 - namespace: default - labels: - app: keep - type: client - id: "9" - network: sepolia -spec: - type: LoadBalancer - ports: - - name: network - port: 3919 - targetPort: network - - name: client-info - port: 9601 - targetPort: client-info - selector: - app: keep - type: client - id: "9" - network: sepolia - loadBalancerIP: null diff --git a/infrastructure/kube/keep-test/keep-maintainer/kustomization.yaml b/infrastructure/kube/keep-test/keep-maintainer/kustomization.yaml deleted file mode 100644 index 59ad3afb84..0000000000 --- a/infrastructure/kube/keep-test/keep-maintainer/kustomization.yaml +++ /dev/null @@ -1,55 +0,0 @@ -resources: - - ../../templates/keep-maintainer - -namespace: default - -commonLabels: - app: keep-maintainer - # The current setup runs only the spv module as a workaround - # for reasons mentioned in `patches` property below. - type: all - network: sepolia - -images: - # Special maintainer version working with the modified version of LightRelay - # contract (SepoliaLightRelay). Source code lives in the `keep-maintainer-testnet` - # branch of the `keep-network/keep-core` repository. - - name: keep-maintainer - newName: gcr.io/keep-test-f3e0/keep-maintainer - newTag: latest - -configMapGenerator: - - name: keep-maintainer-config - behavior: merge - literals: - - network=testnet - - electrum-api-url=ws://electrumx.bitcoin-testnet:8080 - - redemption-request-amount-limit=0 # Use the default value - files: - - .secret/keep-maintainer-keyfile - -secretGenerator: - - name: keep-maintainer-eth-account-password - files: - - .secret/keep-maintainer-password - -patches: - # Testnet's maintainer shouldn't run `--bitcoinDifficulty` module, as the testnet - # uses modified version of LightRelay contract (SepoliaLightRelay) that doesn't - # require the bitcoin difficulty to be submitted. This patch defines manually - # which modules should be started. - - target: - kind: StatefulSet - name: keep-maintainer - patch: |- - - op: add - path: /spec/template/spec/containers/0/args/- - value: --spv - - op: replace - path: /spec/template/spec/containers/0/env/0/valueFrom/secretKeyRef/name - value: eth-network-sepolia - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-test/monitoring/README.adoc b/infrastructure/kube/keep-test/monitoring/README.adoc deleted file mode 100644 index 98df5cb4b7..0000000000 --- a/infrastructure/kube/keep-test/monitoring/README.adoc +++ /dev/null @@ -1,399 +0,0 @@ -:icons: font -:toc: left - -ifdef::env-github[] -:tip-caption: :bulb: -:note-caption: :information_source: -:important-caption: :heavy_exclamation_mark: -:caution-caption: :fire: -:warning-caption: :warning: -endif::[] - -# Monitoring - -## Components - -The monitoring stack has the following components: - -1. <> -2. <> -3. <> - -[ditaa] ----- - +--------+ +--------+ +--------+ +--------+ - | Node | | Node | | Node | | Node | - +--------+ +--------+ +--------+ +--------+ - ^ ^ ^ ^ - | | | | - +--------------------------------------------- - | - | -+--------------+ +--------------+ +--------------+ -| | | | | | -| Prometheus |<-------| Trickster |<-------| Grafana | -| | | | | | -+--------------+ +--------------+ +--------------+ ----- - -## Namespace - -Kubernetes monitoring resources are configured in `monitoring` namespace. - -To create the namespace execute: - -```bash -kubectl create namespace monitoring -``` - -TIP: To easily switch between namespaces use -link:https://github.com/ahmetb/kubectx[`kubens` command]: -`kubens monitoring`. - -## Storage Class - -To define a Storage Class used by the Persistent Volume Claims execute: - -```bash -kubectl apply -f storage-class.yaml -``` - -[#prometheus] -## Prometheus - -Prometheus is used to collect metrics from the endpoints. - -### Cluster Role - -To let the Prometheus monitor Kubernetes cluster resources a Cluster Role has to -be created: - -```bash -kubectl create -f prometheus-cluster-role.yaml -``` - -NOTE: This step is necessary only if the Prometheus instance should scrape the -endpoints discovered in the Kubernetes cluster. It may not be necessary for -the production, where Keep Network Nodes will be discovered with -<> tool. - -TIP: In case of permissions issues please refer to the <> -section. - -[#cluster-role-binding] -#### Cluster Role Binding - -Additional Cluster Role Binding may be required for your user to create -a Cluster Role. It can be done by the Owner in the GCP IAM or by executing a -command: - -```bash -ACCOUNT=$(gcloud info --format='value(config.account)') -kubectl create clusterrolebinding owner-cluster-admin-binding \ - --clusterrole cluster-admin \ - --user $ACCOUNT -``` - -### Config Map - -Prometheus configuration files are held in a Config Map that is generated with <> tool. -The files included in the Config Map are: - -- link:prometheus/config/config.yaml[`config.yaml`] is a link:https://prometheus.io/docs/prometheus/latest/configuration/configuration/[Prometheus configuration file], -- link:prometheus/config/external-clients-targets.yaml[`external-clients-targets.yaml`] -is a list of endpoints to monitor (see: <> section), -- `rules.yaml` is a link:https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/#configuring-rules[Prometheus rules configuration] file. - -By externalizing Prometheus configuration to a Config Map, there is no need to build Prometheus image whenever it needs configuration amendments. Updating the Config Map -and restarting the Prometheus pod is enough to reconfigure Prometheus. - -NOTE: To apply the configuration to the cluster please see <> -section. - -### Persistent Volume Claim - -Prometheus stores data in a Persistent Volume Claim configured in a -link:prometheus/prometheus-pvc.yaml[prometheus-pvc.yaml] file. - -NOTE: To apply the configuration to the cluster please see <> -section. - -### Deployment - -Prometheus instance is configured as a Deployment in a -link:prometheus/prometheus-deployment.yaml[prometheus-deployment.yaml] file. - -The configuration uses Config Map resources and Persistent Volume claim described -in the previous sections. - -NOTE: To apply the configuration to the cluster please see <> -section. - -### Service - -Prometheus is exposed as a Service configured in -link:prometheus/prometheus-service.yaml[prometheus-service.yaml] file. - -NOTE: To apply the configuration to the cluster please see <> -section. - -The service will be available under http://prometheus.monitoring.svc.cluster.local. - -The FQDN was resolved automatically from the service configuration by `kube-dns`: - -```yaml -metadata: - name: prometheus - namespace: monitoring -... -spec: - ports: - - port: 8080 -``` - -NOTE: To access the cluster you may need a VPN connection to the `keep-test` network. - -### Health Check - -To verify health of the service open the following website: -http://prometheus.monitoring.svc.cluster.local:9090/prometheus/-/healthy - -Read more about health checks in the link:https://prometheus.io/docs/prometheus/latest/management_api/[Prometheus documentation]. - -### Keep Nodes Discovery - -There are three scrape jobs configured for Prometheus: - -[#keep-discovered-nodes] -#### keep-discovered-nodes - -The nodes to monitor are discovered with -link:https://github.com/keep-network/prometheus-sd[Prometheus Custom Service Discovery]. - -[#keep-external-nodes] -#### keep-external-nodes - -The nodes to monitor are configured in a fixed: `external-clients-targets.yaml`. - -#### keep-internal-nodes - -The nodes to monitor are resolved from Kubernetes' services labeled `app=keep`. - -[#trickster] -## Trickster - -link:https://github.com/trickstercache/trickster[Trickster] is used as a caching-proxy between Grafana and Prometheus. - -Queries to metrics should be made to the Trickster instance instead of the Prometheus. Trickster will obtain data from Prometheus and cache the results for future usage. - -### Config Map - -Trickster configuration file is held in a Config Map that is generated with <> tool. -The files included in the Config Map are: - -- link:trickster/config/trickster.yaml[`trickster.yaml`] is a configuration file, based on the link:https://github.com/trickstercache/trickster/blob/main/examples/conf/example.full.yaml[example], - -NOTE: To apply the configuration to the cluster please see <> -section. - -### Deployment - -Trickster instance is configured as a Deployment in a -link:trickster/trickster-deployment.yaml[trickster-deployment.yaml] file. - -The configuration uses Config Map resources described -in the previous sections. - -NOTE: To apply the configuration to the cluster please see <> -section. - -### Service - -Trickster is exposed as a Service configured in -link:trickster/trickster-service.yaml[trickster-service.yaml] file. - -NOTE: To apply the configuration to the cluster please see <> -section. - -The service will be available under http://trickster.monitoring.svc.cluster.local. - -NOTE: To access the cluster you may need a VPN connection to the `keep-test` network. - -### Health Check - -To verify health of the service open the following website: -http://trickster.monitoring.svc.cluster.local:8480/trickster/ping - -To verify Trickster's connection with Prometheus open the following website: -http://trickster.monitoring.svc.cluster.local:8481/trickster/health - -Read more about health checks in the link:https://github.com/trickstercache/trickster/blob/main/docs/health.md[Trickster documentation]. - -[#grafana] -## Grafana - -### Config Map - -Grafana configuration files are held in Config Maps that are generated with <> tool. - -NOTE: To apply the configuration to the cluster please see <> -section. - -#### Config - -The files included in the `grafana-config` Config Map are: - -- link:grafana/datasources.yaml[`datasources.yaml`] defines a reference to the -Prometheus instance, - -- link:grafana/dashboards.yaml[`dashboards.yaml`] defines path to Grafana -Dashboards configuration. - -#### Dashboards - -The files included in the `grafana-dashboards` Config Map are Grafana -link:grafana/dashboards[`dashboards`] for data presentation. - -### Persistent Volume Claim - -Grafana stores data in a Persistent Volume Claim configured in a -link:grafana/grafana-pvc.yaml[grafana-pvc.yaml] file. - -NOTE: To apply the configuration to the cluster please see <> -section. - -#### Deployment - -Grafana instance is configured as a Deployment in a -link:grafana/grafana-deployment.yaml[grafana-deployment.yaml] file. - -The configuration uses Config Map resources and Persistent Volume claim described -in the previous sections. - -NOTE: To apply the configuration to the cluster please see <> -section. - -### Service - -Grafana is exposed as a Service configured in -link:grafana/grafana-service.yaml[grafana-service.yaml] file. - -NOTE: To apply the configuration to the cluster please see <> -section. - -The service will be available under http://grafana.monitoring.svc.cluster.local:3000/. - -[#grafana-google] -### Google OAuth2 - -Grafana is integrated with Google OAuth2 authentication. - -You can login to the Grafana with a Google account under any of the following domains: - -- `threshold.network`, -- `keep.network`, -- `thesis.co`. - -Read more about configuration in the link:https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/google/[Grafana documentation]. - -## Kubernetes - -[#kustomization] -### Kustomization - -Kubernetes resources configuration uses link:https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization[Kustomization] to set common fields and -generate Config Maps. - -[#kustomization-prometheus] -#### Prometheus - -Configuration is stored in link:./prometheus/kustomization.yaml[prometheus/kustomization.yaml] -file. - -To preview generated config run: `kubectl kustomize prometheus/` - -To see a configuration diff run: `kubectl diff -k prometheus/` - -To apply the configuration run: `kubectl apply -k prometheus/` - -[#kustomization-trickster] -#### Trickster - -Configuration is stored in link:./trickster/kustomization.yaml[trickster/kustomization.yaml] -file. - -To preview generated config run: `kubectl kustomize trickster/` - -To see a configuration diff run: `kubectl diff -k trickster/` - -To apply the configuration run: `kubectl apply -k trickster/` - -[#kustomization-grafana] -#### Grafana - -Configuration is stored in link:./grafana/kustomization.yaml[grafana/kustomization.yaml] file. - -To preview generated config run `kubectl kustomize grafana/` - -To see a configuration diff run: `kubectl diff -k grafana/` - -To apply the configuration run `kubectl apply -k grafana/` - -## Ingress - -Ingress is used to expose the services to the internet. As an Ingress controller -we use Google Kubernetes Engine (GKE) built-in and managed Ingress controller -called link:https://cloud.google.com/kubernetes-engine/docs/concepts/ingress[GKE Ingress]. - -Following resources are exposed publicly: - -https://monitoring.test.threshold.network/grafana - -https://monitoring.test.threshold.network/prometheus (via Trickster) - -### Configuration - -To configure the Ingress following steps have to be executed: - -1. Create Static IP for the Monitoring Ingress: -+ -```bash -gcloud compute addresses create keep-test-monitoring-ingress --global -``` - -2. Create a Cloud DNS entry to point to the IP created in the previous step (`gcloud compute addresses list`). -Follow the -link:https://cloud.google.com/dns/docs/set-up-dns-records-domain-name#create_a_record_to_point_the_domain_to_an_external_ip_address[Google Cloud documentation]. - -3. Deploy the Ingress configuration: -+ -```bash -kubectl apply -f monitoring-ingress.yaml -``` - -## Public Dashboard - -By default Grafana requires login to view the dashboards. We enabled this possibility -for Google accounts in selected domains (see: <> section). -To share the monitoring dashboard broadly we configured a -link:https://grafana.com/docs/grafana/latest/dashboards/dashboard-public/[Public Dashboard]. - -The dashboard is exposed publicly with an additional Google Cloud Load Balancer -and a redirection under: - -https://public.monitoring.test.threshold.network - -## Resources - -This configuration was inspired by this link:https://devopscube.com/setup-prometheus-monitoring-on-kubernetes/[tutorial]. - -Google Cloud Documentation: - -- link:https://cloud.google.com/kubernetes-engine/docs/concepts/ingress[GKE Ingress for HTTP(S) Load Balancing] -- link:https://cloud.google.com/dns/docs/set-up-dns-records-domain-name[Set up DNS records for a domain name with Cloud DNS] -- link:https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs#gcloud[Using Google-managed SSL certificates] - -// TODO: -// - [ ] Revisit kubernetes scrape configuration in Prometheus' `config.yaml` - -// remove not needed entries -// - [ ] Add Grafana dashboard for Kubernetes resources monitoring diff --git a/infrastructure/kube/keep-test/monitoring/grafana/config/dashboards.yaml b/infrastructure/kube/keep-test/monitoring/grafana/config/dashboards.yaml deleted file mode 100644 index 54bf65f56f..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/config/dashboards.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: 1 -providers: - - name: dashboards-provider - type: file - disableDeletion: true - updateIntervalSeconds: 10 - allowUiUpdates: true - options: - path: "/var/lib/grafana/dashboards" - foldersFromFilesStructure: true diff --git a/infrastructure/kube/keep-test/monitoring/grafana/config/datasources.yaml b/infrastructure/kube/keep-test/monitoring/grafana/config/datasources.yaml deleted file mode 100644 index bb65f9e0fd..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/config/datasources.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: 1 -datasources: - - name: Trickster - type: prometheus - access: proxy - editable: true - orgId: 1 - url: http://trickster:8480/prometheus - version: 1 - isDefault: true - - - name: Prometheus - type: prometheus - access: proxy - editable: true - orgId: 1 - url: http://prometheus:9090/prometheus - version: 1 diff --git a/infrastructure/kube/keep-test/monitoring/grafana/config/grafana.ini b/infrastructure/kube/keep-test/monitoring/grafana/config/grafana.ini deleted file mode 100644 index 60a3e1f036..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/config/grafana.ini +++ /dev/null @@ -1,19 +0,0 @@ -[auth.google] -enabled = true -scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email -auth_url = https://accounts.google.com/o/oauth2/auth -token_url = https://accounts.google.com/o/oauth2/token -allow_sign_up = true - -[auth.github] -enabled = true -allow_sign_up = true -scopes = user:email,read:org -auth_url = https://github.com/login/oauth/authorize -token_url = https://github.com/login/oauth/access_token -api_url = https://api.github.com/user -allowed_organizations = keep-network threshold-network -role_attribute_path = contains(groups[*], '@keep-network/developers') && 'Editor' || 'Viewer' - -[feature_toggles] -publicDashboards = true diff --git a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/infrastructure/kubernetes-deployments.json b/infrastructure/kube/keep-test/monitoring/grafana/dashboards/infrastructure/kubernetes-deployments.json deleted file mode 100644 index 9549c0bcc0..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/infrastructure/kubernetes-deployments.json +++ /dev/null @@ -1,1387 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "Monitors Kubernetes deployments in cluster using Prometheus. Shows overall cluster CPU / Memory of deployments, replicas in each deployment. Uses Kube state metrics and cAdvisor metrics (741)", - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 8588, - "graphTooltip": 0, - "id": 2, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 65 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 8, - "x": 0, - "y": 0 - }, - "id": 1, - "links": [], - "maxDataPoints": 100, - "options": { - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (container_memory_working_set_bytes{pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\", kubernetes_io_hostname=~\"^$Node$\", pod_name!=\"\"}) / sum (kube_node_status_allocatable_memory_bytes{node=~\"^$Node.*$\"}) * 100", - "format": "time_series", - "interval": "10s", - "intervalFactor": 1, - "refId": "A", - "step": 900 - } - ], - "title": "Deployment memory usage", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 2, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 65 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 90 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 8, - "x": 8, - "y": 0 - }, - "id": 2, - "links": [], - "maxDataPoints": 100, - "options": { - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "expr": "sum (rate (container_cpu_usage_seconds_total{pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\", kubernetes_io_hostname=~\"^$Node$\"}[2m])) / sum (machine_cpu_cores{kubernetes_io_hostname=~\"^$Node$\"}) * 100", - "format": "time_series", - "interval": "10s", - "intervalFactor": 1, - "refId": "A", - "step": 900 - } - ], - "title": "Deployment CPU usage", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 30 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 8, - "x": 16, - "y": 0 - }, - "id": 3, - "links": [], - "maxDataPoints": 100, - "options": { - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": false, - "showThresholdMarkers": true - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "(((sum(kube_deployment_status_replicas{deployment=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_statefulset_replicas{statefulset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_daemonset_status_desired_number_scheduled{daemonset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0))) - ((sum(kube_deployment_status_replicas_available{deployment=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_statefulset_status_replicas{statefulset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_daemonset_status_number_ready{daemonset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)))) / ((sum(kube_deployment_status_replicas{deployment=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_statefulset_replicas{statefulset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_daemonset_status_desired_number_scheduled{daemonset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0))) * 100", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 1800 - } - ], - "title": "Unavailable Replicas", - "type": "gauge" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 0, - "y": 5 - }, - "id": 4, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (container_memory_working_set_bytes{pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\", kubernetes_io_hostname=~\"^$Node$\", pod_name!=\"\"})", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 1800 - } - ], - "title": "Used", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 4, - "y": 5 - }, - "id": 5, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (kube_node_status_allocatable_memory_bytes{node=~\"^$Node.*$\"})", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 1800 - } - ], - "title": "Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 8, - "y": 5 - }, - "id": 6, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (rate (container_cpu_usage_seconds_total{pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\", kubernetes_io_hostname=~\"^$Node$\"}[1m]))", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 1800 - } - ], - "title": "Used", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 12, - "y": 5 - }, - "id": 7, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (machine_cpu_cores{kubernetes_io_hostname=~\"^$Node$\"})", - "intervalFactor": 2, - "refId": "A", - "step": 1800 - } - ], - "title": "Total", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 16, - "y": 5 - }, - "id": 8, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "(sum(kube_deployment_status_replicas_available{deployment=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_statefulset_status_replicas{statefulset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_daemonset_status_number_ready{daemonset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0))", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 1800 - } - ], - "title": "Available (cluster)", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 20, - "y": 5 - }, - "id": 9, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.8", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "(sum(kube_deployment_status_replicas{deployment=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_statefulset_replicas{statefulset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0)) + (sum(kube_daemonset_status_desired_number_scheduled{daemonset=~\".*$Deployment$Statefulset$Daemonset\"}) or vector(0))", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{ $Daemonset }}", - "refId": "A", - "step": 1800 - } - ], - "title": "Total (cluster)", - "type": "stat" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "decimals": 3, - "editable": true, - "error": false, - "fill": 0, - "fillGradient": 0, - "grid": {}, - "gridPos": { - "h": 11, - "w": 24, - "x": 0, - "y": 8 - }, - "height": "", - "hiddenSeries": false, - "id": 10, - "legend": { - "alignAsTable": true, - "avg": false, - "current": true, - "hideEmpty": false, - "hideZero": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "sort": "current", - "sortDesc": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 2, - "links": [], - "nullPointMode": "connected", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.8", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "/avlbl.*/", - "yaxis": 2 - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (rate (container_cpu_usage_seconds_total{image!=\"\",name=~\"^k8s_.*\",io_kubernetes_container_name!=\"POD\",pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\",kubernetes_io_hostname=~\"^$Node$\"}[1m])) by (pod_name,kubernetes_io_hostname)", - "format": "time_series", - "hide": false, - "interval": "10s", - "intervalFactor": 1, - "legendFormat": "real: {{ kubernetes_io_hostname }} | {{ pod_name }} ", - "metric": "container_cpu", - "refId": "A", - "step": 60 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (kube_pod_container_resource_requests_cpu_cores{pod=~\"^$Deployment$Statefulset$Daemonset.*$\",node=~\"^$Node$\"}) by (pod,node)", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "rqst: {{ node }} | {{ pod }}", - "refId": "B", - "step": 120 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum ((kube_node_status_allocatable_cpu_cores{node=~\"^$Node$\"})) by (node)", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "avlbl: {{ node }}", - "refId": "C", - "step": 30 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "CPU usage", - "tooltip": { - "msResolution": true, - "shared": true, - "sort": 2, - "value_type": "cumulative" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": "cores", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "decimals": 2, - "editable": true, - "error": false, - "fill": 0, - "fillGradient": 0, - "grid": {}, - "gridPos": { - "h": 13, - "w": 24, - "x": 0, - "y": 19 - }, - "hiddenSeries": false, - "id": 11, - "legend": { - "alignAsTable": true, - "avg": false, - "current": true, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "sort": "current", - "sortDesc": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 2, - "links": [], - "nullPointMode": "connected", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.8", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "/^avlbl.*$/", - "yaxis": 2 - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (container_memory_working_set_bytes{id!=\"/\",pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\",kubernetes_io_hostname=~\"^$Node$\"}) by (pod_name,kubernetes_io_hostname)", - "format": "time_series", - "hide": false, - "interval": "10s", - "intervalFactor": 1, - "legendFormat": "real: {{kubernetes_io_hostname }} | {{ pod_name }}", - "metric": "container_memory_usage:sort_desc", - "refId": "A", - "step": 60 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum ((kube_pod_container_resource_requests_memory_bytes{pod=~\"^$Deployment$Statefulset$Daemonset.*$\",node=~\"^$Node$\"})) by (pod,node)", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "rqst: {{ node }} | {{ pod }}", - "refId": "B", - "step": 120 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum ((kube_node_status_allocatable_memory_bytes{node=~\"^$Node$\"})) by (node)", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "avlbl: {{ node }}", - "refId": "C", - "step": 30 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Memory usage", - "tooltip": { - "msResolution": false, - "shared": true, - "sort": 2, - "value_type": "cumulative" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "logBase": 1, - "show": true - }, - { - "format": "bytes", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 32 - }, - "hiddenSeries": false, - "id": 12, - "legend": { - "alignAsTable": true, - "avg": false, - "current": true, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "sort": "current", - "sortDesc": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.8", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "expr": "100 * (kubelet_volume_stats_used_bytes{kubernetes_io_hostname=~\"^$Node$\", persistentvolumeclaim=~\".*$Deployment$Statefulset$Daemonset.*$\"} / kubelet_volume_stats_capacity_bytes{kubernetes_io_hostname=~\"^$Node$\", persistentvolumeclaim=~\".*$Deployment$Statefulset$Daemonset.*$\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{ persistentvolumeclaim }} | {{ kubernetes_io_hostname }}", - "refId": "A", - "step": 120 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Disk Usage", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "percent", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": false - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "decimals": 2, - "editable": true, - "error": false, - "fill": 1, - "fillGradient": 0, - "grid": {}, - "gridPos": { - "h": 13, - "w": 24, - "x": 0, - "y": 41 - }, - "hiddenSeries": false, - "id": 13, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "sort": "current", - "sortDesc": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 2, - "links": [], - "nullPointMode": "connected", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.8", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum (rate (container_network_receive_bytes_total{id!=\"/\",pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\",kubernetes_io_hostname=~\"^$Node$\"}[1m])) by (pod_name, kubernetes_io_hostname)", - "format": "time_series", - "interval": "10s", - "intervalFactor": 1, - "legendFormat": "-> {{ kubernetes_io_hostname }} | {{ pod_name }}", - "metric": "network", - "refId": "A", - "step": 60 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "- sum( rate (container_network_transmit_bytes_total{id!=\"/\",pod_name=~\"^$Deployment$Statefulset$Daemonset.*$\",kubernetes_io_hostname=~\"^$Node$\"}[1m])) by (pod_name, kubernetes_io_hostname)", - "format": "time_series", - "interval": "10s", - "intervalFactor": 1, - "legendFormat": "<- {{ kubernetes_io_hostname }} | {{ pod_name }}", - "metric": "network", - "refId": "B", - "step": 60 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "All processes network I/O", - "tooltip": { - "msResolution": false, - "shared": true, - "sort": 2, - "value_type": "cumulative" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": false - } - ], - "yaxis": { - "align": false - } - } - ], - "refresh": "30s", - "schemaVersion": 37, - "style": "dark", - "tags": [ - "kubernetes", - "deployment", - "infrastructure" - ], - "templating": { - "list": [ - { - "allValue": "()", - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "Deployment", - "options": [], - "query": { - "query": "label_values(deployment)", - "refId": "prometheus-Deployment-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": "()", - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "Statefulset", - "options": [], - "query": { - "query": "label_values(statefulset)", - "refId": "prometheus-Statefulset-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": "()", - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "Daemonset", - "options": [], - "query": { - "query": "label_values(daemonset)", - "refId": "prometheus-Daemonset-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": ".*", - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "Node", - "options": [], - "query": { - "query": "label_values(kubernetes_io_hostname)", - "refId": "prometheus-Node-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "browser", - "title": "Kubernetes Deployments", - "uid": "oWe9aYxmk", - "version": 2, - "weekStart": "" -} diff --git a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/keep/keep-network-nodes-public.json b/infrastructure/kube/keep-test/monitoring/grafana/dashboards/keep/keep-network-nodes-public.json deleted file mode 100644 index 251faf1588..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/keep/keep-network-nodes-public.json +++ /dev/null @@ -1,1032 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 5, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 30 - }, - { - "color": "green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 11, - "x": 0, - "y": 0 - }, - "id": 8, - "interval": "1m", - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "sum by(job) (sum by(chain_address) (up{job=\"keep-discovered-nodes\"}))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Nodes Up", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "super-light-red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 30 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 16, - "w": 13, - "x": 11, - "y": 0 - }, - "id": 3, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (connected_wellknown_peers_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "Connected Bootstraps", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "log": 2, - "type": "log" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "area" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "light-yellow", - "value": 300 - }, - { - "color": "green", - "value": 900 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 28, - "w": 11, - "x": 0, - "y": 8 - }, - "id": 4, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "asc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (tbtc_pre_params_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "TBTC PreParams Count", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "0xDc7C1b54eB3944454dD19Bd8Ed0299F92A758B0C" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 16 - }, - "id": 2, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (connected_peers_count{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "Connected Peers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 11, - "x": 0, - "y": 36 - }, - "id": 10, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": false, - "displayName": "chain_address" - } - ] - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "up{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "__auto", - "range": false, - "refId": "Nodes" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "client_info{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "legendFormat": "", - "range": false, - "refId": "Client Info" - } - ], - "title": "Client Versions", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "byField": "chain_address" - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "Time 1": false, - "Time 2": true, - "Value": true, - "Value #A": true, - "Value #B": true, - "Value #Client Info": true, - "Value #Nodes": true, - "__name__": true, - "__name__ 1": true, - "__name__ 2": true, - "app": true, - "controller_revision_hash": true, - "id": true, - "instance": false, - "instance 1": false, - "instance 2": true, - "job": true, - "job 1": true, - "job 2": true, - "kubernetes_namespace": true, - "kubernetes_pod_name": true, - "kubernetes_pod_name_monitoring": true, - "network": true, - "network_id": true, - "network_id 1": true, - "network_id 2": true, - "statefulset_kubernetes_io_pod_name": true, - "type": true - }, - "indexByName": { - "Time 1": 3, - "Time 2": 8, - "Value #Client Info": 13, - "Value #Nodes": 7, - "__name__ 1": 4, - "__name__ 2": 9, - "chain_address": 0, - "instance 1": 1, - "instance 2": 10, - "job 1": 5, - "job 2": 11, - "network_id 1": 6, - "network_id 2": 12, - "version": 2 - }, - "renameByName": { - "chain_address": "Chain Address", - "instance 1": "Instance", - "version": "Client Version" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information whether the node is connected to the Bitcoin network", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "0": { - "index": 1, - "text": "False" - }, - "1": { - "index": 0, - "text": "True" - }, - "N/A": { - "index": 3, - "text": "False" - }, - "Null": { - "index": 2, - "text": "False" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "0xcAB2a402bAc470686d14956FB310D51BbEF9fA31" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 13, - "x": 11, - "y": 36 - }, - "id": 11, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (btc_connectivity{job=\"keep-discovered-nodes\"})", - "format": "heatmap", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "BTC Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information whether the node is connected to the Ethereum network", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "0": { - "index": 1, - "text": "False" - }, - "1": { - "index": 0, - "text": "True" - }, - "N/A": { - "index": 3, - "text": "False" - }, - "Null": { - "index": 2, - "text": "False" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "0x794f8F4F12996632781c7526054c448797acF41b" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 13, - "x": 11, - "y": 46 - }, - "id": 12, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (eth_connectivity{job=\"keep-discovered-nodes\"})", - "format": "heatmap", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "ETH Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 70, - "lineWidth": 0, - "spanNulls": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 13, - "x": 11, - "y": 56 - }, - "id": 6, - "options": { - "alignValue": "left", - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "mergeValues": true, - "rowHeight": 0.9, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (up{job=\"keep-discovered-nodes\"})", - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "A" - } - ], - "title": "Uptime (experimental)", - "type": "state-timeline" - } - ], - "refresh": false, - "schemaVersion": 37, - "style": "dark", - "tags": [ - "tbtc", - "keep", - "public" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-7d", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "Keep Nodes (Public)", - "uid": "hhDyYDI4z", - "version": 24, - "weekStart": "" -} diff --git a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/keep/keep-network-nodes.json b/infrastructure/kube/keep-test/monitoring/grafana/dashboards/keep/keep-network-nodes.json deleted file mode 100644 index a03d2e8de7..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/keep/keep-network-nodes.json +++ /dev/null @@ -1,1272 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "description": "", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 30 - }, - { - "color": "green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 11, - "x": 0, - "y": 0 - }, - "id": 8, - "interval": "1m", - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "sum by(job) (sum by(chain_address) (up{job=\"keep-discovered-nodes\"}))", - "format": "time_series", - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Nodes Up", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "super-light-red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 30 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 16, - "w": 13, - "x": 11, - "y": 0 - }, - "id": 3, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (connected_wellknown_peers_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "Connected Bootstraps", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "log": 2, - "type": "log" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "area" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "light-yellow", - "value": 300 - }, - { - "color": "green", - "value": 900 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 28, - "w": 11, - "x": 0, - "y": 8 - }, - "id": 4, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "asc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "min by(chain_address) (tbtc_pre_params_count{job=\"keep-discovered-nodes\"})", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "C" - } - ], - "title": "TBTC PreParams Count", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 13, - "x": 11, - "y": 16 - }, - "id": 2, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (connected_peers_count{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "Connected Peers", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 11, - "x": 0, - "y": 36 - }, - "id": 10, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": false, - "displayName": "chain_address" - } - ] - }, - "pluginVersion": "9.2.5", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "up{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "__auto", - "range": false, - "refId": "Nodes" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "client_info{job=\"keep-discovered-nodes\"}", - "format": "table", - "hide": false, - "instant": true, - "legendFormat": "", - "range": false, - "refId": "Client Info" - } - ], - "title": "Client Versions", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "byField": "chain_address" - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Time": true, - "Time 1": false, - "Time 2": true, - "Value": true, - "Value #A": true, - "Value #B": true, - "Value #Client Info": true, - "Value #Nodes": true, - "__name__": true, - "__name__ 1": true, - "__name__ 2": true, - "app": true, - "controller_revision_hash": true, - "id": true, - "instance": false, - "instance 1": false, - "instance 2": true, - "job": true, - "job 1": true, - "job 2": true, - "kubernetes_namespace": true, - "kubernetes_pod_name": true, - "kubernetes_pod_name_monitoring": true, - "network": true, - "network_id": true, - "network_id 1": true, - "network_id 2": true, - "statefulset_kubernetes_io_pod_name": true, - "type": true - }, - "indexByName": { - "Time 1": 3, - "Time 2": 8, - "Value #Client Info": 13, - "Value #Nodes": 7, - "__name__ 1": 4, - "__name__ 2": 9, - "chain_address": 0, - "instance 1": 1, - "instance 2": 10, - "job 1": 5, - "job 2": 11, - "network_id 1": 6, - "network_id 2": 12, - "version": 2 - }, - "renameByName": { - "chain_address": "Chain Address", - "instance 1": "Instance", - "version": "Client Version" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information whether the node is connected to the Bitcoin network", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "0": { - "index": 1, - "text": "False" - }, - "1": { - "index": 0, - "text": "True" - }, - "N/A": { - "index": 2, - "text": "False" - }, - "Null": { - "index": 3, - "text": "False" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 13, - "x": 11, - "y": 36 - }, - "id": 13, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (btc_connectivity{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "BTC Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Provides information whether the node is connected to the Ethereum network", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [ - { - "options": { - "0": { - "index": 1, - "text": "False" - }, - "1": { - "index": 0, - "text": "True" - }, - "N/A": { - "index": 3, - "text": "False" - }, - "Null": { - "index": 2, - "text": "False" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "percentage", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "super-light-yellow", - "value": 50 - }, - { - "color": "super-light-green", - "value": 80 - } - ] - }, - "unit": "bool" - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "0x8e78De834407E863A79a9820688CdA0AedFfAB6a" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 13, - "x": 11, - "y": 46 - }, - "id": 14, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (eth_connectivity{job=\"keep-discovered-nodes\"})", - "hide": false, - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "Discovered Keep Nodes" - } - ], - "title": "ETH Connectivity", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 70, - "lineWidth": 0, - "spanNulls": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 25, - "w": 13, - "x": 11, - "y": 56 - }, - "id": 6, - "options": { - "alignValue": "left", - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "mergeValues": true, - "rowHeight": 0.9, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "min by(chain_address) (up{job=\"keep-discovered-nodes\"})", - "interval": "", - "legendFormat": "{{chain_address}}", - "range": true, - "refId": "A" - } - ], - "title": "Uptime (experimental)", - "type": "state-timeline" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "A number of running instances for each operator address.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 60, - "lineWidth": 0, - "spanNulls": false - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "super-light-orange" - }, - { - "color": "super-light-green", - "value": 1 - }, - { - "color": "super-light-red", - "value": 2 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 11, - "x": 0, - "y": 61 - }, - "id": 12, - "options": { - "alignValue": "center", - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "mergeValues": true, - "rowHeight": 0.9, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "builder", - "expr": "count by(chain_address) (up{job=\"keep-discovered-nodes\"})", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Node Instances", - "type": "state-timeline" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "description": "Inbound network join requests across all monitored nodes, with the failure-reason breakdown. A high failure share is expected: unrecognized peers probing the network are rejected by the on-chain firewall check. Investigate when the mix shifts (e.g. firewall rpc error or timeout growth) or when bursts coincide with peer loss.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 20, - "w": 11, - "x": 0, - "y": 81 - }, - "id": 15, - "interval": "1m", - "options": { - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right", - "showLegend": true, - "sortBy": "Last", - "sortDesc": true - }, - "tooltip": { - "mode": "multi", - "sort": "desc" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "total", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_success_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "success", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_timeout_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: timeout", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_eof_reset_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: eof/reset", - "range": true, - "refId": "E" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_protocol_crypto_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: protocol/crypto", - "range": true, - "refId": "F" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_firewall_unrecognized_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: firewall unrecognized", - "range": true, - "refId": "G" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P09205B1DD12FB1C6" - }, - "editorMode": "code", - "exemplar": false, - "expr": "sum(increase(performance_network_join_requests_failed_firewall_rpc_error_total{job=\"keep-discovered-nodes\"}[10m]))", - "format": "time_series", - "hide": false, - "instant": false, - "interval": "", - "legendFormat": "failed: firewall rpc error", - "range": true, - "refId": "H" - } - ], - "title": "Network Join Requests (per 10m)", - "type": "timeseries" - } - ], - "refresh": false, - "schemaVersion": 37, - "style": "dark", - "tags": [ - "tbtc", - "keep" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-7d", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "", - "title": "Keep Nodes", - "uid": "tMgEvbnVk", - "version": 33, - "weekStart": "" -} diff --git a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/prometheus.json b/infrastructure/kube/keep-test/monitoring/grafana/dashboards/prometheus.json deleted file mode 100644 index 0223f42228..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/dashboards/prometheus.json +++ /dev/null @@ -1,3707 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "enable": true, - "expr": "sum(changes(prometheus_config_last_reload_success_timestamp_seconds{instance=~\"$instance\"}[10m])) by (instance)", - "hide": false, - "iconColor": "rgb(0, 96, 19)", - "limit": 100, - "name": "reloads", - "showIn": 0, - "step": "5m", - "type": "alert" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "enable": true, - "expr": "count(sum(up{instance=\"$instance\"}) by (instance) < 1)", - "hide": false, - "iconColor": "rgba(255, 96, 96, 1)", - "limit": 100, - "name": "down", - "showIn": 0, - "step": "5m", - "type": "alert" - } - ] - }, - "description": "Get started faster with Grafana Cloud then easily build these dashboards. https://grafana.com/products/cloud/\nOverview of metrics from Prometheus 2.0. \nUseful for using prometheus to monitor your prometheus.\nRevisions welcome!", - "editable": true, - "fiscalYearStartMonth": 0, - "gnetId": 3662, - "graphTooltip": 0, - "id": 5, - "links": [], - "liveNow": false, - "panels": [ - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 34, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "at a glance", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Percentage of uptime during the most recent $interval period. Change the period with the 'interval' dropdown above.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 3, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 90 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 99 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 6, - "x": 0, - "y": 1 - }, - "id": 2, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "avg(avg_over_time(up{instance=~\"$instance\",job=~\"$job\"}[$interval]) * 100)", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "", - "refId": "A", - "step": 40 - } - ], - "title": "Uptime [$interval]", - "type": "stat" - }, - { - "columns": [], - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Servers which are DOWN RIGHT NOW! \nFIX THEM!!", - "fontSize": "100%", - "gridPos": { - "h": 7, - "w": 6, - "x": 6, - "y": 1 - }, - "hideTimeOverride": true, - "id": 25, - "links": [], - "scroll": true, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "align": "auto", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "hidden" - }, - { - "alias": "", - "align": "auto", - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "pattern": "/__name__|job|Value/", - "thresholds": [], - "type": "hidden", - "unit": "short" - }, - { - "alias": " ", - "align": "auto", - "colorMode": "cell", - "colors": [ - "rgba(255, 0, 0, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(255, 0, 0, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "link": false, - "pattern": "instance", - "thresholds": [ - "", - "", - "" - ], - "type": "string", - "unit": "short" - } - ], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "up{instance=~\"$instance\",job=~\"$job\"} < 1", - "format": "table", - "intervalFactor": 2, - "refId": "A", - "step": 2 - } - ], - "timeFrom": "1s", - "title": "Currently Down", - "transform": "table", - "type": "table-old" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Total number of time series in prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1000000 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 2000000 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 6, - "x": 12, - "y": 1 - }, - "id": 12, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_tsdb_head_series{job=~\"$job\",instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "refId": "B", - "step": 40 - } - ], - "title": "Total Series", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "rgb(31, 120, 193)", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 6, - "x": 18, - "y": 1 - }, - "id": 14, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_tsdb_head_chunks{job=~\"$job\",instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "refId": "B", - "step": 40 - } - ], - "title": "Memory Chunks", - "type": "stat" - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 35, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "quick numbers", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "The total number of rule group evaluations missed due to slow rule group evaluation.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 10 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 4, - "x": 0, - "y": 9 - }, - "id": 16, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(sum_over_time(prometheus_evaluator_iterations_missed_total{job=~\"$job\",instance=~\"$instance\"}[$interval]))", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 40 - } - ], - "title": "Missed Iterations [$interval]", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "The total number of rule group evaluations skipped due to throttled metric storage.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 10 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 4, - "x": 4, - "y": 9 - }, - "id": 18, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(sum_over_time(prometheus_evaluator_iterations_skipped_total{job=~\"$job\",instance=~\"$instance\"}[$interval]))", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 40 - } - ], - "title": "Skipped Iterations [$interval]", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Total number of scrapes that hit the sample limit and were rejected.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 10 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 4, - "x": 8, - "y": 9 - }, - "id": 19, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(sum_over_time(prometheus_target_scrapes_exceeded_sample_limit_total{job=~\"$job\",instance=~\"$instance\"}[$interval]))", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 40 - } - ], - "title": "Tardy Scrapes [$interval]", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Number of times the database failed to reload block data from disk.", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 10 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 4, - "x": 12, - "y": 9 - }, - "id": 13, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(sum_over_time(prometheus_tsdb_reloads_failures_total{job=~\"$job\",instance=~\"$instance\"}[$interval]))", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 40 - } - ], - "title": "Reload Failures [$interval]", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Sum of all skipped scrapes", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(50, 172, 45, 0.97)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 1 - }, - { - "color": "rgba(245, 54, 54, 0.9)", - "value": 10 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 8, - "x": 16, - "y": 9 - }, - "id": 20, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.1.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(sum_over_time(prometheus_target_scrapes_exceeded_sample_limit_total{job=~\"$job\",instance=~\"$instance\"}[$interval])) + \nsum(sum_over_time(prometheus_target_scrapes_sample_duplicate_timestamp_total{job=~\"$job\",instance=~\"$instance\"}[$interval])) + \nsum(sum_over_time(prometheus_target_scrapes_sample_out_of_bounds_total{job=~\"$job\",instance=~\"$instance\"}[$interval])) + \nsum(sum_over_time(prometheus_target_scrapes_sample_out_of_order_total{job=~\"$job\",instance=~\"$instance\"}[$interval])) ", - "format": "time_series", - "intervalFactor": 2, - "refId": "A", - "step": 40 - } - ], - "title": "Skipped Scrapes [$interval]", - "type": "stat" - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 16 - }, - "id": 36, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "errors", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "All non-zero failures and errors", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 17 - }, - "hiddenSeries": false, - "id": 33, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(net_conntrack_dialer_conn_failed_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Failed Connections", - "refId": "A", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_evaluator_iterations_missed_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Missed Iterations", - "refId": "B", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_evaluator_iterations_skipped_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Skipped Iterations", - "refId": "C", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_rule_evaluation_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Evaluation", - "refId": "D", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_azure_refresh_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Azure Refresh", - "refId": "E", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_consul_rpc_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Consul RPC", - "refId": "F", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_dns_lookup_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "DNS Lookup", - "refId": "G", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_ec2_refresh_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "EC2 Refresh", - "refId": "H", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_gce_refresh_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "GCE Refresh", - "refId": "I", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_marathon_refresh_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Marathon Refresh", - "refId": "J", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_openstack_refresh_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Openstack Refresh", - "refId": "K", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_sd_triton_refresh_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Triton Refresh", - "refId": "L", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_target_scrapes_exceeded_sample_limit_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Sample Limit", - "refId": "M", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_target_scrapes_sample_duplicate_timestamp_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Duplicate Timestamp", - "refId": "N", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_target_scrapes_sample_out_of_bounds_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Timestamp Out of Bounds", - "refId": "O", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_target_scrapes_sample_out_of_order_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Sample Out of Order", - "refId": "P", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_treecache_zookeeper_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Zookeeper", - "refId": "Q", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_tsdb_compactions_failed_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "TSDB Compactions", - "refId": "R", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_tsdb_head_series_not_found{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Series Not Found", - "refId": "S", - "step": 2 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(increase(prometheus_tsdb_reloads_failures_total{instance=~\"$instance\"}[5m])) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Reload", - "refId": "T", - "step": 2 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Failures and Errors", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Errors", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 24 - }, - "id": 37, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "up", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 25 - }, - "hiddenSeries": false, - "id": 1, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "up{instance=~\"$instance\",job=~\"$job\"}", - "format": "time_series", - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Upness (stacked)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 0, - "format": "none", - "label": "Up", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": false - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 25 - }, - "hiddenSeries": false, - "id": 5, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "prometheus_tsdb_head_chunks{job=~\"$job\",instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Storage Memory Chunks", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Chunks", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 32 - }, - "id": 38, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "series", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 33 - }, - "hiddenSeries": false, - "id": 3, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "prometheus_tsdb_head_series{job=~\"$job\",instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Series Count", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Series", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 33 - }, - "hiddenSeries": false, - "id": 32, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "removed", - "transform": "negative-Y" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum( increase(prometheus_tsdb_head_series_created_total{instance=~\"$instance\"}[5m]) )", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "created", - "refId": "A", - "step": 4 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum( increase(prometheus_tsdb_head_series_removed_total{instance=~\"$instance\"}[5m]) )", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "removed", - "refId": "B", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Series Created / Removed", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Series Count", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 40 - }, - "id": 39, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "appended samples", - "type": "row" - }, - { - "aliasColors": { - "10.58.3.10:80": "#BA43A9" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Rate of total number of appended samples", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 41 - }, - "hiddenSeries": false, - "id": 4, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "rate(prometheus_tsdb_head_samples_appended_total{job=~\"$job\",instance=~\"$instance\"}[1m])", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Appended Samples per Second", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Samples / Second", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 48 - }, - "id": 40, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "sync", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Total number of syncs that were executed on a scrape pool.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 49 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_target_scrape_pool_sync_total{job=~\"$job\",instance=~\"$instance\"}) by (scrape_job)", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "{{scrape_job}}", - "refId": "B", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Scrape Sync Total", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Syncs", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Actual interval to sync the scrape pool.", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 49 - }, - "hiddenSeries": false, - "id": 21, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(prometheus_target_sync_length_seconds_sum{job=~\"$job\",instance=~\"$instance\"}[2m])) by (scrape_job) * 1000", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{scrape_job}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Target Sync", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Milliseconds", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 56 - }, - "id": 41, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "scrapes", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 57 - }, - "hiddenSeries": false, - "id": 29, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "scrape_duration_seconds{instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Scrape Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Seconds", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Total number of rejected scrapes", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 57 - }, - "hiddenSeries": false, - "id": 30, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_target_scrapes_exceeded_sample_limit_total{job=~\"$job\",instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "exceeded sample limit", - "refId": "A", - "step": 4 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_target_scrapes_sample_duplicate_timestamp_total{job=~\"$job\",instance=~\"$instance\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "duplicate timestamp", - "refId": "B", - "step": 4 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_target_scrapes_sample_out_of_bounds_total{job=~\"$job\",instance=~\"$instance\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "out of bounds", - "refId": "C", - "step": 4 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_target_scrapes_sample_out_of_order_total{job=~\"$job\",instance=~\"$instance\"}) ", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "out of order", - "refId": "D", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Rejected Scrapes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 0, - "format": "short", - "label": "Scrapes", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 64 - }, - "id": 42, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "durations", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "The duration of rule group evaluations", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 65 - }, - "hiddenSeries": false, - "id": 10, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "1000 * rate(prometheus_evaluator_duration_seconds_sum{job=~\"$job\", instance=~\"$instance\"}[5m]) / rate(prometheus_evaluator_duration_seconds_count{job=~\"$job\", instance=~\"$instance\"}[5m])", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "E", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Average Rule Evaluation Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Milliseconds", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 65 - }, - "hiddenSeries": false, - "id": 11, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(http_request_duration_microseconds_count{job=~\"$job\",instance=~\"$instance\"}[1m])) by (handler) > 0", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{handler}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "HTTP Request Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Microseconds", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 72 - }, - "hiddenSeries": false, - "id": 15, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(prometheus_engine_query_duration_seconds_sum{job=~\"$job\",instance=~\"$instance\"}) by (slice)", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{slice}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Prometheus Engine Query Duration Seconds", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Seconds", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "Rule-group evaluations \n - total\n - missed due to slow rule group evaluation\n - skipped due to throttled metric storage", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 72 - }, - "hiddenSeries": false, - "id": 31, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(prometheus_evaluator_iterations_total{job=~\"$job\", instance=~\"$instance\"}[5m]))", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Total", - "refId": "B", - "step": 4 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(prometheus_evaluator_iterations_missed_total{job=~\"$job\", instance=~\"$instance\"}[5m]))", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Missed", - "refId": "A", - "step": 4 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(prometheus_evaluator_iterations_skipped_total{job=~\"$job\", instance=~\"$instance\"}[5m]))", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "Skipped", - "refId": "C", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Rule Evaluator Iterations", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "iterations", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 79 - }, - "id": 43, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "notifications", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 80 - }, - "hiddenSeries": false, - "id": 22, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "rate(prometheus_notifications_sent_total[5m])", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Notifications Sent", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Notifications", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 87 - }, - "id": 44, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "config", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 88 - }, - "hiddenSeries": false, - "id": 23, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "(time() - prometheus_config_last_reload_success_timestamp_seconds{job=~\"$job\",instance=~\"$instance\"}) / 60", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Minutes Since Successful Config Reload", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Minutes", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 88 - }, - "hiddenSeries": false, - "id": 24, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "prometheus_config_last_reload_successful{job=~\"$job\",instance=~\"$instance\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 4 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "Successful Config Reload", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 0, - "format": "short", - "label": "Success", - "logBase": 1, - "max": "1", - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": false, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 95 - }, - "id": 45, - "panels": [], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "garbage collection", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "GC invocation durations", - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 96 - }, - "hiddenSeries": false, - "id": 28, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "9.1.2", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(go_gc_duration_seconds_sum{instance=~\"$instance\",job=~\"$job\"}[2m])) by (instance)", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{instance}}", - "refId": "A", - "step": 2 - } - ], - "thresholds": [], - "timeRegions": [], - "title": "GC Rate / 2m", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "logBase": 1, - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ], - "yaxis": { - "align": false - } - }, - { - "collapsed": true, - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 103 - }, - "id": 46, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "description": "This is probably wrong! Please help.", - "fill": 1, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 104 - }, - "id": 26, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "allocated", - "stack": false - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_alloc_bytes_total{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "hide": true, - "intervalFactor": 2, - "legendFormat": "alloc_bytes_total", - "refId": "A", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_alloc_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "hide": false, - "intervalFactor": 2, - "legendFormat": "allocated", - "refId": "B", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_buck_hash_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "profiling bucket hash table", - "refId": "C", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_gc_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "GC metadata", - "refId": "D", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_heap_alloc_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "heap in-use", - "refId": "E", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_heap_idle_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "heap idle", - "refId": "F", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_heap_inuse_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "heap in use", - "refId": "G", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_heap_released_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "heap released", - "refId": "H", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_heap_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "heap system", - "refId": "I", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_mcache_inuse_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "mcache in use", - "refId": "J", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_mcache_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "mcache sys", - "refId": "K", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_mspan_inuse_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "mspan in use", - "refId": "L", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_mspan_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "mspan sys", - "refId": "M", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_next_gc_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "heap next gc", - "refId": "N", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_other_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "other sys", - "refId": "O", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_stack_inuse_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "stack in use", - "refId": "P", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_stack_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "stack sys", - "refId": "Q", - "step": 10 - }, - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(go_memstats_sys_bytes{job=~\"$job\", instance=~\"$instance\"})", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "sys", - "refId": "R", - "step": 10 - } - ], - "thresholds": [], - "title": "Go Memory Usage (FIXME)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "bytes", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ] - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 12, - "y": 104 - }, - "id": 9, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "prometheus_target_interval_length_seconds{instance=~\"$instance\", job=~\"$job\"}", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{quantile}} {{interval}}", - "refId": "A", - "step": 20 - } - ], - "thresholds": [], - "title": "Scrape Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Seconds", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ] - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "fill": 1, - "gridPos": { - "h": 7, - "w": 6, - "x": 18, - "y": 104 - }, - "id": 7, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "links": [], - "nullPointMode": "null", - "percentage": false, - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "expr": "sum(rate(prometheus_target_interval_length_seconds_count{job=~\"$job\",instance=~\"$instance\"}[5m])) by (interval)", - "format": "time_series", - "intervalFactor": 2, - "legendFormat": "{{interval}}", - "refId": "A", - "step": 20 - } - ], - "thresholds": [], - "title": "Target Scrapes / 5m", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "mode": "time", - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "Scrapes", - "logBase": 1, - "min": "0", - "show": true - }, - { - "format": "short", - "logBase": 1, - "show": true - } - ] - } - ], - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "refId": "A" - } - ], - "title": "Broken, ignore", - "type": "row" - } - ], - "refresh": "30s", - "schemaVersion": 37, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": true, - "name": "job", - "options": [], - "query": { - "query": "query_result(prometheus_tsdb_head_samples_appended_total)", - "refId": "prometheus-job-Variable-Query" - }, - "refresh": 2, - "regex": "/.*job=\"([^\"]+)/", - "skipUrlSync": false, - "sort": 1, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "current": { - "selected": false, - "text": "All", - "value": "$__all" - }, - "datasource": { - "type": "prometheus", - "uid": "P1809F7CD0C75ACF3" - }, - "definition": "", - "hide": 0, - "includeAll": true, - "multi": true, - "name": "instance", - "options": [], - "query": { - "query": "query_result(up{job=~\"$job\"})", - "refId": "prometheus-instance-Variable-Query" - }, - "refresh": 2, - "regex": "/.*instance=\"([^\"]+).*/", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "current": { - "selected": true, - "text": "1h", - "value": "1h" - }, - "hide": 0, - "includeAll": false, - "multi": false, - "name": "interval", - "options": [ - { - "selected": true, - "text": "1h", - "value": "1h" - }, - { - "selected": false, - "text": "3h", - "value": "3h" - }, - { - "selected": false, - "text": "6h", - "value": "6h" - }, - { - "selected": false, - "text": "12h", - "value": "12h" - }, - { - "selected": false, - "text": "1d", - "value": "1d" - }, - { - "selected": false, - "text": "2d", - "value": "2d" - }, - { - "selected": false, - "text": "7d", - "value": "7d" - }, - { - "selected": false, - "text": "30d", - "value": "30d" - }, - { - "selected": false, - "text": "90d", - "value": "90d" - }, - { - "selected": false, - "text": "180d", - "value": "180d" - } - ], - "query": "1h, 3h, 6h, 12h, 1d, 2d, 7d, 30d, 90d, 180d", - "skipUrlSync": false, - "type": "custom" - } - ] - }, - "time": { - "from": "now-30m", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] - }, - "timezone": "", - "title": "Prometheus Overview", - "uid": "jNCsuX44k", - "version": 2, - "weekStart": "" -} diff --git a/infrastructure/kube/keep-test/monitoring/grafana/grafana-deployment.yaml b/infrastructure/kube/keep-test/monitoring/grafana/grafana-deployment.yaml deleted file mode 100644 index bae85548f2..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/grafana-deployment.yaml +++ /dev/null @@ -1,114 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grafana -spec: - replicas: 1 - selector: - matchLabels: - app: grafana - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - runAsNonRoot: true - containers: - - name: grafana - image: grafana/grafana:9.2.5 - env: - - name: GF_SERVER_DOMAIN - value: monitoring.test.keep.network - - name: GF_SERVER_ROOT_URL - value: "%(protocol)s://%(domain)s:80/grafana/" - - name: GF_SERVER_SERVE_FROM_SUB_PATH - value: "true" - - name: GF_FEATURE_TOGGLES_PUBLICDASHBOARDS - value: "true" - - name: GF_AUTH_GOOGLE_CLIENT_ID - valueFrom: - secretKeyRef: - name: grafana-auth-google - key: client_id - - name: GF_AUTH_GOOGLE_CLIENT_SECRET - valueFrom: - secretKeyRef: - name: grafana-auth-google - key: client_secret - - name: GF_AUTH_GITHUB_CLIENT_ID - valueFrom: - secretKeyRef: - name: grafana-auth-github - key: client_id - - name: GF_AUTH_GITHUB_CLIENT_SECRET - valueFrom: - secretKeyRef: - name: grafana-auth-github - key: client_secret - ports: - - name: grafana - containerPort: 3000 - readinessProbe: - httpGet: - path: /api/health - port: grafana - initialDelaySeconds: 10 - periodSeconds: 30 - timeoutSeconds: 2 - livenessProbe: - initialDelaySeconds: 30 - tcpSocket: - port: grafana - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 250m - memory: 512Mi - volumeMounts: - - name: grafana-grafana-ini - mountPath: /etc/grafana/grafana.ini - subPath: grafana.ini - - name: grafana-config-datasources - mountPath: /etc/grafana/provisioning/datasources - - name: grafana-config-dashboards - mountPath: /etc/grafana/provisioning/dashboards - - name: grafana-storage - mountPath: /var/lib/grafana - - name: grafana-dashboards-infrastructure - mountPath: /var/lib/grafana/dashboards/infrastructure - - name: grafana-dashboards-keep - mountPath: /var/lib/grafana/dashboards/keep - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: grafana-storage - persistentVolumeClaim: - claimName: grafana-pvc - - name: grafana-dashboards-keep - configMap: - name: grafana-dashboards-keep - - name: grafana-dashboards-infrastructure - configMap: - name: grafana-dashboards-infrastructure - - name: grafana-config-datasources - configMap: - name: grafana-config - items: - - key: datasources.yaml - path: datasources.yaml - - name: grafana-config-dashboards - configMap: - name: grafana-config - items: - - key: dashboards.yaml - path: dashboards.yaml - - name: grafana-grafana-ini - configMap: - name: grafana-config - items: - - key: grafana.ini - path: grafana.ini diff --git a/infrastructure/kube/keep-test/monitoring/grafana/grafana-pvc.yaml b/infrastructure/kube/keep-test/monitoring/grafana/grafana-pvc.yaml deleted file mode 100644 index 46b9de4205..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/grafana-pvc.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: grafana-pvc - namespace: monitoring - labels: - app: grafana -spec: - storageClassName: monitoring-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi diff --git a/infrastructure/kube/keep-test/monitoring/grafana/grafana-service.yaml b/infrastructure/kube/keep-test/monitoring/grafana/grafana-service.yaml deleted file mode 100644 index 2db62dbeda..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/grafana-service.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: grafana -spec: - selector: - app: grafana - type: NodePort - ports: - - port: 3000 - targetPort: grafana diff --git a/infrastructure/kube/keep-test/monitoring/grafana/kustomization.yaml b/infrastructure/kube/keep-test/monitoring/grafana/kustomization.yaml deleted file mode 100644 index d8478e7325..0000000000 --- a/infrastructure/kube/keep-test/monitoring/grafana/kustomization.yaml +++ /dev/null @@ -1,29 +0,0 @@ -resources: - - grafana-deployment.yaml - - grafana-pvc.yaml - - grafana-service.yaml - -namespace: monitoring - -commonLabels: - app: grafana - type: monitoring - -configMapGenerator: - - name: grafana-config - files: - - config/grafana.ini - - config/dashboards.yaml - - config/datasources.yaml - - name: grafana-dashboards-keep - files: - - dashboards/keep/keep-network-nodes-public.json - - dashboards/keep/keep-network-nodes.json - - name: grafana-dashboards-infrastructure - files: - - dashboards/infrastructure/kubernetes-deployments.json - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-test/monitoring/monitoring-ingress.yaml b/infrastructure/kube/keep-test/monitoring/monitoring-ingress.yaml deleted file mode 100644 index 357448d0cc..0000000000 --- a/infrastructure/kube/keep-test/monitoring/monitoring-ingress.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: monitoring - namespace: monitoring - annotations: - kubernetes.io/ingress.class: "gce" - # The static IP has to be created with the following command: - # `gcloud compute addresses create keep-test-monitoring-ingress --global` - kubernetes.io/ingress.global-static-ip-name: "keep-test-monitoring-ingress" - networking.gke.io/managed-certificates: monitoring-cert -spec: - defaultBackend: - service: - name: grafana - port: - number: 3000 - rules: - - http: - paths: - - path: "/grafana" - pathType: Prefix - backend: - service: - name: grafana - port: - number: 3000 ---- -apiVersion: networking.gke.io/v1 -kind: ManagedCertificate -metadata: - name: monitoring-cert - namespace: monitoring -spec: - domains: - - monitoring.test.threshold.network - - monitoring.test.keep.network diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/config/config.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/config/config.yaml deleted file mode 100644 index 96086d5d64..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/config/config.yaml +++ /dev/null @@ -1,153 +0,0 @@ -global: - scrape_interval: 1m - scrape_timeout: 10s - evaluation_interval: 1m -# TODO: Configure Alert Manager -# alerting: -# alertmanagers: -# - follow_redirects: true -# enable_http2: true -# scheme: http -# timeout: 10s -# api_version: v2 -# static_configs: -# - targets: -# - alertmanager.monitoring.svc:9093 -rule_files: - - /etc/prometheus/rules.yaml -scrape_configs: - - job_name: keep-discovered-nodes - honor_timestamps: true - metrics_path: /metrics - scheme: http - follow_redirects: true - enable_http2: true - relabel_configs: - - source_labels: [__meta_chain_address] - separator: ; - regex: (.*) - target_label: chain_address - replacement: $1 - action: replace - - source_labels: [__meta_network_id] - separator: ; - regex: (.*) - target_label: network_id - replacement: $1 - action: replace - file_sd_configs: - - files: - - /etc/prometheus/sd/keep-sd.json - refresh_interval: 5m - - job_name: keep-external-nodes - honor_timestamps: true - metrics_path: /metrics - scheme: http - follow_redirects: true - enable_http2: true - file_sd_configs: - - files: - - /etc/prometheus/external-clients-targets.yaml - refresh_interval: 5m - - job_name: keep-internal-nodes - honor_timestamps: true - metrics_path: /metrics - scheme: http - follow_redirects: true - enable_http2: true - relabel_configs: - - source_labels: [__meta_kubernetes_service_port_name] - separator: ; - regex: metrics - replacement: $1 - action: keep - kubernetes_sd_configs: - - role: service - kubeconfig_file: "" - follow_redirects: true - enable_http2: true - namespaces: - own_namespace: false - names: - - default - selectors: - - role: service - label: app=keep - - job_name: grafana - honor_timestamps: true - metrics_path: /metrics - scheme: http - follow_redirects: true - enable_http2: true - relabel_configs: - - source_labels: [__meta_kubernetes_pod_label_app] - separator: ; - regex: grafana.* - replacement: $1 - action: keep - - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] - separator: ; - regex: (.+) - target_label: __metrics_path__ - replacement: $1 - action: replace - - separator: ; - regex: __meta_kubernetes_pod_label_(.+) - replacement: $1 - action: labelmap - - source_labels: [__meta_kubernetes_namespace] - separator: ; - regex: (.*) - target_label: kubernetes_namespace - replacement: $1 - action: replace - - source_labels: [__meta_kubernetes_pod_name] - separator: ; - regex: (.*) - target_label: kubernetes_pod_name - replacement: $1 - action: replace - kubernetes_sd_configs: - - role: pod - kubeconfig_file: "" - follow_redirects: true - enable_http2: true - - job_name: prometheus - honor_timestamps: true - metrics_path: /metrics - scheme: http - follow_redirects: true - enable_http2: true - relabel_configs: - - source_labels: [__meta_kubernetes_pod_label_app] - separator: ; - regex: prometheus.* - replacement: $1 - action: keep - - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] - separator: ; - regex: (.+) - target_label: __metrics_path__ - replacement: $1 - action: replace - - separator: ; - regex: __meta_kubernetes_pod_label_(.+) - replacement: $1 - action: labelmap - - source_labels: [__meta_kubernetes_namespace] - separator: ; - regex: (.*) - target_label: kubernetes_namespace - replacement: $1 - action: replace - - source_labels: [__meta_kubernetes_pod_name] - separator: ; - regex: (.*) - target_label: kubernetes_pod_name - replacement: $1 - action: replace - kubernetes_sd_configs: - - role: pod - kubeconfig_file: "" - follow_redirects: true - enable_http2: true diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/config/external-clients-targets.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/config/external-clients-targets.yaml deleted file mode 100644 index 83f68bff7d..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/config/external-clients-targets.yaml +++ /dev/null @@ -1,4 +0,0 @@ -- targets: - - bst-a01.test.keep.boar.network:9601 - - keep-validator-0.eks-ap-northeast-2-secure.staging.staked.cloud:9601 - - bootstrap-alpha.test.threshold.p2p.org:9601 diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/config/rules.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/config/rules.yaml deleted file mode 100644 index eaec118f2b..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/config/rules.yaml +++ /dev/null @@ -1,53 +0,0 @@ -groups: - # TODO: Define some common rules to record: https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/ - - name: keep-network-join-requests - rules: - # Fires only when an abnormal burst of inbound join-request failures - # coincides with peer loss or coordination degradation on the same - # node. A high failure ratio alone is expected behavior (unrecognized - # peers probing the network are rejected by the on-chain firewall - # check) and intentionally does not fire this alert. - - alert: KeepNodeJoinFailureBurstWithConnectivityDegradation - expr: | - ( - sum by (chain_address) ( - rate(performance_network_join_requests_failed_total{job="keep-discovered-nodes"}[30m]) - ) - > - 4 * sum by (chain_address) ( - rate(performance_network_join_requests_failed_total{job="keep-discovered-nodes"}[6h] offset 30m) - ) + 0.05 - ) - and on (chain_address) - ( - min by (chain_address) ( - connected_wellknown_peers_count{job="keep-discovered-nodes"} - ) == 0 - or - min by (chain_address) ( - delta(connected_peers_count{job="keep-discovered-nodes"}[30m]) - ) < -5 - or - sum by (chain_address) ( - increase(performance_coordination_failed_total{job="keep-discovered-nodes"}[1h]) - ) > 0 - or - sum by (chain_address) ( - increase(performance_coordination_leader_timeout_total{job="keep-discovered-nodes"}[1h]) - ) > 2 - ) - for: 15m - labels: - severity: warning - annotations: - summary: >- - Join-request failure burst with connectivity degradation on - {{ $labels.chain_address }} - description: >- - Inbound network join-request failures on node - {{ $labels.chain_address }} spiked to more than 4x their 6h - baseline while the node also shows well-known peer isolation, - peer loss, or coordination degradation. Check the per-reason - breakdown (performance_network_join_requests_failed_*_total) - to tell genuine non-recognition (firewall_unrecognized) apart - from firewall RPC errors, timeouts, and connection resets. diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/kustomization.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/kustomization.yaml deleted file mode 100644 index d3b4eecc57..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/kustomization.yaml +++ /dev/null @@ -1,22 +0,0 @@ -resources: - - prometheus-deployment.yaml - - prometheus-pvc.yaml - - prometheus-service.yaml - -namespace: monitoring - -commonLabels: - app: prometheus - type: monitoring - -configMapGenerator: - - name: prometheus-config - files: - - config/config.yaml - - config/external-clients-targets.yaml - - config/rules.yaml - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-cluster-role.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-cluster-role.yaml deleted file mode 100644 index 5c67a8ca9f..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-cluster-role.yaml +++ /dev/null @@ -1,34 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: prometheus -rules: - - apiGroups: [""] - resources: - - nodes - - nodes/proxy - - services - - endpoints - - pods - verbs: ["get", "list", "watch"] - - apiGroups: - - extensions - resources: - - ingresses - verbs: ["get", "list", "watch"] - - nonResourceURLs: ["/metrics"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: prometheus -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: prometheus -subjects: - - kind: ServiceAccount - name: default - namespace: monitoring diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-deployment.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-deployment.yaml deleted file mode 100644 index 1aa0116e0d..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-deployment.yaml +++ /dev/null @@ -1,92 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: prometheus -spec: - replicas: 1 - strategy: - type: Recreate - selector: - matchLabels: - app: prometheus - type: monitoring - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - runAsNonRoot: true - containers: - - name: prometheus - image: prom/prometheus:v2.43.1 - args: - - --config.file=/etc/prometheus/config.yaml - - --storage.tsdb.path=/etc/prometheus/data - - --storage.tsdb.retention.time=1y - - --web.external-url=/prometheus/ - ports: - - name: prometheus - containerPort: 9090 - readinessProbe: - httpGet: - path: "/prometheus/-/ready" - port: prometheus - initialDelaySeconds: 10 - periodSeconds: 30 - timeoutSeconds: 2 - livenessProbe: - httpGet: - path: "/prometheus/-/healthy" - port: prometheus - initialDelaySeconds: 10 - periodSeconds: 30 - timeoutSeconds: 2 - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 500m - memory: 512Mi - volumeMounts: - - name: prometheus-config-volume - mountPath: /etc/prometheus/ - - name: prometheus-storage-volume - mountPath: /etc/prometheus/data/ - - name: prometheus-sd-volume - mountPath: /etc/prometheus/sd/ - securityContext: - readOnlyRootFilesystem: true - - name: keep-sd - image: keepnetwork/keep-prometheus-sd - args: - - --output.file=/etc/prometheus/sd/keep-sd.json - - --source.address=bootstrap-0.test.keep.network:9601 - - --source.address=bootstrap-1.test.keep.network:9601 - - --refresh.interval=5m - - --scan.timeout=3s - - --log.json - - --scan.allowPrivateAddresses - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 250m - memory: 256Mi - volumeMounts: - - name: prometheus-sd-volume - mountPath: /etc/prometheus/sd/ - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: prometheus-config-volume - configMap: - name: prometheus-config - - name: prometheus-storage-volume - persistentVolumeClaim: - claimName: prometheus-pvc - - name: prometheus-sd-volume - emptyDir: {} diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-pvc.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-pvc.yaml deleted file mode 100644 index 6ca54ca443..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-pvc.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: prometheus-pvc -spec: - storageClassName: monitoring-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 100Gi diff --git a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-service.yaml b/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-service.yaml deleted file mode 100644 index ce86a2fc1e..0000000000 --- a/infrastructure/kube/keep-test/monitoring/prometheus/prometheus-service.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: prometheus -spec: - selector: - app: prometheus - type: NodePort - ports: - - port: 9090 - targetPort: prometheus diff --git a/infrastructure/kube/keep-test/monitoring/storage-class.yaml b/infrastructure/kube/keep-test/monitoring/storage-class.yaml deleted file mode 100644 index bf375bd8c0..0000000000 --- a/infrastructure/kube/keep-test/monitoring/storage-class.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: monitoring-storage -provisioner: kubernetes.io/gce-pd -parameters: - type: pd-ssd - replication-type: none -reclaimPolicy: Retain -allowVolumeExpansion: true -mountOptions: - - debug -volumeBindingMode: Immediate diff --git a/infrastructure/kube/keep-test/monitoring/trickster/config/trickster.yaml b/infrastructure/kube/keep-test/monitoring/trickster/config/trickster.yaml deleted file mode 100644 index 0c4b5797c5..0000000000 --- a/infrastructure/kube/keep-test/monitoring/trickster/config/trickster.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Trickster Configuration File. -# -# A full configuration file example can be found here: -# https://github.com/trickstercache/trickster/blob/main/examples/conf/example.full.yaml - -frontend: - listen_port: 8480 - -backends: - default: - provider: prometheus - origin_url: http://prometheus:9090 - is_default: true - healthcheck: - path: /prometheus/-/ready - upstream_path: /prometheus/-/ready - interval_ms: 5000 - expected_body: "Prometheus Server is Ready.\n" - -metrics: - listen_port: 8481 - listen_address: "" - -logging: - log_level: info diff --git a/infrastructure/kube/keep-test/monitoring/trickster/kustomization.yaml b/infrastructure/kube/keep-test/monitoring/trickster/kustomization.yaml deleted file mode 100644 index 0ca82fb0a6..0000000000 --- a/infrastructure/kube/keep-test/monitoring/trickster/kustomization.yaml +++ /dev/null @@ -1,19 +0,0 @@ -resources: - - trickster-deployment.yaml - - trickster-service.yaml - -namespace: monitoring - -commonLabels: - app: trickster - type: monitoring - -configMapGenerator: - - name: trickster-config - files: - - config/trickster.yaml - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/keep-test/monitoring/trickster/trickster-deployment.yaml b/infrastructure/kube/keep-test/monitoring/trickster/trickster-deployment.yaml deleted file mode 100644 index f63c615dad..0000000000 --- a/infrastructure/kube/keep-test/monitoring/trickster/trickster-deployment.yaml +++ /dev/null @@ -1,58 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: trickster -spec: - replicas: 1 - selector: - matchLabels: - app: trickster - type: monitoring - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - runAsNonRoot: true - containers: - - name: trickster - image: trickstercache/trickster:2 - ports: - - name: trickster - containerPort: 8480 - - name: metrics - containerPort: 8481 - readinessProbe: - httpGet: - path: "/trickster/health/default" - port: metrics - livenessProbe: - httpGet: - path: "/trickster/ping" - port: trickster - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 500m - memory: 512Mi - volumeMounts: - - name: trickster-config - mountPath: /etc/trickster - env: - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - securityContext: - readOnlyRootFilesystem: true - volumes: - - name: trickster-config - configMap: - name: trickster-config - items: - - key: trickster.yaml - path: trickster.yaml diff --git a/infrastructure/kube/keep-test/monitoring/trickster/trickster-service.yaml b/infrastructure/kube/keep-test/monitoring/trickster/trickster-service.yaml deleted file mode 100644 index 420994a406..0000000000 --- a/infrastructure/kube/keep-test/monitoring/trickster/trickster-service.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: trickster -spec: - selector: - app: trickster - type: NodePort - ports: - - name: trickster - port: 8480 - targetPort: trickster - - name: metrics - port: 8481 - targetPort: metrics diff --git a/infrastructure/kube/keep-test/tbtc-v2-monitoring/.env.secret b/infrastructure/kube/keep-test/tbtc-v2-monitoring/.env.secret deleted file mode 100644 index a7d6ed3bf9..0000000000 --- a/infrastructure/kube/keep-test/tbtc-v2-monitoring/.env.secret +++ /dev/null @@ -1,4 +0,0 @@ -ethereum-url= -electrum-url= -sentry-dsn= -discord-webhook-url= \ No newline at end of file diff --git a/infrastructure/kube/keep-test/tbtc-v2-monitoring/README.md b/infrastructure/kube/keep-test/tbtc-v2-monitoring/README.md deleted file mode 100644 index baa876dd6a..0000000000 --- a/infrastructure/kube/keep-test/tbtc-v2-monitoring/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# TBTCv2 system events monitoring - -Configuration to run TBTCv2 system events monitoring. It is a test -overlay of the [base `tbtc-v2-monitoring` configuration](../../templates/tbtc-v2-monitoring) - -To apply the configuration execute: - -```sh -kubectl apply -k ./ -``` diff --git a/infrastructure/kube/keep-test/tbtc-v2-monitoring/kustomization.yaml b/infrastructure/kube/keep-test/tbtc-v2-monitoring/kustomization.yaml deleted file mode 100644 index 67b1bd0cf9..0000000000 --- a/infrastructure/kube/keep-test/tbtc-v2-monitoring/kustomization.yaml +++ /dev/null @@ -1,25 +0,0 @@ -bases: - - ../../templates/tbtc-v2-monitoring - -images: - - name: tbtc-v2-monitoring - newName: gcr.io/keep-test-f3e0/tbtc-v2-monitoring - newTag: latest - -configMapGenerator: - - name: tbtc-v2-monitoring-config - literals: - - environment=testnet - - large-deposit-threshold-sat=1000000000 # 10 BTC - - large-redemption-threshold-sat=1000000000 # 10 BTC - -secretGenerator: - - name: tbtc-v2-monitoring-config - envs: - - .env.secret - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated - diff --git a/infrastructure/kube/lcl/dashboard.yaml b/infrastructure/kube/lcl/dashboard.yaml deleted file mode 100644 index c9d9e45243..0000000000 --- a/infrastructure/kube/lcl/dashboard.yaml +++ /dev/null @@ -1,44 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: dashboard - labels: - app: dashboard -spec: - ports: - - port: 3000 - targetPort: 3000 - name: tcp-3000 - - port: 3001 - targetPort: 3001 - name: tcp-3001 - selector: - app: dashboard - type: LoadBalancer - ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: dashboard -spec: - replicas: 1 - template: - metadata: - labels: - app: dashboard - spec: - imagePullSecrets: - - name: google-container-registry-auth - containers: - - name: dashboard - image: gcr.io/keep-dev-fe24/eth-dashboard-node:latest - ports: - - containerPort: 3000 - - containerPort: 3001 - env: - - name: WS_SECRET - value: BANZAI!!!! - - name: BOOTNODE_URL - value: http://dashboard:3001 diff --git a/infrastructure/kube/lcl/k8s-pod.yaml b/infrastructure/kube/lcl/k8s-pod.yaml deleted file mode 100644 index 7b1933feb0..0000000000 --- a/infrastructure/kube/lcl/k8s-pod.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: keep-dev-environment -spec: - containers: - - name: keep-client - image: gcr.io/keep.network/keep-client diff --git a/infrastructure/kube/lcl/keystore-configmap-job.yaml b/infrastructure/kube/lcl/keystore-configmap-job.yaml deleted file mode 100644 index 2ce41b29e4..0000000000 --- a/infrastructure/kube/lcl/keystore-configmap-job.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: keystore-configmap-job -spec: - template: - metadata: - name: batch-configmap-job - spec: - containers: - - name: batch-configmap-job - image: gcr.io/google_containers/busybox - volumeMounts: - - name: keystore-configmap-volume - mountPath: /keystore - command: ["cat", "$(KEEP_ETHEREUM_KEYFILE)"] - env: - - name: KEEP_ETHEREUM_ACCOUNT - value: "8b99e241b3a65030661cf8788de8e5ca45c48f2b" - - name: KEEP_ETHEREUM_KEYFILE - value: "/keystore/8b99e241b3a65030661cf8788de8e5ca45c48f2b" - volumes: - - name: keystore-configmap-volume - configMap: - name: 8b99e241b3a65030661cf8788de8e5ca45c48f2b - items: - - key: 8b99e241b3a65030661cf8788de8e5ca45c48f2b - path: 8b99e241b3a65030661cf8788de8e5ca45c48f2b - restartPolicy: Never - backoffLimit: 4 diff --git a/infrastructure/kube/lcl/miner-nodes.yaml b/infrastructure/kube/lcl/miner-nodes.yaml deleted file mode 100644 index 26081ab984..0000000000 --- a/infrastructure/kube/lcl/miner-nodes.yaml +++ /dev/null @@ -1,89 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: miner-node - labels: - app: geth - type: miner -spec: - ports: - - port: 8545 - targetPort: 8545 - name: tcp-8545 - - port: 8546 - targetPort: 8546 - name: tcp-8546 - - port: 30303 - targetPort: 30303 - name: tcp-30303 - - port: 30303 - targetPort: 30303 - name: udp-30303 - protocol: UDP - selector: - app: geth - type: miner ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: miner-node -spec: - replicas: 1 # must be 1 to utilize local persistent volume (see docs) - template: - metadata: - labels: - app: geth - type: miner - spec: - imagePullSecrets: - - name: google-container-registry-auth - containers: - - name: miner - image: gcr.io/keep-dev-fe24/eth-geth-node:latest - volumeMounts: - - mountPath: "/hostvolume" - name: hostvolume - ports: - - containerPort: 8545 - - containerPort: 8546 - - containerPort: 30303 - env: - - name: INSTANCE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: NETWORKID - value: "1101" - - name: WS_SERVER - value: ws://dashboard:3000 - - name: WS_SECRET - value: BANZAI!!!! - - name: BOOTNODE_URL - value: http://dashboard:3001 - - name: BOOTNODE_NETWORK - value: network_1 - - name: BOOTNODE_PUBLIC_IP - value: dashboard - - name: ENABLE_MINER - value: "1" - - name: MINER_THREADS - value: "1" - - name: HOSTVOLUME - value: "/hostvolume" - - name: ETH_IPC_PATH - value: "/root/.geth/geth.ipc" - volumes: - - name: hostvolume - hostPath: - path: /tmp/k8-volumes/miner - type: DirectoryOrCreate diff --git a/infrastructure/kube/lcl/tx-nodes.yaml b/infrastructure/kube/lcl/tx-nodes.yaml deleted file mode 100644 index a00211310a..0000000000 --- a/infrastructure/kube/lcl/tx-nodes.yaml +++ /dev/null @@ -1,85 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: tx-node - labels: - app: geth - type: tx -spec: - ports: - - port: 8545 - targetPort: 8545 - name: tcp-8545 - - port: 8546 - targetPort: 8546 - name: tcp-8546 - - port: 30303 - targetPort: 30303 - name: tcp-30303 - # - port: 30303 - # targetPort: 30303 - # name: udp-30303 - # protocol: UDP - selector: - app: geth - type: tx - type: LoadBalancer ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: tx-node -spec: - replicas: 1 # must be 1 to utilize local persistent volume (see docs) - template: - metadata: - labels: - app: geth - type: tx - spec: - imagePullSecrets: - - name: google-container-registry-auth - containers: - - name: tx - image: gcr.io/keep-dev-fe24/eth-geth-node:latest - volumeMounts: - - mountPath: "/hostvolume" - name: hostvolume - ports: - - containerPort: 8545 - - containerPort: 8546 - - containerPort: 30303 - env: - - name: INSTANCE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: HOST_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: NETWORKID - value: "1101" - - name: WS_SERVER - value: ws://dashboard:3000 - - name: WS_SECRET - value: BANZAI!!!! - - name: BOOTNODE_URL - value: http://dashboard:3001 - - name: BOOTNODE_NETWORK - value: network_1 - - name: BOOTNODE_PUBLIC_IP - value: dashboard - - name: HOSTVOLUME - value: "/hostvolume" - - name: ETH_IPC_PATH - value: "/root/.geth/geth.ipc" - volumes: - - name: hostvolume - hostPath: - path: /tmp/k8-volumes/tx - type: DirectoryOrCreate diff --git a/infrastructure/kube/templates/bitcoin/bitcoind/.env.sample b/infrastructure/kube/templates/bitcoin/bitcoind/.env.sample deleted file mode 100644 index fd955ccfd0..0000000000 --- a/infrastructure/kube/templates/bitcoin/bitcoind/.env.sample +++ /dev/null @@ -1,2 +0,0 @@ -rpc-user= -rpc-password= diff --git a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-service.yaml b/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-service.yaml deleted file mode 100644 index c2513a87bf..0000000000 --- a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: bitcoind -spec: - type: ClusterIP - ports: - - name: rpc - port: 8332 - targetPort: rpc - - name: network - port: 8333 - targetPort: network diff --git a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-statefulset.yaml b/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-statefulset.yaml deleted file mode 100644 index 3b1968f487..0000000000 --- a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-statefulset.yaml +++ /dev/null @@ -1,92 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: bitcoind -spec: - replicas: 1 - serviceName: bitcoind - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - # https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods - fsGroupChangePolicy: "OnRootMismatch" - containers: - - name: bitcoind - image: keepnetwork/bitcoind:24.1 - imagePullPolicy: Always - command: - - bitcoind - - -chain=$(CHAIN) - - -datadir=/mnt/bitcoind/data - - -rpcport=8332 - - -port=8333 - - -rpcbind=0.0.0.0 - - -rpcallowip=0.0.0.0/0 - - -rpcuser=$(RPC_USER) - - -rpcpassword=$(RPC_PASSWORD) - - -disablewallet=1 - - -txindex=1 - env: - - name: RPC_USER - valueFrom: - secretKeyRef: - name: bitcoind - key: rpc-user - - name: RPC_PASSWORD - valueFrom: - secretKeyRef: - name: bitcoind - key: rpc-password - - name: CHAIN - valueFrom: - configMapKeyRef: - name: bitcoind - key: chain - ports: - - name: rpc - containerPort: 8332 - protocol: TCP - - name: network - containerPort: 8333 - protocol: TCP - livenessProbe: - tcpSocket: - port: rpc - initialDelaySeconds: 20 - periodSeconds: 10 - # TODO: Define readiness probe based on: https://bitcoin.stackexchange.com/a/103370 - # readinessProbe: - # exec: - # command: - # - bitcoin-cli - # - getblockcount - # initialDelaySeconds: 20 - # periodSeconds: 15 - resources: - requests: - cpu: 100m - memory: 2Gi - volumeMounts: - - mountPath: /mnt/bitcoind/data - name: bitcoind-data - volumes: - - name: bitcoind-data - persistentVolumeClaim: - claimName: bitcoind-data - volumeClaimTemplates: - - metadata: - name: bitcoind-data - spec: - storageClassName: bitcoind - accessModes: - - ReadWriteOnce - resources: - requests: - # Estimated required storage based on the network: - # - for mainnet: 650 Gi (default) - # - for testnet: 40 Gi - storage: 650Gi diff --git a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-storageclass.yaml b/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-storageclass.yaml deleted file mode 100644 index 3f66a4ba3c..0000000000 --- a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-storageclass.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: bitcoind -# Requires Google Compute Engine persistent disk CSI Driver to be enabled on the -# cluster, see: https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/gce-pd-csi-driver -provisioner: pd.csi.storage.gke.io -parameters: - type: pd-ssd - replication-type: none -reclaimPolicy: Retain -allowVolumeExpansion: true -volumeBindingMode: WaitForFirstConsumer diff --git a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-volumesnapshotclass.yaml b/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-volumesnapshotclass.yaml deleted file mode 100644 index 8529d3fa4c..0000000000 --- a/infrastructure/kube/templates/bitcoin/bitcoind/bitcoind-volumesnapshotclass.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: snapshot.storage.k8s.io/v1 -kind: VolumeSnapshotClass -metadata: - name: bitcoind -driver: pd.csi.storage.gke.io -deletionPolicy: Retain diff --git a/infrastructure/kube/templates/bitcoin/bitcoind/kustomization.yaml b/infrastructure/kube/templates/bitcoin/bitcoind/kustomization.yaml deleted file mode 100644 index ea88cfd445..0000000000 --- a/infrastructure/kube/templates/bitcoin/bitcoind/kustomization.yaml +++ /dev/null @@ -1,24 +0,0 @@ -resources: - - bitcoind-service.yaml - - bitcoind-statefulset.yaml - - bitcoind-storageclass.yaml - - bitcoind-volumesnapshotclass.yaml - -commonLabels: - chain: bitcoin - app: bitcoind - -configMapGenerator: - - name: bitcoind - literals: - - chain=main - -secretGenerator: - - name: bitcoind - envs: - - .env.sample - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-service.yaml b/infrastructure/kube/templates/bitcoin/electrumx/electrumx-service.yaml deleted file mode 100644 index 5b618b17c4..0000000000 --- a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-service.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -apiVersion: v1 -kind: Service -metadata: - name: electrumx -spec: - type: LoadBalancer - # Replace the value. - loadBalancerIP: XX.XX.XX.XX - # Expose the service on ports that are proxied by Cloudflare. - # See: https://developers.cloudflare.com/fundamentals/get-started/reference/network-ports/ - ports: - - name: tcp - port: 80 - targetPort: tcp - - name: ssl - port: 443 - targetPort: ssl - - name: ws - port: 8080 - targetPort: ws - - name: wss - port: 8443 - targetPort: wss diff --git a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-statefulset.yaml b/infrastructure/kube/templates/bitcoin/electrumx/electrumx-statefulset.yaml deleted file mode 100644 index 4835c42aec..0000000000 --- a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-statefulset.yaml +++ /dev/null @@ -1,104 +0,0 @@ ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: electrumx -spec: - replicas: 1 - serviceName: electrumx - podManagementPolicy: Parallel - template: - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - # https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#configure-volume-permission-and-ownership-change-policy-for-pods - fsGroupChangePolicy: "OnRootMismatch" - containers: - - name: electrumx - image: lukechilds/electrumx:v1.16.0 # TODO: switch to our image - imagePullPolicy: Always - # Full list of env vars: https://electrumx.readthedocs.io/en/latest/environment.html - env: - - name: COIN - value: BitcoinSegwit - - name: NET - value: mainnet - - name: DAEMON_USER - valueFrom: - secretKeyRef: - name: bitcoind - key: rpc-user - - name: DAEMON_TOKEN - valueFrom: - secretKeyRef: - name: bitcoind - key: rpc-password - - name: DAEMON_HOST - valueFrom: - configMapKeyRef: - name: electrumx - key: daemon-host - - name: DAEMON_URL - value: http://$(DAEMON_USER):$(DAEMON_TOKEN)@$(DAEMON_HOST) - - name: DB_DIRECTORY - value: /mnt/electrum/data - - name: SSL_CERTFILE - value: /mnt/electrum/cert/tls.crt - - name: SSL_KEYFILE - value: /mnt/electrum/cert/tls.key - - name: SERVICES - value: tcp://:50001,ssl://:50002,ws://:50003,wss://:50004,rpc://0.0.0.0:8000 - - name: COST_SOFT_LIMIT - value: "0" - - name: COST_HARD_LIMIT - value: "0" - - name: LOG_LEVEL - value: debug - ports: - - name: tcp - containerPort: 50001 - - name: ssl - containerPort: 50002 - - name: ws - containerPort: 50003 - - name: wss - containerPort: 50004 - - name: rpc - containerPort: 8000 - livenessProbe: - tcpSocket: - port: rpc - initialDelaySeconds: 20 - periodSeconds: 30 - readinessProbe: - tcpSocket: - port: tcp - initialDelaySeconds: 20 - periodSeconds: 30 - resources: - requests: - cpu: 500m - memory: 8Gi - volumeMounts: - - name: electrumx-data - mountPath: /mnt/electrum/data - - name: tbtc-network-cloudflare-origin-cert - mountPath: /mnt/electrum/cert - volumes: - - name: electrumx-data - persistentVolumeClaim: - claimName: electrumx - - name: tbtc-network-cloudflare-origin-cert - secret: - secretName: tbtc-network-cloudflare-origin-cert - volumeClaimTemplates: - - metadata: - name: electrumx-data - spec: - storageClassName: electrumx-v2 - accessModes: [ReadWriteOnce] - resources: - requests: - storage: 450Gi diff --git a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-storageclass.yaml b/infrastructure/kube/templates/bitcoin/electrumx/electrumx-storageclass.yaml deleted file mode 100644 index b83cd30c20..0000000000 --- a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-storageclass.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: electrumx-v2 -# Requires Google Compute Engine persistent disk CSI Driver to be enabled on the -# cluster, see: https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/gce-pd-csi-driver -provisioner: pd.csi.storage.gke.io -parameters: - type: pd-ssd - replication-type: none -reclaimPolicy: Retain -allowVolumeExpansion: true -volumeBindingMode: WaitForFirstConsumer diff --git a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-volumesnapshotclass.yaml b/infrastructure/kube/templates/bitcoin/electrumx/electrumx-volumesnapshotclass.yaml deleted file mode 100644 index 45069c78c1..0000000000 --- a/infrastructure/kube/templates/bitcoin/electrumx/electrumx-volumesnapshotclass.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: snapshot.storage.k8s.io/v1 -# Read more: https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/volume-snapshots#v1 -kind: VolumeSnapshotClass -metadata: - name: electrumx -driver: pd.csi.storage.gke.io -deletionPolicy: Retain diff --git a/infrastructure/kube/templates/bitcoin/electrumx/kustomization.yaml b/infrastructure/kube/templates/bitcoin/electrumx/kustomization.yaml deleted file mode 100644 index d001cae390..0000000000 --- a/infrastructure/kube/templates/bitcoin/electrumx/kustomization.yaml +++ /dev/null @@ -1,19 +0,0 @@ -resources: - - electrumx-service.yaml - - electrumx-statefulset.yaml - - electrumx-storageclass.yaml - - electrumx-volumesnapshotclass.yaml - -commonLabels: - chain: bitcoin - app: electrumx - -configMapGenerator: - - name: electrumx - literals: - - daemon-host=bitcoind:8332 - -generatorOptions: - disableNameSuffixHash: true - annotations: - note: generated diff --git a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/Dockerfile b/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/Dockerfile deleted file mode 100644 index 5fb390f023..0000000000 --- a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM node:11 AS runtime - -WORKDIR /tmp - -COPY ./package.json /tmp/package.json -COPY ./package-lock.json /tmp/package-lock.json - -RUN npm install - -COPY ./TokenStaking.json /tmp/TokenStaking.json -COPY ./KeepToken.json /tmp/KeepToken.json -COPY ./KeepRandomBeaconService.json /tmp/KeepRandomBeaconService.json -COPY ./KeepRandomBeaconOperator.json /tmp/KeepRandomBeaconOperator.json - -COPY ./keep-client-config-template.toml /tmp/keep-client-config-template.toml - -COPY ./provision-keep-client.js /tmp/provision-keep-client.js - -ENTRYPOINT ["node", "./provision-keep-client.js"] diff --git a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/keep-client-config-template.toml b/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/keep-client-config-template.toml deleted file mode 100644 index f328b9c606..0000000000 --- a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/keep-client-config-template.toml +++ /dev/null @@ -1,30 +0,0 @@ -# Values from Kube ConfigMap and set via InitContainer run, do not update manually. - -[ethereum] - URL = "" - URLRPC = "" - - [ethereum.account] - Address = "" - KeyFile = "" - - [ethereum.ContractAddresses] - # Hex-encoded address of KeepRandomBeaconOperator contract - KeepRandomBeaconOperator = "" - - # Hex-encoded address of TokenStaking contract - TokenStaking = "" - - # Hex-encoded address of KeepRandomBeaconService contract. Only needed - KeepRandomBeaconService = "" - -[LibP2P] - Peers = [] - Port = "" - AnnouncedAddresses = [] - -[Storage] - DataDir = "" - -[ClientInfo] - Port = "" diff --git a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/package-lock.json b/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/package-lock.json deleted file mode 100644 index af075441bb..0000000000 --- a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/package-lock.json +++ /dev/null @@ -1,3935 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/plugin-transform-runtime": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz", - "integrity": "sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw==", - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "resolve": "^1.8.1", - "semver": "^5.5.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "@babel/runtime": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.0.tgz", - "integrity": "sha512-qArkXsjJq7H+T86WrIFV0Fnu/tNOkZ4cgXmjkzAu3b/58D5mFIO8JH/y77t7C9q0OdDRdh9s7Ue5GasYssxtXw==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@ethersproject/abi": { - "version": "5.0.0-beta.153", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz", - "integrity": "sha512-aXweZ1Z7vMNzJdLpR1CZUAIgnwjrZeUSvN9syCwlBaEBUFJmFY+HHnfuTI5vIhVs/mRkfJVrbEyl51JZQqyjAg==", - "requires": { - "@ethersproject/address": ">=5.0.0-beta.128", - "@ethersproject/bignumber": ">=5.0.0-beta.130", - "@ethersproject/bytes": ">=5.0.0-beta.129", - "@ethersproject/constants": ">=5.0.0-beta.128", - "@ethersproject/hash": ">=5.0.0-beta.128", - "@ethersproject/keccak256": ">=5.0.0-beta.127", - "@ethersproject/logger": ">=5.0.0-beta.129", - "@ethersproject/properties": ">=5.0.0-beta.131", - "@ethersproject/strings": ">=5.0.0-beta.130" - } - }, - "@ethersproject/address": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.0.2.tgz", - "integrity": "sha512-+rz26RKj7ujGfQynys4V9VJRbR+wpC6eL8F22q3raWMH3152Ha31GwJPWzxE/bEA+43M/zTNVwY0R53gn53L2Q==", - "requires": { - "@ethersproject/bignumber": "^5.0.0", - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/keccak256": "^5.0.0", - "@ethersproject/logger": "^5.0.0", - "@ethersproject/rlp": "^5.0.0", - "bn.js": "^4.4.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.0.5.tgz", - "integrity": "sha512-24ln7PV0g8ZzjcVZiLW9Wod0i+XCmK6zKkAaxw5enraTIT1p7gVOcSXFSzNQ9WYAwtiFQPvvA+TIO2oEITZNJA==", - "requires": { - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/logger": "^5.0.0", - "bn.js": "^4.4.0" - } - }, - "@ethersproject/bytes": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.0.3.tgz", - "integrity": "sha512-AyPMAlY+Amaw4Zfp8OAivm1xYPI8mqiUYmEnSUk1CnS2NrQGHEMmFJFiOJdS3gDDpgSOFhWIjZwxKq2VZpqNTA==", - "requires": { - "@ethersproject/logger": "^5.0.0" - } - }, - "@ethersproject/constants": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.0.2.tgz", - "integrity": "sha512-nNoVlNP6bgpog7pQ2EyD1xjlaXcy1Cl4kK5v1KoskHj58EtB6TK8M8AFGi3GgHTdMldfT4eN3OsoQ/CdOTVNFA==", - "requires": { - "@ethersproject/bignumber": "^5.0.0" - } - }, - "@ethersproject/hash": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.0.2.tgz", - "integrity": "sha512-dWGvNwmVRX2bxoQQ3ciMw46Vzl1nqfL+5R8+2ZxsRXD3Cjgw1dL2mdjJF7xMMWPvPdrlhKXWSK0gb8VLwHZ8Cw==", - "requires": { - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/keccak256": "^5.0.0", - "@ethersproject/logger": "^5.0.0", - "@ethersproject/strings": "^5.0.0" - } - }, - "@ethersproject/keccak256": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.0.2.tgz", - "integrity": "sha512-MbroXutc0gPNYIrUjS4Aw0lDuXabdzI7+l7elRWr1G6G+W0v00e/3gbikWkCReGtt2Jnt4lQSgnflhDwQGcIhA==", - "requires": { - "@ethersproject/bytes": "^5.0.0", - "js-sha3": "0.5.7" - }, - "dependencies": { - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" - } - } - }, - "@ethersproject/logger": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.0.4.tgz", - "integrity": "sha512-alA2LiAy1LdQ/L1SA9ajUC7MvGAEQLsICEfKK4erX5qhkXE1LwLSPIzobtOWFsMHf2yrXGKBLnnpuVHprI3sAw==" - }, - "@ethersproject/properties": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.0.2.tgz", - "integrity": "sha512-FxAisPGAOACQjMJzewl9OJG6lsGCPTm5vpUMtfeoxzAlAb2lv+kHzQPUh9h4jfAILzE8AR1jgXMzRmlhwyra1Q==", - "requires": { - "@ethersproject/logger": "^5.0.0" - } - }, - "@ethersproject/rlp": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.0.2.tgz", - "integrity": "sha512-oE0M5jqQ67fi2SuMcrpoewOpEuoXaD8M9JeR9md1bXRMvDYgKXUtDHs22oevpEOdnO2DPIRabp6MVHa4aDuWmw==", - "requires": { - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/logger": "^5.0.0" - } - }, - "@ethersproject/signing-key": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.3.tgz", - "integrity": "sha512-5QPZaBRGCLzfVMbFb3LcVjNR0UbTXnwDHASnQYfbzwUOnFYHKxHsrcbl/5ONGoppgi8yXgOocKqlPCFycJJVWQ==", - "requires": { - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/logger": "^5.0.0", - "@ethersproject/properties": "^5.0.0", - "elliptic": "6.5.3" - } - }, - "@ethersproject/strings": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.0.2.tgz", - "integrity": "sha512-oNa+xvSqsFU96ndzog0IBTtsRFGOqGpzrXJ7shXLBT7juVeSEyZA/sYs0DMZB5mJ9FEjHdZKxR/rTyBY91vuXg==", - "requires": { - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/constants": "^5.0.0", - "@ethersproject/logger": "^5.0.0" - } - }, - "@ethersproject/transactions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.2.tgz", - "integrity": "sha512-jZp0ZbbJlq4JLZY6qoMzNtp2HQsX6USQposi3ns0MPUdn3OdZJBDtrcO15r/2VS5t/K1e1GE5MI1HmMKlcTbbQ==", - "requires": { - "@ethersproject/address": "^5.0.0", - "@ethersproject/bignumber": "^5.0.0", - "@ethersproject/bytes": "^5.0.0", - "@ethersproject/constants": "^5.0.0", - "@ethersproject/keccak256": "^5.0.0", - "@ethersproject/logger": "^5.0.0", - "@ethersproject/properties": "^5.0.0", - "@ethersproject/rlp": "^5.0.0", - "@ethersproject/signing-key": "^5.0.0" - } - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@truffle/hdwallet-provider": { - "version": "1.0.40", - "resolved": "https://registry.npmjs.org/@truffle/hdwallet-provider/-/hdwallet-provider-1.0.40.tgz", - "integrity": "sha512-6SCzccdiFnlTREeVrGgd+ViVZCLFzrOYEIF/4qyzH2L6ilq/Taps5txKsd+/j8Jzz67ZRAB6utKxwBDv2wfW3A==", - "requires": { - "@trufflesuite/web3-provider-engine": "15.0.13-0", - "@types/web3": "^1.0.20", - "any-promise": "^1.3.0", - "bindings": "^1.5.0", - "ethereum-cryptography": "^0.1.3", - "ethereum-protocol": "^1.0.1", - "ethereumjs-tx": "^1.0.0", - "ethereumjs-util": "^6.1.0", - "ethereumjs-wallet": "^0.6.3", - "source-map-support": "^0.5.19" - } - }, - "@trufflesuite/eth-json-rpc-filters": { - "version": "4.1.2-1", - "resolved": "https://registry.npmjs.org/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz", - "integrity": "sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ==", - "requires": { - "@trufflesuite/eth-json-rpc-middleware": "^4.4.2-0", - "await-semaphore": "^0.1.3", - "eth-query": "^2.1.2", - "json-rpc-engine": "^5.1.3", - "lodash.flatmap": "^4.5.0", - "safe-event-emitter": "^1.0.1" - } - }, - "@trufflesuite/eth-json-rpc-middleware": { - "version": "4.4.2-1", - "resolved": "https://registry.npmjs.org/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz", - "integrity": "sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA==", - "requires": { - "@trufflesuite/eth-sig-util": "^1.4.2", - "btoa": "^1.2.1", - "clone": "^2.1.1", - "eth-json-rpc-errors": "^1.0.1", - "eth-query": "^2.1.2", - "ethereumjs-block": "^1.6.0", - "ethereumjs-tx": "^1.3.7", - "ethereumjs-util": "^5.1.2", - "ethereumjs-vm": "^2.6.0", - "fetch-ponyfill": "^4.0.0", - "json-rpc-engine": "^5.1.3", - "json-stable-stringify": "^1.0.1", - "pify": "^3.0.0", - "safe-event-emitter": "^1.0.1" - }, - "dependencies": { - "eth-json-rpc-errors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz", - "integrity": "sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg==", - "requires": { - "fast-safe-stringify": "^2.0.6" - } - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "@trufflesuite/eth-sig-util": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz", - "integrity": "sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA==", - "requires": { - "ethereumjs-abi": "^0.6.8", - "ethereumjs-util": "^5.1.1" - }, - "dependencies": { - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "@trufflesuite/web3-provider-engine": { - "version": "15.0.13-0", - "resolved": "https://registry.npmjs.org/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.13-0.tgz", - "integrity": "sha512-bgGE2Sg56XMu0dhJl7UMiFfEFSvxW47G0RCQ3schV2kUilLKeqvGHE9z1ElVi8W30F/pF7VDbp5DkprvFC9+HQ==", - "requires": { - "@trufflesuite/eth-json-rpc-filters": "^4.1.2-1", - "@trufflesuite/eth-json-rpc-middleware": "^4.4.2-1", - "@trufflesuite/eth-sig-util": "^1.4.2", - "async": "^2.5.0", - "backoff": "^2.5.0", - "clone": "^2.0.0", - "cross-fetch": "^2.1.0", - "eth-block-tracker": "^4.4.2", - "eth-json-rpc-errors": "^2.0.2", - "eth-json-rpc-infura": "^4.0.1", - "ethereumjs-block": "^1.2.2", - "ethereumjs-tx": "^1.2.0", - "ethereumjs-util": "^5.1.5", - "ethereumjs-vm": "^2.3.4", - "json-stable-stringify": "^1.0.1", - "promise-to-callback": "^1.0.0", - "readable-stream": "^2.2.9", - "request": "^2.85.0", - "semaphore": "^1.0.3", - "ws": "^5.1.1", - "xhr": "^2.2.0", - "xtend": "^4.0.1" - }, - "dependencies": { - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "14.0.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", - "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==" - }, - "@types/pbkdf2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", - "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/secp256k1": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.1.tgz", - "integrity": "sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog==", - "requires": { - "@types/node": "*" - } - }, - "@types/web3": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/web3/-/web3-1.2.2.tgz", - "integrity": "sha512-eFiYJKggNrOl0nsD+9cMh2MLk4zVBfXfGnVeRFbpiZzBE20eet4KLA3fXcjSuHaBn0RnQzwLAGdgzgzdet4C0A==", - "requires": { - "web3": "*" - } - }, - "abstract-leveldown": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", - "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", - "requires": { - "xtend": "~4.0.0" - } - }, - "aes-js": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", - "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" - }, - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "async-eventemitter": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", - "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", - "requires": { - "async": "^2.4.0" - } - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "await-semaphore": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/await-semaphore/-/await-semaphore-0.1.3.tgz", - "integrity": "sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q==" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" - }, - "backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", - "requires": { - "precond": "0.2" - } - }, - "base-x": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", - "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "blakejs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", - "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=" - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "bn.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", - "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==" - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "^3.0.2" - } - }, - "bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "requires": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" - }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "buffer-to-arraybuffer": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", - "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - } - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "checkpoint-store": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", - "integrity": "sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=", - "requires": { - "functional-red-black-tree": "^1.0.1" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "requires": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "dependencies": { - "multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "requires": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - } - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", - "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "content-hash": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", - "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", - "requires": { - "cids": "^0.7.1", - "multicodec": "^0.5.5", - "multihashes": "^0.4.15" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-fetch": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.6.tgz", - "integrity": "sha512-9JZz+vXCmfKUZ68zAptS7k4Nu8e2qcibe7WVZYps7sAgk5R8GYTc+T1WR0v1rlP9HxgARmOX1UTIJZFytajpNA==", - "requires": { - "node-fetch": "^2.6.7", - "whatwg-fetch": "^2.0.4" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - } - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "deferred-leveldown": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", - "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", - "requires": { - "abstract-leveldown": "~2.6.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "requires": { - "iconv-lite": "^0.6.2" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" - } - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eth-block-tracker": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", - "integrity": "sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw==", - "requires": { - "@babel/plugin-transform-runtime": "^7.5.5", - "@babel/runtime": "^7.5.5", - "eth-query": "^2.1.0", - "json-rpc-random-id": "^1.0.1", - "pify": "^3.0.0", - "safe-event-emitter": "^1.0.1" - } - }, - "eth-ens-namehash": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", - "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", - "requires": { - "idna-uts46-hx": "^2.3.1", - "js-sha3": "^0.5.7" - }, - "dependencies": { - "js-sha3": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" - } - } - }, - "eth-json-rpc-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz", - "integrity": "sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA==", - "requires": { - "fast-safe-stringify": "^2.0.6" - } - }, - "eth-json-rpc-infura": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.2.tgz", - "integrity": "sha512-dvgOrci9lZqpjpp0hoC3Zfedhg3aIpLFVDH0TdlKxRlkhR75hTrKTwxghDrQwE0bn3eKrC8RsN1m/JdnIWltpw==", - "requires": { - "cross-fetch": "^2.1.1", - "eth-json-rpc-errors": "^1.0.1", - "eth-json-rpc-middleware": "^4.1.4", - "json-rpc-engine": "^5.1.3" - }, - "dependencies": { - "eth-json-rpc-errors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz", - "integrity": "sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg==", - "requires": { - "fast-safe-stringify": "^2.0.6" - } - } - } - }, - "eth-json-rpc-middleware": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.1.tgz", - "integrity": "sha512-yoSuRgEYYGFdVeZg3poWOwAlRI+MoBIltmOB86MtpoZjvLbou9EB/qWMOWSmH2ryCWLW97VYY6NWsmWm3OAA7A==", - "requires": { - "btoa": "^1.2.1", - "clone": "^2.1.1", - "eth-json-rpc-errors": "^1.0.1", - "eth-query": "^2.1.2", - "eth-sig-util": "^1.4.2", - "ethereumjs-block": "^1.6.0", - "ethereumjs-tx": "^1.3.7", - "ethereumjs-util": "^5.1.2", - "ethereumjs-vm": "^2.6.0", - "fetch-ponyfill": "^4.0.0", - "json-rpc-engine": "^5.1.3", - "json-stable-stringify": "^1.0.1", - "pify": "^3.0.0", - "safe-event-emitter": "^1.0.1" - }, - "dependencies": { - "eth-json-rpc-errors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz", - "integrity": "sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg==", - "requires": { - "fast-safe-stringify": "^2.0.6" - } - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "eth-lib": { - "version": "0.1.29", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", - "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", - "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "nano-json-stream-parser": "^0.1.2", - "servify": "^0.1.12", - "ws": "^3.0.0", - "xhr-request-promise": "^0.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } - } - } - }, - "eth-query": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz", - "integrity": "sha1-1nQdkAAQa1FRDHLbktY2VFam2l4=", - "requires": { - "json-rpc-random-id": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "eth-rpc-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz", - "integrity": "sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg==", - "requires": { - "fast-safe-stringify": "^2.0.6" - } - }, - "eth-sig-util": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz", - "integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=", - "requires": { - "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git", - "ethereumjs-util": "^5.1.1" - }, - "dependencies": { - "ethereumjs-abi": { - "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1cfbb13862f90f0b391d8a699544d5fe4dfb8c7b", - "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", - "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" - }, - "dependencies": { - "ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - } - } - } - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "ethereum-bloom-filters": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz", - "integrity": "sha512-cDcJJSJ9GMAcURiAWO3DxIEhTL/uWqlQnvgKpuYQzYPrt/izuGU+1ntQmHt0IRq6ADoSYHFnB+aCEFIldjhkMQ==", - "requires": { - "js-sha3": "^0.8.0" - } - }, - "ethereum-common": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", - "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==" - }, - "ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "requires": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "ethereum-protocol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz", - "integrity": "sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg==" - }, - "ethereumjs-abi": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", - "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", - "requires": { - "bn.js": "^4.11.8", - "ethereumjs-util": "^6.0.0" - } - }, - "ethereumjs-account": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", - "integrity": "sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA==", - "requires": { - "ethereumjs-util": "^5.0.0", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - }, - "dependencies": { - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "ethereumjs-block": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz", - "integrity": "sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg==", - "requires": { - "async": "^2.0.1", - "ethereum-common": "0.2.0", - "ethereumjs-tx": "^1.2.2", - "ethereumjs-util": "^5.0.0", - "merkle-patricia-tree": "^2.1.2" - }, - "dependencies": { - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "ethereumjs-common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", - "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==" - }, - "ethereumjs-tx": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz", - "integrity": "sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==", - "requires": { - "ethereum-common": "^0.0.18", - "ethereumjs-util": "^5.0.0" - }, - "dependencies": { - "ethereum-common": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", - "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "ethereumjs-util": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", - "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", - "requires": { - "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "0.1.6", - "rlp": "^2.2.3" - } - }, - "ethereumjs-vm": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", - "integrity": "sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw==", - "requires": { - "async": "^2.1.2", - "async-eventemitter": "^0.2.2", - "ethereumjs-account": "^2.0.3", - "ethereumjs-block": "~2.2.0", - "ethereumjs-common": "^1.1.0", - "ethereumjs-util": "^6.0.0", - "fake-merkle-patricia-tree": "^1.0.1", - "functional-red-black-tree": "^1.0.1", - "merkle-patricia-tree": "^2.3.2", - "rustbn.js": "~0.2.0", - "safe-buffer": "^5.1.1" - }, - "dependencies": { - "ethereumjs-block": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz", - "integrity": "sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg==", - "requires": { - "async": "^2.0.1", - "ethereumjs-common": "^1.5.0", - "ethereumjs-tx": "^2.1.1", - "ethereumjs-util": "^5.0.0", - "merkle-patricia-tree": "^2.1.2" - }, - "dependencies": { - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "ethereumjs-tx": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", - "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", - "requires": { - "ethereumjs-common": "^1.5.0", - "ethereumjs-util": "^6.0.0" - } - } - } - }, - "ethereumjs-wallet": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz", - "integrity": "sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA==", - "requires": { - "aes-js": "^3.1.1", - "bs58check": "^2.1.2", - "ethereum-cryptography": "^0.1.3", - "ethereumjs-util": "^6.0.0", - "randombytes": "^2.0.6", - "safe-buffer": "^5.1.2", - "scryptsy": "^1.2.1", - "utf8": "^3.0.0", - "uuid": "^3.3.2" - } - }, - "ethjs-unit": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", - "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", - "requires": { - "bn.js": "4.11.6", - "number-to-bn": "1.7.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" - } - } - }, - "ethjs-util": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", - "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", - "requires": { - "is-hex-prefixed": "1.0.0", - "strip-hex-prefix": "1.0.0" - } - }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - } - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fake-merkle-patricia-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", - "integrity": "sha1-S4w6z7Ugr635hgsfFM2M40As3dM=", - "requires": { - "checkpoint-store": "^1.1.0" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fetch-ponyfill": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz", - "integrity": "sha1-rjzl9zLGReq4fkroeTQUcJsjmJM=", - "requires": { - "node-fetch": "~1.7.1" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "requires": { - "has-symbol-support-x": "^1.4.1" - } - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "http-https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "idna-uts46-hx": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", - "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", - "requires": { - "punycode": "2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" - } - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "immediate": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", - "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-fn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", - "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=" - }, - "is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" - }, - "is-hex-prefixed": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" - } - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "json-rpc-engine": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.3.0.tgz", - "integrity": "sha512-+diJ9s8rxB+fbJhT7ZEf8r8spaLRignLd8jTgQ/h5JSGppAHGtNMZtCoabipCaleR1B3GTGxbXBOqhaJSGmPGQ==", - "requires": { - "eth-rpc-errors": "^3.0.0", - "safe-event-emitter": "^1.0.1" - } - }, - "json-rpc-random-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz", - "integrity": "sha1-uknZat7RRE27jaPSA3SKy7zeyMg=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "keccak": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", - "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", - "requires": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "level-codec": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", - "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" - }, - "level-errors": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", - "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", - "requires": { - "errno": "~0.1.1" - } - }, - "level-iterator-stream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", - "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", - "requires": { - "inherits": "^2.0.1", - "level-errors": "^1.0.3", - "readable-stream": "^1.0.33", - "xtend": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "level-ws": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", - "integrity": "sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos=", - "requires": { - "readable-stream": "~1.0.15", - "xtend": "~2.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "requires": { - "object-keys": "~0.4.0" - } - } - } - }, - "levelup": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", - "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", - "requires": { - "deferred-leveldown": "~1.2.1", - "level-codec": "~7.0.0", - "level-errors": "~1.0.3", - "level-iterator-stream": "~1.3.0", - "prr": "~1.0.1", - "semver": "~5.4.1", - "xtend": "~4.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.flatmap": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", - "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "memdown": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", - "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", - "requires": { - "abstract-leveldown": "~2.7.1", - "functional-red-black-tree": "^1.0.1", - "immediate": "^3.2.3", - "inherits": "~2.0.1", - "ltgt": "~2.2.0", - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "abstract-leveldown": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", - "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", - "requires": { - "xtend": "~4.0.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merkle-patricia-tree": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", - "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", - "requires": { - "async": "^1.4.2", - "ethereumjs-util": "^5.0.0", - "level-ws": "0.0.0", - "levelup": "^1.2.1", - "memdown": "^1.0.0", - "readable-stream": "^2.0.0", - "rlp": "^2.0.0", - "semaphore": ">=1.0.1" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, - "ethereumjs-util": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", - "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", - "requires": { - "bn.js": "^4.11.0", - "create-hash": "^1.1.2", - "elliptic": "^6.5.2", - "ethereum-cryptography": "^0.1.3", - "ethjs-util": "^0.1.3", - "rlp": "^2.0.0", - "safe-buffer": "^5.1.1" - } - } - } - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "requires": { - "dom-walk": "^0.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "mkdirp-promise": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", - "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", - "requires": { - "mkdirp": "*" - } - }, - "mock-fs": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.12.0.tgz", - "integrity": "sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "multicodec": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", - "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", - "requires": { - "varint": "^5.0.0" - } - }, - "multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "requires": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - }, - "dependencies": { - "multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "requires": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - } - } - }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" - }, - "nano-json-stream-parser": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", - "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-gyp-build": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", - "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - }, - "number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", - "requires": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" - }, - "oboe": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", - "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", - "requires": { - "http-https": "^1.0.0" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-timeout": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", - "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", - "requires": { - "p-finally": "^1.0.0" - } - }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-headers": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", - "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise-to-callback": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/promise-to-callback/-/promise-to-callback-1.0.0.tgz", - "integrity": "sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=", - "requires": { - "is-fn": "^1.0.0", - "set-immediate-shim": "^1.0.1" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rlp": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz", - "integrity": "sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg==", - "requires": { - "bn.js": "^4.11.1" - } - }, - "rustbn.js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", - "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-event-emitter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz", - "integrity": "sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg==", - "requires": { - "events": "^3.0.0" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" - }, - "scryptsy": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz", - "integrity": "sha1-oyJfpLJST4AnAHYeKFW987LZIWM=", - "requires": { - "pbkdf2": "^3.0.3" - } - }, - "secp256k1": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", - "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", - "requires": { - "elliptic": "^6.5.2", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semaphore": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", - "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==" - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" - }, - "servify": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", - "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", - "requires": { - "body-parser": "^1.16.0", - "cors": "^2.8.1", - "express": "^4.14.0", - "request": "^2.79.0", - "xhr": "^2.3.3" - } - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", - "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", - "requires": { - "decompress-response": "^3.3.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", - "requires": { - "is-hex-prefixed": "1.0.0" - } - }, - "swarm-js": { - "version": "0.1.40", - "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", - "integrity": "sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA==", - "requires": { - "bluebird": "^3.5.0", - "buffer": "^5.0.5", - "eth-lib": "^0.1.26", - "fs-extra": "^4.0.2", - "got": "^7.1.0", - "mime-types": "^2.1.16", - "mkdirp-promise": "^5.0.1", - "mock-fs": "^4.1.0", - "setimmediate": "^1.0.5", - "tar": "^4.0.2", - "xhr-request": "^1.0.1" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, - "got": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", - "requires": { - "decompress-response": "^3.2.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-plain-obj": "^1.1.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "p-cancelable": "^0.3.0", - "p-timeout": "^1.1.1", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "url-parse-lax": "^1.0.0", - "url-to-options": "^1.0.1" - } - }, - "p-cancelable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - } - } - }, - "tar": { - "version": "4.4.19", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", - "requires": { - "chownr": "^1.1.4", - "fs-minipass": "^1.2.7", - "minipass": "^2.9.0", - "minizlib": "^1.3.3", - "mkdirp": "^0.5.5", - "safe-buffer": "^5.2.1", - "yallist": "^3.1.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" - }, - "tomlify-j0.4": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tomlify-j0.4/-/tomlify-j0.4-3.0.0.tgz", - "integrity": "sha512-2Ulkc8T7mXJ2l0W476YC/A209PR38Nw8PuaCNtk9uI3t1zzFdGQeWYGQvmj2PZkVvRC/Yoi4xQKMRnWc/N29tQ==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } - }, - "url-set-query": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", - "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" - }, - "utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "varint": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", - "integrity": "sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "web3": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.9.tgz", - "integrity": "sha512-Mo5aBRm0JrcNpN/g4VOrDzudymfOnHRC3s2VarhYxRA8aWgF5rnhQ0ziySaugpic1gksbXPe105pUWyRqw8HUA==", - "requires": { - "web3-bzz": "1.2.9", - "web3-core": "1.2.9", - "web3-eth": "1.2.9", - "web3-eth-personal": "1.2.9", - "web3-net": "1.2.9", - "web3-shh": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-bzz": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.9.tgz", - "integrity": "sha512-ogVQr9jHodu9HobARtvUSmWG22cv2EUQzlPeejGWZ7j5h20HX40EDuWyomGY5VclIj5DdLY76Tmq88RTf/6nxA==", - "requires": { - "@types/node": "^10.12.18", - "got": "9.6.0", - "swarm-js": "^0.1.40", - "underscore": "1.9.1" - }, - "dependencies": { - "@types/node": { - "version": "10.17.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.28.tgz", - "integrity": "sha512-dzjES1Egb4c1a89C7lKwQh8pwjYmlOAG9dW1pBgxEk57tMrLnssOfEthz8kdkNaBd7lIqQx7APm5+mZ619IiCQ==" - } - } - }, - "web3-core": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.9.tgz", - "integrity": "sha512-fSYv21IP658Ty2wAuU9iqmW7V+75DOYMVZsDH/c14jcF/1VXnedOcxzxSj3vArsCvXZNe6XC5/wAuGZyQwR9RA==", - "requires": { - "@types/bn.js": "^4.11.4", - "@types/node": "^12.6.1", - "bignumber.js": "^9.0.0", - "web3-core-helpers": "1.2.9", - "web3-core-method": "1.2.9", - "web3-core-requestmanager": "1.2.9", - "web3-utils": "1.2.9" - }, - "dependencies": { - "@types/node": { - "version": "12.12.53", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", - "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==" - } - } - }, - "web3-core-helpers": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.9.tgz", - "integrity": "sha512-t0WAG3orLCE3lqi77ZoSRNFok3VQWZXTniZigDQjyOJYMAX7BU3F3js8HKbjVnAxlX3tiKoDxI0KBk9F3AxYuw==", - "requires": { - "underscore": "1.9.1", - "web3-eth-iban": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-core-method": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.9.tgz", - "integrity": "sha512-bjsIoqP3gs7A/gP8+QeLUCyOKJ8bopteCSNbCX36Pxk6TYfYWNuC6hP+2GzUuqdP3xaZNe+XEElQFUNpR3oyAg==", - "requires": { - "@ethersproject/transactions": "^5.0.0-beta.135", - "underscore": "1.9.1", - "web3-core-helpers": "1.2.9", - "web3-core-promievent": "1.2.9", - "web3-core-subscriptions": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-core-promievent": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.9.tgz", - "integrity": "sha512-0eAUA2zjgXTleSrnc1wdoKQPPIHU6KHf4fAscu4W9kKrR+mqP1KsjYrxY9wUyjNnXxfQ+5M29ipvbiaK8OqdOw==", - "requires": { - "eventemitter3": "3.1.2" - } - }, - "web3-core-requestmanager": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.9.tgz", - "integrity": "sha512-1PwKV2m46ALUnIN5VPPgjOj8yMLJhhqZYvYJE34hTN5SErOkwhzx5zScvo5MN7v7KyQGFnpVCZKKGCiEnDmtFA==", - "requires": { - "underscore": "1.9.1", - "web3-core-helpers": "1.2.9", - "web3-providers-http": "1.2.9", - "web3-providers-ipc": "1.2.9", - "web3-providers-ws": "1.2.9" - } - }, - "web3-core-subscriptions": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.9.tgz", - "integrity": "sha512-Y48TvXPSPxEM33OmXjGVDMzTd0j8X0t2+sDw66haeBS8eYnrEzasWuBZZXDq0zNUsqyxItgBGDn+cszkgEnFqg==", - "requires": { - "eventemitter3": "3.1.2", - "underscore": "1.9.1", - "web3-core-helpers": "1.2.9" - } - }, - "web3-eth": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.9.tgz", - "integrity": "sha512-sIKO4iE9FEBa/CYUd6GdPd7GXt/wISqxUd8PlIld6+hvMJj02lgO7Z7p5T9mZIJcIZJGvZX81ogx8oJ9yif+Ag==", - "requires": { - "underscore": "1.9.1", - "web3-core": "1.2.9", - "web3-core-helpers": "1.2.9", - "web3-core-method": "1.2.9", - "web3-core-subscriptions": "1.2.9", - "web3-eth-abi": "1.2.9", - "web3-eth-accounts": "1.2.9", - "web3-eth-contract": "1.2.9", - "web3-eth-ens": "1.2.9", - "web3-eth-iban": "1.2.9", - "web3-eth-personal": "1.2.9", - "web3-net": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-eth-abi": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.9.tgz", - "integrity": "sha512-3YwUYbh/DMfDbhMWEebAdjSd5bj3ZQieOjLzWFHU23CaLEqT34sUix1lba+hgUH/EN6A7bKAuKOhR3p0OvTn7Q==", - "requires": { - "@ethersproject/abi": "5.0.0-beta.153", - "underscore": "1.9.1", - "web3-utils": "1.2.9" - } - }, - "web3-eth-accounts": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.9.tgz", - "integrity": "sha512-jkbDCZoA1qv53mFcRHCinoCsgg8WH+M0YUO1awxmqWXRmCRws1wW0TsuSQ14UThih5Dxolgl+e+aGWxG58LMwg==", - "requires": { - "crypto-browserify": "3.12.0", - "eth-lib": "^0.2.8", - "ethereumjs-common": "^1.3.2", - "ethereumjs-tx": "^2.1.1", - "scrypt-js": "^3.0.1", - "underscore": "1.9.1", - "uuid": "3.3.2", - "web3-core": "1.2.9", - "web3-core-helpers": "1.2.9", - "web3-core-method": "1.2.9", - "web3-utils": "1.2.9" - }, - "dependencies": { - "eth-lib": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", - "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", - "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "xhr-request-promise": "^0.1.2" - } - }, - "ethereumjs-tx": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", - "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", - "requires": { - "ethereumjs-common": "^1.5.0", - "ethereumjs-util": "^6.0.0" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } - } - }, - "web3-eth-contract": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.9.tgz", - "integrity": "sha512-PYMvJf7EG/HyssUZa+pXrc8IB06K/YFfWYyW4R7ed3sab+9wWUys1TlWxBCBuiBXOokSAyM6H6P6/cKEx8FT8Q==", - "requires": { - "@types/bn.js": "^4.11.4", - "underscore": "1.9.1", - "web3-core": "1.2.9", - "web3-core-helpers": "1.2.9", - "web3-core-method": "1.2.9", - "web3-core-promievent": "1.2.9", - "web3-core-subscriptions": "1.2.9", - "web3-eth-abi": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-eth-ens": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.9.tgz", - "integrity": "sha512-kG4+ZRgZ8I1WYyOBGI8QVRHfUSbbJjvJAGA1AF/NOW7JXQ+x7gBGeJw6taDWJhSshMoEKWcsgvsiuoG4870YxQ==", - "requires": { - "content-hash": "^2.5.2", - "eth-ens-namehash": "2.0.8", - "underscore": "1.9.1", - "web3-core": "1.2.9", - "web3-core-helpers": "1.2.9", - "web3-core-promievent": "1.2.9", - "web3-eth-abi": "1.2.9", - "web3-eth-contract": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-eth-iban": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.9.tgz", - "integrity": "sha512-RtdVvJE0pyg9dHLy0GzDiqgnLnssSzfz/JYguhC1wsj9+Gnq1M6Diy3NixACWUAp6ty/zafyOaZnNQ+JuH9TjQ==", - "requires": { - "bn.js": "4.11.8", - "web3-utils": "1.2.9" - }, - "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - } - } - }, - "web3-eth-personal": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.9.tgz", - "integrity": "sha512-cFiNrktxZ1C/rIdJFzQTvFn3/0zcsR3a+Jf8Y3KxeQDHszQtosjLWptP7bsUmDwEh4hzh0Cy3KpOxlYBWB8bJQ==", - "requires": { - "@types/node": "^12.6.1", - "web3-core": "1.2.9", - "web3-core-helpers": "1.2.9", - "web3-core-method": "1.2.9", - "web3-net": "1.2.9", - "web3-utils": "1.2.9" - }, - "dependencies": { - "@types/node": { - "version": "12.12.53", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", - "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==" - } - } - }, - "web3-net": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.9.tgz", - "integrity": "sha512-d2mTn8jPlg+SI2hTj2b32Qan6DmtU9ap/IUlJTeQbZQSkTLf0u9suW8Vjwyr4poJYXTurdSshE7OZsPNn30/ZA==", - "requires": { - "web3-core": "1.2.9", - "web3-core-method": "1.2.9", - "web3-utils": "1.2.9" - } - }, - "web3-providers-http": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.9.tgz", - "integrity": "sha512-F956tCIj60Ttr0UvEHWFIhx+be3He8msoPzyA44/kfzzYoMAsCFRn5cf0zQG6al0znE75g6HlWVSN6s3yAh51A==", - "requires": { - "web3-core-helpers": "1.2.9", - "xhr2-cookies": "1.1.0" - } - }, - "web3-providers-ipc": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.9.tgz", - "integrity": "sha512-NQ8QnBleoHA2qTJlqoWu7EJAD/FR5uimf7Ielzk4Z2z+m+6UAuJdJMSuQNj+Umhz9L/Ys6vpS1vHx9NizFl+aQ==", - "requires": { - "oboe": "2.1.4", - "underscore": "1.9.1", - "web3-core-helpers": "1.2.9" - } - }, - "web3-providers-ws": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.9.tgz", - "integrity": "sha512-6+UpvINeI//dglZoAKStUXqxDOXJy6Iitv2z3dbgInG4zb8tkYl/VBDL80UjUg3ZvzWG0g7EKY2nRPEpON2TFA==", - "requires": { - "eventemitter3": "^4.0.0", - "underscore": "1.9.1", - "web3-core-helpers": "1.2.9", - "websocket": "^1.0.31" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", - "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" - } - } - }, - "web3-shh": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.9.tgz", - "integrity": "sha512-PWa8b/EaxaMinFaxy6cV0i0EOi2M7a/ST+9k9nhyhCjVa2vzXuNoBNo2IUOmeZ0WP2UQB8ByJ2+p4htlJaDOjA==", - "requires": { - "web3-core": "1.2.9", - "web3-core-method": "1.2.9", - "web3-core-subscriptions": "1.2.9", - "web3-net": "1.2.9" - } - }, - "web3-utils": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.9.tgz", - "integrity": "sha512-9hcpuis3n/LxFzEVjwnVgvJzTirS2S9/MiNAa7l4WOEoywY+BSNwnRX4MuHnjkh9NY25B6QOjuNG6FNnSjTw1w==", - "requires": { - "bn.js": "4.11.8", - "eth-lib": "0.2.7", - "ethereum-bloom-filters": "^1.0.6", - "ethjs-unit": "0.1.6", - "number-to-bn": "1.7.0", - "randombytes": "^2.1.0", - "underscore": "1.9.1", - "utf8": "3.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "eth-lib": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", - "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", - "requires": { - "bn.js": "^4.11.6", - "elliptic": "^6.4.0", - "xhr-request-promise": "^0.1.2" - } - } - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "websocket": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.31.tgz", - "integrity": "sha512-VAouplvGKPiKFDTeCCO65vYHsyay8DqoBSlzIO3fayrfOgU94lQN5a1uWVnFrMLceTJw/+fQXR5PGbUVRaHshQ==", - "requires": { - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "nan": "^2.14.0", - "typedarray-to-buffer": "^3.1.5", - "yaeti": "^0.0.6" - } - }, - "whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xhr": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", - "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", - "requires": { - "global": "~4.3.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "xhr-request": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", - "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", - "requires": { - "buffer-to-arraybuffer": "^0.0.5", - "object-assign": "^4.1.1", - "query-string": "^5.0.1", - "simple-get": "^2.7.0", - "timed-out": "^4.0.1", - "url-set-query": "^1.0.0", - "xhr": "^2.0.4" - } - }, - "xhr-request-promise": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", - "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", - "requires": { - "xhr-request": "^1.1.0" - } - }, - "xhr2-cookies": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", - "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", - "requires": { - "cookiejar": "^2.1.1" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } -} diff --git a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/package.json b/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/package.json deleted file mode 100644 index 1f01db8deb..0000000000 --- a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "dependencies": { - "concat-stream": "^2.0.0", - "toml": "^3.0.0", - "tomlify-j0.4": "^3.0.0", - "@truffle/hdwallet-provider": "^1.0.38", - "web3": "1.2.9" - } -} diff --git a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/provision-keep-client.js b/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/provision-keep-client.js deleted file mode 100755 index 88bb5a83e1..0000000000 --- a/infrastructure/kube/templates/keep-client/initcontainer/provision-keep-client/provision-keep-client.js +++ /dev/null @@ -1,229 +0,0 @@ -const fs = require('fs'); -const toml = require('toml'); -const tomlify = require('tomlify-j0.4'); -const concat = require('concat-stream'); -const Web3 = require('web3'); -const HDWalletProvider = require("@truffle/hdwallet-provider"); - -// ETH host info -const ethRPCUrl = process.env.ETH_RPC_URL -const ethWSUrl = process.env.ETH_WS_URL -const ethNetworkId = process.env.ETH_NETWORK_ID; - -// Contract owner info -const contractOwnerAddress = process.env.CONTRACT_OWNER_ETH_ACCOUNT_ADDRESS; -const authorizer = contractOwnerAddress -const purse = contractOwnerAddress; - -const contractOwnerProvider = new HDWalletProvider(process.env.CONTRACT_OWNER_ETH_ACCOUNT_PRIVATE_KEY, ethRPCUrl); - -const operatorKeyFile = process.env.KEEP_CLIENT_ETH_KEYFILE_PATH; - -// LibP2P network info -const libp2pPeers = [process.env.KEEP_CLIENT_PEERS] -const libp2pPort = Number(process.env.KEEP_CLIENT_PORT) -const libp2pAnnouncedAddresses = [process.env.KEEP_CLIENT_ANNOUNCED_ADDRESSES] - -/* -We override transactionConfirmationBlocks and transactionBlockTimeout because they're -25 and 50 blocks respectively at default. The result of this on small private testnets -is long wait times for scripts to execute. -*/ -const web3_options = { - defaultBlock: 'latest', - defaultGas: 4712388, - transactionBlockTimeout: 25, - transactionConfirmationBlocks: 3, - transactionPollingTimeout: 480 -}; - -const web3 = new Web3(contractOwnerProvider, null, web3_options); - -/* -Each file is sourced directly from the InitContainer. Files are generated by -Truffle during contract and copied to the InitContainer image via Circle. -*/ - -// TokenStaking -const tokenStakingContractJsonFile = '/tmp/TokenStaking.json'; -const tokenStakingContractParsed = JSON.parse(fs.readFileSync(tokenStakingContractJsonFile)); -const tokenStakingContractAbi = tokenStakingContractParsed.abi; -const tokenStakingContractAddress = tokenStakingContractParsed.networks[ethNetworkId].address; -const tokenStakingContract = new web3.eth.Contract(tokenStakingContractAbi, tokenStakingContractAddress); - -// KeepToken -const keepTokenContractJsonFile = '/tmp/KeepToken.json'; -const keepTokenContractParsed = JSON.parse(fs.readFileSync(keepTokenContractJsonFile)); -const keepTokenContractAbi = keepTokenContractParsed.abi; -const keepTokenContractAddress = keepTokenContractParsed.networks[ethNetworkId].address; -const keepTokenContract = new web3.eth.Contract(keepTokenContractAbi, keepTokenContractAddress); - -// keepRandomBeaconService, only contract address for config file create -const keepRandomBeaconServiceJsonFile = '/tmp/KeepRandomBeaconService.json'; -const keepRandomBeaconServiceParsed = JSON.parse(fs.readFileSync(keepRandomBeaconServiceJsonFile)); -const keepRandomBeaconServiceContractAddress = keepRandomBeaconServiceParsed.networks[ethNetworkId].address; - -// KeepRandomBeaconOperator, only contract address for config file create -const keepRandomBeaconOperatorJsonFile = '/tmp/KeepRandomBeaconOperator.json'; -const keepRandomBeaconOperatorParsed = JSON.parse(fs.readFileSync(keepRandomBeaconOperatorJsonFile)); -const keepRandomBeaconOperatorContractAddress = keepRandomBeaconOperatorParsed.networks[ethNetworkId].address; - -async function provisionKeepClient() { - - try { - console.log(`\n<<<<<<<<<<<< Read operator address from key file >>>>>>>>>>>>`) - const operatorAddress = readAddressFromKeyFile(operatorKeyFile) - - console.log(`\n<<<<<<<<<<<< Funding Operator Account ${operatorAddress} >>>>>>>>>>>>`); - await fundOperator(operatorAddress, '10'); - - console.log(`\n<<<<<<<<<<<< Staking Operator Account ${operatorAddress} >>>>>>>>>>>>`); - await stakeOperator(operatorAddress, contractOwnerAddress, authorizer); - - console.log(`\n<<<<<<<<<<<< Authorizing Operator Contract ${keepRandomBeaconOperatorContractAddress} >>>>>>>>>>>>`); - await authorizeOperatorContract(operatorAddress, authorizer); - - console.log('\n<<<<<<<<<<<< Creating keep-client Config File >>>>>>>>>>>>'); - await createKeepClientConfig(operatorAddress); - process.exit() - } - catch(error) { - console.error(error.message); - throw error; - } -}; - -async function isStaked(operatorAddress) { - - console.log('Checking if operator address is staked:'); - let stakedAmount = await tokenStakingContract.methods.balanceOf(operatorAddress).call(); - return stakedAmount != 0; -} - -async function isFunded(operatorAddress) { - - console.log('Checking if operator address has ether:') - let fundedAmount = await web3.utils.fromWei( - await web3.eth.getBalance(operatorAddress), 'ether') - return fundedAmount >= 1; -} - -async function stakeOperator(operatorAddress, contractOwnerAddress, authorizer) { - - let beneficiary = contractOwnerAddress; - let staked = await isStaked(operatorAddress); - - /* - We need to stake only in cases where an operator account is not already staked. If the account - is staked, or the client type is relay-requester we need to exit staking, albeit for different - reasons. In the case where the account is already staked, additional staking will fail. - Clients of type relay-requester don't need to be staked to submit a request, they're acting more - as a consumer of the network, rather than an operator. - */ - if (process.env.KEEP_CLIENT_TYPE === 'relay-requester') { - console.log('Subtype relay-requester set. No staking needed, exiting!'); - return; - } else if (staked === true) { - console.log('Operator account already staked, exiting!'); - return; - } else { - console.log(`Staking 4000000 KEEP tokens on operator account ${operatorAddress}`); - } - - let delegation = '0x' + Buffer.concat([ - Buffer.from(beneficiary.substr(2), 'hex'), - Buffer.from(operatorAddress.substr(2), 'hex'), - Buffer.from(authorizer.substr(2), 'hex') - ]).toString('hex'); - - await keepTokenContract.methods.approveAndCall( - tokenStakingContract.options.address, - formatAmount(4000000, 18), - delegation).send({from: contractOwnerAddress}) - - console.log(`Staked!`); -}; - -async function authorizeOperatorContract(operatorAddress, authorizer) { - - if (process.env.KEEP_CLIENT_TYPE === 'relay-requester') { - console.log('Subtype relay-requester set. No authorization needed, exiting!'); - return; - } else { - console.log(`Authorizing Operator Contract ${keepRandomBeaconOperatorContractAddress} for operator account ${operatorAddress}`); - } - await tokenStakingContract.methods.authorizeOperatorContract( - operatorAddress, - keepRandomBeaconOperatorContractAddress).send({from: authorizer}); - - console.log(`Authorized!`); -}; - -function readAddressFromKeyFile(keyFilePath) { - const keyFile = JSON.parse(fs.readFileSync(keyFilePath, 'utf8')) - - return web3.utils.toHex(keyFile.address) -} - -async function fundOperator(operatorAddress, etherToTransfer) { - - let funded = await isFunded(operatorAddress); - let transferAmount = web3.utils.toWei(etherToTransfer, 'ether'); - - if (funded === true) { - console.log('Operator address is already funded, exiting!'); - return; - } else { - console.log(`Funding account ${operatorAddress} with ${etherToTransfer} ether from purse ${purse}`); - await web3.eth.sendTransaction({from:purse, to:operatorAddress, value:transferAmount}); - console.log(`Account ${operatorAddress} funded!`); - } -}; - -async function createKeepClientConfig() { - - let parsedConfigFile = toml.parse(fs.readFileSync('/tmp/keep-client-config-template.toml', 'utf8')); - - parsedConfigFile.ethereum.URL = ethWSUrl; - parsedConfigFile.ethereum.URLRPC = ethRPCUrl; - - parsedConfigFile.ethereum.account.KeyFile = operatorKeyFile; - - parsedConfigFile.ethereum.ContractAddresses.KeepRandomBeaconOperator = keepRandomBeaconOperatorContractAddress; - parsedConfigFile.ethereum.ContractAddresses.KeepRandomBeaconService = keepRandomBeaconServiceContractAddress; - parsedConfigFile.ethereum.ContractAddresses.TokenStaking = tokenStakingContractAddress; - - parsedConfigFile.LibP2P.Peers = libp2pPeers - parsedConfigFile.LibP2P.Port = libp2pPort - parsedConfigFile.LibP2P.AnnouncedAddresses = libp2pAnnouncedAddresses - - parsedConfigFile.Storage.DataDir = process.env.KEEP_CLIENT_DATA_DIR; - - parsedConfigFile.Metrics.Port = Number(process.env.METRICS_PORT) - - /* - tomlify.toToml() writes our Seed/Port values as a float. The added precision renders our config - file unreadable by the keep-client as it interprets 3919.0 as a string when it expects an int. - Here we format the default rendering to write the config file with Seed/Port values as needed. - */ - let formattedConfigFile = tomlify.toToml(parsedConfigFile, { - space: 2, - replace: (key, value) => { return (key == 'Port') ? value.toFixed(0) : false } - }); - fs.writeFileSync('/mnt/keep-client/config/keep-client-config.toml', formattedConfigFile) - console.log('keep-client config written to /mnt/keep-client/config/keep-client-config.toml'); -}; - -/* -\heimdall aliens numbers. Really though, the approveAndCall function expects numbers -in a particular format, this function facilitates that. -*/ -function formatAmount(amount, decimals) { - return '0x' + web3.utils.toBN(amount).mul(web3.utils.toBN(10).pow(web3.utils.toBN(decimals))).toString('hex'); -}; - -provisionKeepClient().catch(error => { - console.error(error); - process.exit(1); -}); - diff --git a/infrastructure/kube/templates/tbtc-v2-monitoring/README.md b/infrastructure/kube/templates/tbtc-v2-monitoring/README.md deleted file mode 100644 index 9be00f78cc..0000000000 --- a/infrastructure/kube/templates/tbtc-v2-monitoring/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# TBTCv2 system events monitoring - -Base configuration to run [TBTCv2 system events monitoring](https://github.com/keep-network/tbtc-v2/tree/main/monitoring). -It is referenced by environment-specific overlays: -- [`keep-prd` production overlay](../../keep-prd/tbtc-v2-monitoring) -- [`keep-test` test overlay](../../keep-test/tbtc-v2-monitoring) diff --git a/infrastructure/scripts/create-google-container-registry-secret.sh b/infrastructure/scripts/create-google-container-registry-secret.sh deleted file mode 100755 index 13d06cea10..0000000000 --- a/infrastructure/scripts/create-google-container-registry-secret.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -HELP="Usage: ./$(basename $0) -c \nExample: ./$(basename $0) -c docker-for-desktop" - -while getopts ":c:" opt; do - case $opt in - c ) LOCAL_KUBE_CONTEXT=$OPTARG;; - - \?) - echo -e $HELP - exit 1 - esac -done - -if [ $# -eq 0 ] -then - echo -e $HELP - exit 1 -fi - -function use_local_context() { - - kubectl config use-context $LOCAL_KUBE_CONTEXT -} - -function create_google_container_registry_secret() { - - DOCKER_PASSWORD="$(gcloud auth print-access-token)" - DOCKER_EMAIL="$(gcloud info | grep Account | awk '{print $2}' | tr -d "[]")" - - kubectl create secret docker-registry google-container-registry-auth \ - --docker-server=https://gcr.io \ - --docker-username=oauth2accesstoken \ - --docker-password=$DOCKER_PASSWORD \ - --docker-email=$DOCKER_EMAIL -} - -echo "Setting kube context to local:" -use_local_context -echo "----------------" - -echo "Creating secret for accessing Google private container registry:" -create_google_container_registry_secret \ No newline at end of file diff --git a/infrastructure/scripts/download-gke-creds.sh b/infrastructure/scripts/download-gke-creds.sh deleted file mode 100755 index 11468a0bb7..0000000000 --- a/infrastructure/scripts/download-gke-creds.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -HELP="Usage: ./$(basename $0) -e -r " - -while getopts ":e:r:" opt; do - case $opt in - e ) ENVIRONMENT=$OPTARG;; - r ) REGION=$OPTARG;; - - \?) - echo -e $HELP - exit 1 - esac -done - -if [ $# -eq 0 ] -then - echo -e $HELP - exit 1 -fi - -function download_gke_creds() { - - PROJECT_ID=`gcloud projects list | grep -i $ENVIRONMENT | awk '{print $1}'` - CLUSTER_NAME=`gcloud container clusters list --project $PROJECT_ID | grep -i $ENVIRONMENT | awk '{print $1}'` - - gcloud container clusters get-credentials $CLUSTER_NAME --region $REGION --project $PROJECT_ID --internal-ip -} - -download_gke_creds \ No newline at end of file diff --git a/infrastructure/scripts/download-gke-secrets.sh b/infrastructure/scripts/download-gke-secrets.sh deleted file mode 100755 index 023c79af31..0000000000 --- a/infrastructure/scripts/download-gke-secrets.sh +++ /dev/null @@ -1,20 +0,0 @@ -# Bare script to download each secret from a cluster. -# This assumes you're using the intended Kube context. -# This assumes you're on the correct VPN for that context. - -# Downloaded secrets will have key values base64 encoded. -# The last applied actuals should be in metadata. - -# If you want decoded values in one swoop, third party -# tooling is required. e.g. https://github.com/ashleyschuett/kubernetes-secret-decode - -CURRENT_CONTEXT=$(kubectl config current-context) - -printf "current kube context: [${CURRENT_CONTEXT}]\n\n" -printf "SECRETS TO BE DOWNLOADED:\n" - -kubectl get secret --no-headers - -kubectl get secret --no-headers | awk '{print $1}' | \ - xargs -I{} sh -c 'kubectl get secret -o yaml "$1" > "$1.yaml"' - {} - \ No newline at end of file diff --git a/infrastructure/terraform/keep-dev/backend.tf b/infrastructure/terraform/keep-dev/backend.tf deleted file mode 100644 index 2b7003eb29..0000000000 --- a/infrastructure/terraform/keep-dev/backend.tf +++ /dev/null @@ -1,6 +0,0 @@ -terraform { - backend "gcs" { - bucket = "keep-dev-tf-backend-bucket" - prefix = "terraform/state" - } -} diff --git a/infrastructure/terraform/keep-dev/config-files/jupyterhub-values.yaml.tmpl b/infrastructure/terraform/keep-dev/config-files/jupyterhub-values.yaml.tmpl deleted file mode 100644 index 734cb34d7e..0000000000 --- a/infrastructure/terraform/keep-dev/config-files/jupyterhub-values.yaml.tmpl +++ /dev/null @@ -1,8 +0,0 @@ -auth: - type: google - google: - clientId: "42518614489-elr1i0khrg215fo6ic7iqp20e5i7kdat.apps.googleusercontent.com" - clientSecret: ${clientSecret} - callbackUrl: "http://jupyterhub.research.keep.network/hub/oauth_callback" - hostedDomain: "thesis.co" - loginService: "Google" diff --git a/infrastructure/terraform/keep-dev/dns.tf b/infrastructure/terraform/keep-dev/dns.tf deleted file mode 100644 index 6dae4314fd..0000000000 --- a/infrastructure/terraform/keep-dev/dns.tf +++ /dev/null @@ -1,15 +0,0 @@ -resource "google_dns_managed_zone" "dev_keep_network" { - project = "${module.project.project_id}" - description = "keep-dev subdomain for hosts who will be accessed from the outside world." - name = "dev-keep-network" - dns_name = "dev.keep.network." - labels = "${local.labels}" -} - -resource "google_dns_managed_zone" "dev_tbtc_network" { - project = "${module.project.project_id}" - description = "tbtc-dev subdomain for hosts who will be accessed from the outside world." - name = "dev-tbtc-network" - dns_name = "dev.tbtc.network." - labels = "${local.labels}" -} diff --git a/infrastructure/terraform/keep-dev/iam.tf b/infrastructure/terraform/keep-dev/iam.tf deleted file mode 100644 index cdeca639aa..0000000000 --- a/infrastructure/terraform/keep-dev/iam.tf +++ /dev/null @@ -1,13 +0,0 @@ -module "iam_members_editor" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_iam_member" - project = "${module.project.project_id}" - role = "${var.editor_iam_role}" - members = "${var.editor_iam_members}" -} - -module "iam_members_storage_objectviewer" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_iam_member" - project = "${module.project.project_id}" - role = "${var.storage_objectviewer_iam_role}" - members = "${var.storage_objectviewer_iam_members}" -} diff --git a/infrastructure/terraform/keep-dev/jupyterhub.tf b/infrastructure/terraform/keep-dev/jupyterhub.tf deleted file mode 100644 index 34941d93ce..0000000000 --- a/infrastructure/terraform/keep-dev/jupyterhub.tf +++ /dev/null @@ -1,38 +0,0 @@ -data "template_file" "jupyterhub_values" { - template = "${file("${path.module}/config-files/jupyterhub-values.yaml.tmpl")}" - - vars = { - clientSecret = "${data.kubernetes_secret.jupyter_oauth_key.data.jupyter-oauth-key}" - } -} - -data "helm_repository" "jupyterhub" { - name = "jupyterhub" - url = "https://jupyterhub.github.io/helm-chart/" -} - -data "kubernetes_secret" "jupyter_oauth_key" { - metadata { - name = "jupyter-oauth-key" - } -} - -resource "helm_release" "jupyterhub" { - name = "helm-jupyterhub" - namespace = "default" - repository = "${data.helm_repository.jupyterhub.metadata.0.name}" - chart = "jupyterhub" - version = "0.8.2" - - values = ["${data.template_file.jupyterhub_values.rendered}"] - - set { - name = "proxy.secretToken" - value = "${random_string.proxy_secrettoken.result}" - } -} - -resource "random_string" "proxy_secrettoken" { - length = 32 - special = true -} diff --git a/infrastructure/terraform/keep-dev/main.tf b/infrastructure/terraform/keep-dev/main.tf deleted file mode 100644 index f436f8eb57..0000000000 --- a/infrastructure/terraform/keep-dev/main.tf +++ /dev/null @@ -1,249 +0,0 @@ -/* Set your locals. - * Terraform doesn't allow for string interpolation in variable maps. - * We cheat it by defining a local. A local instance variable mapping - * allows for string interpolation in maps. Locals are also good for - * names who are a construct of multiple values, to keep module blocks - * clean. -*/ -locals { - public_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-pub-${var.region_data["region"]}" - private_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-pri-${var.region_data["region"]}" - gke_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-gke-${var.region_data["region"]}" - service_account_prefix = "serviceAccount" - - labels { - contact = "${var.contacts}" - environment = "${var.environment}" - vertical = "${var.vertical}" - } -} - -module "project" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_project" - name = "${var.project_name}" - org_id = "${var.gcp_thesis_org_id}" - billing_account = "${var.gcp_thesis_billing_account}" - project_owner_members = "${var.project_owner_members}" - labels = "${local.labels}" -} - -# Remote state storage bucket -module "backend_bucket" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_bucket" - name = "${var.backend_bucket_name}" - project = "${module.project.project_id}" - location = "${var.region_data["region"]}" - labels = "${local.labels}" -} - -# Create vpc and primary subnets -module "vpc" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_vpc" - vpc_network_name = "${var.vpc_network_name}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - routing_mode = "${var.routing_mode}" - - public_subnet_name = "${local.public_subnet_name}" - public_subnet_ip_cidr_range = "${var.public_subnet_ip_cidr_range}" - - private_subnet_name = "${local.private_subnet_name}" - private_subnet_ip_cidr_range = "${var.private_subnet_ip_cidr_range}" -} - -module "nat_gateway_external_ips" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_ip" - name = "${var.nat_gateway_ip_name}" - count = "${var.nat_gateway_ip_allocation_count}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - address_type = "${var.nat_gateway_ip_address_type}" - labels = "${local.labels}" -} - -module "nat_gateway_zone_a" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_a"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${module.nat_gateway_external_ips.ip_address_name[0]}" # Here's an example of taking a value from a list. - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_b" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_b"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${module.nat_gateway_external_ips.ip_address_name[1]}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_c" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_c"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${module.nat_gateway_external_ips.ip_address_name[2]}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -# create gke cluster -module "gke_cluster" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_gke" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - vpc_network_name = "${module.vpc.vpc_network_name}" - - gke_subnet { - name = "${local.gke_subnet_name}" - primary_ip_cidr_range = "${var.gke_subnet["primary_ip_cidr_range"]}" - services_secondary_range_name = "${var.gke_subnet["services_secondary_range_name"]}" - services_secondary_ip_cidr_range = "${var.gke_subnet["services_secondary_ip_cidr_range"]}" - cluster_secondary_range_name = "${var.gke_subnet["cluster_secondary_range_name"]}" - cluster_secondary_ip_cidr_range = "${var.gke_subnet["cluster_secondary_ip_cidr_range"]}" - } - - gke_cluster { - name = "${var.gke_cluster["name"]}" - private_cluster = "${var.gke_cluster["private_cluster"]}" - master_ipv4_cidr_block = "${var.gke_cluster["master_ipv4_cidr_block"]}" - daily_maintenance_window_start_time = "${var.gke_cluster["daily_maintenance_window_start_time"]}" - network_policy_enabled = "${var.gke_cluster["network_policy_enabled"]}" - network_policy_provider = "${var.gke_cluster["network_policy_provider"]}" - logging_service = "${var.gke_cluster["logging_service"]}" - } - - gke_node_pool { - name = "${var.gke_node_pool["name"]}" - node_count = "${var.gke_node_pool["node_count"]}" - machine_type = "${var.gke_node_pool["machine_type"]}" - disk_type = "${var.gke_node_pool["disk_type"]}" - disk_size_gb = "${var.gke_node_pool["disk_size_gb"]}" - oauth_scopes = "${var.gke_node_pool["oauth_scopes"]}" - auto_repair = "${var.gke_node_pool["auto_repair"]}" - auto_upgrade = "${var.gke_node_pool["auto_upgrade"]}" - tags = "${module.nat_gateway_zone_a.routing_tag_regional}" - } - - labels = "${local.labels}" -} - -/* Using this module will create a data read and an update for the - * prometheus-to-sd resource on each Terraform planand apply run. These - * updates will do nothing and are an artifact of the depends_on in the - * modules data resource. Terraform team is aware and have a proposed fix - * in the works. -*/ -module "gke_cluster_metrics" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gke_metrics" - namespace = "${var.gke_metrics_namespace}" - - kube_state_metrics { - version = "${var.kube_state_metrics["version"]}" - } - - prometheus_to_sd { - version = "${var.prometheus_to_sd["version"]}" - } -} - -module "openvpn" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/helm_openvpn" - - openvpn { - name = "${var.openvpn["name"]}" - version = "${var.openvpn["version"]}" - } - - openvpn_parameters { - route_all_traffic_through_vpn = "${var.openvpn_parameters["route_all_traffic_through_vpn"]}" - gke_master_ipv4_cidr_address = "${var.openvpn_parameters["gke_master_ipv4_cidr_address"]}" - } -} - -module "pull_deployment_infrastructure" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_pull_deploy" - project = "${module.project.project_id}" - create_ci_publish_to_gcr_service_account = "${var.create_ci_publish_to_gcr_service_account}" - - keel { - name = "${var.keel["name"]}" - namespace = "${var.keel["namespace"]}" - version = "${var.keel["version"]}" - } - - keel_parameters { - helm_provider_enabled = "${var.keel_parameters["helm_provider_enabled"]}" - rbac_install_enabled = "${var.keel_parameters["rbac_install_enabled"]}" - gcr_enabled = "${var.keel_parameters["gcr_enabled"]}" - } -} - -module "push_deployment_infrastructure" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_push_deploy" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - vpc_network_name = "${module.vpc.vpc_network_name}" - vpc_public_subnet_name = "${module.vpc.vpc_public_subnet_name}" - vpc_gke_subnet_name = "${module.gke_cluster.vpc_gke_subnet_name}" - - jumphost { - name = "${var.jumphost["name"]}" - zone = "${var.region_data["zone_a"]}" - tags = "${var.jumphost["tags"]}" - } - - utility_box { - name = "${var.utility_box["name"]}" - machine_type = "${var.utility_box["machine_type"]}" - tools = "${var.utility_box["tools"]}" - zone = "${var.region_data["zone_a"]}" - tags = "${module.nat_gateway_zone_a.routing_tag_regional},${var.utility_box["tags"]}" - } - - labels = "${local.labels}" -} - -resource "google_storage_bucket" "keep_dev_contract_data" { - name = "keep-dev-contract-data" - project = "${module.project.project_id}" - location = "US-CENTRAL1" - storage_class = "REGIONAL" - labels = "${local.labels}" - - versioning { - enabled = true - } -} - -resource "random_id" "ci_get_bucket_object_service_account_random_account_id" { - byte_length = 2 -} - -resource "google_service_account" "ci_get_bucket_object_service_account" { - project = "${module.project.project_id}" - account_id = "ci-get-bucket-object-${random_id.ci_get_bucket_object_service_account_random_account_id.hex}" - display_name = "ci-get-bucket-object" -} - -resource "google_project_iam_member" "ci_get_bucket_object_service_account" { - project = "${module.project.project_id}" - role = "roles/storage.objectViewer" - member = "${local.service_account_prefix}:${google_service_account.ci_get_bucket_object_service_account.email}" -} diff --git a/infrastructure/terraform/keep-dev/outputs.tf b/infrastructure/terraform/keep-dev/outputs.tf deleted file mode 100644 index 27c86fc22a..0000000000 --- a/infrastructure/terraform/keep-dev/outputs.tf +++ /dev/null @@ -1,79 +0,0 @@ -output "contacts" { - value = "${var.contacts}" -} - -output "vertical" { - value = "${var.vertical}" -} - -output "environment" { - value = "${var.environment}" -} - -output "region_data" { - value = "${var.region_data}" -} - -output "project_name" { - value = "${module.project.project_name}" -} - -output "project_id" { - value = "${module.project.project_id}" -} - -output "project_owner_members" { - value = "${var.project_owner_members}" -} - -output "backend_bucket_name" { - value = "${module.backend_bucket.bucket_name}" -} - -output "vpc_network_name" { - value = "${module.vpc.vpc_network_name}" -} - -output "vpc_network_gateway_ip" { - value = "${module.vpc.vpc_network_gateway_ip}" -} - -output "vpc_public_subnet_name" { - value = "${module.vpc.vpc_public_subnet_name}" -} - -output "vpc_private_subnet_name" { - value = "${module.vpc.vpc_private_subnet_name}" -} - -output "nat_gateway_external_ips" { - value = "${module.nat_gateway_external_ips.ip_address_set}" -} - -output "nat_gateway_zone_a_instance" { - value = "${module.nat_gateway_zone_a.instance}" -} - -output "nat_gateway_zone_b_instance" { - value = "${module.nat_gateway_zone_b.instance}" -} - -output "nat_gateway_zone_c_instance" { - value = "${module.nat_gateway_zone_c.instance}" -} - -output "nat_gateway_region_route_tag" { - value = "${module.nat_gateway_zone_a.routing_tag_regional}" -} - -output "nat_gateway_zone_a_route_tag" { - value = "${module.nat_gateway_zone_a.routing_tag_zonal}" -} - -output "nat_gateway_zone_b_route_tag" { - value = "${module.nat_gateway_zone_b.routing_tag_zonal}" -} - -output "nat_gateway_zone_c_route_tag" { - value = "${module.nat_gateway_zone_c.routing_tag_zonal}" -} diff --git a/infrastructure/terraform/keep-dev/provider.tf b/infrastructure/terraform/keep-dev/provider.tf deleted file mode 100644 index 10d7ba75a5..0000000000 --- a/infrastructure/terraform/keep-dev/provider.tf +++ /dev/null @@ -1,53 +0,0 @@ -data "google_client_config" "default" {} - -# Configure the Google Cloud provider -provider "google" { - version = "<= 1.19.0" - region = "${var.region_data["region"]}" -} - -provider "google-beta" { - version = "<= 1.19.0" - region = "${var.region_data["region"]}" -} - -provider "kubernetes" { - version = "<= 1.5.0" - load_config_file = false - host = "https://${var.gke_cluster["master_private_endpoint"]}" - token = "${data.google_client_config.default.access_token}" - cluster_ca_certificate = "${base64decode(module.gke_cluster.cluster_ca_certificate)}" -} - -module "helm_provider_helper" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/helm_tiller_helper" - tiller_namespace_name = "${var.tiller_namespace_name}" -} - -provider "helm" { - version = "<= 0.10.2" - - kubernetes { - host = "https://${var.gke_cluster["master_private_endpoint"]}" - token = "${data.google_client_config.default.access_token}" - cluster_ca_certificate = "${base64decode(module.gke_cluster.cluster_ca_certificate)}" - } - - tiller_image = "gcr.io/kubernetes-helm/tiller:v2.14.2" - service_account = "${module.helm_provider_helper.tiller_service_account}" - override = ["spec.template.spec.automountserviceaccounttoken=true"] - namespace = "${module.helm_provider_helper.tiller_namespace}" - install_tiller = true -} - -provider "null" { - version = "<= 2.0.0" -} - -provider "random" { - version = "<= 2.0.0" -} - -provider "template" { - version = "<= 1.0.0" -} diff --git a/infrastructure/terraform/keep-dev/variables.tf b/infrastructure/terraform/keep-dev/variables.tf deleted file mode 100644 index 2136a9be3e..0000000000 --- a/infrastructure/terraform/keep-dev/variables.tf +++ /dev/null @@ -1,243 +0,0 @@ -# env vars -variable "gcp_thesis_org_id" { - description = "The ID for the organization the project will be created under. Local ENV VAR" -} - -variable "gcp_thesis_billing_account" { - description = "The billing account to associate with your project. Must be associated with org already. Local ENV VAR" -} - -# generic vars -variable "region_data" { - description = "Region and zone info." - - default { - region = "us-central1" - zone_a = "us-central1-a" - zone_b = "us-central1-b" - zone_c = "us-central1-c" - zone_f = "us-central1-f" - } -} - -variable "contacts" { - description = "The person(s) who contribute to this tf stack." - default = "sthompson22" -} - -variable "vertical" { - description = "Name of the vertical that the generated resources belong to. e.g. cfc, keep" - default = "keep" -} - -variable "environment" { - description = "Environment you're creating resources in. Usually project name" - default = "keep-dev" -} - -# project vars -variable "project_name" { - description = "Name for the project." - default = "keep-dev" -} - -variable "project_owner_members" { - description = "List of service and user accounts to add with owner permissions to project." - - default = [ - "user:sloan.thompson@thesis.co", - "user:antonio.salazarcardozo@thesis.co", - ] -} - -# module IAM members: editor -variable "editor_iam_role" { - default = "roles/editor" -} - -variable "editor_iam_members" { - default = ["user:jakub.nowakowski@thesis.co", "user:nicholas.evans@thesis.co", "user:nik.grinkevich@thesis.co", "user:piotr.dyraga@thesis.co", "user:rafal.czajkowski@thesis.co", "user:dymitr.paremski@thesis.co"] -} - -# module IAM members: storage.objectViewer -variable "storage_objectviewer_iam_role" { - default = "roles/storage.objectViewer" -} - -variable "storage_objectviewer_iam_members" { - default = ["user:liam.zebedee@thesis.co"] -} - -# bucket vars -## backend bucket -variable "backend_bucket_name" { - description = "Bucket for storing keep-dev Terraform remote state." - default = "keep-dev-tf-backend-bucket" -} - -# network vars -## vpc vars -### vpc-network -variable "vpc_network_name" { - description = "The name for your vpc-network" - default = "keep-dev-vpc-network" -} - -variable "routing_mode" { - description = "The dynamic router mode for the vpc-network." - default = "regional" -} - -### vpc-subnet -#### public subnet -variable "public_subnet_ip_cidr_range" { - description = "IP address range assigned to the public subnet." - default = "10.0.0.0/16" -} - -#### private subnet -variable "private_subnet_ip_cidr_range" { - description = "IP address range assigned to the private subnet." - default = "10.1.0.0/16" -} - -## nat gateway vars -### external IP address vars -variable "nat_gateway_ip_allocation_count" { - description = "Generate 3 external IPs, one for each NAT instance." - default = "3" -} - -variable "nat_gateway_ip_name" { - description = "The name for your nat gateway IPs." - default = "keep-dev-nat-gateway-external-ip" -} - -variable "nat_gateway_ip_address_type" { - description = "external or internal, for NATs we use external." - default = "external" -} - -# helm provider -variable "tiller_namespace_name" { - default = "tiller" -} - -# gke -variable "gke_cluster" { - description = "The Google managed part of the cluster configuration." - - default { - name = "keep-dev" - private_cluster = true - master_ipv4_cidr_block = "172.16.0.0/28" - master_private_endpoint = "172.16.0.2" - daily_maintenance_window_start_time = "00:00" - network_policy_enabled = false - network_policy_provider = "PROVIDER_UNSPECIFIED" - logging_service = "logging.googleapis.com" - } -} - -variable "gke_node_pool" { - description = "A node pool for the gke cluster." - - default { - name = "default-node-pool" - node_count = "1" - machine_type = "n1-standard-4" - disk_type = "pd-ssd" - disk_size_gb = 100 - auto_repair = "true" - auto_upgrade = "true" - oauth_scopes = "https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring" - } -} - -variable "gke_subnet" { - description = "Subnet for deploying GKE cluster resources." - - default { - primary_ip_cidr_range = "10.2.0.0/16" - - services_secondary_range_name = "keep-dev-gke-services-secondary-range" - services_secondary_ip_cidr_range = "10.102.100.0/24" - - cluster_secondary_range_name = "keep-dev-gke-cluster-secondary-range" - cluster_secondary_ip_cidr_range = "10.102.0.0/20" - } -} - -# gke_metrics -variable "gke_metrics_namespace" { - default = "metrics" -} - -variable "kube_state_metrics" { - default { - version = "0.13.0" - } -} - -variable "prometheus_to_sd" { - default { - version = "0.1.1" - } -} - -# openvpn -variable "openvpn" { - default { - name = "helm-openvpn" - version = "3.13.3" - } -} - -variable "openvpn_parameters" { - default { - route_all_traffic_through_vpn = "false" - gke_master_ipv4_cidr_address = "172.16.0.0" - } -} - -# deployment infrastructure -## pull -variable "create_ci_publish_to_gcr_service_account" { - description = "Create ServiceAccount for CI to publish images to keep-dev GCR." - default = true -} - -variable "keel" { - default { - name = "helm-keel" - namespace = "tiller" - version = "0.8.16" - } -} - -variable "keel_parameters" { - default { - helm_provider_enabled = true - rbac_install_enabled = true - gcr_enabled = true - } -} - -## push - -# gcp_deploy -variable "jumphost" { - default { - name = "keep-dev-jumphost" - tags = "public-subnet" - } -} - -variable "utility_box" { - default { - name = "keep-dev-utility-box" - tags = "gke-subnet" - machine_type = "g1-small" - tools = "kubectl, helm, jq, nodejs, geth" - } -} diff --git a/infrastructure/terraform/keep-prd/backend.tf b/infrastructure/terraform/keep-prd/backend.tf deleted file mode 100644 index b02c4562eb..0000000000 --- a/infrastructure/terraform/keep-prd/backend.tf +++ /dev/null @@ -1,6 +0,0 @@ -terraform { - backend "gcs" { - bucket = "keep-prd-terraform-backend-bucket" - prefix = "terraform/state" - } -} diff --git a/infrastructure/terraform/keep-prd/base.tf b/infrastructure/terraform/keep-prd/base.tf deleted file mode 100644 index 74c1b36c5e..0000000000 --- a/infrastructure/terraform/keep-prd/base.tf +++ /dev/null @@ -1,69 +0,0 @@ -data "google_client_config" "default" {} - -# Configure the Google Cloud provider -provider "google" { - version = "<= 1.19.0" - region = "${var.region_data["region"]}" -} - -provider "google-beta" { - version = "<= 1.19.0" - region = "${var.region_data["region"]}" -} - -provider "null" { - version = "<= 2.0.0" -} - -provider "random" { - version = "<= 2.0.0" -} - -provider "template" { - version = "<= 1.0.0" -} - -/* Set your locals. - * Terraform doesn't allow for string interpolation in variable maps. - * We cheat it by defining a local. A local instance variable mapping - * allows for string interpolation in maps. Locals are also good for - * names who are a construct of multiple values, to keep module blocks - * clean. -*/ -locals { - public_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-pub-${var.region_data["region"]}" - private_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-pri-${var.region_data["region"]}" - gke_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-gke-${var.region_data["region"]}" - - labels { - contact = "${var.contacts}" - environment = "${var.environment}" - vertical = "${var.vertical}" - } -} - -module "project" { - source = "git@github.com:thesis/terraform-google-bootstrap-project.git?ref=0.1.0" - project_name = "${var.project_name}" - org_id = "${var.gcp_thesis_org_id}" - billing_account = "${var.gcp_thesis_billing_account}" - project_owner_members = "${var.project_owner_members}" - project_service_list = "${var.project_service_list}" - location = "${var.region_data["region"]}" - labels = "${local.labels}" -} - -# Create vpc and primary subnets -module "vpc" { - source = "git@github.com:thesis/terraform-google-vpc.git?ref=0.1.0" - vpc_network_name = "${var.vpc_network_name}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - routing_mode = "${var.routing_mode}" - - public_subnet_name = "${local.public_subnet_name}" - public_subnet_ip_cidr_range = "${var.public_subnet_ip_cidr_range}" - - private_subnet_name = "${local.private_subnet_name}" - private_subnet_ip_cidr_range = "${var.private_subnet_ip_cidr_range}" -} diff --git a/infrastructure/terraform/keep-prd/config-files/files/helm-repositories.yaml b/infrastructure/terraform/keep-prd/config-files/files/helm-repositories.yaml deleted file mode 100644 index b54351e382..0000000000 --- a/infrastructure/terraform/keep-prd/config-files/files/helm-repositories.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: "" -generated: "0001-01-01T00:00:00Z" -repositories: -- caFile: "" - certFile: "" - keyFile: "" - name: stable - password: "" - url: https://kubernetes-charts.storage.googleapis.com - username: "" diff --git a/infrastructure/terraform/keep-prd/gke.tf b/infrastructure/terraform/keep-prd/gke.tf deleted file mode 100644 index 9921be65af..0000000000 --- a/infrastructure/terraform/keep-prd/gke.tf +++ /dev/null @@ -1,60 +0,0 @@ -provider "kubernetes" { - version = "= 1.11.1" - load_config_file = false - host = "https://${var.gke_cluster["master_private_endpoint"]}" - token = "${data.google_client_config.default.access_token}" - cluster_ca_certificate = "${base64decode(module.gke_cluster.cluster_ca_certificate)}" -} - -provider "helm" { - version = "= 1.1.1" - repository_config_path = "./config-files/helm-repositories.yaml" - - kubernetes { - host = "https://${var.gke_cluster["master_private_endpoint"]}" - token = "${data.google_client_config.default.access_token}" - cluster_ca_certificate = "${base64decode(module.gke_cluster.cluster_ca_certificate)}" - } -} - -# create gke cluster -module "gke_cluster" { - source = "git@github.com:thesis/terraform-google-kubernetes-engine.git?ref=0.1.0" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - vpc_network_name = "${module.vpc.vpc_network_name}" - - gke_subnet { - name = "${local.gke_subnet_name}" - primary_ip_cidr_range = "${var.gke_subnet["primary_ip_cidr_range"]}" - services_secondary_range_name = "${var.gke_subnet["services_secondary_range_name"]}" - services_secondary_ip_cidr_range = "${var.gke_subnet["services_secondary_ip_cidr_range"]}" - cluster_secondary_range_name = "${var.gke_subnet["cluster_secondary_range_name"]}" - cluster_secondary_ip_cidr_range = "${var.gke_subnet["cluster_secondary_ip_cidr_range"]}" - } - - gke_cluster { - name = "${var.gke_cluster["name"]}" - private_cluster = "${var.gke_cluster["private_cluster"]}" - master_ipv4_cidr_block = "${var.gke_cluster["master_ipv4_cidr_block"]}" - daily_maintenance_window_start_time = "${var.gke_cluster["daily_maintenance_window_start_time"]}" - network_policy_enabled = "${var.gke_cluster["network_policy_enabled"]}" - network_policy_provider = "${var.gke_cluster["network_policy_provider"]}" - logging_service = "${var.gke_cluster["logging_service"]}" - monitoring_service = "${var.gke_cluster["monitoring_service"]}" - } - - gke_node_pool { - name = "${var.gke_node_pool["name"]}" - node_count = "${var.gke_node_pool["node_count"]}" - machine_type = "${var.gke_node_pool["machine_type"]}" - disk_type = "${var.gke_node_pool["disk_type"]}" - disk_size_gb = "${var.gke_node_pool["disk_size_gb"]}" - oauth_scopes = "${var.gke_node_pool["oauth_scopes"]}" - auto_repair = "${var.gke_node_pool["auto_repair"]}" - auto_upgrade = "${var.gke_node_pool["auto_upgrade"]}" - tags = "${module.nat_gateway_zone_a.routing_tag_regional}" - } - - labels = "${local.labels}" -} diff --git a/infrastructure/terraform/keep-prd/nats.tf b/infrastructure/terraform/keep-prd/nats.tf deleted file mode 100644 index 1e4a01965f..0000000000 --- a/infrastructure/terraform/keep-prd/nats.tf +++ /dev/null @@ -1,91 +0,0 @@ -resource "google_compute_address" "nat_gateway_zone_a" { - name = "${var.nat_gateway_ip["zone_a_name"]}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - address_type = "${var.nat_gateway_ip["address_type"]}" - network_tier = "${var.nat_gateway_ip["network_tier"]}" - labels = "${local.labels}" -} - -resource "google_compute_address" "nat_gateway_zone_b" { - name = "${var.nat_gateway_ip["zone_b_name"]}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - address_type = "${var.nat_gateway_ip["address_type"]}" - network_tier = "${var.nat_gateway_ip["network_tier"]}" - labels = "${local.labels}" -} - -resource "google_compute_address" "nat_gateway_zone_c" { - name = "${var.nat_gateway_ip["zone_c_name"]}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - address_type = "${var.nat_gateway_ip["address_type"]}" - network_tier = "${var.nat_gateway_ip["network_tier"]}" - labels = "${local.labels}" -} - -resource "google_compute_address" "nat_gateway_zone_f" { - name = "${var.nat_gateway_ip["zone_f_name"]}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - address_type = "${var.nat_gateway_ip["address_type"]}" - network_tier = "${var.nat_gateway_ip["network_tier"]}" - labels = "${local.labels}" -} - -module "nat_gateway_zone_a" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_a"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${google_compute_address.nat_gateway_zone_a.name}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_b" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_b"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${google_compute_address.nat_gateway_zone_b.name}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_c" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_c"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${google_compute_address.nat_gateway_zone_c.name}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_f" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_f"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${google_compute_address.nat_gateway_zone_f.name}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} diff --git a/infrastructure/terraform/keep-prd/variables.tf b/infrastructure/terraform/keep-prd/variables.tf deleted file mode 100644 index 938bea7630..0000000000 --- a/infrastructure/terraform/keep-prd/variables.tf +++ /dev/null @@ -1,161 +0,0 @@ -# env vars -variable "gcp_thesis_org_id" { - description = "The ID for the organization the project will be created under. Local ENV VAR." -} - -variable "gcp_thesis_billing_account" { - description = "The billing account to associate with your project. Must be associated with org already. Local ENV VAR." -} - -# generic vars -variable "region_data" { - description = "Region and zone info." - - default { - region = "us-central1" - zone_a = "us-central1-a" - zone_b = "us-central1-b" - zone_c = "us-central1-c" - zone_f = "us-central1-f" - } -} - -variable "contacts" { - description = "The person(s) who contribute to this tf stack." - default = "it" -} - -variable "vertical" { - description = "Name of the vertical that the generated resources belong to. e.g. cfc, keep." - default = "keep" -} - -variable "environment" { - description = "Environment you're creating resources in. Usually project name." - default = "keep-prd" -} - -# project vars -variable "project_name" { - description = "Name for the project." - default = "keep-prd" -} - -variable "project_owner_members" { - description = "List of service and user accounts to add with owner permissions to project." - - default = [ - "user:sloan.thompson@thesis.co", - "user:antonio.salazarcardozo@thesis.co", - ] -} - -variable "project_service_list" { - description = "List of google APIs/Services to enable with project creation." - - default = [ - "compute.googleapis.com", - "container.googleapis.com", - "dns.googleapis.com", - ] -} - -# network vars -## vpc vars -### vpc-network -variable "vpc_network_name" { - description = "The name for your vpc-network." - default = "keep-prd-vpc-network" -} - -variable "routing_mode" { - description = "The dynamic router mode for the vpc-network." - default = "regional" -} - -### vpc-subnet -#### public subnet -variable "public_subnet_ip_cidr_range" { - description = "IP address range assigned to the public subnet." - default = "10.0.0.0/16" -} - -#### private subnet -variable "private_subnet_ip_cidr_range" { - description = "IP address range assigned to the private subnet." - default = "10.4.0.0/16" -} - -## nat gateway vars -### external IP address vars - -variable "nat_gateway_ip" { - default { - zone_a_name = "nat-gateway-a" - zone_b_name = "nat-gateway-b" - zone_c_name = "nat-gateway-c" - zone_f_name = "nat-gateway-f" - address_type = "EXTERNAL" - network_tier = "PREMIUM" - } -} - -# gke -variable "gke_cluster" { - description = "The Google managed part of the cluster configuration." - - default { - name = "keep-prd" - private_cluster = true - master_ipv4_cidr_block = "172.16.0.0/28" - master_private_endpoint = "172.16.0.2" - daily_maintenance_window_start_time = "00:00" - network_policy_enabled = false - network_policy_provider = "PROVIDER_UNSPECIFIED" - logging_service = "logging.googleapis.com/kubernetes" - monitoring_service = "monitoring.googleapis.com/kubernetes" - } -} - -variable "gke_node_pool" { - description = "Default node pool for the keep-prd cluster." - - default { - name = "default" - node_count = "2" - machine_type = "n1-standard-4" - disk_type = "pd-ssd" - disk_size_gb = 100 - auto_repair = "true" - auto_upgrade = "true" - oauth_scopes = "https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring" - } -} - -variable "gke_subnet" { - description = "Subnet for deploying GKE cluster resources." - - default { - primary_ip_cidr_range = "10.8.0.0/16" - - services_secondary_range_name = "keep-prd-gke-services-secondary-range" - services_secondary_ip_cidr_range = "10.108.100.0/24" - - cluster_secondary_range_name = "keep-prd-gke-cluster-secondary-range" - cluster_secondary_ip_cidr_range = "10.108.0.0/20" - } -} - -# helm_release openvpn -variable "openvpn" { - description = "Configuration values for the keep-prd VPN server." - - default { - name = "openvpn" - namespace = "default" - helm_chart = "stable/openvpn" - helm_chart_version = "4.2.2" - route_all_traffic_through_vpn = "false" - gke_master_cidr = "172.16.0.0" - } -} diff --git a/infrastructure/terraform/keep-prd/vpn.tf b/infrastructure/terraform/keep-prd/vpn.tf deleted file mode 100644 index 4cb719ace2..0000000000 --- a/infrastructure/terraform/keep-prd/vpn.tf +++ /dev/null @@ -1,18 +0,0 @@ -resource "helm_release" "openvpn" { - name = "${var.openvpn["name"]}" - namespace = "${var.openvpn["namespace"]}" - chart = "${var.openvpn["helm_chart"]}" - version = "${var.openvpn["helm_chart_version"]}" - keyring = "" - - set { - name = "openvpn.redirectGateway" - value = "${var.openvpn["route_all_traffic_through_vpn"]}" - } - - # Netmask is not configurable because GKE requires /28 for master subnet range. - set { - name = "openvpn.serverConf" - value = "push \"route ${var.openvpn["gke_master_cidr"]} 255.255.255.240\"" - } -} diff --git a/infrastructure/terraform/keep-test/apis.tf b/infrastructure/terraform/keep-test/apis.tf deleted file mode 100644 index 19a63f0fb9..0000000000 --- a/infrastructure/terraform/keep-test/apis.tf +++ /dev/null @@ -1,9 +0,0 @@ -resource "google_project_service" "compute" { - project = "${module.project.project_id}" - service = "compute.googleapis.com" -} - -resource "google_project_service" "cloud_dns" { - project = "${module.project.project_id}" - service = "dns.googleapis.com" -} diff --git a/infrastructure/terraform/keep-test/backend.tf b/infrastructure/terraform/keep-test/backend.tf deleted file mode 100644 index 72db75a0c9..0000000000 --- a/infrastructure/terraform/keep-test/backend.tf +++ /dev/null @@ -1,6 +0,0 @@ -terraform { - backend "gcs" { - bucket = "keep-test-tf-backend-bucket" - prefix = "terraform/state" - } -} diff --git a/infrastructure/terraform/keep-test/base.tf b/infrastructure/terraform/keep-test/base.tf deleted file mode 100644 index 573aefd903..0000000000 --- a/infrastructure/terraform/keep-test/base.tf +++ /dev/null @@ -1,78 +0,0 @@ -data "google_client_config" "default" {} - -# Configure the Google Cloud provider -provider "google" { - version = "<= 1.19.0" - region = "${var.region_data["region"]}" -} - -provider "google-beta" { - version = "<= 1.19.0" - region = "${var.region_data["region"]}" -} - -provider "null" { - version = "<= 2.0.0" -} - -provider "random" { - version = "<= 2.0.0" -} - -provider "template" { - version = "<= 1.0.0" -} - -/* Set your locals. - * Terraform doesn't allow for string interpolation in variable maps. - * We cheat it by defining a local. A local instance variable mapping - * allows for string interpolation in maps. Locals are also good for - * names who are a construct of multiple values, to keep module blocks - * clean. -*/ -locals { - public_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-pub-${var.region_data["region"]}" - private_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-pri-${var.region_data["region"]}" - gke_subnet_name = "${var.environment}-${module.vpc.vpc_subnet_prefix}-gke-${var.region_data["region"]}" - - labels { - contact = "${var.contacts}" - environment = "${var.environment}" - vertical = "${var.vertical}" - } -} - -module "project" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_project" - name = "${var.project_name}" - org_id = "${var.gcp_thesis_org_id}" - billing_account = "${var.gcp_thesis_billing_account}" - project_owner_members = "${var.project_owner_members}" - labels = "${local.labels}" -} - -resource "google_storage_bucket" "backend_bucket" { - name = "${var.backend_bucket_name}" - project = "${module.project.project_id}" - location = "${var.region_data["region"]}" - labels = "${local.labels}" - - versioning { - enabled = true - } -} - -# Create vpc and primary subnets -module "vpc" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_vpc" - vpc_network_name = "${var.vpc_network_name}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - routing_mode = "${var.routing_mode}" - - public_subnet_name = "${local.public_subnet_name}" - public_subnet_ip_cidr_range = "${var.public_subnet_ip_cidr_range}" - - private_subnet_name = "${local.private_subnet_name}" - private_subnet_ip_cidr_range = "${var.private_subnet_ip_cidr_range}" -} diff --git a/infrastructure/terraform/keep-test/deployment.tf b/infrastructure/terraform/keep-test/deployment.tf deleted file mode 100644 index 66ca541d21..0000000000 --- a/infrastructure/terraform/keep-test/deployment.tf +++ /dev/null @@ -1,73 +0,0 @@ -locals { - service_account_prefix = "serviceAccount" -} - -module "pull_deployment_infrastructure" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_pull_deploy" - project = "${module.project.project_id}" - create_ci_publish_to_gcr_service_account = "${var.create_ci_publish_to_gcr_service_account}" - - keel { - name = "${var.keel["name"]}" - namespace = "${var.keel["namespace"]}" - version = "${var.keel["version"]}" - } - - keel_parameters { - helm_provider_enabled = "${var.keel_parameters["helm_provider_enabled"]}" - rbac_install_enabled = "${var.keel_parameters["rbac_install_enabled"]}" - gcr_enabled = "${var.keel_parameters["gcr_enabled"]}" - } -} - -module "push_deployment_infrastructure" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_push_deploy" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - vpc_network_name = "${module.vpc.vpc_network_name}" - vpc_public_subnet_name = "${module.vpc.vpc_public_subnet_name}" - vpc_gke_subnet_name = "${module.gke_cluster.vpc_gke_subnet_name}" - - jumphost { - name = "${var.jumphost["name"]}" - zone = "${var.region_data["zone_a"]}" - tags = "${var.jumphost["tags"]}" - } - - utility_box { - name = "${var.utility_box["name"]}" - machine_type = "${var.utility_box["machine_type"]}" - tools = "${var.utility_box["tools"]}" - zone = "${var.region_data["zone_a"]}" - tags = "${module.nat_gateway_zone_a.routing_tag_regional},${var.utility_box["tags"]}" - } - - labels = "${local.labels}" -} - -resource "random_id" "ci_get_bucket_object_service_account_random_account_id" { - byte_length = 2 -} - -resource "google_service_account" "ci_get_bucket_object_service_account" { - project = "${module.project.project_id}" - account_id = "ci-get-bucket-object-${random_id.ci_get_bucket_object_service_account_random_account_id.hex}" - display_name = "ci-get-bucket-object" -} - -resource "google_project_iam_member" "ci_get_bucket_object_service_account" { - project = "${module.project.project_id}" - role = "roles/storage.objectViewer" - member = "${local.service_account_prefix}:${google_service_account.ci_get_bucket_object_service_account.email}" -} - -resource "google_storage_bucket" "keep_contract_data" { - name = "${var.keep_contract_data_bucket_name}" - project = "${module.project.project_id}" - location = "${var.region_data["region"]}" - labels = "${local.labels}" - - versioning { - enabled = true - } -} diff --git a/infrastructure/terraform/keep-test/dns.tf b/infrastructure/terraform/keep-test/dns.tf deleted file mode 100644 index 13f7a5f05b..0000000000 --- a/infrastructure/terraform/keep-test/dns.tf +++ /dev/null @@ -1,15 +0,0 @@ -resource "google_dns_managed_zone" "test_keep_network" { - project = "${module.project.project_id}" - description = "keep-test subdomain for hosts who will be accessed from the outside world." - name = "test-keep-network" - dns_name = "test.keep.network." - labels = "${local.labels}" -} - -resource "google_dns_managed_zone" "test_tbtc_network" { - project = "${module.project.project_id}" - description = "tbtc-test subdomain for hosts who will be accessed from the outside world." - name = "test-tbtc-network" - dns_name = "test.tbtc.network." - labels = "${local.labels}" -} diff --git a/infrastructure/terraform/keep-test/gke.tf b/infrastructure/terraform/keep-test/gke.tf deleted file mode 100644 index 15ae4e123e..0000000000 --- a/infrastructure/terraform/keep-test/gke.tf +++ /dev/null @@ -1,70 +0,0 @@ -provider "kubernetes" { - version = "= 1.5.0" - load_config_file = false - host = "https://${var.gke_cluster["master_private_endpoint"]}" - token = "${data.google_client_config.default.access_token}" - cluster_ca_certificate = "${base64decode(module.gke_cluster.cluster_ca_certificate)}" -} - -module "helm_provider_helper" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/helm_tiller_helper" - tiller_namespace_name = "${var.tiller_namespace_name}" -} - -provider "helm" { - version = "= 0.7.0" - - kubernetes { - host = "https://${var.gke_cluster["master_private_endpoint"]}" - token = "${data.google_client_config.default.access_token}" - cluster_ca_certificate = "${base64decode(module.gke_cluster.cluster_ca_certificate)}" - } - - tiller_image = "gcr.io/kubernetes-helm/tiller:v2.11.0" - service_account = "${module.helm_provider_helper.tiller_service_account}" - override = ["spec.template.spec.automountserviceaccounttoken=true"] - namespace = "${module.helm_provider_helper.tiller_namespace}" - install_tiller = true -} - -# create gke cluster -module "gke_cluster" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_gke" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - vpc_network_name = "${module.vpc.vpc_network_name}" - - gke_subnet { - name = "${local.gke_subnet_name}" - primary_ip_cidr_range = "${var.gke_subnet["primary_ip_cidr_range"]}" - services_secondary_range_name = "${var.gke_subnet["services_secondary_range_name"]}" - services_secondary_ip_cidr_range = "${var.gke_subnet["services_secondary_ip_cidr_range"]}" - cluster_secondary_range_name = "${var.gke_subnet["cluster_secondary_range_name"]}" - cluster_secondary_ip_cidr_range = "${var.gke_subnet["cluster_secondary_ip_cidr_range"]}" - } - - gke_cluster { - name = "${var.gke_cluster["name"]}" - private_cluster = "${var.gke_cluster["private_cluster"]}" - master_ipv4_cidr_block = "${var.gke_cluster["master_ipv4_cidr_block"]}" - daily_maintenance_window_start_time = "${var.gke_cluster["daily_maintenance_window_start_time"]}" - network_policy_enabled = "${var.gke_cluster["network_policy_enabled"]}" - network_policy_provider = "${var.gke_cluster["network_policy_provider"]}" - logging_service = "${var.gke_cluster["logging_service"]}" - monitoring_service = "${var.gke_cluster["monitoring_service"]}" - } - - gke_node_pool { - name = "${var.gke_node_pool["name"]}" - node_count = "${var.gke_node_pool["node_count"]}" - machine_type = "${var.gke_node_pool["machine_type"]}" - disk_type = "${var.gke_node_pool["disk_type"]}" - disk_size_gb = "${var.gke_node_pool["disk_size_gb"]}" - oauth_scopes = "${var.gke_node_pool["oauth_scopes"]}" - auto_repair = "${var.gke_node_pool["auto_repair"]}" - auto_upgrade = "${var.gke_node_pool["auto_upgrade"]}" - tags = "${module.nat_gateway_zone_a.routing_tag_regional}" - } - - labels = "${local.labels}" -} diff --git a/infrastructure/terraform/keep-test/nats.tf b/infrastructure/terraform/keep-test/nats.tf deleted file mode 100644 index e8203969b9..0000000000 --- a/infrastructure/terraform/keep-test/nats.tf +++ /dev/null @@ -1,51 +0,0 @@ -module "nat_gateway_external_ips" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/gcp_ip" - name = "${var.nat_gateway_ip_name}" - count = "${var.nat_gateway_ip_allocation_count}" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - address_type = "${var.nat_gateway_ip_address_type}" - labels = "${local.labels}" -} - -module "nat_gateway_zone_a" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_a"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${module.nat_gateway_external_ips.ip_address_name[0]}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_b" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_b"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${module.nat_gateway_external_ips.ip_address_name[1]}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} - -module "nat_gateway_zone_c" { - source = "GoogleCloudPlatform/nat-gateway/google" - version = "1.2.2" - name = "${module.vpc.vpc_network_name}-" - project = "${module.project.project_id}" - region = "${var.region_data["region"]}" - zone = "${var.region_data["zone_c"]}" - network = "${module.vpc.vpc_network_name}" - subnetwork = "${module.vpc.vpc_public_subnet_self_link}" - ip_address_name = "${module.nat_gateway_external_ips.ip_address_name[2]}" - ssh_fw_rule = false - instance_labels = "${local.labels}" -} diff --git a/infrastructure/terraform/keep-test/variables.tf b/infrastructure/terraform/keep-test/variables.tf deleted file mode 100644 index ca4c647a3c..0000000000 --- a/infrastructure/terraform/keep-test/variables.tf +++ /dev/null @@ -1,233 +0,0 @@ -# env vars -variable "gcp_thesis_org_id" { - description = "The ID for the organization the project will be created under. Local ENV VAR" -} - -variable "gcp_thesis_billing_account" { - description = "The billing account to associate with your project. Must be associated with org already. Local ENV VAR" -} - -# generic vars -variable "region_data" { - type = "map" - description = "Region and zone info." - - default { - region = "us-central1" - zone_a = "us-central1-a" - zone_b = "us-central1-b" - zone_c = "us-central1-c" - zone_f = "us-central1-f" - } -} - -variable "contacts" { - description = "The person(s) who contribute to this tf stack." - default = "sthompson22" -} - -variable "vertical" { - description = "Name of the vertical that the generated resources belong to. e.g. cfc, keep" - default = "keep" -} - -variable "environment" { - description = "Environment you're creating resources in. Usually project name" - default = "keep-test" -} - -# project vars -variable "project_name" { - description = "Name for the project." - default = "keep-test" -} - -variable "project_owner_members" { - description = "List of service and user accounts to add with owner permissions to project." - - default = [ - "user:sloan.thompson@thesis.co", - "user:antonio.salazarcardozo@thesis.co", - "user:markus.fix@thesis.co", - "serviceAccount:terraform-admin@thesis-terraform-admin.iam.gserviceaccount.com", - ] -} - -# bucket vars -## backend bucket -variable "backend_bucket_name" { - description = "Bucket for storing keep-test Terraform remote state." - default = "keep-test-tf-backend-bucket" -} - -# network vars -## vpc vars -### vpc-network -variable "vpc_network_name" { - description = "The name for your vpc-network" - default = "keep-test-vpc-network" -} - -variable "routing_mode" { - description = "The dynamic router mode for the vpc-network." - default = "regional" -} - -### vpc-subnet -#### public subnet -variable "public_subnet_ip_cidr_range" { - description = "IP address range assigned to the public subnet." - default = "10.0.0.0/16" -} - -#### private subnet -variable "private_subnet_ip_cidr_range" { - description = "IP address range assigned to the private subnet." - default = "10.1.0.0/16" -} - -## nat gateway vars -### external IP address vars -variable "nat_gateway_ip_allocation_count" { - description = "Generate 3 external IPs, one for each NAT instance." - default = "3" -} - -variable "nat_gateway_ip_name" { - description = "The name for your nat gateway IPs." - default = "keep-test-nat-gateway-external-ip" -} - -variable "nat_gateway_ip_address_type" { - description = "external or internal, for NATs we use external." - default = "external" -} - -# helm provider -variable "tiller_namespace_name" { - default = "tiller" -} - -# gke -variable "gke_cluster" { - description = "The Google managed part of the cluster configuration." - - default { - name = "keep-test" - private_cluster = true - master_ipv4_cidr_block = "172.16.0.0/28" - master_private_endpoint = "172.16.0.2" - daily_maintenance_window_start_time = "00:00" - network_policy_enabled = false - network_policy_provider = "PROVIDER_UNSPECIFIED" - logging_service = "logging.googleapis.com/kubernetes" - monitoring_service = "monitoring.googleapis.com/kubernetes" - } -} - -variable "gke_node_pool" { - description = "A node pool for the gke cluster." - - default { - name = "default" - node_count = "1" - machine_type = "n1-standard-4" - disk_type = "pd-ssd" - disk_size_gb = 100 - auto_repair = "true" - auto_upgrade = "true" - oauth_scopes = "https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring" - } -} - -variable "gke_subnet" { - description = "Subnet for deploying GKE cluster resources." - - default { - primary_ip_cidr_range = "10.2.0.0/16" - - services_secondary_range_name = "keep-test-gke-services-secondary-range" - services_secondary_ip_cidr_range = "10.102.100.0/24" - - cluster_secondary_range_name = "keep-test-gke-cluster-secondary-range" - cluster_secondary_ip_cidr_range = "10.102.0.0/20" - } -} - -# gke_metrics -variable "gke_metrics_namespace" { - default = "metrics" -} - -variable "kube_state_metrics" { - default { - version = "0.13.0" - } -} - -variable "prometheus_to_sd" { - default { - version = "0.1.1" - } -} - -# openvpn -variable "openvpn" { - default { - name = "helm-openvpn" - version = "3.13.0" - } -} - -variable "openvpn_parameters" { - default { - route_all_traffic_through_vpn = "false" - gke_master_ipv4_cidr_address = "172.16.0.0" - } -} - -# deployment infrastructure -## pull -variable "create_ci_publish_to_gcr_service_account" { - description = "Create ServiceAccount for CI to publish images to keep-test GCR." - default = true -} - -variable "keel" { - default { - name = "helm-keel" - namespace = "tiller" - version = "0.7.7" - } -} - -variable "keel_parameters" { - default { - helm_provider_enabled = true - rbac_install_enabled = true - gcr_enabled = true - } -} - -## push -variable "jumphost" { - default { - name = "keep-test-jumphost" - tags = "public-subnet" - } -} - -variable "utility_box" { - default { - name = "keep-test-utility-box" - tags = "gke-subnet" - machine_type = "g1-small" - tools = "kubectl, helm, jq, npm, geth" - } -} - -## global -variable "keep_contract_data_bucket_name" { - description = "The name for the bucket that we publish compiled contract data to after CI driven migration." - default = "keep-test-contract-data" -} diff --git a/infrastructure/terraform/keep-test/vpn.tf b/infrastructure/terraform/keep-test/vpn.tf deleted file mode 100644 index e8186bd1a3..0000000000 --- a/infrastructure/terraform/keep-test/vpn.tf +++ /dev/null @@ -1,13 +0,0 @@ -module "openvpn" { - source = "git@github.com:thesis/infrastructure.git//terraform/modules/helm_openvpn" - - openvpn { - name = "${var.openvpn["name"]}" - version = "${var.openvpn["version"]}" - } - - openvpn_parameters { - route_all_traffic_through_vpn = "${var.openvpn_parameters["route_all_traffic_through_vpn"]}" - gke_master_ipv4_cidr_address = "${var.openvpn_parameters["gke_master_ipv4_cidr_address"]}" - } -} diff --git a/pkg/altbn128/altbn128_test.go b/pkg/altbn128/altbn128_test.go index 304eff948e..27563fcf96 100644 --- a/pkg/altbn128/altbn128_test.go +++ b/pkg/altbn128/altbn128_test.go @@ -81,3 +81,52 @@ func assertEqual(t *testing.T, n int, n2 int, msg string) { t.Errorf("%v: [%v] != [%v]", msg, n, n2) } } + +// --- Benchmarks --- + +func BenchmarkCompressG1(b *testing.B) { + _, p, err := bn256.RandomG1(rand.Reader) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + G1Point{p}.Compress() + } +} + +func BenchmarkDecompressG1(b *testing.B) { + _, p, err := bn256.RandomG1(rand.Reader) + if err != nil { + b.Fatal(err) + } + buf := G1Point{p}.Compress() + b.ResetTimer() + for range b.N { + _, _ = DecompressToG1(buf) + } +} + +func BenchmarkCompressDecompressRoundTripG1(b *testing.B) { + _, p, err := bn256.RandomG1(rand.Reader) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + buf := G1Point{p}.Compress() + _, _ = DecompressToG1(buf) + } +} + +func BenchmarkCompressDecompressRoundTripG2(b *testing.B) { + _, p, err := bn256.RandomG2(rand.Reader) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + buf := G2Point{p}.Compress() + _, _ = DecompressToG2(buf) + } +} diff --git a/pkg/beacon/dkg/marshalling.go b/pkg/beacon/dkg/marshaling.go similarity index 97% rename from pkg/beacon/dkg/marshalling.go rename to pkg/beacon/dkg/marshaling.go index 8f2b9feed3..34f8d6353b 100644 --- a/pkg/beacon/dkg/marshalling.go +++ b/pkg/beacon/dkg/marshaling.go @@ -1,3 +1,4 @@ +// marshaling.go: protobuf (un)marshaling for the public types in this package. package dkg import ( diff --git a/pkg/beacon/dkg/marshalling_test.go b/pkg/beacon/dkg/marshaling_test.go similarity index 100% rename from pkg/beacon/dkg/marshalling_test.go rename to pkg/beacon/dkg/marshaling_test.go diff --git a/pkg/beacon/dkg/result/marshalling.go b/pkg/beacon/dkg/result/marshaling.go similarity index 95% rename from pkg/beacon/dkg/result/marshalling.go rename to pkg/beacon/dkg/result/marshaling.go index 147e4b5dc2..dc334773aa 100644 --- a/pkg/beacon/dkg/result/marshalling.go +++ b/pkg/beacon/dkg/result/marshaling.go @@ -1,3 +1,4 @@ +// marshaling.go: protobuf (un)marshaling for the public types in this package. package result import ( diff --git a/pkg/beacon/dkg/result/marshalling_test.go b/pkg/beacon/dkg/result/marshaling_test.go similarity index 100% rename from pkg/beacon/dkg/result/marshalling_test.go rename to pkg/beacon/dkg/result/marshaling_test.go diff --git a/pkg/beacon/gjkr/marshaling_test.go b/pkg/beacon/gjkr/marshaling_test.go index d7694bface..b75c8f98dc 100644 --- a/pkg/beacon/gjkr/marshaling_test.go +++ b/pkg/beacon/gjkr/marshaling_test.go @@ -434,3 +434,120 @@ func TestFuzzMisbehavedEphemeralKeysMessageRoundtrip(t *testing.T) { func TestFuzzMisbehavedEphemeralKeysMessageUnmarshaler(t *testing.T) { pbutils.FuzzUnmarshaler(&MisbehavedEphemeralKeysMessage{}) } + +// --- Benchmarks --- + +// buildEphemeralKeyMap generates n key pairs and returns the public key map as +// it would appear in a real EphemeralPublicKeyMessage (one entry per peer). +func buildEphemeralKeyMap(b *testing.B, n int) map[group.MemberIndex]*ephemeral.PublicKey { + b.Helper() + m := make(map[group.MemberIndex]*ephemeral.PublicKey, n) + for i := 0; i < n; i++ { + kp, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + m[group.MemberIndex(i+1)] = kp.PublicKey + } + return m +} + +func BenchmarkMarshalEphemeralPublicKeyMessage(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &EphemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex]*ephemeral.PublicKey{ + group.MemberIndex(211): kp1.PublicKey, + group.MemberIndex(19): kp2.PublicKey, + }, + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +func BenchmarkUnmarshalEphemeralPublicKeyMessage(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &EphemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex]*ephemeral.PublicKey{ + group.MemberIndex(211): kp1.PublicKey, + group.MemberIndex(19): kp2.PublicKey, + }, + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(EphemeralPublicKeyMessage).Unmarshal(data) + } +} + +// The _64Keys benchmarks below (BenchmarkMarshalEphemeralPublicKeyMessage_64Keys +// and BenchmarkUnmarshalEphemeralPublicKeyMessage_64Keys) measure marshal and +// unmarshal cost on a 64-member beacon group (63 peer keys per message) as +// the gjkr package stands today. Unmarshal currently parses every peer key +// eagerly through ephemeral.UnmarshalPublicKey (each call wraps +// btcec.ParsePubKey and dominates the work), so these numbers reflect that +// eager-parsing cost. +// +// pkg/tecdsa/dkg and pkg/tecdsa/signing received a +// deferred-ephemeral-key-parsing optimization in this release cycle that +// turns the per-message cost from O(N^2) over the group into O(1) at +// unmarshal plus O(1) per key on demand. Porting that optimization to gjkr +// is intentionally out of scope here and is tracked as a follow-up +// improvement. The _64Keys benchmarks are kept as a pre-optimization +// baseline so reviewers do not mistake the suffix or the surrounding +// comments for evidence that gjkr already has the optimization. + +// BenchmarkMarshalEphemeralPublicKeyMessage_64Keys benchmarks marshaling with +// the beacon group size (64 members = 63 peer keys per message). +func BenchmarkMarshalEphemeralPublicKeyMessage_64Keys(b *testing.B) { + msg := &EphemeralPublicKeyMessage{ + senderID: group.MemberIndex(1), + ephemeralPublicKeys: buildEphemeralKeyMap(b, 63), + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +// BenchmarkUnmarshalEphemeralPublicKeyMessage_64Keys benchmarks unmarshaling +// with the beacon group size. Each btcec.ParsePubKey call dominates; with 63 +// peers this represents the real per-participant beacon DKG cost. +func BenchmarkUnmarshalEphemeralPublicKeyMessage_64Keys(b *testing.B) { + msg := &EphemeralPublicKeyMessage{ + senderID: group.MemberIndex(1), + ephemeralPublicKeys: buildEphemeralKeyMap(b, 63), + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(EphemeralPublicKeyMessage).Unmarshal(data) + } +} diff --git a/pkg/beacon/registry/marshalling.go b/pkg/beacon/registry/marshaling.go similarity index 92% rename from pkg/beacon/registry/marshalling.go rename to pkg/beacon/registry/marshaling.go index a4460582a6..b4df0f383b 100644 --- a/pkg/beacon/registry/marshalling.go +++ b/pkg/beacon/registry/marshaling.go @@ -1,3 +1,4 @@ +// marshaling.go: protobuf (un)marshaling for the public types in this package. package registry import ( diff --git a/pkg/beacon/registry/marshalling_test.go b/pkg/beacon/registry/marshaling_test.go similarity index 100% rename from pkg/beacon/registry/marshalling_test.go rename to pkg/beacon/registry/marshaling_test.go diff --git a/pkg/bitcoin/electrum/electrum_integration_test.go b/pkg/bitcoin/electrum/electrum_integration_test.go index 382f6d8880..c8bb18312b 100644 --- a/pkg/bitcoin/electrum/electrum_integration_test.go +++ b/pkg/bitcoin/electrum/electrum_integration_test.go @@ -58,7 +58,7 @@ var testConfigs = map[string]testConfig{ URL: "tcp://electrum.blockstream.info:60001", Network: bitcoin.Testnet, RequestTimeout: requestTimeout * 2, - RequestRetryTimeout: requestRetryTimeout * 2, + RequestRetryTimeout: requestRetryTimeout * 6, // allow slower public electrum responses }, network: bitcoin.Testnet, }, @@ -67,7 +67,7 @@ var testConfigs = map[string]testConfig{ URL: "ssl://electrum.blockstream.info:60002", Network: bitcoin.Testnet, RequestTimeout: requestTimeout * 2, - RequestRetryTimeout: requestRetryTimeout * 2, + RequestRetryTimeout: requestRetryTimeout * 6, // allow slower public electrum responses }, network: bitcoin.Testnet, }, diff --git a/pkg/bitcoin/transaction_builder_test.go b/pkg/bitcoin/transaction_builder_test.go index b1acc70567..8ca9f615ce 100644 --- a/pkg/bitcoin/transaction_builder_test.go +++ b/pkg/bitcoin/transaction_builder_test.go @@ -1,6 +1,7 @@ package bitcoin import ( + "encoding/hex" "fmt" "math/big" "reflect" @@ -601,3 +602,80 @@ func TestTransactionBuilder_ComputeSignatureHashesMissingPrevOut(t *testing.T) { t.Fatalf("unexpected error: [%v]", err) } } + +// --- Benchmarks --- + +// witnessP2WPKHTxHex is a P2WPKH transaction whose output[0] (value=35400) +// is used as the UTXO source for ComputeSignatureHashes benchmarks. +// https://live.blockcypher.com/btc-testnet/tx/f8eaf242a55ea15e602f9f990e33f67f99dfbe25d1802bbde63cc1caabf99668 +const witnessP2WPKHTxHex = "01000000000102bc187be612bc3db8cfcdec56b75e9bc0262ab6eacfe27cc1a699bacd53e3d07400000000c948304502210089a89aaf3fec97ac9ffa91cdff59829f0cb3ef852a468153e2c0e2b473466d2e022072902bb923ef016ac52e941ced78f816bf27991c2b73211e227db27ec200bc0a012103989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d94c5c14934b98637ca318a4d6e7ca6ffd1690b8e77df6377508f9f0c90d000395237576a9148db50eb52063ea9d98b3eac91489a90f738986f68763ac6776a914e257eccafbc07c381642ce6e7e55120fb077fbed8804e0250162b175ac68ffffffffdc557e737b6688c5712649b86f7757a722dc3d42786f23b2fa826394dfec545c0000000000ffffffff01488a0000000000001600148db50eb52063ea9d98b3eac91489a90f738986f6000347304402203747f5ee31334b11ebac6a2a156b1584605de8d91a654cd703f9c8438634997402202059d680211776f93c25636266b02e059ed9fcc6209f7d3d9926c49a0d8750ed012103989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d95c14934b98637ca318a4d6e7ca6ffd1690b8e77df6377508f9f0c90d000395237576a9148db50eb52063ea9d98b3eac91489a90f738986f68763ac6776a914e257eccafbc07c381642ce6e7e55120fb077fbed8804e0250162b175ac6800000000" + +// buildSigHashBuilder constructs a TransactionBuilder with n inputs all +// pointing to the same P2WPKH UTXO. ComputeSignatureHashes is non-mutating so +// the same builder can be reused across b.N iterations. +func buildSigHashBuilder(b *testing.B, n int) *TransactionBuilder { + b.Helper() + + txBytes, err := hex.DecodeString(witnessP2WPKHTxHex) + if err != nil { + b.Fatal(err) + } + + tx := new(Transaction) + if err := tx.Deserialize(txBytes); err != nil { + b.Fatal(err) + } + + localChain := newLocalChain() + if err := localChain.addTransaction(tx); err != nil { + b.Fatal(err) + } + + builder := NewTransactionBuilder(localChain) + utxo := &UnspentTransactionOutput{ + Outpoint: &TransactionOutpoint{ + TransactionHash: tx.Hash(), + OutputIndex: 0, + }, + Value: 35400, + } + for i := 0; i < n; i++ { + if err := builder.AddPublicKeyHashInput(utxo); err != nil { + b.Fatal(err) + } + } + return builder +} + +func BenchmarkComputeSignatureHashes_1Input(b *testing.B) { + builder := buildSigHashBuilder(b, 1) + if _, err := builder.ComputeSignatureHashes(); err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _, _ = builder.ComputeSignatureHashes() + } +} + +func BenchmarkComputeSignatureHashes_5Inputs(b *testing.B) { + builder := buildSigHashBuilder(b, 5) + if _, err := builder.ComputeSignatureHashes(); err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _, _ = builder.ComputeSignatureHashes() + } +} + +func BenchmarkComputeSignatureHashes_20Inputs(b *testing.B) { + builder := buildSigHashBuilder(b, 20) + if _, err := builder.ComputeSignatureHashes(); err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _, _ = builder.ComputeSignatureHashes() + } +} diff --git a/pkg/bls/bls_test.go b/pkg/bls/bls_test.go index 0d3a6da03a..330dbf8ce3 100644 --- a/pkg/bls/bls_test.go +++ b/pkg/bls/bls_test.go @@ -2,6 +2,7 @@ package bls import ( "crypto/rand" + "fmt" "math/big" "testing" @@ -185,3 +186,95 @@ func TestThresholdBLS(t *testing.T) { } } + +// --- Benchmarks --- + +func BenchmarkSign(b *testing.B) { + pi, _ := new(big.Int).SetString( + "31415926535897932384626433832795028841971693993751058209749445923078164062862", 10) + message := pi.Bytes() + secretKey := big.NewInt(123) + b.ResetTimer() + for range b.N { + Sign(secretKey, message) + } +} + +func BenchmarkVerify(b *testing.B) { + pi, _ := new(big.Int).SetString( + "31415926535897932384626433832795028841971693993751058209749445923078164062862", 10) + message := pi.Bytes() + secretKey := big.NewInt(123) + publicKey := new(bn256.G2).ScalarBaseMult(secretKey) + signature := Sign(secretKey, message) + b.ResetTimer() + for range b.N { + Verify(publicKey, message, signature) + } +} + +// BenchmarkAggregateBLS benchmarks aggregate signature verification for group +// sizes representative of small committees (10), medium (50), and production +// random beacon groups (100). +func BenchmarkAggregateBLS(b *testing.B) { + pi, _ := new(big.Int).SetString( + "31415926535897932384626433832795028841971693993751058209749445923078164062862", 10) + message := new(bn256.G1).ScalarBaseMult(pi) + + for _, n := range []int{10, 50, 100} { + n := n + var signatures []*bn256.G1 + var publicKeys []*bn256.G2 + for i := 0; i < n; i++ { + k, _, err := bn256.RandomG1(rand.Reader) + if err != nil { + b.Fatal(err) + } + pub := new(bn256.G2).ScalarBaseMult(k) + publicKeys = append(publicKeys, pub) + signatures = append(signatures, SignG1(k, message)) + } + b.Run(fmt.Sprintf("N=%d", n), func(b *testing.B) { + b.ResetTimer() + for range b.N { + aggSig := AggregateG1Points(signatures) + aggPub := AggregateG2Points(publicKeys) + VerifyG1(aggPub, message, aggSig) + } + }) + } +} + +// BenchmarkThresholdVerify benchmarks threshold signature recovery with a +// 51-of-100 configuration representative of production beacon groups. +func BenchmarkThresholdVerify(b *testing.B) { + pi, _ := new(big.Int).SetString( + "31415926535897932384626433832795028841971693993751058209749445923078164062862", 10) + message := new(bn256.G1).ScalarBaseMult(pi) + + const numPlayers = 100 + const threshold = 51 + + var masterSecretKey []*big.Int + var signatureShares []*SignatureShare + + for i := 0; i < threshold; i++ { + sk, _, err := bn256.RandomG2(rand.Reader) + if err != nil { + b.Fatal(err) + } + masterSecretKey = append(masterSecretKey, sk) + } + for i := 1; i <= numPlayers; i++ { + share := GetSecretKeyShare(masterSecretKey, i) + signatureShares = append(signatureShares, &SignatureShare{ + I: i, + V: SignG1(share.V, message), + }) + } + + b.ResetTimer() + for range b.N { + _, _ = RecoverSignature(signatureShares[:threshold], threshold) + } +} diff --git a/pkg/chain/ethereum/bitcoin_difficulty.go b/pkg/chain/ethereum/bitcoin_difficulty.go index 75852f0e6d..dc8e0d5050 100644 --- a/pkg/chain/ethereum/bitcoin_difficulty.go +++ b/pkg/chain/ethereum/bitcoin_difficulty.go @@ -295,9 +295,7 @@ func (bdc *BitcoinDifficultyChain) RetargetWithRefund(headers []*bitcoin.BlockHe ) } - // Add 20% to the gas estimate as the transaction tends to fail with the - // original gas estimate. - gasEstimateWithMargin := float64(gasEstimate) * float64(1.2) + gasEstimateWithMargin := gasEstimateWithMargin(gasEstimate) // Update Bitcoin difficulty via LightRelayMaintainerProxy. tx, err := bdc.lightRelayMaintainerProxy.Retarget( diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index d0f9657cf0..6a94b4f919 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -344,13 +344,13 @@ func (bc *baseChain) GetBlockNumberByTimestamp( return 0, fmt.Errorf("cannot get current block: [%v]", err) } - if block.Time() < timestamp { + if block.Time < timestamp { return 0, fmt.Errorf("requested timestamp is in the future") } // Corner case shortcut. - if block.Time() == timestamp { - return block.NumberU64(), nil + if block.Time == timestamp { + return block.Number.Uint64(), nil } // The Ethereum average block time (https://etherscan.io/chart/blocktime) @@ -366,9 +366,9 @@ func (bc *baseChain) GetBlockNumberByTimestamp( // the better one. const averageBlockTime = 13 - for block.Time() > timestamp { + for block.Time > timestamp { // timeDiff is always >0 due to the for-loop condition. - timeDiff := block.Time() - timestamp + timeDiff := block.Time - timestamp // blockDiff is an integer whose value can be: // - >=1 if timeDiff >= averageBlockTime // - ==0 if timeDiff < averageBlockTime @@ -380,21 +380,21 @@ func (bc *baseChain) GetBlockNumberByTimestamp( break } - block, err = bc.blockByNumber(block.NumberU64() - blockDiff) + block, err = bc.blockByNumber(block.Number.Uint64() - blockDiff) if err != nil { return 0, fmt.Errorf("cannot get block: [%v]", err) } } // Once we quit the above for-loop, the following cases are possible: - // - Case 1: block.Time() < timestamp - // - Case 2: block.Time() > timestamp (difference is < averageBlockTime) - // - Case 3: block.Time() == timestamp + // - Case 1: block.Time < timestamp + // - Case 2: block.Time > timestamp (difference is < averageBlockTime) + // - Case 3: block.Time == timestamp // // First, try to reduce Case 1 by walking forward block by block until // we achieve Case 2 or 3. - for block.Time() < timestamp { - block, err = bc.blockByNumber(block.NumberU64() + 1) + for block.Time < timestamp { + block, err = bc.blockByNumber(block.Number.Uint64() + 1) if err != nil { return 0, fmt.Errorf("cannot get block: [%v]", err) } @@ -402,16 +402,16 @@ func (bc *baseChain) GetBlockNumberByTimestamp( // At this point, only Case 2 or 3 are possible. If we have Case 2, // just get the previous block and compare which one lies closer to // the requested timestamp. - if block.Time() > timestamp { - previousBlock, err := bc.blockByNumber(block.NumberU64() - 1) + if block.Time > timestamp { + previousBlock, err := bc.blockByNumber(block.Number.Uint64() - 1) if err != nil { return 0, fmt.Errorf("cannot get block: [%v]", err) } - return closerBlock(timestamp, previousBlock, block).NumberU64(), nil + return closerBlock(timestamp, previousBlock, block).Number.Uint64(), nil } - return block.NumberU64(), nil + return block.Number.Uint64(), nil } // GetBlockHashByNumber gets the block hash for the given block number. @@ -427,8 +427,11 @@ func (bc *baseChain) GetBlockHashByNumber(blockNumber uint64) ( return header.Hash(), nil } -// currentBlock fetches the current block. -func (bc *baseChain) currentBlock() (*types.Block, error) { +// currentBlock fetches the current block header. Times out if the underlying +// client call takes more than 30 seconds. The returned *types.Header carries +// only header fields; callers that need transactions, uncles or receipts must +// fetch the full block separately. +func (bc *baseChain) currentBlock() (*types.Header, error) { // Use the latest header instead of block counter state. Some modern mainnet // blocks contain transaction types not supported by older block-counting // code paths, while this method only needs the latest block number/time as an @@ -436,21 +439,16 @@ func (bc *baseChain) currentBlock() (*types.Block, error) { ctx, cancelCtx := context.WithTimeout(context.Background(), 30*time.Second) defer cancelCtx() - header, err := bc.client.HeaderByNumber(ctx, nil) - if err != nil { - return nil, err - } - - return types.NewBlockWithHeader(header), nil + return bc.client.HeaderByNumber(ctx, nil) } -// blockByNumber returns the block for the given block number. Times out +// blockByNumber returns the header for the given block number. Times out // if the underlying client call takes more than 30 seconds. -func (bc *baseChain) blockByNumber(number uint64) (*types.Block, error) { +func (bc *baseChain) blockByNumber(number uint64) (*types.Header, error) { ctx, cancelCtx := context.WithTimeout(context.Background(), 30*time.Second) defer cancelCtx() - return bc.client.BlockByNumber(ctx, big.NewInt(int64(number))) + return bc.client.HeaderByNumber(ctx, big.NewInt(int64(number))) } // headerByNumber returns the header for the given block number. Times out @@ -462,10 +460,10 @@ func (bc *baseChain) headerByNumber(number uint64) (*types.Header, error) { return bc.client.HeaderByNumber(ctx, big.NewInt(int64(number))) } -// closerBlock check timestamps of blocks b1 and b2 and returns the block -// whose timestamp lies closer to the requested timestamp. If the distance -// is same for both blocks, the block with greater block number is returned. -func closerBlock(timestamp uint64, b1, b2 *types.Block) *types.Block { +// closerBlock check timestamps of block headers b1 and b2 and returns the one +// whose timestamp lies closer to the requested timestamp. If the distance is +// the same for both headers, the one with greater block number is returned. +func closerBlock(timestamp uint64, b1, b2 *types.Header) *types.Header { abs := func(x int64) int64 { if x < 0 { return -x @@ -473,12 +471,12 @@ func closerBlock(timestamp uint64, b1, b2 *types.Block) *types.Block { return x } - b1Diff := abs(int64(b1.Time() - timestamp)) - b2Diff := abs(int64(b2.Time() - timestamp)) + b1Diff := abs(int64(b1.Time - timestamp)) + b2Diff := abs(int64(b2.Time - timestamp)) // If the differences are same, return the block with greater number. if b1Diff == b2Diff { - if b2.NumberU64() > b1.NumberU64() { + if b2.Number.Uint64() > b1.Number.Uint64() { return b2 } return b1 @@ -531,3 +529,12 @@ func decryptKey(config ethereum.Config) (*keystore.Key, error) { config.Account.KeyFilePassword, ) } + +// gasEstimateWithMargin returns the given gas estimate multiplied by a fixed +// 20% safety margin. The original contract estimates for some transactions +// (notably reimbursement flows) turned out to be too low and caused the +// calls to run out of gas before reimbursement completed. +func gasEstimateWithMargin(gasEstimate uint64) uint64 { + const marginMultiplier = 1.2 + return uint64(float64(gasEstimate) * marginMultiplier) +} diff --git a/pkg/chain/ethereum/ethereum_integration_test.go b/pkg/chain/ethereum/ethereum_integration_test.go index 3c21f04a40..31ef0e5bc4 100644 --- a/pkg/chain/ethereum/ethereum_integration_test.go +++ b/pkg/chain/ethereum/ethereum_integration_test.go @@ -30,7 +30,7 @@ import ( func TestBaseChain_GetBlockNumberByTimestamp(t *testing.T) { ethereumURL := os.Getenv("ETHEREUM_MAINNET_RPC_URL") if ethereumURL == "" { - t.Skip("ETHEREUM_MAINNET_RPC_URL not set; skipping integration test") + t.Skip("ETHEREUM_MAINNET_RPC_URL not set; skipping mainnet integration test") } client, err := ethclient.Dial(ethereumURL) diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index 50e42be20e..bac873545d 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -1,38 +1,35 @@ +// tbtc.go: TbtcChain adapter construction and shared state. See tbtc_*.go for +// per-concern implementations (tbtc_deposit.go, tbtc_dkg.go, tbtc_moving_funds.go, +// tbtc_redemption.go, tbtc_wallet.go, tbtc_sortition.go, tbtc_inactivity.go). +// +// These files were split out of a single monolithic tbtc.go with no rename +// markers git can detect (each file is a fresh addition, not a tracked move), +// so a plain `git revert` of the split commit cannot be applied cleanly on +// top of any later commit that also touches this package: it would re-delete +// the per-concern files and reintroduce the old tbtc.go, silently dropping +// whatever those later commits changed. Reconstructing the pre-split state +// requires a manual merge, not a mechanical revert. package ethereum import ( - "context" "crypto/ecdsa" "encoding/binary" "errors" "fmt" "math/big" - "reflect" - "sort" "time" "github.com/keep-network/keep-common/pkg/cache" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" - "github.com/keep-network/keep-core/pkg/bitcoin" "github.com/keep-network/keep-common/pkg/chain/ethereum" - "github.com/keep-network/keep-core/pkg/chain" - ecdsaabi "github.com/keep-network/keep-core/pkg/chain/ethereum/ecdsa/gen/abi" + "github.com/keep-network/keep-core/pkg/bitcoin" ecdsacontract "github.com/keep-network/keep-core/pkg/chain/ethereum/ecdsa/gen/contract" - tbtcabi "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen/abi" tbtccontract "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen/contract" - "github.com/keep-network/keep-core/pkg/crypto/secp256k1" "github.com/keep-network/keep-core/pkg/internal/byteutils" - "github.com/keep-network/keep-core/pkg/operator" - "github.com/keep-network/keep-core/pkg/protocol/group" - "github.com/keep-network/keep-core/pkg/protocol/inactivity" - "github.com/keep-network/keep-core/pkg/subscription" "github.com/keep-network/keep-core/pkg/tbtc" - "github.com/keep-network/keep-core/pkg/tecdsa/dkg" ) // Definitions of contract names. @@ -279,562 +276,6 @@ func newTbtcChain( }, nil } -// EcdsaWalletGroupParametersFromChain mirrors EcdsaDkgValidator sizing constants -// when EcdsaDkgValidator contract address was configured under [ethereum] -// contract addresses or developer.ecdsaDkgValidatorAddress alias. When absent, -// returns (nil, nil) and callers use defaultGroupParameters(network). -func (tc *TbtcChain) EcdsaWalletGroupParametersFromChain( - ctx context.Context, -) (*tbtc.GroupParameters, error) { - if tc.ecdsaDkgValidatorAddress == (common.Address{}) { - return nil, nil - } - return ecdsaWalletGroupParametersFromValidator( - ctx, - tc.baseChain.client, - tc.ecdsaDkgValidatorAddress, - ) -} - -// Staking returns address of the TokenStaking contract the WalletRegistry is -// connected to. -func (tc *TbtcChain) Staking() (chain.Address, error) { - stakingContractAddress, err := tc.walletRegistry.Staking() - if err != nil { - return "", fmt.Errorf( - "failed to get the token staking address: [%w]", - err, - ) - } - - return chain.Address(stakingContractAddress.String()), nil -} - -// IsRecognized checks whether the given operator is recognized by the TbtcChain -// as eligible to join the network. If the operator has a stake delegation or -// had a stake delegation in the past, it will be recognized. -func (tc *TbtcChain) IsRecognized(operatorPublicKey *operator.PublicKey) (bool, error) { - operatorAddress, err := operatorPublicKeyToChainAddress(operatorPublicKey) - if err != nil { - return false, fmt.Errorf( - "cannot convert from operator key to chain address: [%v]", - err, - ) - } - - stakingProvider, err := tc.walletRegistry.OperatorToStakingProvider( - operatorAddress, - ) - if err != nil { - return false, fmt.Errorf( - "failed to map operator [%v] to a staking provider: [%v]", - operatorAddress, - err, - ) - } - - if (stakingProvider == common.Address{}) { - return false, nil - } - - // Check if the staking provider has an owner. This check ensures that there - // is/was a stake delegation for the given staking provider. - _, _, _, hasStakeDelegation, err := tc.baseChain.RolesOf( - chain.Address(stakingProvider.Hex()), - ) - if err != nil { - return false, fmt.Errorf( - "failed to check stake delegation for staking provider [%v]: [%v]", - stakingProvider, - err, - ) - } - - if !hasStakeDelegation { - return false, nil - } - - return true, nil -} - -// OperatorToStakingProvider returns the staking provider address for the -// operator. If the staking provider has not been registered for the -// operator, the returned address is empty and the boolean flag is set to -// false. If the staking provider has been registered, the address is not -// empty and the boolean flag indicates true. -func (tc *TbtcChain) OperatorToStakingProvider() (chain.Address, bool, error) { - stakingProvider, err := tc.walletRegistry.OperatorToStakingProvider(tc.key.Address) - if err != nil { - return "", false, fmt.Errorf( - "failed to map operator [%v] to a staking provider: [%v]", - tc.key.Address, - err, - ) - } - - if (stakingProvider == common.Address{}) { - return "", false, nil - } - - return chain.Address(stakingProvider.Hex()), true, nil -} - -// EligibleStake returns the current value of the staking provider's -// eligible stake. Eligible stake is defined as the currently authorized -// stake minus the pending authorization decrease. Eligible stake -// is what is used for operator's weight in the sortition pool. -// If the authorized stake minus the pending authorization decrease -// is below the minimum authorization, eligible stake is 0. -func (tc *TbtcChain) EligibleStake(stakingProvider chain.Address) (*big.Int, error) { - eligibleStake, err := tc.walletRegistry.EligibleStake( - common.HexToAddress(stakingProvider.String()), - ) - if err != nil { - return nil, fmt.Errorf( - "failed to get eligible stake for staking provider %s: [%w]", - stakingProvider, - err, - ) - } - - return eligibleStake, nil -} - -// IsPoolLocked returns true if the sortition pool is locked and no state -// changes are allowed. -func (tc *TbtcChain) IsPoolLocked() (bool, error) { - return tc.sortitionPool.IsLocked() -} - -// IsOperatorInPool returns true if the operator is registered in -// the sortition pool. -func (tc *TbtcChain) IsOperatorInPool() (bool, error) { - return tc.walletRegistry.IsOperatorInPool(tc.key.Address) -} - -// IsOperatorUpToDate checks if the operator's authorized stake is in sync -// with operator's weight in the sortition pool. -// If the operator's authorized stake is not in sync with sortition pool -// weight, function returns false. -// If the operator is not in the sortition pool and their authorized stake -// is non-zero, function returns false. -func (tc *TbtcChain) IsOperatorUpToDate() (bool, error) { - return tc.walletRegistry.IsOperatorUpToDate(tc.key.Address) -} - -// JoinSortitionPool executes a transaction to have the operator join the -// sortition pool. -func (tc *TbtcChain) JoinSortitionPool() error { - _, err := tc.walletRegistry.JoinSortitionPool() - return err -} - -// UpdateOperatorStatus executes a transaction to update the operator's -// state in the sortition pool. -func (tc *TbtcChain) UpdateOperatorStatus() error { - _, err := tc.walletRegistry.UpdateOperatorStatus(tc.key.Address) - return err -} - -// IsEligibleForRewards checks whether the operator is eligible for rewards -// or not. -func (tc *TbtcChain) IsEligibleForRewards() (bool, error) { - return tc.sortitionPool.IsEligibleForRewards(tc.key.Address) -} - -// Checks whether the operator is able to restore their eligibility for -// rewards right away. -func (tc *TbtcChain) CanRestoreRewardEligibility() (bool, error) { - return tc.sortitionPool.CanRestoreRewardEligibility(tc.key.Address) -} - -// Restores reward eligibility for the operator. -func (tc *TbtcChain) RestoreRewardEligibility() error { - _, err := tc.sortitionPool.RestoreRewardEligibility(tc.key.Address) - return err -} - -// Returns true if the chaosnet phase is active, false otherwise. -func (tc *TbtcChain) IsChaosnetActive() (bool, error) { - return tc.sortitionPool.IsChaosnetActive() -} - -// Returns true if operator is a beta operator, false otherwise. -// Chaosnet status does not matter. -func (tc *TbtcChain) IsBetaOperator() (bool, error) { - return tc.sortitionPool.IsBetaOperator(tc.key.Address) -} - -// GetOperatorID returns the ID number of the given operator address. An ID -// number of 0 means the operator has not been allocated an ID number yet. -func (tc *TbtcChain) GetOperatorID( - operatorAddress chain.Address, -) (chain.OperatorID, error) { - return tc.sortitionPool.GetOperatorID( - common.HexToAddress(operatorAddress.String()), - ) -} - -// SelectGroup returns the group members selected for the current group -// selection. The function returns an error if the chain's state does not allow -// for group selection at the moment. -func (tc *TbtcChain) SelectGroup() (*tbtc.GroupSelectionResult, error) { - operatorsIDs, err := tc.walletRegistry.SelectGroup() - if err != nil { - return nil, fmt.Errorf( - "cannot select group in the sortition pool: [%v]", - err, - ) - } - - operatorsAddresses, err := tc.sortitionPool.GetIDOperators(operatorsIDs) - if err != nil { - return nil, fmt.Errorf( - "cannot convert operators' IDs to addresses: [%v]", - err, - ) - } - - // Should not happen as this is guaranteed by the contract but, just in case. - if len(operatorsIDs) != len(operatorsAddresses) { - return nil, fmt.Errorf("operators IDs and addresses mismatch") - } - - ids := make([]chain.OperatorID, len(operatorsIDs)) - addresses := make([]chain.Address, len(operatorsIDs)) - for i := range ids { - ids[i] = operatorsIDs[i] - addresses[i] = chain.Address(operatorsAddresses[i].String()) - } - - return &tbtc.GroupSelectionResult{ - OperatorsIDs: ids, - OperatorsAddresses: addresses, - }, nil -} - -func (tc *TbtcChain) OnDKGStarted( - handler func(event *tbtc.DKGStartedEvent), -) subscription.EventSubscription { - onEvent := func( - seed *big.Int, - blockNumber uint64, - ) { - handler(&tbtc.DKGStartedEvent{ - Seed: seed, - BlockNumber: blockNumber, - }) - } - - return tc.walletRegistry.DkgStartedEvent(nil, nil).OnEvent(onEvent) -} - -func (tc *TbtcChain) PastDKGStartedEvents( - filter *tbtc.DKGStartedEventFilter, -) ([]*tbtc.DKGStartedEvent, error) { - var startBlock uint64 - var endBlock *uint64 - var seed []*big.Int - - if filter != nil { - startBlock = filter.StartBlock - endBlock = filter.EndBlock - seed = filter.Seed - } - - events, err := tc.walletRegistry.PastDkgStartedEvents( - startBlock, - endBlock, - seed, - ) - if err != nil { - return nil, err - } - - dkgStartedEvents := make([]*tbtc.DKGStartedEvent, len(events)) - for i, event := range events { - dkgStartedEvents[i] = &tbtc.DKGStartedEvent{ - Seed: event.Seed, - BlockNumber: event.Raw.BlockNumber, - } - } - - sort.SliceStable(dkgStartedEvents, func(i, j int) bool { - return dkgStartedEvents[i].BlockNumber < dkgStartedEvents[j].BlockNumber - }) - - return dkgStartedEvents, err -} - -func (tc *TbtcChain) OnDKGResultSubmitted( - handler func(event *tbtc.DKGResultSubmittedEvent), -) subscription.EventSubscription { - onEvent := func( - resultHash [32]byte, - seed *big.Int, - result ecdsaabi.EcdsaDkgResult, - blockNumber uint64, - ) { - tbtcResult, err := convertDkgResultFromAbiType(result) - if err != nil { - logger.Errorf( - "unexpected DKG result in DKGResultSubmitted event: [%v]", - err, - ) - return - } - - handler(&tbtc.DKGResultSubmittedEvent{ - Seed: seed, - ResultHash: resultHash, - Result: tbtcResult, - BlockNumber: blockNumber, - }) - } - - return tc.walletRegistry. - DkgResultSubmittedEvent(nil, nil, nil). - OnEvent(onEvent) -} - -// convertDkgResultFromAbiType converts the WalletRegistry-specific DKG -// result to the format applicable for the TBTC application. -func convertDkgResultFromAbiType( - result ecdsaabi.EcdsaDkgResult, -) (*tbtc.DKGChainResult, error) { - if err := validateMemberIndex(result.SubmitterMemberIndex); err != nil { - return nil, fmt.Errorf( - "unexpected submitter member index: [%v]", - err, - ) - } - - signingMembersIndexes := make( - []group.MemberIndex, - len(result.SigningMembersIndices), - ) - for i, memberIndex := range result.SigningMembersIndices { - if err := validateMemberIndex(memberIndex); err != nil { - return nil, fmt.Errorf( - "unexpected signing member index: [%v]", - err, - ) - } - - signingMembersIndexes[i] = group.MemberIndex(memberIndex.Uint64()) - } - - return &tbtc.DKGChainResult{ - SubmitterMemberIndex: group.MemberIndex(result.SubmitterMemberIndex.Uint64()), - GroupPublicKey: result.GroupPubKey, - MisbehavedMembersIndexes: result.MisbehavedMembersIndices, - Signatures: result.Signatures, - SigningMembersIndexes: signingMembersIndexes, - Members: result.Members, - MembersHash: result.MembersHash, - }, nil -} - -// convertDkgResultToAbiType converts the TBTC-specific DKG result to -// the format applicable for the WalletRegistry ABI. -func convertDkgResultToAbiType( - result *tbtc.DKGChainResult, -) ecdsaabi.EcdsaDkgResult { - signingMembersIndices := make([]*big.Int, len(result.SigningMembersIndexes)) - for i, memberIndex := range result.SigningMembersIndexes { - signingMembersIndices[i] = big.NewInt(int64(memberIndex)) - } - - return ecdsaabi.EcdsaDkgResult{ - SubmitterMemberIndex: big.NewInt(int64(result.SubmitterMemberIndex)), - GroupPubKey: result.GroupPublicKey, - MisbehavedMembersIndices: result.MisbehavedMembersIndexes, - Signatures: result.Signatures, - SigningMembersIndices: signingMembersIndices, - Members: result.Members, - MembersHash: result.MembersHash, - } -} - -func validateMemberIndex(chainMemberIndex *big.Int) error { - maxMemberIndex := big.NewInt(group.MaxMemberIndex) - if chainMemberIndex.Cmp(maxMemberIndex) > 0 { - return fmt.Errorf("invalid member index value: [%v]", chainMemberIndex) - } - - return nil -} - -func (tc *TbtcChain) OnDKGResultChallenged( - handler func(event *tbtc.DKGResultChallengedEvent), -) subscription.EventSubscription { - onEvent := func( - resultHash [32]byte, - challenger common.Address, - reason string, - blockNumber uint64, - ) { - handler(&tbtc.DKGResultChallengedEvent{ - ResultHash: resultHash, - Challenger: chain.Address(challenger.Hex()), - Reason: reason, - BlockNumber: blockNumber, - }) - } - - return tc.walletRegistry. - DkgResultChallengedEvent(nil, nil, nil). - OnEvent(onEvent) -} - -func (tc *TbtcChain) OnDKGResultApproved( - handler func(event *tbtc.DKGResultApprovedEvent), -) subscription.EventSubscription { - onEvent := func( - resultHash [32]byte, - approver common.Address, - blockNumber uint64, - ) { - handler(&tbtc.DKGResultApprovedEvent{ - ResultHash: resultHash, - Approver: chain.Address(approver.Hex()), - BlockNumber: blockNumber, - }) - } - - return tc.walletRegistry. - DkgResultApprovedEvent(nil, nil, nil). - OnEvent(onEvent) -} - -// AssembleDKGResult assembles the DKG chain result according to the rules -// expected by the given chain. -func (tc *TbtcChain) AssembleDKGResult( - submitterMemberIndex group.MemberIndex, - groupPublicKey *ecdsa.PublicKey, - operatingMembersIndexes []group.MemberIndex, - misbehavedMembersIndexes []group.MemberIndex, - signatures map[group.MemberIndex][]byte, - groupSelectionResult *tbtc.GroupSelectionResult, -) (*tbtc.DKGChainResult, error) { - serializedGroupPublicKey, err := convertPubKeyToChainFormat(groupPublicKey) - if err != nil { - return nil, fmt.Errorf( - "could not convert group public key to chain format: [%v]", - err, - ) - } - - // Sort misbehavedMembersIndexes slice in ascending order as expected - // by the on-chain contract. - sort.Slice(misbehavedMembersIndexes[:], func(i, j int) bool { - return misbehavedMembersIndexes[i] < misbehavedMembersIndexes[j] - }) - - signingMemberIndices, signatureBytes, err := convertSignaturesToChainFormat( - signatures, - ) - if err != nil { - return nil, fmt.Errorf( - "could not convert signatures to chain format: [%v]", - err, - ) - } - - // Sort operatingOperatorsIDs slice in ascending order as the slice - // holding the operators IDs used to compute the members hash is - // expected to be sorted in the same way. - sort.Slice(operatingMembersIndexes[:], func(i, j int) bool { - return operatingMembersIndexes[i] < operatingMembersIndexes[j] - }) - - operatingOperatorsIDs := make([]chain.OperatorID, len(operatingMembersIndexes)) - for i, operatingMemberIndex := range operatingMembersIndexes { - operatingOperatorsIDs[i] = - groupSelectionResult.OperatorsIDs[operatingMemberIndex-1] - } - - membersHash, err := computeOperatorsIDsHash(operatingOperatorsIDs) - if err != nil { - return nil, fmt.Errorf("could not compute members hash: [%v]", err) - } - - return &tbtc.DKGChainResult{ - SubmitterMemberIndex: submitterMemberIndex, - GroupPublicKey: serializedGroupPublicKey[:], - MisbehavedMembersIndexes: misbehavedMembersIndexes, - Signatures: signatureBytes, - SigningMembersIndexes: signingMemberIndices, - Members: groupSelectionResult.OperatorsIDs, - MembersHash: membersHash, - }, nil -} - -func (tc *TbtcChain) SubmitDKGResult( - dkgResult *tbtc.DKGChainResult, -) error { - _, err := tc.walletRegistry.SubmitDkgResult( - convertDkgResultToAbiType(dkgResult), - ) - - return err -} - -// computeOperatorsIDsHash computes the keccak256 hash for the given list -// of operators IDs. -func computeOperatorsIDsHash(operatorsIDs chain.OperatorIDs) ([32]byte, error) { - uint32SliceType, err := abi.NewType("uint32[]", "uint32[]", nil) - if err != nil { - return [32]byte{}, err - } - - bytes, err := abi.Arguments{{Type: uint32SliceType}}.Pack(operatorsIDs) - if err != nil { - return [32]byte{}, err - } - - return crypto.Keccak256Hash(bytes), nil -} - -// convertSignaturesToChainFormat converts signatures map to two slices. The -// first slice contains indices of members from the map, sorted in ascending order -// as required by the contract. The second slice is a slice of concatenated -// signatures. Signatures and member indices are returned in the matching order. -// It requires each signature to be exactly 65-byte long. -func convertSignaturesToChainFormat( - signatures map[group.MemberIndex][]byte, -) ([]group.MemberIndex, []byte, error) { - membersIndexes := make([]group.MemberIndex, 0) - for memberIndex := range signatures { - membersIndexes = append(membersIndexes, memberIndex) - } - - sort.Slice(membersIndexes, func(i, j int) bool { - return membersIndexes[i] < membersIndexes[j] - }) - - signatureSize := 65 - - var signaturesSlice []byte - - for _, memberIndex := range membersIndexes { - signature := signatures[memberIndex] - - if len(signature) != signatureSize { - return nil, nil, fmt.Errorf( - "invalid signature size for member [%v] got [%d] bytes but [%d] bytes required", - memberIndex, - len(signature), - signatureSize, - ) - } - - signaturesSlice = append(signaturesSlice, signature...) - } - - return membersIndexes, signaturesSlice, nil -} - // convertPubKeyToChainFormat takes X and Y coordinates of a signer's public key // and concatenates it to a 64-byte long array. If any of coordinates is shorter // than 32-byte it is preceded with zeros. @@ -858,1553 +299,16 @@ func convertPubKeyToChainFormat(publicKey *ecdsa.PublicKey) ([64]byte, error) { return serialized, nil } -func (tc *TbtcChain) GetDKGState() (tbtc.DKGState, error) { - walletCreationState, err := tc.walletRegistry.GetWalletCreationState() - if err != nil { - return 0, err - } - - var state tbtc.DKGState - - switch walletCreationState { - case 0: - state = tbtc.Idle - case 1: - state = tbtc.AwaitingSeed - case 2: - state = tbtc.AwaitingResult - case 3: - state = tbtc.Challenge - default: - err = fmt.Errorf( - "unexpected wallet creation state: [%v]", - walletCreationState, - ) - } - - return state, err -} - -// CalculateDKGResultSignatureHash calculates a 32-byte hash that is used -// to produce a signature supporting the given groupPublicKey computed -// as result of the given DKG process. The misbehavedMembersIndexes parameter -// should contain indexes of members that were considered as misbehaved -// during the DKG process. The startBlock argument is the block at which -// the given DKG process started. -func (tc *TbtcChain) CalculateDKGResultSignatureHash( - groupPublicKey *ecdsa.PublicKey, - misbehavedMembersIndexes []group.MemberIndex, - startBlock uint64, -) (dkg.ResultSignatureHash, error) { - groupPublicKeyBytes := secp256k1.Marshal(groupPublicKey) - // Crop the 04 prefix as the calculateDKGResultSignatureHash function - // expects an unprefixed 64-byte public key, - unprefixedGroupPublicKeyBytes := groupPublicKeyBytes[1:] - - // Sort misbehavedMembersIndexes slice in ascending order as expected - // by the calculateDKGResultSignatureHash function. - sort.Slice(misbehavedMembersIndexes[:], func(i, j int) bool { - return misbehavedMembersIndexes[i] < misbehavedMembersIndexes[j] - }) - - return calculateDKGResultSignatureHash( - tc.chainID, - unprefixedGroupPublicKeyBytes, - misbehavedMembersIndexes, - big.NewInt(int64(startBlock)), - ) -} - -// calculateDKGResultSignatureHash computes the keccak256 hash for the given DKG -// result parameters. It expects that the groupPublicKey is a 64-byte uncompressed -// public key without the 04 prefix and misbehavedMembersIndexes slice is -// sorted in ascending order. Those expectations are forced by the contract. -func calculateDKGResultSignatureHash( - chainID *big.Int, - groupPublicKey []byte, - misbehavedMembersIndexes []group.MemberIndex, - startBlock *big.Int, -) (dkg.ResultSignatureHash, error) { - publicKeySize := 64 - - if len(groupPublicKey) != publicKeySize { - return dkg.ResultSignatureHash{}, fmt.Errorf( - "wrong group public key length", - ) - } - - uint256Type, err := abi.NewType("uint256", "uint256", nil) - if err != nil { - return dkg.ResultSignatureHash{}, err - } - bytesType, err := abi.NewType("bytes", "bytes", nil) - if err != nil { - return dkg.ResultSignatureHash{}, err - } - uint8SliceType, err := abi.NewType("uint8[]", "uint8[]", nil) - if err != nil { - return dkg.ResultSignatureHash{}, err - } - - bytes, err := abi.Arguments{ - {Type: uint256Type}, - {Type: bytesType}, - {Type: uint8SliceType}, - {Type: uint256Type}, - }.Pack( - chainID, - groupPublicKey, - misbehavedMembersIndexes, - startBlock, - ) - if err != nil { - return dkg.ResultSignatureHash{}, err - } - - return dkg.ResultSignatureHash(crypto.Keccak256Hash(bytes)), nil -} - -func (tc *TbtcChain) IsDKGResultValid( - dkgResult *tbtc.DKGChainResult, -) (bool, error) { - outcome, err := tc.walletRegistry.IsDkgResultValid( - convertDkgResultToAbiType(dkgResult), - ) - if err != nil { - return false, fmt.Errorf("cannot check result validity: [%v]", err) - } - - return parseDkgResultValidationOutcome(&outcome) -} - -// parseDkgResultValidationOutcome parses the DKG validation outcome and returns -// a boolean indicating whether the result is valid or not. The outcome parameter -// must be a pointer to a struct containing a boolean flag as the first field. -// -// TODO: Find a better way to get the validity flag. This would require changes -// in the contracts binding generator. -func parseDkgResultValidationOutcome( - outcome interface{}, -) (bool, error) { - value := reflect.ValueOf(outcome) - switch value.Kind() { - case reflect.Pointer: - default: - return false, fmt.Errorf("result validation outcome is not a pointer") - } - - field := value.Elem().Field(0) - switch field.Kind() { - case reflect.Bool: - return field.Bool(), nil - default: - return false, fmt.Errorf("cannot parse result validation outcome") - } -} - -func (tc *TbtcChain) ChallengeDKGResult(dkgResult *tbtc.DKGChainResult) error { - _, err := tc.walletRegistry.ChallengeDkgResult( - convertDkgResultToAbiType(dkgResult), - ) +// buildTxOutpointKey computes keccak256(txHash || uint32BE(outputIndex)) and +// returns it as a *big.Int. Used by both the deposit and moved-funds request +// lookup paths; the contract-side mapping is identical for both. +func buildTxOutpointKey(txHash bitcoin.Hash, outputIndex uint32) *big.Int { + indexBytes := make([]byte, 4) + binary.BigEndian.PutUint32(indexBytes, outputIndex) - return err -} - -func (tc *TbtcChain) ApproveDKGResult(dkgResult *tbtc.DKGChainResult) error { - result := convertDkgResultToAbiType(dkgResult) - - gasEstimate, err := tc.walletRegistry.ApproveDkgResultGasEstimate(result) - if err != nil { - return err - } - - // The original estimate for this contract call turned out to be too low. - // Here we add a 20% margin to overcome the gas problems. - gasEstimateWithMargin := float64(gasEstimate) * float64(1.2) - - _, err = tc.walletRegistry.ApproveDkgResult( - result, - ethutil.TransactionOptions{ - GasLimit: uint64(gasEstimateWithMargin), - }, - ) - - return err -} - -func (tc *TbtcChain) DKGParameters() (*tbtc.DKGParameters, error) { - parameters, err := tc.walletRegistry.DkgParameters() - if err != nil { - return nil, err - } - - return &tbtc.DKGParameters{ - SubmissionTimeoutBlocks: parameters.ResultSubmissionTimeout.Uint64(), - ChallengePeriodBlocks: parameters.ResultChallengePeriodLength.Uint64(), - ApprovePrecedencePeriodBlocks: parameters.SubmitterPrecedencePeriodLength.Uint64(), - }, nil -} - -func (tc *TbtcChain) OnInactivityClaimed( - handler func(event *tbtc.InactivityClaimedEvent), -) subscription.EventSubscription { - onEvent := func( - walletID [32]byte, - nonce *big.Int, - notifier common.Address, - blockNumber uint64, - ) { - handler(&tbtc.InactivityClaimedEvent{ - WalletID: walletID, - Nonce: nonce, - Notifier: chain.Address(notifier.Hex()), - BlockNumber: blockNumber, - }) - } - - return tc.walletRegistry.InactivityClaimedEvent(nil, nil).OnEvent(onEvent) -} - -func (tc *TbtcChain) AssembleInactivityClaim( - walletID [32]byte, - inactiveMembersIndices []group.MemberIndex, - signatures map[group.MemberIndex][]byte, - heartbeatFailed bool, -) ( - *tbtc.InactivityClaim, - error, -) { - signingMemberIndices, signatureBytes, err := convertSignaturesToChainFormat( - signatures, - ) - if err != nil { - return nil, fmt.Errorf( - "could not convert signatures to chain format: [%v]", - err, - ) - } - - return &tbtc.InactivityClaim{ - WalletID: walletID, - InactiveMembersIndices: inactiveMembersIndices, - HeartbeatFailed: heartbeatFailed, - Signatures: signatureBytes, - SigningMembersIndices: signingMemberIndices, - }, nil -} - -// convertInactivityClaimToAbiType converts the TBTC-specific inactivity claim -// to the format applicable for the WalletRegistry ABI. -func convertInactivityClaimToAbiType( - claim *tbtc.InactivityClaim, -) ecdsaabi.EcdsaInactivityClaim { - inactiveMembersIndices := make([]*big.Int, len(claim.InactiveMembersIndices)) - for i, memberIndex := range claim.InactiveMembersIndices { - inactiveMembersIndices[i] = big.NewInt(int64(memberIndex)) - } - - signingMembersIndices := make([]*big.Int, len(claim.SigningMembersIndices)) - for i, memberIndex := range claim.SigningMembersIndices { - signingMembersIndices[i] = big.NewInt(int64(memberIndex)) - } - - return ecdsaabi.EcdsaInactivityClaim{ - WalletID: claim.WalletID, - InactiveMembersIndices: inactiveMembersIndices, - HeartbeatFailed: claim.HeartbeatFailed, - Signatures: claim.Signatures, - SigningMembersIndices: signingMembersIndices, - } -} - -func (tc *TbtcChain) SubmitInactivityClaim( - claim *tbtc.InactivityClaim, - nonce *big.Int, - groupMembers []uint32, -) error { - _, err := tc.walletRegistry.NotifyOperatorInactivity( - convertInactivityClaimToAbiType(claim), - nonce, - groupMembers, - ) - - return err -} - -func (tc *TbtcChain) CalculateInactivityClaimHash( - claim *inactivity.ClaimPreimage, -) (inactivity.ClaimHash, error) { - walletPublicKeyBytes := secp256k1.Marshal(claim.WalletPublicKey) - // Crop the 04 prefix as the calculateInactivityClaimHash function expects - // an unprefixed 64-byte public key, - unprefixedGroupPublicKeyBytes := walletPublicKeyBytes[1:] - - // The type representing inactive member index should be `big.Int` as the - // smart contract reading the calculated hash uses `uint256` for inactive - // member indexes. - inactiveMembersIndexes := make([]*big.Int, len(claim.InactiveMembersIndexes)) - for i, index := range claim.InactiveMembersIndexes { - inactiveMembersIndexes[i] = big.NewInt(int64(index)) - } - - return calculateInactivityClaimHash( - tc.chainID, - claim.Nonce, - unprefixedGroupPublicKeyBytes, - inactiveMembersIndexes, - claim.HeartbeatFailed, - ) -} - -func calculateInactivityClaimHash( - chainID *big.Int, - nonce *big.Int, - walletPublicKey []byte, - inactiveMembersIndexes []*big.Int, - heartbeatFailed bool, -) (inactivity.ClaimHash, error) { - publicKeySize := 64 - - if len(walletPublicKey) != publicKeySize { - return inactivity.ClaimHash{}, fmt.Errorf( - "wrong wallet public key length", - ) - } - - uint256Type, err := abi.NewType("uint256", "uint256", nil) - if err != nil { - return inactivity.ClaimHash{}, err - } - bytesType, err := abi.NewType("bytes", "bytes", nil) - if err != nil { - return inactivity.ClaimHash{}, err - } - uint256SliceType, err := abi.NewType("uint256[]", "uint256[]", nil) - if err != nil { - return inactivity.ClaimHash{}, err - } - boolType, err := abi.NewType("bool", "bool", nil) - if err != nil { - return inactivity.ClaimHash{}, err - } - - bytes, err := abi.Arguments{ - {Type: uint256Type}, - {Type: uint256Type}, - {Type: bytesType}, - {Type: uint256SliceType}, - {Type: boolType}, - }.Pack( - chainID, - nonce, - walletPublicKey, - inactiveMembersIndexes, - heartbeatFailed, - ) - if err != nil { - return inactivity.ClaimHash{}, err - } - - return inactivity.ClaimHash(crypto.Keccak256Hash(bytes)), nil -} - -func (tc *TbtcChain) GetInactivityClaimNonce( - walletID [32]byte, -) (*big.Int, error) { - nonce, err := tc.walletRegistry.InactivityClaimNonce(walletID) - if err != nil { - return nil, fmt.Errorf( - "failed to get inactivity claim nonce: [%w]", - err, - ) - } - - return nonce, nil -} - -func (tc *TbtcChain) PastDepositRevealedEvents( - filter *tbtc.DepositRevealedEventFilter, -) ([]*tbtc.DepositRevealedEvent, error) { - var startBlock uint64 - var endBlock *uint64 - var depositor []common.Address - var walletPublicKeyHash [][20]byte - - if filter != nil { - startBlock = filter.StartBlock - endBlock = filter.EndBlock - - for _, d := range filter.Depositor { - depositor = append(depositor, common.HexToAddress(d.String())) - } - - walletPublicKeyHash = filter.WalletPublicKeyHash - } - - events, err := tc.bridge.PastDepositRevealedEvents( - startBlock, - endBlock, - depositor, - walletPublicKeyHash, - ) - if err != nil { - return nil, err - } - - convertedEvents := make([]*tbtc.DepositRevealedEvent, 0) - for _, event := range events { - var vault *chain.Address - if event.Vault != [20]byte{} { - v := chain.Address(event.Vault.Hex()) - vault = &v - } - - convertedEvent := &tbtc.DepositRevealedEvent{ - // We can map the event.FundingTxHash field directly to the - // bitcoin.Hash type. This is because event.FundingTxHash is - // a [32]byte type representing a hash in the bitcoin.InternalByteOrder, - // just as bitcoin.Hash assumes. - FundingTxHash: event.FundingTxHash, - FundingOutputIndex: event.FundingOutputIndex, - Depositor: chain.Address(event.Depositor.Hex()), - Amount: event.Amount, - BlindingFactor: event.BlindingFactor, - WalletPublicKeyHash: event.WalletPubKeyHash, - RefundPublicKeyHash: event.RefundPubKeyHash, - RefundLocktime: event.RefundLocktime, - Vault: vault, - BlockNumber: event.Raw.BlockNumber, - } - - convertedEvents = append(convertedEvents, convertedEvent) - } - - sort.SliceStable( - convertedEvents, - func(i, j int) bool { - return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber - }, - ) - - return convertedEvents, err -} - -func (tc *TbtcChain) PastRedemptionRequestedEvents( - filter *tbtc.RedemptionRequestedEventFilter, -) ([]*tbtc.RedemptionRequestedEvent, error) { - var startBlock uint64 - var endBlock *uint64 - var redeemers []common.Address - var walletPublicKeyHash [][20]byte - - if filter != nil { - startBlock = filter.StartBlock - endBlock = filter.EndBlock - - for _, r := range filter.Redeemer { - redeemers = append(redeemers, common.HexToAddress(r.String())) - } - - walletPublicKeyHash = filter.WalletPublicKeyHash - } - - events, err := tc.bridge.PastRedemptionRequestedEvents( - startBlock, - endBlock, - walletPublicKeyHash, - redeemers, - ) - if err != nil { - return nil, err - } - - convertedEvents := make([]*tbtc.RedemptionRequestedEvent, 0) - for _, event := range events { - redeemerOutputScript, err := bitcoin.NewScriptFromVarLenData( - event.RedeemerOutputScript, - ) - if err != nil { - return nil, err - } - - convertedEvent := &tbtc.RedemptionRequestedEvent{ - WalletPublicKeyHash: event.WalletPubKeyHash, - RedeemerOutputScript: redeemerOutputScript, - Redeemer: chain.Address(event.Redeemer.Hex()), - RequestedAmount: event.RequestedAmount, - TreasuryFee: event.TreasuryFee, - TxMaxFee: event.TreasuryFee, - BlockNumber: event.Raw.BlockNumber, - } - - convertedEvents = append(convertedEvents, convertedEvent) - } - - sort.SliceStable( - convertedEvents, - func(i, j int) bool { - return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber - }, - ) - - return convertedEvents, err -} - -func (tc *TbtcChain) GetDepositRequest( - fundingTxHash bitcoin.Hash, - fundingOutputIndex uint32, -) (*tbtc.DepositChainRequest, bool, error) { - depositKey := buildDepositKey(fundingTxHash, fundingOutputIndex) - depositCacheKey := depositKey.Text(16) - - tc.sweptDepositsCache.Sweep() - if cachedRequest, ok := tc.sweptDepositsCache.Get(depositCacheKey); ok { - return cachedRequest, true, nil - } - - chainRequest, err := tc.bridge.Deposits(depositKey) - if err != nil { - return nil, false, fmt.Errorf( - "cannot get deposit request for key [0x%x]: [%v]", - depositKey.Text(16), - err, - ) - } - - // Deposit not found. - if chainRequest.RevealedAt == 0 { - return nil, false, nil - } - - var vault *chain.Address - if chainRequest.Vault != [20]byte{} { - v := chain.Address(chainRequest.Vault.Hex()) - vault = &v - } - - var extraData *[32]byte - if chainRequest.ExtraData != [32]byte{} { - extraData = &chainRequest.ExtraData - } - - request := &tbtc.DepositChainRequest{ - Depositor: chain.Address(chainRequest.Depositor.Hex()), - Amount: chainRequest.Amount, - RevealedAt: time.Unix(int64(chainRequest.RevealedAt), 0), - Vault: vault, - TreasuryFee: chainRequest.TreasuryFee, - SweptAt: time.Unix(int64(chainRequest.SweptAt), 0), - ExtraData: extraData, - } - - // If the request was swept on-chain, there is a guarantee that no - // further changes will occur regarding its parameters. - // Such a request can be cached. - if isSwept := request.SweptAt.Unix() != 0; isSwept { - tc.sweptDepositsCache.Add(depositCacheKey, request) - } - - return request, true, nil -} - -func (tc *TbtcChain) PastNewWalletRegisteredEvents( - filter *tbtc.NewWalletRegisteredEventFilter, -) ([]*tbtc.NewWalletRegisteredEvent, error) { - var startBlock uint64 - var endBlock *uint64 - var ecdsaWalletID [][32]byte - var walletPublicKeyHash [][20]byte - - if filter != nil { - startBlock = filter.StartBlock - endBlock = filter.EndBlock - ecdsaWalletID = filter.EcdsaWalletID - walletPublicKeyHash = filter.WalletPublicKeyHash - } - - events, err := tc.bridge.PastNewWalletRegisteredEvents( - startBlock, - endBlock, - ecdsaWalletID, - walletPublicKeyHash, - ) - if err != nil { - return nil, err - } - - convertedEvents := make([]*tbtc.NewWalletRegisteredEvent, 0) - for _, event := range events { - convertedEvent := &tbtc.NewWalletRegisteredEvent{ - EcdsaWalletID: event.EcdsaWalletID, - WalletPublicKeyHash: event.WalletPubKeyHash, - BlockNumber: event.Raw.BlockNumber, - } - - convertedEvents = append(convertedEvents, convertedEvent) - } - - sort.SliceStable( - convertedEvents, - func(i, j int) bool { - return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber - }, - ) - - return convertedEvents, err -} - -func (tc *TbtcChain) CalculateWalletID( - walletPublicKey *ecdsa.PublicKey, -) ([32]byte, error) { - return calculateWalletID(walletPublicKey) -} - -func calculateWalletID(walletPublicKey *ecdsa.PublicKey) ([32]byte, error) { - walletPublicKeyBytes, err := convertPubKeyToChainFormat(walletPublicKey) - if err != nil { - return [32]byte{}, fmt.Errorf( - "error while converting wallet public key to chain format: [%v]", - err, - ) - } - - return crypto.Keccak256Hash(walletPublicKeyBytes[:]), nil -} - -func (tc *TbtcChain) IsWalletRegistered(EcdsaWalletID [32]byte) (bool, error) { - isWalletRegistered, err := tc.walletRegistry.IsWalletRegistered( - EcdsaWalletID, - ) - if err != nil { - return false, fmt.Errorf( - "cannot check if wallet with ECDSA ID [0x%x] is registered: [%v]", - EcdsaWalletID, - err, - ) - } - - return isWalletRegistered, nil -} - -func (tc *TbtcChain) GetWallet( - walletPublicKeyHash [20]byte, -) (*tbtc.WalletChainData, error) { - wallet, err := tc.bridge.Wallets(walletPublicKeyHash) - if err != nil { - return nil, fmt.Errorf( - "cannot get wallet for public key hash [0x%x]: [%v]", - walletPublicKeyHash, - err, - ) - } - - // Wallet not found. - if wallet.CreatedAt == 0 { - return nil, fmt.Errorf( - "no wallet for public key hash [0x%x]", - wallet, - ) - } - - walletState, err := parseWalletState(wallet.State) - if err != nil { - return nil, fmt.Errorf("cannot parse wallet state: [%v]", err) - } - - return &tbtc.WalletChainData{ - EcdsaWalletID: wallet.EcdsaWalletID, - MainUtxoHash: wallet.MainUtxoHash, - PendingRedemptionsValue: wallet.PendingRedemptionsValue, - CreatedAt: time.Unix(int64(wallet.CreatedAt), 0), - MovingFundsRequestedAt: time.Unix(int64(wallet.MovingFundsRequestedAt), 0), - ClosingStartedAt: time.Unix(int64(wallet.ClosingStartedAt), 0), - PendingMovedFundsSweepRequestsCount: wallet.PendingMovedFundsSweepRequestsCount, - State: walletState, - MovingFundsTargetWalletsCommitmentHash: wallet.MovingFundsTargetWalletsCommitmentHash, - }, nil -} - -func (tc *TbtcChain) OnWalletClosed( - handler func(event *tbtc.WalletClosedEvent), -) subscription.EventSubscription { - onEvent := func( - walletID [32]byte, - blockNumber uint64, - ) { - handler(&tbtc.WalletClosedEvent{ - WalletID: walletID, - BlockNumber: blockNumber, - }) - } - return tc.walletRegistry.WalletClosedEvent(nil, nil).OnEvent(onEvent) -} - -func (tc *TbtcChain) ComputeMainUtxoHash( - mainUtxo *bitcoin.UnspentTransactionOutput, -) [32]byte { - return computeMainUtxoHash(mainUtxo) -} - -func computeMainUtxoHash(mainUtxo *bitcoin.UnspentTransactionOutput) [32]byte { - outputIndexBytes := make([]byte, 4) - binary.BigEndian.PutUint32(outputIndexBytes, mainUtxo.Outpoint.OutputIndex) - - valueBytes := make([]byte, 8) - binary.BigEndian.PutUint64(valueBytes, uint64(mainUtxo.Value)) - - mainUtxoHash := crypto.Keccak256Hash( - append( - append( - mainUtxo.Outpoint.TransactionHash[:], - outputIndexBytes..., - ), valueBytes..., - ), - ) - - return mainUtxoHash -} - -func (tc *TbtcChain) ComputeMovingFundsCommitmentHash( - targetWallets [][20]byte, -) [32]byte { - return computeMovingFundsCommitmentHash(targetWallets) -} - -func computeMovingFundsCommitmentHash(targetWallets [][20]byte) [32]byte { - packedWallets := []byte{} - - for _, wallet := range targetWallets { - packedWallets = append(packedWallets, wallet[:]...) - // Each wallet hash must be padded with 12 zero bytes following the - // actual hash. - packedWallets = append(packedWallets, make([]byte, 12)...) - } - - return crypto.Keccak256Hash(packedWallets) -} - -func (tc *TbtcChain) BuildDepositKey( - fundingTxHash bitcoin.Hash, - fundingOutputIndex uint32, -) *big.Int { - return buildDepositKey(fundingTxHash, fundingOutputIndex) -} - -func (tc *TbtcChain) BuildRedemptionKey( - walletPublicKeyHash [20]byte, - redeemerOutputScript bitcoin.Script, -) (*big.Int, error) { - return buildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) -} - -func (tc *TbtcChain) GetDepositParameters() (tbtc.DepositParameters, error) { - parameters, err := tc.bridge.DepositParameters() - if err != nil { - return tbtc.DepositParameters{}, err - } - - return tbtc.DepositParameters{ - DustThreshold: parameters.DepositDustThreshold, - TreasuryFeeDivisor: parameters.DepositTreasuryFeeDivisor, - TxMaxFee: parameters.DepositTxMaxFee, - RevealAheadPeriod: parameters.DepositRevealAheadPeriod, - }, nil -} - -func (tc *TbtcChain) GetPendingRedemptionRequest( - walletPublicKeyHash [20]byte, - redeemerOutputScript bitcoin.Script, -) (*tbtc.RedemptionRequest, bool, error) { - redemptionKey, err := buildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) - if err != nil { - return nil, false, fmt.Errorf("cannot build redemption key: [%v]", err) - } - - redemptionRequest, err := tc.bridge.PendingRedemptions(redemptionKey) - if err != nil { - return nil, false, fmt.Errorf( - "cannot get pending redemption request for key [0x%x]: [%v]", - redemptionKey.Text(16), - err, - ) - } - - // Redemption not found. - if redemptionRequest.RequestedAt == 0 { - return nil, false, nil - } - - return &tbtc.RedemptionRequest{ - Redeemer: chain.Address(redemptionRequest.Redeemer.Hex()), - RedeemerOutputScript: redeemerOutputScript, - RequestedAmount: redemptionRequest.RequestedAmount, - TreasuryFee: redemptionRequest.TreasuryFee, - TxMaxFee: redemptionRequest.TxMaxFee, - RequestedAt: time.Unix(int64(redemptionRequest.RequestedAt), 0), - }, true, nil -} - -func (tc *TbtcChain) SubmitRedemptionProofWithReimbursement( - transaction *bitcoin.Transaction, - proof *bitcoin.SpvProof, - mainUTXO bitcoin.UnspentTransactionOutput, - walletPublicKeyHash [20]byte, -) error { - bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ - Version: transaction.SerializeVersion(), - InputVector: transaction.SerializeInputs(), - OutputVector: transaction.SerializeOutputs(), - Locktime: transaction.SerializeLocktime(), - } - redemptionProof := tbtcabi.BitcoinTxProof2{ - MerkleProof: proof.MerkleProof, - TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), - BitcoinHeaders: proof.BitcoinHeaders, - CoinbasePreimage: proof.CoinbasePreimage, - CoinbaseProof: proof.CoinbaseProof, - } - utxo := tbtcabi.BitcoinTxUTXO2{ - TxHash: mainUTXO.Outpoint.TransactionHash, - TxOutputIndex: mainUTXO.Outpoint.OutputIndex, - TxOutputValue: uint64(mainUTXO.Value), - } - - gasEstimate, err := tc.maintainerProxy.SubmitRedemptionProofGasEstimate( - bitcoinTxInfo, - redemptionProof, - utxo, - walletPublicKeyHash, - ) - if err != nil { - return err - } - - // The original estimate for this contract call is too low and the call - // fails on reimbursing the submitter. Example: - // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 - // Here we add a 20% margin to overcome the gas problems. - gasEstimateWithMargin := float64(gasEstimate) * float64(1.2) - - _, err = tc.maintainerProxy.SubmitRedemptionProof( - bitcoinTxInfo, - redemptionProof, - utxo, - walletPublicKeyHash, - ethutil.TransactionOptions{ - GasLimit: uint64(gasEstimateWithMargin), - }, - ) - - return err -} - -func buildRedemptionKey( - walletPublicKeyHash [20]byte, - redeemerOutputScript bitcoin.Script, -) (*big.Int, error) { - // The Bridge contract builds the redemption key using the length-prefixed - // redeemer output script. - prefixedRedeemerOutputScript, err := redeemerOutputScript.ToVarLenData() - if err != nil { - return nil, fmt.Errorf("cannot build prefixed redeemer output script: [%v]", err) - } - - redeemerOutputScriptHash := crypto.Keccak256Hash(prefixedRedeemerOutputScript) - - redemptionKey := crypto.Keccak256Hash( - append(redeemerOutputScriptHash[:], walletPublicKeyHash[:]...), - ) - - return redemptionKey.Big(), nil + return crypto.Keccak256Hash(append(txHash[:], indexBytes...)).Big() } func (tc *TbtcChain) TxProofDifficultyFactor() (*big.Int, error) { return tc.bridge.TxProofDifficultyFactor() } - -func (tc *TbtcChain) SubmitDepositSweepProofWithReimbursement( - transaction *bitcoin.Transaction, - proof *bitcoin.SpvProof, - mainUTXO bitcoin.UnspentTransactionOutput, - vault common.Address, -) error { - bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ - Version: transaction.SerializeVersion(), - InputVector: transaction.SerializeInputs(), - OutputVector: transaction.SerializeOutputs(), - Locktime: transaction.SerializeLocktime(), - } - sweepProof := tbtcabi.BitcoinTxProof2{ - MerkleProof: proof.MerkleProof, - TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), - BitcoinHeaders: proof.BitcoinHeaders, - CoinbasePreimage: proof.CoinbasePreimage, - CoinbaseProof: proof.CoinbaseProof, - } - utxo := tbtcabi.BitcoinTxUTXO2{ - TxHash: mainUTXO.Outpoint.TransactionHash, - TxOutputIndex: mainUTXO.Outpoint.OutputIndex, - TxOutputValue: uint64(mainUTXO.Value), - } - - gasEstimate, err := tc.maintainerProxy.SubmitDepositSweepProofGasEstimate( - bitcoinTxInfo, - sweepProof, - utxo, - vault, - ) - if err != nil { - return err - } - - // The original estimate for this contract call is too low and the call - // fails on reimbursing the submitter. Example: - // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 - // Here we add a 20% margin to overcome the gas problems. - gasEstimateWithMargin := float64(gasEstimate) * float64(1.2) - - _, err = tc.maintainerProxy.SubmitDepositSweepProof( - bitcoinTxInfo, - sweepProof, - utxo, - vault, - ethutil.TransactionOptions{ - GasLimit: uint64(gasEstimateWithMargin), - }, - ) - - return err -} - -func (tc *TbtcChain) GetRedemptionParameters() (tbtc.RedemptionParameters, error) { - parameters, err := tc.bridge.RedemptionParameters() - if err != nil { - return tbtc.RedemptionParameters{}, err - } - - return tbtc.RedemptionParameters{ - DustThreshold: parameters.RedemptionDustThreshold, - TreasuryFeeDivisor: parameters.RedemptionTreasuryFeeDivisor, - TxMaxFee: parameters.RedemptionTxMaxFee, - TxMaxTotalFee: parameters.RedemptionTxMaxTotalFee, - Timeout: parameters.RedemptionTimeout, - TimeoutSlashingAmount: parameters.RedemptionTimeoutSlashingAmount, - TimeoutNotifierRewardMultiplier: parameters.RedemptionTimeoutNotifierRewardMultiplier, - }, nil -} - -func (tc *TbtcChain) GetWalletParameters() (tbtc.WalletParameters, error) { - parameters, err := tc.bridge.WalletParameters() - if err != nil { - return tbtc.WalletParameters{}, err - } - - return tbtc.WalletParameters{ - CreationPeriod: parameters.WalletCreationPeriod, - CreationMinBtcBalance: parameters.WalletCreationMinBtcBalance, - CreationMaxBtcBalance: parameters.WalletCreationMaxBtcBalance, - ClosureMinBtcBalance: parameters.WalletClosureMinBtcBalance, - MaxAge: parameters.WalletMaxAge, - MaxBtcTransfer: parameters.WalletMaxBtcTransfer, - ClosingPeriod: parameters.WalletClosingPeriod, - }, nil -} - -func (tc *TbtcChain) GetLiveWalletsCount() (uint32, error) { - return tc.bridge.LiveWalletsCount() -} - -func (tc *TbtcChain) PastMovingFundsCommitmentSubmittedEvents( - filter *tbtc.MovingFundsCommitmentSubmittedEventFilter, -) ([]*tbtc.MovingFundsCommitmentSubmittedEvent, error) { - var startBlock uint64 - var endBlock *uint64 - var walletPublicKeyHash [][20]byte - - if filter != nil { - startBlock = filter.StartBlock - endBlock = filter.EndBlock - walletPublicKeyHash = filter.WalletPublicKeyHash - } - - events, err := tc.bridge.PastMovingFundsCommitmentSubmittedEvents( - startBlock, - endBlock, - walletPublicKeyHash, - ) - if err != nil { - return nil, err - } - - convertedEvents := make([]*tbtc.MovingFundsCommitmentSubmittedEvent, 0) - for _, event := range events { - convertedEvent := &tbtc.MovingFundsCommitmentSubmittedEvent{ - WalletPublicKeyHash: event.WalletPubKeyHash, - TargetWallets: event.TargetWallets, - Submitter: chain.Address(event.Submitter.Hex()), - BlockNumber: event.Raw.BlockNumber, - } - - convertedEvents = append(convertedEvents, convertedEvent) - } - - sort.SliceStable( - convertedEvents, - func(i, j int) bool { - return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber - }, - ) - - return convertedEvents, err -} - -func (tc *TbtcChain) PastMovingFundsCompletedEvents( - filter *tbtc.MovingFundsCompletedEventFilter, -) ([]*tbtc.MovingFundsCompletedEvent, error) { - var startBlock uint64 - var endBlock *uint64 - var walletPublicKeyHash [][20]byte - - if filter != nil { - startBlock = filter.StartBlock - endBlock = filter.EndBlock - walletPublicKeyHash = filter.WalletPublicKeyHash - } - - events, err := tc.bridge.PastMovingFundsCompletedEvents( - startBlock, - endBlock, - walletPublicKeyHash, - ) - if err != nil { - return nil, err - } - - convertedEvents := make([]*tbtc.MovingFundsCompletedEvent, 0) - for _, event := range events { - convertedEvent := &tbtc.MovingFundsCompletedEvent{ - WalletPublicKeyHash: event.WalletPubKeyHash, - MovingFundsTxHash: event.MovingFundsTxHash, - BlockNumber: event.Raw.BlockNumber, - } - - convertedEvents = append(convertedEvents, convertedEvent) - } - - sort.SliceStable( - convertedEvents, - func(i, j int) bool { - return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber - }, - ) - - return convertedEvents, err -} - -func buildDepositKey( - fundingTxHash bitcoin.Hash, - fundingOutputIndex uint32, -) *big.Int { - fundingOutputIndexBytes := make([]byte, 4) - binary.BigEndian.PutUint32(fundingOutputIndexBytes, fundingOutputIndex) - - depositKey := crypto.Keccak256Hash( - append(fundingTxHash[:], fundingOutputIndexBytes...), - ) - - return depositKey.Big() -} - -func convertDepositSweepProposalToAbiType( - walletPublicKeyHash [20]byte, - proposal *tbtc.DepositSweepProposal, -) tbtcabi.WalletProposalValidatorDepositSweepProposal { - depositsKeys := make( - []tbtcabi.WalletProposalValidatorDepositKey, - len(proposal.DepositsKeys), - ) - - for i, depositKey := range proposal.DepositsKeys { - // We can map the depositKey.FundingTxHash field directly to the - // [32]byte type. This is because depositKey.FundingTxHash is - // a bitcoin.Hash type representing a hash in the - // bitcoin.InternalByteOrder, just as the on-chain contract assumes. - depositsKeys[i] = tbtcabi.WalletProposalValidatorDepositKey{ - FundingTxHash: depositKey.FundingTxHash, - FundingOutputIndex: depositKey.FundingOutputIndex, - } - } - - return tbtcabi.WalletProposalValidatorDepositSweepProposal{ - WalletPubKeyHash: walletPublicKeyHash, - DepositsKeys: depositsKeys, - SweepTxFee: proposal.SweepTxFee, - DepositsRevealBlocks: proposal.DepositsRevealBlocks, - } -} - -func parseWalletState(value uint8) (tbtc.WalletState, error) { - switch value { - case 0: - return tbtc.StateUnknown, nil - case 1: - return tbtc.StateLive, nil - case 2: - return tbtc.StateMovingFunds, nil - case 3: - return tbtc.StateClosing, nil - case 4: - return tbtc.StateClosed, nil - case 5: - return tbtc.StateTerminated, nil - default: - return 0, fmt.Errorf("unexpected wallet state value: [%v]", value) - } -} - -func (tc *TbtcChain) ValidateDepositSweepProposal( - walletPublicKeyHash [20]byte, - proposal *tbtc.DepositSweepProposal, - depositsExtraInfo []struct { - *tbtc.Deposit - FundingTx *bitcoin.Transaction - }, -) error { - dei := make([]tbtcabi.WalletProposalValidatorDepositExtraInfo, len(depositsExtraInfo)) - for i, depositExtraInfo := range depositsExtraInfo { - fundingTx := tbtcabi.BitcoinTxInfo2{ - Version: depositExtraInfo.FundingTx.SerializeVersion(), - InputVector: depositExtraInfo.FundingTx.SerializeInputs(), - OutputVector: depositExtraInfo.FundingTx.SerializeOutputs(), - Locktime: depositExtraInfo.FundingTx.SerializeLocktime(), - } - - dei[i] = tbtcabi.WalletProposalValidatorDepositExtraInfo{ - FundingTx: fundingTx, - BlindingFactor: depositExtraInfo.Deposit.BlindingFactor, - WalletPubKeyHash: depositExtraInfo.Deposit.WalletPublicKeyHash, - RefundPubKeyHash: depositExtraInfo.Deposit.RefundPublicKeyHash, - RefundLocktime: depositExtraInfo.Deposit.RefundLocktime, - } - } - - valid, err := tc.walletProposalValidator.ValidateDepositSweepProposal( - convertDepositSweepProposalToAbiType(walletPublicKeyHash, proposal), - dei, - ) - if err != nil { - return fmt.Errorf("validation failed: [%v]", err) - } - - // Should never happen because `validateDepositSweepProposal` returns true - // or reverts (returns an error) but do the check just in case. - if !valid { - return fmt.Errorf("unexpected validation result") - } - - return nil -} - -func (tc *TbtcChain) GetDepositSweepMaxSize() (uint16, error) { - return tc.walletProposalValidator.DEPOSITSWEEPMAXSIZE() -} - -func (tc *TbtcChain) SubmitMovingFundsCommitment( - walletPublicKeyHash [20]byte, - walletMainUTXO bitcoin.UnspentTransactionOutput, - walletMembersIDs []uint32, - walletMemberIndex uint32, - targetWallets [][20]byte, -) error { - mainUtxo := tbtcabi.BitcoinTxUTXO{ - TxHash: walletMainUTXO.Outpoint.TransactionHash, - TxOutputIndex: walletMainUTXO.Outpoint.OutputIndex, - TxOutputValue: uint64(walletMainUTXO.Value), - } - _, err := tc.bridge.SubmitMovingFundsCommitment( - walletPublicKeyHash, - mainUtxo, - walletMembersIDs, - big.NewInt(int64(walletMemberIndex)), - targetWallets, - ) - return err -} - -func (tc *TbtcChain) SubmitMovingFundsProofWithReimbursement( - transaction *bitcoin.Transaction, - proof *bitcoin.SpvProof, - mainUTXO bitcoin.UnspentTransactionOutput, - walletPublicKeyHash [20]byte, -) error { - bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ - Version: transaction.SerializeVersion(), - InputVector: transaction.SerializeInputs(), - OutputVector: transaction.SerializeOutputs(), - Locktime: transaction.SerializeLocktime(), - } - movingFundsProof := tbtcabi.BitcoinTxProof2{ - MerkleProof: proof.MerkleProof, - TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), - BitcoinHeaders: proof.BitcoinHeaders, - CoinbasePreimage: proof.CoinbasePreimage, - CoinbaseProof: proof.CoinbaseProof, - } - utxo := tbtcabi.BitcoinTxUTXO2{ - TxHash: mainUTXO.Outpoint.TransactionHash, - TxOutputIndex: mainUTXO.Outpoint.OutputIndex, - TxOutputValue: uint64(mainUTXO.Value), - } - - gasEstimate, err := tc.maintainerProxy.SubmitMovingFundsProofGasEstimate( - bitcoinTxInfo, - movingFundsProof, - utxo, - walletPublicKeyHash, - ) - if err != nil { - return err - } - - // The original estimate for this contract call is too low and the call - // fails on reimbursing the submitter. Example: - // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 - // Here we add a 20% margin to overcome the gas problems. - gasEstimateWithMargin := float64(gasEstimate) * float64(1.2) - - _, err = tc.maintainerProxy.SubmitMovingFundsProof( - bitcoinTxInfo, - movingFundsProof, - utxo, - walletPublicKeyHash, - ethutil.TransactionOptions{ - GasLimit: uint64(gasEstimateWithMargin), - }, - ) - - return err -} - -func (tc *TbtcChain) SubmitMovedFundsSweepProofWithReimbursement( - transaction *bitcoin.Transaction, - proof *bitcoin.SpvProof, - mainUTXO bitcoin.UnspentTransactionOutput, -) error { - bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ - Version: transaction.SerializeVersion(), - InputVector: transaction.SerializeInputs(), - OutputVector: transaction.SerializeOutputs(), - Locktime: transaction.SerializeLocktime(), - } - movedFundsSweepProof := tbtcabi.BitcoinTxProof2{ - MerkleProof: proof.MerkleProof, - TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), - BitcoinHeaders: proof.BitcoinHeaders, - CoinbasePreimage: proof.CoinbasePreimage, - CoinbaseProof: proof.CoinbaseProof, - } - utxo := tbtcabi.BitcoinTxUTXO2{ - TxHash: mainUTXO.Outpoint.TransactionHash, - TxOutputIndex: mainUTXO.Outpoint.OutputIndex, - TxOutputValue: uint64(mainUTXO.Value), - } - - gasEstimate, err := tc.maintainerProxy.SubmitMovedFundsSweepProofGasEstimate( - bitcoinTxInfo, - movedFundsSweepProof, - utxo, - ) - if err != nil { - return err - } - - // The original estimate for this contract call is too low and the call - // fails on reimbursing the submitter. Example: - // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 - // Here we add a 20% margin to overcome the gas problems. - gasEstimateWithMargin := float64(gasEstimate) * float64(1.2) - - _, err = tc.maintainerProxy.SubmitMovedFundsSweepProof( - bitcoinTxInfo, - movedFundsSweepProof, - utxo, - ethutil.TransactionOptions{ - GasLimit: uint64(gasEstimateWithMargin), - }, - ) - - return err -} - -func (tc *TbtcChain) ValidateMovedFundsSweepProposal( - walletPublicKeyHash [20]byte, - proposal *tbtc.MovedFundsSweepProposal, -) error { - abiProposal := tbtcabi.WalletProposalValidatorMovedFundsSweepProposal{ - WalletPubKeyHash: walletPublicKeyHash, - MovingFundsTxHash: proposal.MovingFundsTxHash, - MovingFundsTxOutputIndex: proposal.MovingFundsTxOutputIndex, - MovedFundsSweepTxFee: proposal.SweepTxFee, - } - - valid, err := tc.walletProposalValidator.ValidateMovedFundsSweepProposal( - abiProposal, - ) - if err != nil { - return fmt.Errorf("validation failed: [%v]", err) - } - - // Should never happen because `validateMovedFundsSweepProposal` returns - // true or reverts (returns an error) but do the check just in case. - if !valid { - return fmt.Errorf("unexpected validation result") - } - - return nil -} - -func (tc *TbtcChain) ValidateRedemptionProposal( - walletPublicKeyHash [20]byte, - proposal *tbtc.RedemptionProposal, -) error { - abiProposal, err := convertRedemptionProposalToAbiType( - walletPublicKeyHash, - proposal, - ) - if err != nil { - return fmt.Errorf("cannot convert proposal to abi type: [%v]", err) - } - - valid, err := tc.walletProposalValidator.ValidateRedemptionProposal( - abiProposal, - ) - if err != nil { - return fmt.Errorf("validation failed: [%v]", err) - } - - // Should never happen because `validateRedemptionProposal` returns true - // or reverts (returns an error) but do the check just in case. - if !valid { - return fmt.Errorf("unexpected validation result") - } - - return nil -} - -func convertRedemptionProposalToAbiType( - walletPublicKeyHash [20]byte, - proposal *tbtc.RedemptionProposal, -) (tbtcabi.WalletProposalValidatorRedemptionProposal, error) { - redeemersOutputScripts := make( - [][]byte, - len(proposal.RedeemersOutputScripts), - ) - - for i, script := range proposal.RedeemersOutputScripts { - // The on-chain script representation must be prepended with the script's - // byte-length while bitcoin.Script is not. We need to add the - // length prefix. - prefixedScript, err := script.ToVarLenData() - if err != nil { - return tbtcabi.WalletProposalValidatorRedemptionProposal{}, fmt.Errorf( - "cannot convert redeemer output script: [%v]", - err, - ) - } - - redeemersOutputScripts[i] = prefixedScript - } - - return tbtcabi.WalletProposalValidatorRedemptionProposal{ - WalletPubKeyHash: walletPublicKeyHash, - RedeemersOutputScripts: redeemersOutputScripts, - RedemptionTxFee: proposal.RedemptionTxFee, - }, nil -} - -func (tc *TbtcChain) GetRedemptionMaxSize() (uint16, error) { - return tc.walletProposalValidator.REDEMPTIONMAXSIZE() -} - -func (tc *TbtcChain) GetRedemptionRequestMinAge() (uint32, error) { - return tc.walletProposalValidator.REDEMPTIONREQUESTMINAGE() -} - -func (tc *TbtcChain) ValidateHeartbeatProposal( - walletPublicKeyHash [20]byte, - proposal *tbtc.HeartbeatProposal, -) error { - valid, err := tc.walletProposalValidator.ValidateHeartbeatProposal( - tbtcabi.WalletProposalValidatorHeartbeatProposal{ - WalletPubKeyHash: walletPublicKeyHash, - Message: proposal.Message[:], - }, - ) - if err != nil { - return fmt.Errorf("validation failed: [%v]", err) - } - - // Should never happen because `validateHeartbeatProposal` returns true - // or reverts (returns an error) but do the check just in case. - if !valid { - return fmt.Errorf("unexpected validation result") - } - - return nil -} - -func (tc *TbtcChain) GetMovingFundsParameters() (tbtc.MovingFundsParameters, error) { - parameters, err := tc.bridge.MovingFundsParameters() - if err != nil { - return tbtc.MovingFundsParameters{}, err - } - - return tbtc.MovingFundsParameters{ - TxMaxTotalFee: parameters.MovingFundsTxMaxTotalFee, - DustThreshold: parameters.MovingFundsDustThreshold, - TimeoutResetDelay: parameters.MovingFundsTimeoutResetDelay, - Timeout: parameters.MovingFundsTimeout, - TimeoutSlashingAmount: parameters.MovingFundsTimeoutSlashingAmount, - TimeoutNotifierRewardMultiplier: parameters.MovingFundsTimeoutNotifierRewardMultiplier, - CommitmentGasOffset: parameters.MovingFundsCommitmentGasOffset, - SweepTxMaxTotalFee: parameters.MovedFundsSweepTxMaxTotalFee, - SweepTimeout: parameters.MovedFundsSweepTimeout, - SweepTimeoutSlashingAmount: parameters.MovedFundsSweepTimeoutSlashingAmount, - SweepTimeoutNotifierRewardMultiplier: parameters.MovedFundsSweepTimeoutNotifierRewardMultiplier, - }, nil -} - -func (tc *TbtcChain) GetMovedFundsSweepRequest( - movingFundsTxHash bitcoin.Hash, - movingFundsTxOutpointIndex uint32, -) (*tbtc.MovedFundsSweepRequest, bool, error) { - movedFundsKey := buildMovedFundsKey( - movingFundsTxHash, - movingFundsTxOutpointIndex, - ) - - movedFundsSweepRequest, err := tc.bridge.MovedFundsSweepRequests( - movedFundsKey, - ) - if err != nil { - return nil, false, fmt.Errorf( - "cannot get moved funds sweep request for key [0x%x]: [%v]", - movedFundsKey.Text(16), - err, - ) - } - - // Moved funds sweep request not found. - if movedFundsSweepRequest.CreatedAt == 0 { - return nil, false, nil - } - - state, err := parseMovedFundsSweepRequestState(movedFundsSweepRequest.State) - if err != nil { - return nil, false, fmt.Errorf( - "cannot parse state for moved funds sweep request [0x%x]: [%v]", - movedFundsKey.Text(16), - err, - ) - } - - return &tbtc.MovedFundsSweepRequest{ - WalletPublicKeyHash: movedFundsSweepRequest.WalletPubKeyHash, - Value: movedFundsSweepRequest.Value, - CreatedAt: time.Unix(int64(movedFundsSweepRequest.CreatedAt), 0), - State: state, - }, true, nil -} - -func parseMovedFundsSweepRequestState(value uint8) ( - tbtc.MovedFundsSweepRequestState, - error, -) { - switch value { - case 0: - return tbtc.MovedFundsStateUnknown, nil - case 1: - return tbtc.MovedFundsStatePending, nil - case 2: - return tbtc.MovedFundsStateProcessed, nil - case 3: - return tbtc.MovedFundsStateTimedOut, nil - default: - return 0, fmt.Errorf( - "unexpected moved funds sweep request state value: [%v]", - value, - ) - } -} - -func buildMovedFundsKey( - movingFundsTxHash bitcoin.Hash, - movingFundsTxOutpointIndex uint32, -) *big.Int { - indexBytes := make([]byte, 4) - binary.BigEndian.PutUint32(indexBytes, movingFundsTxOutpointIndex) - - movedFundsKey := crypto.Keccak256Hash( - append(movingFundsTxHash[:], indexBytes...), - ) - - return movedFundsKey.Big() -} - -func (tc *TbtcChain) ValidateMovingFundsProposal( - walletPublicKeyHash [20]byte, - mainUTXO *bitcoin.UnspentTransactionOutput, - proposal *tbtc.MovingFundsProposal, -) error { - abiProposal := tbtcabi.WalletProposalValidatorMovingFundsProposal{ - WalletPubKeyHash: walletPublicKeyHash, - TargetWallets: proposal.TargetWallets, - MovingFundsTxFee: proposal.MovingFundsTxFee, - } - abiMainUTXO := tbtcabi.BitcoinTxUTXO3{ - TxHash: mainUTXO.Outpoint.TransactionHash, - TxOutputIndex: mainUTXO.Outpoint.OutputIndex, - TxOutputValue: uint64(mainUTXO.Value), - } - - valid, err := tc.walletProposalValidator.ValidateMovingFundsProposal( - abiProposal, - abiMainUTXO, - ) - if err != nil { - return fmt.Errorf("validation failed: [%v]", err) - } - - // Should never happen because `validateMovingFundsProposal` returns true - // or reverts (returns an error) but do the check just in case. - if !valid { - return fmt.Errorf("unexpected validation result") - } - - return nil -} - -func (tc *TbtcChain) GetRedemptionDelay( - walletPublicKeyHash [20]byte, - redeemerOutputScript bitcoin.Script, -) (time.Duration, error) { - if tc.redemptionWatchtower == nil { - return 0, nil - } - - redemptionKey, err := tc.BuildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) - if err != nil { - return 0, fmt.Errorf("cannot build redemption key: [%v]", err) - } - - delay, err := tc.redemptionWatchtower.GetRedemptionDelay(redemptionKey) - if err != nil { - return 0, fmt.Errorf("cannot get redemption delay: [%v]", err) - } - - return time.Duration(delay) * time.Second, nil -} - -func (tc *TbtcChain) GetDepositMinAge() (uint32, error) { - return tc.walletProposalValidator.DEPOSITMINAGE() -} diff --git a/pkg/chain/ethereum/tbtc_deposit.go b/pkg/chain/ethereum/tbtc_deposit.go new file mode 100644 index 0000000000..44698b98b0 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_deposit.go @@ -0,0 +1,301 @@ +// tbtc_deposit.go: deposit lifecycle (request, reveal, funding) for the TbtcChain adapter. +package ethereum + +import ( + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" + "github.com/keep-network/keep-core/pkg/bitcoin" + + "github.com/keep-network/keep-core/pkg/chain" + tbtcabi "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen/abi" + "github.com/keep-network/keep-core/pkg/tbtc" +) + +func (tc *TbtcChain) PastDepositRevealedEvents( + filter *tbtc.DepositRevealedEventFilter, +) ([]*tbtc.DepositRevealedEvent, error) { + var startBlock uint64 + var endBlock *uint64 + var depositor []common.Address + var walletPublicKeyHash [][20]byte + + if filter != nil { + startBlock = filter.StartBlock + endBlock = filter.EndBlock + + for _, d := range filter.Depositor { + depositor = append(depositor, common.HexToAddress(d.String())) + } + + walletPublicKeyHash = filter.WalletPublicKeyHash + } + + events, err := tc.bridge.PastDepositRevealedEvents( + startBlock, + endBlock, + depositor, + walletPublicKeyHash, + ) + if err != nil { + return nil, err + } + + convertedEvents := make([]*tbtc.DepositRevealedEvent, 0) + for _, event := range events { + var vault *chain.Address + if event.Vault != [20]byte{} { + v := chain.Address(event.Vault.Hex()) + vault = &v + } + + convertedEvent := &tbtc.DepositRevealedEvent{ + // We can map the event.FundingTxHash field directly to the + // bitcoin.Hash type. This is because event.FundingTxHash is + // a [32]byte type representing a hash in the bitcoin.InternalByteOrder, + // just as bitcoin.Hash assumes. + FundingTxHash: event.FundingTxHash, + FundingOutputIndex: event.FundingOutputIndex, + Depositor: chain.Address(event.Depositor.Hex()), + Amount: event.Amount, + BlindingFactor: event.BlindingFactor, + WalletPublicKeyHash: event.WalletPubKeyHash, + RefundPublicKeyHash: event.RefundPubKeyHash, + RefundLocktime: event.RefundLocktime, + Vault: vault, + BlockNumber: event.Raw.BlockNumber, + } + + convertedEvents = append(convertedEvents, convertedEvent) + } + + sort.SliceStable( + convertedEvents, + func(i, j int) bool { + return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber + }, + ) + + return convertedEvents, err +} + +func (tc *TbtcChain) GetDepositRequest( + fundingTxHash bitcoin.Hash, + fundingOutputIndex uint32, +) (*tbtc.DepositChainRequest, bool, error) { + depositKey := buildDepositKey(fundingTxHash, fundingOutputIndex) + depositCacheKey := depositKey.Text(16) + + tc.sweptDepositsCache.Sweep() + if cachedRequest, ok := tc.sweptDepositsCache.Get(depositCacheKey); ok { + return cachedRequest, true, nil + } + + chainRequest, err := tc.bridge.Deposits(depositKey) + if err != nil { + return nil, false, fmt.Errorf( + "cannot get deposit request for key [0x%x]: [%v]", + depositKey.Text(16), + err, + ) + } + + // Deposit not found. + if chainRequest.RevealedAt == 0 { + return nil, false, nil + } + + var vault *chain.Address + if chainRequest.Vault != [20]byte{} { + v := chain.Address(chainRequest.Vault.Hex()) + vault = &v + } + + var extraData *[32]byte + if chainRequest.ExtraData != [32]byte{} { + extraData = &chainRequest.ExtraData + } + + request := &tbtc.DepositChainRequest{ + Depositor: chain.Address(chainRequest.Depositor.Hex()), + Amount: chainRequest.Amount, + RevealedAt: time.Unix(int64(chainRequest.RevealedAt), 0), + Vault: vault, + TreasuryFee: chainRequest.TreasuryFee, + SweptAt: time.Unix(int64(chainRequest.SweptAt), 0), + ExtraData: extraData, + } + + // If the request was swept on-chain, there is a guarantee that no + // further changes will occur regarding its parameters. + // Such a request can be cached. + if isSwept := request.SweptAt.Unix() != 0; isSwept { + tc.sweptDepositsCache.Add(depositCacheKey, request) + } + + return request, true, nil +} + +func (tc *TbtcChain) BuildDepositKey( + fundingTxHash bitcoin.Hash, + fundingOutputIndex uint32, +) *big.Int { + return buildDepositKey(fundingTxHash, fundingOutputIndex) +} + +func (tc *TbtcChain) GetDepositParameters() (tbtc.DepositParameters, error) { + parameters, err := tc.bridge.DepositParameters() + if err != nil { + return tbtc.DepositParameters{}, err + } + + return tbtc.DepositParameters{ + DustThreshold: parameters.DepositDustThreshold, + TreasuryFeeDivisor: parameters.DepositTreasuryFeeDivisor, + TxMaxFee: parameters.DepositTxMaxFee, + RevealAheadPeriod: parameters.DepositRevealAheadPeriod, + }, nil +} + +func (tc *TbtcChain) SubmitDepositSweepProofWithReimbursement( + transaction *bitcoin.Transaction, + proof *bitcoin.SpvProof, + mainUTXO bitcoin.UnspentTransactionOutput, + vault common.Address, +) error { + bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ + Version: transaction.SerializeVersion(), + InputVector: transaction.SerializeInputs(), + OutputVector: transaction.SerializeOutputs(), + Locktime: transaction.SerializeLocktime(), + } + sweepProof := tbtcabi.BitcoinTxProof2{ + MerkleProof: proof.MerkleProof, + TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), + BitcoinHeaders: proof.BitcoinHeaders, + CoinbasePreimage: proof.CoinbasePreimage, + CoinbaseProof: proof.CoinbaseProof, + } + utxo := tbtcabi.BitcoinTxUTXO2{ + TxHash: mainUTXO.Outpoint.TransactionHash, + TxOutputIndex: mainUTXO.Outpoint.OutputIndex, + TxOutputValue: uint64(mainUTXO.Value), + } + + gasEstimate, err := tc.maintainerProxy.SubmitDepositSweepProofGasEstimate( + bitcoinTxInfo, + sweepProof, + utxo, + vault, + ) + if err != nil { + return err + } + + // The original estimate for this contract call is too low and the call + // fails on reimbursing the submitter. Example: + // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 + gasEstimateWithMargin := gasEstimateWithMargin(gasEstimate) + + _, err = tc.maintainerProxy.SubmitDepositSweepProof( + bitcoinTxInfo, + sweepProof, + utxo, + vault, + ethutil.TransactionOptions{ + GasLimit: uint64(gasEstimateWithMargin), + }, + ) + + return err +} + +func buildDepositKey( + fundingTxHash bitcoin.Hash, + fundingOutputIndex uint32, +) *big.Int { + return buildTxOutpointKey(fundingTxHash, fundingOutputIndex) +} + +func convertDepositSweepProposalToAbiType( + walletPublicKeyHash [20]byte, + proposal *tbtc.DepositSweepProposal, +) tbtcabi.WalletProposalValidatorDepositSweepProposal { + depositsKeys := make( + []tbtcabi.WalletProposalValidatorDepositKey, + len(proposal.DepositsKeys), + ) + + for i, depositKey := range proposal.DepositsKeys { + // We can map the depositKey.FundingTxHash field directly to the + // [32]byte type. This is because depositKey.FundingTxHash is + // a bitcoin.Hash type representing a hash in the + // bitcoin.InternalByteOrder, just as the on-chain contract assumes. + depositsKeys[i] = tbtcabi.WalletProposalValidatorDepositKey{ + FundingTxHash: depositKey.FundingTxHash, + FundingOutputIndex: depositKey.FundingOutputIndex, + } + } + + return tbtcabi.WalletProposalValidatorDepositSweepProposal{ + WalletPubKeyHash: walletPublicKeyHash, + DepositsKeys: depositsKeys, + SweepTxFee: proposal.SweepTxFee, + DepositsRevealBlocks: proposal.DepositsRevealBlocks, + } +} + +func (tc *TbtcChain) ValidateDepositSweepProposal( + walletPublicKeyHash [20]byte, + proposal *tbtc.DepositSweepProposal, + depositsExtraInfo []struct { + *tbtc.Deposit + FundingTx *bitcoin.Transaction + }, +) error { + dei := make([]tbtcabi.WalletProposalValidatorDepositExtraInfo, len(depositsExtraInfo)) + for i, depositExtraInfo := range depositsExtraInfo { + fundingTx := tbtcabi.BitcoinTxInfo2{ + Version: depositExtraInfo.FundingTx.SerializeVersion(), + InputVector: depositExtraInfo.FundingTx.SerializeInputs(), + OutputVector: depositExtraInfo.FundingTx.SerializeOutputs(), + Locktime: depositExtraInfo.FundingTx.SerializeLocktime(), + } + + dei[i] = tbtcabi.WalletProposalValidatorDepositExtraInfo{ + FundingTx: fundingTx, + BlindingFactor: depositExtraInfo.Deposit.BlindingFactor, + WalletPubKeyHash: depositExtraInfo.Deposit.WalletPublicKeyHash, + RefundPubKeyHash: depositExtraInfo.Deposit.RefundPublicKeyHash, + RefundLocktime: depositExtraInfo.Deposit.RefundLocktime, + } + } + + valid, err := tc.walletProposalValidator.ValidateDepositSweepProposal( + convertDepositSweepProposalToAbiType(walletPublicKeyHash, proposal), + dei, + ) + if err != nil { + return fmt.Errorf("validation failed: [%v]", err) + } + + // Should never happen because `validateDepositSweepProposal` returns true + // or reverts (returns an error) but do the check just in case. + if !valid { + return fmt.Errorf("unexpected validation result") + } + + return nil +} + +func (tc *TbtcChain) GetDepositSweepMaxSize() (uint16, error) { + return tc.walletProposalValidator.DEPOSITSWEEPMAXSIZE() +} + +func (tc *TbtcChain) GetDepositMinAge() (uint32, error) { + return tc.walletProposalValidator.DEPOSITMINAGE() +} diff --git a/pkg/chain/ethereum/tbtc_deposit_test.go b/pkg/chain/ethereum/tbtc_deposit_test.go new file mode 100644 index 0000000000..4ccdc7328e --- /dev/null +++ b/pkg/chain/ethereum/tbtc_deposit_test.go @@ -0,0 +1,31 @@ +package ethereum + +import ( + "testing" + + "github.com/keep-network/keep-core/internal/testutils" + "github.com/keep-network/keep-core/pkg/bitcoin" +) + +// Test data based on: https://etherscan.io/tx/0x97c7a293127a604da77f7ef8daf4b19da2bf04327dd891b6d717eaef89bd8bca +func TestBuildDepositKey(t *testing.T) { + fundingTxHash, err := bitcoin.NewHashFromString( + "585b6699f42291d1a9d0776b75f04c295ea203f83504349db11e94fdae7d1b2c", + bitcoin.InternalByteOrder, + ) + if err != nil { + t.Fatal(err) + } + + fundingOutputIndex := uint32(1) + + depositKey := buildDepositKey(fundingTxHash, fundingOutputIndex) + + expectedDepositKey := "3e84c1ea6aeaf2f45fb49623a88affe653b798ea6f675805acc0ec3965b6f317" + testutils.AssertStringsEqual( + t, + "deposit key", + expectedDepositKey, + depositKey.Text(16), + ) +} diff --git a/pkg/chain/ethereum/tbtc_dkg.go b/pkg/chain/ethereum/tbtc_dkg.go new file mode 100644 index 0000000000..4b70e336c8 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_dkg.go @@ -0,0 +1,566 @@ +// tbtc_dkg.go: DKG lifecycle, result assembly and validation for the TbtcChain adapter. +package ethereum + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "reflect" + "sort" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" + + "github.com/keep-network/keep-core/pkg/chain" + ecdsaabi "github.com/keep-network/keep-core/pkg/chain/ethereum/ecdsa/gen/abi" + "github.com/keep-network/keep-core/pkg/crypto/secp256k1" + "github.com/keep-network/keep-core/pkg/protocol/group" + "github.com/keep-network/keep-core/pkg/subscription" + "github.com/keep-network/keep-core/pkg/tbtc" + "github.com/keep-network/keep-core/pkg/tecdsa/dkg" +) + +func (tc *TbtcChain) OnDKGStarted( + handler func(event *tbtc.DKGStartedEvent), +) subscription.EventSubscription { + onEvent := func( + seed *big.Int, + blockNumber uint64, + ) { + handler(&tbtc.DKGStartedEvent{ + Seed: seed, + BlockNumber: blockNumber, + }) + } + + return tc.walletRegistry.DkgStartedEvent(nil, nil).OnEvent(onEvent) +} + +func (tc *TbtcChain) PastDKGStartedEvents( + filter *tbtc.DKGStartedEventFilter, +) ([]*tbtc.DKGStartedEvent, error) { + var startBlock uint64 + var endBlock *uint64 + var seed []*big.Int + + if filter != nil { + startBlock = filter.StartBlock + endBlock = filter.EndBlock + seed = filter.Seed + } + + events, err := tc.walletRegistry.PastDkgStartedEvents( + startBlock, + endBlock, + seed, + ) + if err != nil { + return nil, err + } + + dkgStartedEvents := make([]*tbtc.DKGStartedEvent, len(events)) + for i, event := range events { + dkgStartedEvents[i] = &tbtc.DKGStartedEvent{ + Seed: event.Seed, + BlockNumber: event.Raw.BlockNumber, + } + } + + sort.SliceStable(dkgStartedEvents, func(i, j int) bool { + return dkgStartedEvents[i].BlockNumber < dkgStartedEvents[j].BlockNumber + }) + + return dkgStartedEvents, err +} + +func (tc *TbtcChain) OnDKGResultSubmitted( + handler func(event *tbtc.DKGResultSubmittedEvent), +) subscription.EventSubscription { + onEvent := func( + resultHash [32]byte, + seed *big.Int, + result ecdsaabi.EcdsaDkgResult, + blockNumber uint64, + ) { + tbtcResult, err := convertDkgResultFromAbiType(result) + if err != nil { + // Surface the raw event payload alongside the conversion + // error so the bad event is recoverable from logs instead + // of being silently discarded (the conversion failure + // drops the event before the handler ever sees it). + logger.Errorf( + "unexpected DKG result in DKGResultSubmitted event "+ + "resultHash=[0x%x] seed=[%v] blockNumber=[%d] "+ + "submitterMemberIndex=[%v] signingMembersIndices=[%v]: [%v]", + resultHash, + seed, + blockNumber, + result.SubmitterMemberIndex, + result.SigningMembersIndices, + err, + ) + return + } + + handler(&tbtc.DKGResultSubmittedEvent{ + Seed: seed, + ResultHash: resultHash, + Result: tbtcResult, + BlockNumber: blockNumber, + }) + } + + return tc.walletRegistry. + DkgResultSubmittedEvent(nil, nil, nil). + OnEvent(onEvent) +} + +// convertDkgResultFromAbiType converts the WalletRegistry-specific DKG +// result to the format applicable for the TBTC application. +func convertDkgResultFromAbiType( + result ecdsaabi.EcdsaDkgResult, +) (*tbtc.DKGChainResult, error) { + if err := validateMemberIndex(result.SubmitterMemberIndex); err != nil { + return nil, fmt.Errorf( + "unexpected submitter member index: [%v]", + err, + ) + } + + signingMembersIndexes := make( + []group.MemberIndex, + len(result.SigningMembersIndices), + ) + for i, memberIndex := range result.SigningMembersIndices { + if err := validateMemberIndex(memberIndex); err != nil { + return nil, fmt.Errorf( + "unexpected signing member index: [%v]", + err, + ) + } + + signingMembersIndexes[i] = group.MemberIndex(memberIndex.Uint64()) + } + + return &tbtc.DKGChainResult{ + SubmitterMemberIndex: group.MemberIndex(result.SubmitterMemberIndex.Uint64()), + GroupPublicKey: result.GroupPubKey, + MisbehavedMembersIndexes: result.MisbehavedMembersIndices, + Signatures: result.Signatures, + SigningMembersIndexes: signingMembersIndexes, + Members: result.Members, + MembersHash: result.MembersHash, + }, nil +} + +// convertDkgResultToAbiType converts the TBTC-specific DKG result to +// the format applicable for the WalletRegistry ABI. +func convertDkgResultToAbiType( + result *tbtc.DKGChainResult, +) ecdsaabi.EcdsaDkgResult { + signingMembersIndices := make([]*big.Int, len(result.SigningMembersIndexes)) + for i, memberIndex := range result.SigningMembersIndexes { + signingMembersIndices[i] = big.NewInt(int64(memberIndex)) + } + + return ecdsaabi.EcdsaDkgResult{ + SubmitterMemberIndex: big.NewInt(int64(result.SubmitterMemberIndex)), + GroupPubKey: result.GroupPublicKey, + MisbehavedMembersIndices: result.MisbehavedMembersIndexes, + Signatures: result.Signatures, + SigningMembersIndices: signingMembersIndices, + Members: result.Members, + MembersHash: result.MembersHash, + } +} + +// validateMemberIndex guards a *big.Int member index against both an +// upper bound and a non-positive value. The non-positive check +// (`chainMemberIndex.Sign() <= 0`) is a behavior change introduced +// during the #4191 file split (the upper-bound check predated the +// split). On-chain indices are 1-based and uint64, so the new check +// is unreachable for valid events; it exists to surface a malformed +// event as an error instead of producing a zero `group.MemberIndex`. +func validateMemberIndex(chainMemberIndex *big.Int) error { + maxMemberIndex := big.NewInt(group.MaxMemberIndex) + if chainMemberIndex.Sign() <= 0 || chainMemberIndex.Cmp(maxMemberIndex) > 0 { + return fmt.Errorf("invalid member index value: [%v]", chainMemberIndex) + } + + return nil +} + +func (tc *TbtcChain) OnDKGResultChallenged( + handler func(event *tbtc.DKGResultChallengedEvent), +) subscription.EventSubscription { + onEvent := func( + resultHash [32]byte, + challenger common.Address, + reason string, + blockNumber uint64, + ) { + handler(&tbtc.DKGResultChallengedEvent{ + ResultHash: resultHash, + Challenger: chain.Address(challenger.Hex()), + Reason: reason, + BlockNumber: blockNumber, + }) + } + + return tc.walletRegistry. + DkgResultChallengedEvent(nil, nil, nil). + OnEvent(onEvent) +} + +func (tc *TbtcChain) OnDKGResultApproved( + handler func(event *tbtc.DKGResultApprovedEvent), +) subscription.EventSubscription { + onEvent := func( + resultHash [32]byte, + approver common.Address, + blockNumber uint64, + ) { + handler(&tbtc.DKGResultApprovedEvent{ + ResultHash: resultHash, + Approver: chain.Address(approver.Hex()), + BlockNumber: blockNumber, + }) + } + + return tc.walletRegistry. + DkgResultApprovedEvent(nil, nil, nil). + OnEvent(onEvent) +} + +// AssembleDKGResult assembles the DKG chain result according to the rules +// expected by the given chain. +func (tc *TbtcChain) AssembleDKGResult( + submitterMemberIndex group.MemberIndex, + groupPublicKey *ecdsa.PublicKey, + operatingMembersIndexes []group.MemberIndex, + misbehavedMembersIndexes []group.MemberIndex, + signatures map[group.MemberIndex][]byte, + groupSelectionResult *tbtc.GroupSelectionResult, +) (*tbtc.DKGChainResult, error) { + serializedGroupPublicKey, err := convertPubKeyToChainFormat(groupPublicKey) + if err != nil { + return nil, fmt.Errorf( + "could not convert group public key to chain format: [%v]", + err, + ) + } + + // Sort misbehavedMembersIndexes slice in ascending order as expected + // by the on-chain contract. + sort.Slice(misbehavedMembersIndexes[:], func(i, j int) bool { + return misbehavedMembersIndexes[i] < misbehavedMembersIndexes[j] + }) + + signingMemberIndices, signatureBytes, err := convertSignaturesToChainFormat( + signatures, + ) + if err != nil { + return nil, fmt.Errorf( + "could not convert signatures to chain format: [%v]", + err, + ) + } + + // Sort operatingOperatorsIDs slice in ascending order as the slice + // holding the operators IDs used to compute the members hash is + // expected to be sorted in the same way. + sort.Slice(operatingMembersIndexes[:], func(i, j int) bool { + return operatingMembersIndexes[i] < operatingMembersIndexes[j] + }) + + operatingOperatorsIDs := make([]chain.OperatorID, len(operatingMembersIndexes)) + for i, operatingMemberIndex := range operatingMembersIndexes { + operatingOperatorsIDs[i] = + groupSelectionResult.OperatorsIDs[operatingMemberIndex-1] + } + + membersHash, err := computeOperatorsIDsHash(operatingOperatorsIDs) + if err != nil { + return nil, fmt.Errorf("could not compute members hash: [%v]", err) + } + + return &tbtc.DKGChainResult{ + SubmitterMemberIndex: submitterMemberIndex, + GroupPublicKey: serializedGroupPublicKey[:], + MisbehavedMembersIndexes: misbehavedMembersIndexes, + Signatures: signatureBytes, + SigningMembersIndexes: signingMemberIndices, + Members: groupSelectionResult.OperatorsIDs, + MembersHash: membersHash, + }, nil +} + +func (tc *TbtcChain) SubmitDKGResult( + dkgResult *tbtc.DKGChainResult, +) error { + _, err := tc.walletRegistry.SubmitDkgResult( + convertDkgResultToAbiType(dkgResult), + ) + + return err +} + +// computeOperatorsIDsHash computes the keccak256 hash for the given list +// of operators IDs. +func computeOperatorsIDsHash(operatorsIDs chain.OperatorIDs) ([32]byte, error) { + uint32SliceType, err := abi.NewType("uint32[]", "uint32[]", nil) + if err != nil { + return [32]byte{}, err + } + + bytes, err := abi.Arguments{{Type: uint32SliceType}}.Pack(operatorsIDs) + if err != nil { + return [32]byte{}, err + } + + return crypto.Keccak256Hash(bytes), nil +} + +// convertSignaturesToChainFormat converts signatures map to two slices. The +// first slice contains indices of members from the map, sorted in ascending order +// as required by the contract. The second slice is a slice of concatenated +// signatures. Signatures and member indices are returned in the matching order. +// It requires each signature to be exactly 65-byte long. +func convertSignaturesToChainFormat( + signatures map[group.MemberIndex][]byte, +) ([]group.MemberIndex, []byte, error) { + membersIndexes := make([]group.MemberIndex, 0) + for memberIndex := range signatures { + membersIndexes = append(membersIndexes, memberIndex) + } + + sort.Slice(membersIndexes, func(i, j int) bool { + return membersIndexes[i] < membersIndexes[j] + }) + + signatureSize := 65 + + var signaturesSlice []byte + + for _, memberIndex := range membersIndexes { + signature := signatures[memberIndex] + + if len(signature) != signatureSize { + return nil, nil, fmt.Errorf( + "invalid signature size for member [%v] got [%d] bytes but [%d] bytes required", + memberIndex, + len(signature), + signatureSize, + ) + } + + signaturesSlice = append(signaturesSlice, signature...) + } + + return membersIndexes, signaturesSlice, nil +} + +func (tc *TbtcChain) GetDKGState() (tbtc.DKGState, error) { + walletCreationState, err := tc.walletRegistry.GetWalletCreationState() + if err != nil { + return 0, err + } + + var state tbtc.DKGState + + switch walletCreationState { + case 0: + state = tbtc.Idle + case 1: + state = tbtc.AwaitingSeed + case 2: + state = tbtc.AwaitingResult + case 3: + state = tbtc.Challenge + default: + err = fmt.Errorf( + "unexpected wallet creation state: [%v]", + walletCreationState, + ) + } + + return state, err +} + +// CalculateDKGResultSignatureHash calculates a 32-byte hash that is used +// to produce a signature supporting the given groupPublicKey computed +// as result of the given DKG process. The misbehavedMembersIndexes parameter +// should contain indexes of members that were considered as misbehaved +// during the DKG process. The startBlock argument is the block at which +// the given DKG process started. +func (tc *TbtcChain) CalculateDKGResultSignatureHash( + groupPublicKey *ecdsa.PublicKey, + misbehavedMembersIndexes []group.MemberIndex, + startBlock uint64, +) (dkg.ResultSignatureHash, error) { + groupPublicKeyBytes := secp256k1.Marshal(groupPublicKey) + // Crop the 04 prefix as the calculateDKGResultSignatureHash function + // expects an unprefixed 64-byte public key, + unprefixedGroupPublicKeyBytes := groupPublicKeyBytes[1:] + + // Sort misbehavedMembersIndexes slice in ascending order as expected + // by the calculateDKGResultSignatureHash function. + sort.Slice(misbehavedMembersIndexes[:], func(i, j int) bool { + return misbehavedMembersIndexes[i] < misbehavedMembersIndexes[j] + }) + + return calculateDKGResultSignatureHash( + tc.chainID, + unprefixedGroupPublicKeyBytes, + misbehavedMembersIndexes, + big.NewInt(int64(startBlock)), + ) +} + +// calculateDKGResultSignatureHash computes the keccak256 hash for the given DKG +// result parameters. It expects that the groupPublicKey is a 64-byte uncompressed +// public key without the 04 prefix and misbehavedMembersIndexes slice is +// sorted in ascending order. Those expectations are forced by the contract. +func calculateDKGResultSignatureHash( + chainID *big.Int, + groupPublicKey []byte, + misbehavedMembersIndexes []group.MemberIndex, + startBlock *big.Int, +) (dkg.ResultSignatureHash, error) { + publicKeySize := 64 + + if len(groupPublicKey) != publicKeySize { + return dkg.ResultSignatureHash{}, fmt.Errorf( + "wrong group public key length", + ) + } + + uint256Type, err := abi.NewType("uint256", "uint256", nil) + if err != nil { + return dkg.ResultSignatureHash{}, err + } + bytesType, err := abi.NewType("bytes", "bytes", nil) + if err != nil { + return dkg.ResultSignatureHash{}, err + } + uint8SliceType, err := abi.NewType("uint8[]", "uint8[]", nil) + if err != nil { + return dkg.ResultSignatureHash{}, err + } + + bytes, err := abi.Arguments{ + {Type: uint256Type}, + {Type: bytesType}, + {Type: uint8SliceType}, + {Type: uint256Type}, + }.Pack( + chainID, + groupPublicKey, + misbehavedMembersIndexes, + startBlock, + ) + if err != nil { + return dkg.ResultSignatureHash{}, err + } + + return dkg.ResultSignatureHash(crypto.Keccak256Hash(bytes)), nil +} + +func (tc *TbtcChain) IsDKGResultValid( + dkgResult *tbtc.DKGChainResult, +) (bool, error) { + outcome, err := tc.walletRegistry.IsDkgResultValid( + convertDkgResultToAbiType(dkgResult), + ) + if err != nil { + return false, fmt.Errorf("cannot check result validity: [%v]", err) + } + + return parseDkgResultValidationOutcome(&outcome) +} + +// parseDkgResultValidationOutcome parses the DKG validation outcome and returns +// a boolean indicating whether the result is valid or not. The outcome parameter +// must be a pointer to a struct containing a boolean flag as the first field. +// +// TODO: Find a better way to get the validity flag. This would require changes +// in the contracts binding generator. +// +// The nil-pointer, non-struct-element, and zero-field-count guards below are +// an intentional improvement added during the #4191 file split; they did not +// exist in the pre-split monolithic tbtc.go. They are strict supersets of the +// original behavior (the original code would panic on these inputs) and have +// no equivalent caller contract that relied on the panic, so callers that +// pass well-formed ABI outcomes see no change. +func parseDkgResultValidationOutcome( + outcome interface{}, +) (bool, error) { + value := reflect.ValueOf(outcome) + switch value.Kind() { + case reflect.Pointer: + if value.IsNil() { + return false, fmt.Errorf("result validation outcome is nil") + } + elem := value.Elem() + if elem.Kind() != reflect.Struct { + return false, fmt.Errorf("result validation outcome is not a struct") + } + if elem.NumField() == 0 { + return false, fmt.Errorf("result validation outcome has no fields") + } + default: + return false, fmt.Errorf("result validation outcome is not a pointer") + } + + field := value.Elem().Field(0) + switch field.Kind() { + case reflect.Bool: + return field.Bool(), nil + default: + return false, fmt.Errorf("cannot parse result validation outcome") + } +} + +func (tc *TbtcChain) ChallengeDKGResult(dkgResult *tbtc.DKGChainResult) error { + _, err := tc.walletRegistry.ChallengeDkgResult( + convertDkgResultToAbiType(dkgResult), + ) + + return err +} + +func (tc *TbtcChain) ApproveDKGResult(dkgResult *tbtc.DKGChainResult) error { + result := convertDkgResultToAbiType(dkgResult) + + gasEstimate, err := tc.walletRegistry.ApproveDkgResultGasEstimate(result) + if err != nil { + return err + } + + // The original estimate for this contract call turned out to be too low. + gasEstimateWithMargin := gasEstimateWithMargin(gasEstimate) + + _, err = tc.walletRegistry.ApproveDkgResult( + result, + ethutil.TransactionOptions{ + GasLimit: uint64(gasEstimateWithMargin), + }, + ) + + return err +} + +func (tc *TbtcChain) DKGParameters() (*tbtc.DKGParameters, error) { + parameters, err := tc.walletRegistry.DkgParameters() + if err != nil { + return nil, err + } + + return &tbtc.DKGParameters{ + SubmissionTimeoutBlocks: parameters.ResultSubmissionTimeout.Uint64(), + ChallengePeriodBlocks: parameters.ResultChallengePeriodLength.Uint64(), + ApprovePrecedencePeriodBlocks: parameters.SubmitterPrecedencePeriodLength.Uint64(), + }, nil +} diff --git a/pkg/chain/ethereum/tbtc_dkg_test.go b/pkg/chain/ethereum/tbtc_dkg_test.go new file mode 100644 index 0000000000..6fc3da6df5 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_dkg_test.go @@ -0,0 +1,268 @@ +package ethereum + +import ( + "bytes" + "encoding/hex" + "fmt" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + + "github.com/keep-network/keep-core/internal/testutils" + "github.com/keep-network/keep-core/pkg/chain" + "github.com/keep-network/keep-core/pkg/protocol/group" +) + +func TestComputeOperatorsIDsHash(t *testing.T) { + operatorIDs := []chain.OperatorID{ + 5, 1, 55, 45435534, 33, 345, 23, 235, 3333, 2, + } + + hash, err := computeOperatorsIDsHash(operatorIDs) + if err != nil { + t.Fatal(err) + } + + expectedHash := "8cd41effd4ee91b56d6b2f836efdcac11ab1ef2ae228e348814d0e6c2966d01e" + + testutils.AssertStringsEqual( + t, + "hash", + expectedHash, + hex.EncodeToString(hash[:]), + ) +} + +func TestConvertSignaturesToChainFormat(t *testing.T) { + signatureSize := 65 + + signature1 := common.LeftPadBytes([]byte{1, 2, 3}, signatureSize) + signature2 := common.LeftPadBytes([]byte{4, 5, 6}, signatureSize) + signature3 := common.LeftPadBytes([]byte{7}, signatureSize) + signature4 := common.LeftPadBytes([]byte{8, 9, 10}, signatureSize) + signature5 := common.LeftPadBytes([]byte{11, 12, 13}, signatureSize) + + invalidSignature := common.LeftPadBytes([]byte("invalid"), signatureSize-1) + + var tests = map[string]struct { + signaturesMap map[group.MemberIndex][]byte + expectedIndices []group.MemberIndex + expectedError error + }{ + "one valid signature": { + signaturesMap: map[uint8][]byte{ + 1: signature1, + }, + expectedIndices: []group.MemberIndex{1}, + }, + "five valid signatures": { + signaturesMap: map[group.MemberIndex][]byte{ + 3: signature3, + 1: signature1, + 4: signature4, + 5: signature5, + 2: signature2, + }, + expectedIndices: []group.MemberIndex{1, 2, 3, 4, 5}, + }, + "invalid signature": { + signaturesMap: map[group.MemberIndex][]byte{ + 1: signature1, + 2: invalidSignature, + }, + expectedError: fmt.Errorf("invalid signature size for member [2] got [64] bytes but [65] bytes required"), + }, + } + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + indicesSlice, signaturesSlice, err := + convertSignaturesToChainFormat(test.signaturesMap) + + if !reflect.DeepEqual(err, test.expectedError) { + t.Errorf( + "unexpected error\nexpected: [%v]\nactual: [%v]\n", + test.expectedError, + err, + ) + } + + if test.expectedError == nil { + if !reflect.DeepEqual(test.expectedIndices, indicesSlice) { + t.Errorf( + "unexpected indices\n"+ + "expected: [%v]\n"+ + "actual: [%v]\n", + test.expectedIndices, + indicesSlice, + ) + } + + testutils.AssertIntsEqual( + t, + "signatures slice length", + signatureSize*len(test.signaturesMap), + len(signaturesSlice), + ) + } + + for i, memberIndex := range indicesSlice { + actualSignature := signaturesSlice[signatureSize*i : signatureSize*(i+1)] + if !bytes.Equal( + test.signaturesMap[memberIndex], + actualSignature, + ) { + t.Errorf( + "invalid signatures for member %v\nexpected: %v\nactual: %v\n", + memberIndex, + test.signaturesMap[memberIndex], + actualSignature, + ) + } + } + }) + } +} + +func TestValidateMemberIndex(t *testing.T) { + one := big.NewInt(1) + maxMemberIndex := big.NewInt(255) + + var tests = map[string]struct { + chainMemberIndex *big.Int + expectedError error + }{ + "less than max member index": { + chainMemberIndex: new(big.Int).Sub(maxMemberIndex, one), + expectedError: nil, + }, + "max member index": { + chainMemberIndex: maxMemberIndex, + expectedError: nil, + }, + "greater than max member index": { + chainMemberIndex: new(big.Int).Add(maxMemberIndex, one), + expectedError: fmt.Errorf("invalid member index value: [256]"), + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + err := validateMemberIndex(test.chainMemberIndex) + + if !reflect.DeepEqual(err, test.expectedError) { + t.Errorf( + "unexpected error\nexpected: [%v]\nactual: [%v]\n", + test.expectedError, + err, + ) + } + }) + } +} + +func TestCalculateDKGResultSignatureHash(t *testing.T) { + chainID := big.NewInt(1) + + groupPublicKey, err := hex.DecodeString( + "989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d9d" + + "218b65e7d91c752f7b22eaceb771a9af3a6f3d3f010a5d471a1aeef7d7713af", + ) + if err != nil { + t.Fatal(err) + } + + misbehavedMembersIndexes := []group.MemberIndex{2, 55} + + startBlock := big.NewInt(2000) + + hash, err := calculateDKGResultSignatureHash( + chainID, + groupPublicKey, + misbehavedMembersIndexes, + startBlock, + ) + if err != nil { + t.Fatal(err) + } + + expectedHash := "25f917154586c2be0b6364f5c4758580e535bc01ed4881211000c9267aef3a3b" + + testutils.AssertStringsEqual( + t, + "hash", + expectedHash, + hex.EncodeToString(hash[:]), + ) +} + +func TestParseDkgResultValidationOutcome(t *testing.T) { + isValid, err := parseDkgResultValidationOutcome( + &struct { + bool + string + }{ + true, + "", + }, + ) + if err != nil { + t.Fatal(err) + } + testutils.AssertBoolsEqual(t, "validation outcome", true, isValid) + + isValid, err = parseDkgResultValidationOutcome( + &struct { + bool + string + }{ + false, + "", + }, + ) + if err != nil { + t.Fatal(err) + } + testutils.AssertBoolsEqual(t, "validation outcome", false, isValid) + + _, err = parseDkgResultValidationOutcome( + struct { + bool + string + }{ + true, + "", + }, + ) + expectedErr := fmt.Errorf("result validation outcome is not a pointer") + if !reflect.DeepEqual(expectedErr, err) { + t.Errorf( + "unexpected error\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedErr, + err, + ) + } + + _, err = parseDkgResultValidationOutcome( + &struct { + string + bool + }{ + "", + true, + }, + ) + expectedErr = fmt.Errorf("cannot parse result validation outcome") + if !reflect.DeepEqual(expectedErr, err) { + t.Errorf( + "unexpected error\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedErr, + err, + ) + } +} diff --git a/pkg/chain/ethereum/tbtc_inactivity.go b/pkg/chain/ethereum/tbtc_inactivity.go new file mode 100644 index 0000000000..2c4c2e09c4 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_inactivity.go @@ -0,0 +1,196 @@ +// tbtc_inactivity.go: inactivity-claim lifecycle for the TbtcChain adapter. +package ethereum + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/keep-network/keep-core/pkg/chain" + ecdsaabi "github.com/keep-network/keep-core/pkg/chain/ethereum/ecdsa/gen/abi" + "github.com/keep-network/keep-core/pkg/crypto/secp256k1" + "github.com/keep-network/keep-core/pkg/protocol/group" + "github.com/keep-network/keep-core/pkg/protocol/inactivity" + "github.com/keep-network/keep-core/pkg/subscription" + "github.com/keep-network/keep-core/pkg/tbtc" +) + +func (tc *TbtcChain) OnInactivityClaimed( + handler func(event *tbtc.InactivityClaimedEvent), +) subscription.EventSubscription { + onEvent := func( + walletID [32]byte, + nonce *big.Int, + notifier common.Address, + blockNumber uint64, + ) { + handler(&tbtc.InactivityClaimedEvent{ + WalletID: walletID, + Nonce: nonce, + Notifier: chain.Address(notifier.Hex()), + BlockNumber: blockNumber, + }) + } + + return tc.walletRegistry.InactivityClaimedEvent(nil, nil).OnEvent(onEvent) +} + +func (tc *TbtcChain) AssembleInactivityClaim( + walletID [32]byte, + inactiveMembersIndices []group.MemberIndex, + signatures map[group.MemberIndex][]byte, + heartbeatFailed bool, +) ( + *tbtc.InactivityClaim, + error, +) { + signingMemberIndices, signatureBytes, err := convertSignaturesToChainFormat( + signatures, + ) + if err != nil { + return nil, fmt.Errorf( + "could not convert signatures to chain format: [%v]", + err, + ) + } + + return &tbtc.InactivityClaim{ + WalletID: walletID, + InactiveMembersIndices: inactiveMembersIndices, + HeartbeatFailed: heartbeatFailed, + Signatures: signatureBytes, + SigningMembersIndices: signingMemberIndices, + }, nil +} + +// convertInactivityClaimToAbiType converts the TBTC-specific inactivity claim +// to the format applicable for the WalletRegistry ABI. +func convertInactivityClaimToAbiType( + claim *tbtc.InactivityClaim, +) ecdsaabi.EcdsaInactivityClaim { + inactiveMembersIndices := make([]*big.Int, len(claim.InactiveMembersIndices)) + for i, memberIndex := range claim.InactiveMembersIndices { + inactiveMembersIndices[i] = big.NewInt(int64(memberIndex)) + } + + signingMembersIndices := make([]*big.Int, len(claim.SigningMembersIndices)) + for i, memberIndex := range claim.SigningMembersIndices { + signingMembersIndices[i] = big.NewInt(int64(memberIndex)) + } + + return ecdsaabi.EcdsaInactivityClaim{ + WalletID: claim.WalletID, + InactiveMembersIndices: inactiveMembersIndices, + HeartbeatFailed: claim.HeartbeatFailed, + Signatures: claim.Signatures, + SigningMembersIndices: signingMembersIndices, + } +} + +func (tc *TbtcChain) SubmitInactivityClaim( + claim *tbtc.InactivityClaim, + nonce *big.Int, + groupMembers []uint32, +) error { + _, err := tc.walletRegistry.NotifyOperatorInactivity( + convertInactivityClaimToAbiType(claim), + nonce, + groupMembers, + ) + + return err +} + +func (tc *TbtcChain) CalculateInactivityClaimHash( + claim *inactivity.ClaimPreimage, +) (inactivity.ClaimHash, error) { + walletPublicKeyBytes := secp256k1.Marshal(claim.WalletPublicKey) + // Crop the 04 prefix as the calculateInactivityClaimHash function expects + // an unprefixed 64-byte public key, + unprefixedGroupPublicKeyBytes := walletPublicKeyBytes[1:] + + // The type representing inactive member index should be `big.Int` as the + // smart contract reading the calculated hash uses `uint256` for inactive + // member indexes. + inactiveMembersIndexes := make([]*big.Int, len(claim.InactiveMembersIndexes)) + for i, index := range claim.InactiveMembersIndexes { + inactiveMembersIndexes[i] = big.NewInt(int64(index)) + } + + return calculateInactivityClaimHash( + tc.chainID, + claim.Nonce, + unprefixedGroupPublicKeyBytes, + inactiveMembersIndexes, + claim.HeartbeatFailed, + ) +} + +func calculateInactivityClaimHash( + chainID *big.Int, + nonce *big.Int, + walletPublicKey []byte, + inactiveMembersIndexes []*big.Int, + heartbeatFailed bool, +) (inactivity.ClaimHash, error) { + publicKeySize := 64 + + if len(walletPublicKey) != publicKeySize { + return inactivity.ClaimHash{}, fmt.Errorf( + "wrong wallet public key length", + ) + } + + uint256Type, err := abi.NewType("uint256", "uint256", nil) + if err != nil { + return inactivity.ClaimHash{}, err + } + bytesType, err := abi.NewType("bytes", "bytes", nil) + if err != nil { + return inactivity.ClaimHash{}, err + } + uint256SliceType, err := abi.NewType("uint256[]", "uint256[]", nil) + if err != nil { + return inactivity.ClaimHash{}, err + } + boolType, err := abi.NewType("bool", "bool", nil) + if err != nil { + return inactivity.ClaimHash{}, err + } + + bytes, err := abi.Arguments{ + {Type: uint256Type}, + {Type: uint256Type}, + {Type: bytesType}, + {Type: uint256SliceType}, + {Type: boolType}, + }.Pack( + chainID, + nonce, + walletPublicKey, + inactiveMembersIndexes, + heartbeatFailed, + ) + if err != nil { + return inactivity.ClaimHash{}, err + } + + return inactivity.ClaimHash(crypto.Keccak256Hash(bytes)), nil +} + +func (tc *TbtcChain) GetInactivityClaimNonce( + walletID [32]byte, +) (*big.Int, error) { + nonce, err := tc.walletRegistry.InactivityClaimNonce(walletID) + if err != nil { + return nil, fmt.Errorf( + "failed to get inactivity claim nonce: [%w]", + err, + ) + } + + return nonce, nil +} diff --git a/pkg/chain/ethereum/tbtc_inactivity_test.go b/pkg/chain/ethereum/tbtc_inactivity_test.go new file mode 100644 index 0000000000..ed3c306bee --- /dev/null +++ b/pkg/chain/ethereum/tbtc_inactivity_test.go @@ -0,0 +1,48 @@ +package ethereum + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/keep-network/keep-core/internal/testutils" +) + +func TestCalculateInactivityClaimHash(t *testing.T) { + chainID := big.NewInt(31337) + nonce := big.NewInt(3) + + walletPublicKey, err := hex.DecodeString( + "9a0544440cc47779235ccb76d669590c2cd20c7e431f97e17a1093faf03291c473e" + + "661a208a8a565ca1e384059bd2ff7ff6886df081ff1229250099d388c83df", + ) + if err != nil { + t.Fatal(err) + } + + inactiveMembersIndexes := []*big.Int{ + big.NewInt(1), big.NewInt(2), big.NewInt(30), + } + + heartbeatFailed := true + + hash, err := calculateInactivityClaimHash( + chainID, + nonce, + walletPublicKey, + inactiveMembersIndexes, + heartbeatFailed, + ) + if err != nil { + t.Fatal(err) + } + + expectedHash := "f3210008cba186e90386a1bd0c63b6f29a67666f632350be22ce63ab39fc506e" + + testutils.AssertStringsEqual( + t, + "hash", + expectedHash, + hex.EncodeToString(hash[:]), + ) +} diff --git a/pkg/chain/ethereum/tbtc_moving_funds.go b/pkg/chain/ethereum/tbtc_moving_funds.go new file mode 100644 index 0000000000..15710bbc10 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_moving_funds.go @@ -0,0 +1,398 @@ +// tbtc_moving_funds.go: moving-funds lifecycle for the TbtcChain adapter. +package ethereum + +import ( + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" + "github.com/keep-network/keep-core/pkg/bitcoin" + "github.com/keep-network/keep-core/pkg/chain" + tbtcabi "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen/abi" + "github.com/keep-network/keep-core/pkg/tbtc" +) + +func (tc *TbtcChain) ComputeMovingFundsCommitmentHash( + targetWallets [][20]byte, +) [32]byte { + return computeMovingFundsCommitmentHash(targetWallets) +} + +func computeMovingFundsCommitmentHash(targetWallets [][20]byte) [32]byte { + packedWallets := []byte{} + + for _, wallet := range targetWallets { + packedWallets = append(packedWallets, wallet[:]...) + // Each wallet hash must be padded with 12 zero bytes following the + // actual hash. + packedWallets = append(packedWallets, make([]byte, 12)...) + } + + return crypto.Keccak256Hash(packedWallets) +} + +func (tc *TbtcChain) PastMovingFundsCommitmentSubmittedEvents( + filter *tbtc.MovingFundsCommitmentSubmittedEventFilter, +) ([]*tbtc.MovingFundsCommitmentSubmittedEvent, error) { + var startBlock uint64 + var endBlock *uint64 + var walletPublicKeyHash [][20]byte + + if filter != nil { + startBlock = filter.StartBlock + endBlock = filter.EndBlock + walletPublicKeyHash = filter.WalletPublicKeyHash + } + + events, err := tc.bridge.PastMovingFundsCommitmentSubmittedEvents( + startBlock, + endBlock, + walletPublicKeyHash, + ) + if err != nil { + return nil, err + } + + convertedEvents := make([]*tbtc.MovingFundsCommitmentSubmittedEvent, 0) + for _, event := range events { + convertedEvent := &tbtc.MovingFundsCommitmentSubmittedEvent{ + WalletPublicKeyHash: event.WalletPubKeyHash, + TargetWallets: event.TargetWallets, + Submitter: chain.Address(event.Submitter.Hex()), + BlockNumber: event.Raw.BlockNumber, + } + + convertedEvents = append(convertedEvents, convertedEvent) + } + + sort.SliceStable( + convertedEvents, + func(i, j int) bool { + return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber + }, + ) + + return convertedEvents, err +} + +func (tc *TbtcChain) PastMovingFundsCompletedEvents( + filter *tbtc.MovingFundsCompletedEventFilter, +) ([]*tbtc.MovingFundsCompletedEvent, error) { + var startBlock uint64 + var endBlock *uint64 + var walletPublicKeyHash [][20]byte + + if filter != nil { + startBlock = filter.StartBlock + endBlock = filter.EndBlock + walletPublicKeyHash = filter.WalletPublicKeyHash + } + + events, err := tc.bridge.PastMovingFundsCompletedEvents( + startBlock, + endBlock, + walletPublicKeyHash, + ) + if err != nil { + return nil, err + } + + convertedEvents := make([]*tbtc.MovingFundsCompletedEvent, 0) + for _, event := range events { + convertedEvent := &tbtc.MovingFundsCompletedEvent{ + WalletPublicKeyHash: event.WalletPubKeyHash, + MovingFundsTxHash: event.MovingFundsTxHash, + BlockNumber: event.Raw.BlockNumber, + } + + convertedEvents = append(convertedEvents, convertedEvent) + } + + sort.SliceStable( + convertedEvents, + func(i, j int) bool { + return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber + }, + ) + + return convertedEvents, err +} + +func (tc *TbtcChain) SubmitMovingFundsCommitment( + walletPublicKeyHash [20]byte, + walletMainUTXO bitcoin.UnspentTransactionOutput, + walletMembersIDs []uint32, + walletMemberIndex uint32, + targetWallets [][20]byte, +) error { + mainUtxo := tbtcabi.BitcoinTxUTXO{ + TxHash: walletMainUTXO.Outpoint.TransactionHash, + TxOutputIndex: walletMainUTXO.Outpoint.OutputIndex, + TxOutputValue: uint64(walletMainUTXO.Value), + } + _, err := tc.bridge.SubmitMovingFundsCommitment( + walletPublicKeyHash, + mainUtxo, + walletMembersIDs, + big.NewInt(int64(walletMemberIndex)), + targetWallets, + ) + return err +} + +func (tc *TbtcChain) SubmitMovingFundsProofWithReimbursement( + transaction *bitcoin.Transaction, + proof *bitcoin.SpvProof, + mainUTXO bitcoin.UnspentTransactionOutput, + walletPublicKeyHash [20]byte, +) error { + bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ + Version: transaction.SerializeVersion(), + InputVector: transaction.SerializeInputs(), + OutputVector: transaction.SerializeOutputs(), + Locktime: transaction.SerializeLocktime(), + } + movingFundsProof := tbtcabi.BitcoinTxProof2{ + MerkleProof: proof.MerkleProof, + TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), + BitcoinHeaders: proof.BitcoinHeaders, + CoinbasePreimage: proof.CoinbasePreimage, + CoinbaseProof: proof.CoinbaseProof, + } + utxo := tbtcabi.BitcoinTxUTXO2{ + TxHash: mainUTXO.Outpoint.TransactionHash, + TxOutputIndex: mainUTXO.Outpoint.OutputIndex, + TxOutputValue: uint64(mainUTXO.Value), + } + + gasEstimate, err := tc.maintainerProxy.SubmitMovingFundsProofGasEstimate( + bitcoinTxInfo, + movingFundsProof, + utxo, + walletPublicKeyHash, + ) + if err != nil { + return err + } + + // The original estimate for this contract call is too low and the call + // fails on reimbursing the submitter. Example: + // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 + gasEstimateWithMargin := gasEstimateWithMargin(gasEstimate) + + _, err = tc.maintainerProxy.SubmitMovingFundsProof( + bitcoinTxInfo, + movingFundsProof, + utxo, + walletPublicKeyHash, + ethutil.TransactionOptions{ + GasLimit: uint64(gasEstimateWithMargin), + }, + ) + + return err +} + +func (tc *TbtcChain) SubmitMovedFundsSweepProofWithReimbursement( + transaction *bitcoin.Transaction, + proof *bitcoin.SpvProof, + mainUTXO bitcoin.UnspentTransactionOutput, +) error { + bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ + Version: transaction.SerializeVersion(), + InputVector: transaction.SerializeInputs(), + OutputVector: transaction.SerializeOutputs(), + Locktime: transaction.SerializeLocktime(), + } + movedFundsSweepProof := tbtcabi.BitcoinTxProof2{ + MerkleProof: proof.MerkleProof, + TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), + BitcoinHeaders: proof.BitcoinHeaders, + CoinbasePreimage: proof.CoinbasePreimage, + CoinbaseProof: proof.CoinbaseProof, + } + utxo := tbtcabi.BitcoinTxUTXO2{ + TxHash: mainUTXO.Outpoint.TransactionHash, + TxOutputIndex: mainUTXO.Outpoint.OutputIndex, + TxOutputValue: uint64(mainUTXO.Value), + } + + gasEstimate, err := tc.maintainerProxy.SubmitMovedFundsSweepProofGasEstimate( + bitcoinTxInfo, + movedFundsSweepProof, + utxo, + ) + if err != nil { + return err + } + + // The original estimate for this contract call is too low and the call + // fails on reimbursing the submitter. Example: + // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 + gasEstimateWithMargin := gasEstimateWithMargin(gasEstimate) + + _, err = tc.maintainerProxy.SubmitMovedFundsSweepProof( + bitcoinTxInfo, + movedFundsSweepProof, + utxo, + ethutil.TransactionOptions{ + GasLimit: uint64(gasEstimateWithMargin), + }, + ) + + return err +} + +func (tc *TbtcChain) ValidateMovedFundsSweepProposal( + walletPublicKeyHash [20]byte, + proposal *tbtc.MovedFundsSweepProposal, +) error { + abiProposal := tbtcabi.WalletProposalValidatorMovedFundsSweepProposal{ + WalletPubKeyHash: walletPublicKeyHash, + MovingFundsTxHash: proposal.MovingFundsTxHash, + MovingFundsTxOutputIndex: proposal.MovingFundsTxOutputIndex, + MovedFundsSweepTxFee: proposal.SweepTxFee, + } + + valid, err := tc.walletProposalValidator.ValidateMovedFundsSweepProposal( + abiProposal, + ) + if err != nil { + return fmt.Errorf("validation failed: [%v]", err) + } + + // Should never happen because `validateMovedFundsSweepProposal` returns + // true or reverts (returns an error) but do the check just in case. + if !valid { + return fmt.Errorf("unexpected validation result") + } + + return nil +} + +func (tc *TbtcChain) GetMovingFundsParameters() (tbtc.MovingFundsParameters, error) { + parameters, err := tc.bridge.MovingFundsParameters() + if err != nil { + return tbtc.MovingFundsParameters{}, err + } + + return tbtc.MovingFundsParameters{ + TxMaxTotalFee: parameters.MovingFundsTxMaxTotalFee, + DustThreshold: parameters.MovingFundsDustThreshold, + TimeoutResetDelay: parameters.MovingFundsTimeoutResetDelay, + Timeout: parameters.MovingFundsTimeout, + TimeoutSlashingAmount: parameters.MovingFundsTimeoutSlashingAmount, + TimeoutNotifierRewardMultiplier: parameters.MovingFundsTimeoutNotifierRewardMultiplier, + CommitmentGasOffset: parameters.MovingFundsCommitmentGasOffset, + SweepTxMaxTotalFee: parameters.MovedFundsSweepTxMaxTotalFee, + SweepTimeout: parameters.MovedFundsSweepTimeout, + SweepTimeoutSlashingAmount: parameters.MovedFundsSweepTimeoutSlashingAmount, + SweepTimeoutNotifierRewardMultiplier: parameters.MovedFundsSweepTimeoutNotifierRewardMultiplier, + }, nil +} + +func (tc *TbtcChain) GetMovedFundsSweepRequest( + movingFundsTxHash bitcoin.Hash, + movingFundsTxOutpointIndex uint32, +) (*tbtc.MovedFundsSweepRequest, bool, error) { + movedFundsKey := buildMovedFundsKey( + movingFundsTxHash, + movingFundsTxOutpointIndex, + ) + + movedFundsSweepRequest, err := tc.bridge.MovedFundsSweepRequests( + movedFundsKey, + ) + if err != nil { + return nil, false, fmt.Errorf( + "cannot get moved funds sweep request for key [0x%x]: [%v]", + movedFundsKey.Text(16), + err, + ) + } + + // Moved funds sweep request not found. + if movedFundsSweepRequest.CreatedAt == 0 { + return nil, false, nil + } + + state, err := parseMovedFundsSweepRequestState(movedFundsSweepRequest.State) + if err != nil { + return nil, false, fmt.Errorf( + "cannot parse state for moved funds sweep request [0x%x]: [%v]", + movedFundsKey.Text(16), + err, + ) + } + + return &tbtc.MovedFundsSweepRequest{ + WalletPublicKeyHash: movedFundsSweepRequest.WalletPubKeyHash, + Value: movedFundsSweepRequest.Value, + CreatedAt: time.Unix(int64(movedFundsSweepRequest.CreatedAt), 0), + State: state, + }, true, nil +} + +func parseMovedFundsSweepRequestState(value uint8) ( + tbtc.MovedFundsSweepRequestState, + error, +) { + switch value { + case 0: + return tbtc.MovedFundsStateUnknown, nil + case 1: + return tbtc.MovedFundsStatePending, nil + case 2: + return tbtc.MovedFundsStateProcessed, nil + case 3: + return tbtc.MovedFundsStateTimedOut, nil + default: + return 0, fmt.Errorf( + "unexpected moved funds sweep request state value: [%v]", + value, + ) + } +} + +func buildMovedFundsKey( + movingFundsTxHash bitcoin.Hash, + movingFundsTxOutpointIndex uint32, +) *big.Int { + return buildTxOutpointKey(movingFundsTxHash, movingFundsTxOutpointIndex) +} + +func (tc *TbtcChain) ValidateMovingFundsProposal( + walletPublicKeyHash [20]byte, + mainUTXO *bitcoin.UnspentTransactionOutput, + proposal *tbtc.MovingFundsProposal, +) error { + abiProposal := tbtcabi.WalletProposalValidatorMovingFundsProposal{ + WalletPubKeyHash: walletPublicKeyHash, + TargetWallets: proposal.TargetWallets, + MovingFundsTxFee: proposal.MovingFundsTxFee, + } + abiMainUTXO := tbtcabi.BitcoinTxUTXO3{ + TxHash: mainUTXO.Outpoint.TransactionHash, + TxOutputIndex: mainUTXO.Outpoint.OutputIndex, + TxOutputValue: uint64(mainUTXO.Value), + } + + valid, err := tc.walletProposalValidator.ValidateMovingFundsProposal( + abiProposal, + abiMainUTXO, + ) + if err != nil { + return fmt.Errorf("validation failed: [%v]", err) + } + + // Should never happen because `validateMovingFundsProposal` returns true + // or reverts (returns an error) but do the check just in case. + if !valid { + return fmt.Errorf("unexpected validation result") + } + + return nil +} diff --git a/pkg/chain/ethereum/tbtc_moving_funds_test.go b/pkg/chain/ethereum/tbtc_moving_funds_test.go new file mode 100644 index 0000000000..87e7a594ea --- /dev/null +++ b/pkg/chain/ethereum/tbtc_moving_funds_test.go @@ -0,0 +1,70 @@ +package ethereum + +import ( + "encoding/hex" + "testing" + + "github.com/keep-network/keep-core/internal/testutils" + "github.com/keep-network/keep-core/pkg/bitcoin" +) + +func TestComputeMovingFundsCommitmentHash(t *testing.T) { + toByte20 := func(s string) [20]byte { + bytes, err := hex.DecodeString(s) + if err != nil { + t.Fatal(err) + } + + if len(bytes) != 20 { + t.Fatal("incorrect hexstring length") + } + + var result [20]byte + copy(result[:], bytes[:]) + return result + } + + targetWallets := [][20]byte{ + toByte20("4b440cb29c80c3f256212d8fdd4f2125366f3c91"), + toByte20("888f01315e0268bfa05d5e522f8d63f6824d9a96"), + toByte20("b2a89e53a4227dbe530a52a1c419040735fa636c"), + } + + movingFundsCommitmentHash := computeMovingFundsCommitmentHash( + targetWallets, + ) + + expectedMovingFundsCommitmentHash, err := hex.DecodeString( + "8ba62d1d754a3429e2ff1fb4f523b5fad2b605c873a2968bb5985a625eb96202", + ) + if err != nil { + t.Fatal(err) + } + testutils.AssertBytesEqual( + t, + expectedMovingFundsCommitmentHash, + movingFundsCommitmentHash[:], + ) +} + +func TestBuildMovedFundsKey(t *testing.T) { + fundingTxHash, err := bitcoin.NewHashFromString( + "7cff663e3e08847a5579913f6a66bc6c01f5f48c6ae1783be77418ed188021e6", + bitcoin.InternalByteOrder, + ) + if err != nil { + t.Fatal(err) + } + + fundingOutputIndex := uint32(2) + + movedFundsKey := buildMovedFundsKey(fundingTxHash, fundingOutputIndex) + + expectedMovedFundsKey := "24509b8a853476ebe77af3707bd7ce017d527680e941b6eeaac2d5b712df4f8d" + testutils.AssertStringsEqual( + t, + "moved funds key", + expectedMovedFundsKey, + movedFundsKey.Text(16), + ) +} diff --git a/pkg/chain/ethereum/tbtc_redemption.go b/pkg/chain/ethereum/tbtc_redemption.go new file mode 100644 index 0000000000..63b329ed46 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_redemption.go @@ -0,0 +1,297 @@ +// tbtc_redemption.go: redemption request lifecycle for the TbtcChain adapter. +package ethereum + +import ( + "fmt" + "math/big" + "sort" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/keep-network/keep-common/pkg/chain/ethereum/ethutil" + "github.com/keep-network/keep-core/pkg/bitcoin" + + "github.com/keep-network/keep-core/pkg/chain" + tbtcabi "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen/abi" + "github.com/keep-network/keep-core/pkg/tbtc" +) + +func (tc *TbtcChain) PastRedemptionRequestedEvents( + filter *tbtc.RedemptionRequestedEventFilter, +) ([]*tbtc.RedemptionRequestedEvent, error) { + var startBlock uint64 + var endBlock *uint64 + var redeemers []common.Address + var walletPublicKeyHash [][20]byte + + if filter != nil { + startBlock = filter.StartBlock + endBlock = filter.EndBlock + + for _, r := range filter.Redeemer { + redeemers = append(redeemers, common.HexToAddress(r.String())) + } + + walletPublicKeyHash = filter.WalletPublicKeyHash + } + + events, err := tc.bridge.PastRedemptionRequestedEvents( + startBlock, + endBlock, + walletPublicKeyHash, + redeemers, + ) + if err != nil { + return nil, err + } + + convertedEvents := make([]*tbtc.RedemptionRequestedEvent, 0) + for _, event := range events { + redeemerOutputScript, err := bitcoin.NewScriptFromVarLenData( + event.RedeemerOutputScript, + ) + if err != nil { + return nil, err + } + + convertedEvent := &tbtc.RedemptionRequestedEvent{ + WalletPublicKeyHash: event.WalletPubKeyHash, + RedeemerOutputScript: redeemerOutputScript, + Redeemer: chain.Address(event.Redeemer.Hex()), + RequestedAmount: event.RequestedAmount, + TreasuryFee: event.TreasuryFee, + TxMaxFee: event.TxMaxFee, + BlockNumber: event.Raw.BlockNumber, + } + + convertedEvents = append(convertedEvents, convertedEvent) + } + + sort.SliceStable( + convertedEvents, + func(i, j int) bool { + return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber + }, + ) + + return convertedEvents, err +} + +func (tc *TbtcChain) BuildRedemptionKey( + walletPublicKeyHash [20]byte, + redeemerOutputScript bitcoin.Script, +) (*big.Int, error) { + return buildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) +} + +func (tc *TbtcChain) GetPendingRedemptionRequest( + walletPublicKeyHash [20]byte, + redeemerOutputScript bitcoin.Script, +) (*tbtc.RedemptionRequest, bool, error) { + redemptionKey, err := buildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) + if err != nil { + return nil, false, fmt.Errorf("cannot build redemption key: [%v]", err) + } + + redemptionRequest, err := tc.bridge.PendingRedemptions(redemptionKey) + if err != nil { + return nil, false, fmt.Errorf( + "cannot get pending redemption request for key [0x%x]: [%v]", + redemptionKey, + err, + ) + } + + // Redemption not found. + if redemptionRequest.RequestedAt == 0 { + return nil, false, nil + } + + return &tbtc.RedemptionRequest{ + Redeemer: chain.Address(redemptionRequest.Redeemer.Hex()), + RedeemerOutputScript: redeemerOutputScript, + RequestedAmount: redemptionRequest.RequestedAmount, + TreasuryFee: redemptionRequest.TreasuryFee, + TxMaxFee: redemptionRequest.TxMaxFee, + RequestedAt: time.Unix(int64(redemptionRequest.RequestedAt), 0), + }, true, nil +} + +func (tc *TbtcChain) SubmitRedemptionProofWithReimbursement( + transaction *bitcoin.Transaction, + proof *bitcoin.SpvProof, + mainUTXO bitcoin.UnspentTransactionOutput, + walletPublicKeyHash [20]byte, +) error { + bitcoinTxInfo := tbtcabi.BitcoinTxInfo3{ + Version: transaction.SerializeVersion(), + InputVector: transaction.SerializeInputs(), + OutputVector: transaction.SerializeOutputs(), + Locktime: transaction.SerializeLocktime(), + } + redemptionProof := tbtcabi.BitcoinTxProof2{ + MerkleProof: proof.MerkleProof, + TxIndexInBlock: big.NewInt(int64(proof.TxIndexInBlock)), + BitcoinHeaders: proof.BitcoinHeaders, + CoinbasePreimage: proof.CoinbasePreimage, + CoinbaseProof: proof.CoinbaseProof, + } + utxo := tbtcabi.BitcoinTxUTXO2{ + TxHash: mainUTXO.Outpoint.TransactionHash, + TxOutputIndex: mainUTXO.Outpoint.OutputIndex, + TxOutputValue: uint64(mainUTXO.Value), + } + + gasEstimate, err := tc.maintainerProxy.SubmitRedemptionProofGasEstimate( + bitcoinTxInfo, + redemptionProof, + utxo, + walletPublicKeyHash, + ) + if err != nil { + return err + } + + // The original estimate for this contract call is too low and the call + // fails on reimbursing the submitter. Example: + // 0xe27a92883e0e64da8a3a54a15a260ea2f4d3d48470129ac5c09bfe9637d7e114 + gasEstimateWithMargin := gasEstimateWithMargin(gasEstimate) + + _, err = tc.maintainerProxy.SubmitRedemptionProof( + bitcoinTxInfo, + redemptionProof, + utxo, + walletPublicKeyHash, + ethutil.TransactionOptions{ + GasLimit: uint64(gasEstimateWithMargin), + }, + ) + + return err +} + +func buildRedemptionKey( + walletPublicKeyHash [20]byte, + redeemerOutputScript bitcoin.Script, +) (*big.Int, error) { + // The Bridge contract builds the redemption key using the length-prefixed + // redeemer output script. + prefixedRedeemerOutputScript, err := redeemerOutputScript.ToVarLenData() + if err != nil { + return nil, fmt.Errorf("cannot build prefixed redeemer output script: [%v]", err) + } + + redeemerOutputScriptHash := crypto.Keccak256Hash(prefixedRedeemerOutputScript) + + redemptionKey := crypto.Keccak256Hash( + append(redeemerOutputScriptHash[:], walletPublicKeyHash[:]...), + ) + + return redemptionKey.Big(), nil +} + +func (tc *TbtcChain) GetRedemptionParameters() (tbtc.RedemptionParameters, error) { + parameters, err := tc.bridge.RedemptionParameters() + if err != nil { + return tbtc.RedemptionParameters{}, err + } + + return tbtc.RedemptionParameters{ + DustThreshold: parameters.RedemptionDustThreshold, + TreasuryFeeDivisor: parameters.RedemptionTreasuryFeeDivisor, + TxMaxFee: parameters.RedemptionTxMaxFee, + TxMaxTotalFee: parameters.RedemptionTxMaxTotalFee, + Timeout: parameters.RedemptionTimeout, + TimeoutSlashingAmount: parameters.RedemptionTimeoutSlashingAmount, + TimeoutNotifierRewardMultiplier: parameters.RedemptionTimeoutNotifierRewardMultiplier, + }, nil +} + +func (tc *TbtcChain) ValidateRedemptionProposal( + walletPublicKeyHash [20]byte, + proposal *tbtc.RedemptionProposal, +) error { + abiProposal, err := convertRedemptionProposalToAbiType( + walletPublicKeyHash, + proposal, + ) + if err != nil { + return fmt.Errorf("cannot convert proposal to abi type: [%v]", err) + } + + valid, err := tc.walletProposalValidator.ValidateRedemptionProposal( + abiProposal, + ) + if err != nil { + return fmt.Errorf("validation failed: [%v]", err) + } + + // Should never happen because `validateRedemptionProposal` returns true + // or reverts (returns an error) but do the check just in case. + if !valid { + return fmt.Errorf("unexpected validation result") + } + + return nil +} + +func convertRedemptionProposalToAbiType( + walletPublicKeyHash [20]byte, + proposal *tbtc.RedemptionProposal, +) (tbtcabi.WalletProposalValidatorRedemptionProposal, error) { + redeemersOutputScripts := make( + [][]byte, + len(proposal.RedeemersOutputScripts), + ) + + for i, script := range proposal.RedeemersOutputScripts { + // The on-chain script representation must be prepended with the script's + // byte-length while bitcoin.Script is not. We need to add the + // length prefix. + prefixedScript, err := script.ToVarLenData() + if err != nil { + return tbtcabi.WalletProposalValidatorRedemptionProposal{}, fmt.Errorf( + "cannot convert redeemer output script: [%v]", + err, + ) + } + + redeemersOutputScripts[i] = prefixedScript + } + + return tbtcabi.WalletProposalValidatorRedemptionProposal{ + WalletPubKeyHash: walletPublicKeyHash, + RedeemersOutputScripts: redeemersOutputScripts, + RedemptionTxFee: proposal.RedemptionTxFee, + }, nil +} + +func (tc *TbtcChain) GetRedemptionMaxSize() (uint16, error) { + return tc.walletProposalValidator.REDEMPTIONMAXSIZE() +} + +func (tc *TbtcChain) GetRedemptionRequestMinAge() (uint32, error) { + return tc.walletProposalValidator.REDEMPTIONREQUESTMINAGE() +} + +func (tc *TbtcChain) GetRedemptionDelay( + walletPublicKeyHash [20]byte, + redeemerOutputScript bitcoin.Script, +) (time.Duration, error) { + if tc.redemptionWatchtower == nil { + return 0, nil + } + + redemptionKey, err := tc.BuildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) + if err != nil { + return 0, fmt.Errorf("cannot build redemption key: [%v]", err) + } + + delay, err := tc.redemptionWatchtower.GetRedemptionDelay(redemptionKey) + if err != nil { + return 0, fmt.Errorf("cannot get redemption delay: [%v]", err) + } + + return time.Duration(delay) * time.Second, nil +} diff --git a/pkg/chain/ethereum/tbtc_redemption_test.go b/pkg/chain/ethereum/tbtc_redemption_test.go new file mode 100644 index 0000000000..0dca443af1 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_redemption_test.go @@ -0,0 +1,37 @@ +package ethereum + +import ( + "encoding/hex" + "testing" + + "github.com/keep-network/keep-core/internal/testutils" +) + +func TestBuildRedemptionKey(t *testing.T) { + fromHex := func(hexString string) []byte { + b, err := hex.DecodeString(hexString) + if err != nil { + t.Fatal(err) + } + return b + } + + walletPublicKeyHashBytes := fromHex("8db50eb52063ea9d98b3eac91489a90f738986f6") + var walletPublicKeyHash [20]byte + copy(walletPublicKeyHash[:], walletPublicKeyHashBytes) + + redeemerOutputScript := fromHex("76a9144130879211c54df460e484ddf9aac009cb38ee7488ac") + + redemptionKey, err := buildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) + if err != nil { + t.Fatal(err) + } + + expectedRedemptionKey := "cb493004c645792101cfa4cc5da4c16aa3148065034371a6f1478b7df4b92d39" + testutils.AssertStringsEqual( + t, + "redemption key", + expectedRedemptionKey, + redemptionKey.Text(16), + ) +} diff --git a/pkg/chain/ethereum/tbtc_sortition.go b/pkg/chain/ethereum/tbtc_sortition.go new file mode 100644 index 0000000000..ba33dc2e96 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_sortition.go @@ -0,0 +1,248 @@ +// tbtc_sortition.go: sortition pool membership and unwinding for the TbtcChain adapter. +package ethereum + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + + "github.com/keep-network/keep-core/pkg/chain" + "github.com/keep-network/keep-core/pkg/operator" + "github.com/keep-network/keep-core/pkg/tbtc" +) + +// EcdsaWalletGroupParametersFromChain mirrors EcdsaDkgValidator sizing constants +// when EcdsaDkgValidator contract address was configured under [ethereum] +// contract addresses or developer.ecdsaDkgValidatorAddress alias. When absent, +// returns (nil, nil) and callers use defaultGroupParameters(network). +func (tc *TbtcChain) EcdsaWalletGroupParametersFromChain( + ctx context.Context, +) (*tbtc.GroupParameters, error) { + if tc.ecdsaDkgValidatorAddress == (common.Address{}) { + return nil, nil + } + return ecdsaWalletGroupParametersFromValidator( + ctx, + tc.baseChain.client, + tc.ecdsaDkgValidatorAddress, + ) +} + +// Staking returns address of the TokenStaking contract the WalletRegistry is +// connected to. +func (tc *TbtcChain) Staking() (chain.Address, error) { + stakingContractAddress, err := tc.walletRegistry.Staking() + if err != nil { + return "", fmt.Errorf( + "failed to get the token staking address: [%w]", + err, + ) + } + + return chain.Address(stakingContractAddress.String()), nil +} + +// IsRecognized checks whether the given operator is recognized by the TbtcChain +// as eligible to join the network. If the operator has a stake delegation or +// had a stake delegation in the past, it will be recognized. +func (tc *TbtcChain) IsRecognized(operatorPublicKey *operator.PublicKey) (bool, error) { + operatorAddress, err := operatorPublicKeyToChainAddress(operatorPublicKey) + if err != nil { + return false, fmt.Errorf( + "cannot convert from operator key to chain address: [%v]", + err, + ) + } + + stakingProvider, err := tc.walletRegistry.OperatorToStakingProvider( + operatorAddress, + ) + if err != nil { + return false, fmt.Errorf( + "failed to map operator [%v] to a staking provider: [%v]", + operatorAddress, + err, + ) + } + + if (stakingProvider == common.Address{}) { + return false, nil + } + + // Check if the staking provider has an owner. This check ensures that there + // is/was a stake delegation for the given staking provider. + _, _, _, hasStakeDelegation, err := tc.baseChain.RolesOf( + chain.Address(stakingProvider.Hex()), + ) + if err != nil { + return false, fmt.Errorf( + "failed to check stake delegation for staking provider [%v]: [%v]", + stakingProvider, + err, + ) + } + + if !hasStakeDelegation { + return false, nil + } + + return true, nil +} + +// OperatorToStakingProvider returns the staking provider address for the +// operator. If the staking provider has not been registered for the +// operator, the returned address is empty and the boolean flag is set to +// false. If the staking provider has been registered, the address is not +// empty and the boolean flag indicates true. +func (tc *TbtcChain) OperatorToStakingProvider() (chain.Address, bool, error) { + stakingProvider, err := tc.walletRegistry.OperatorToStakingProvider(tc.key.Address) + if err != nil { + return "", false, fmt.Errorf( + "failed to map operator [%v] to a staking provider: [%v]", + tc.key.Address, + err, + ) + } + + if (stakingProvider == common.Address{}) { + return "", false, nil + } + + return chain.Address(stakingProvider.Hex()), true, nil +} + +// EligibleStake returns the current value of the staking provider's +// eligible stake. Eligible stake is defined as the currently authorized +// stake minus the pending authorization decrease. Eligible stake +// is what is used for operator's weight in the sortition pool. +// If the authorized stake minus the pending authorization decrease +// is below the minimum authorization, eligible stake is 0. +func (tc *TbtcChain) EligibleStake(stakingProvider chain.Address) (*big.Int, error) { + eligibleStake, err := tc.walletRegistry.EligibleStake( + common.HexToAddress(stakingProvider.String()), + ) + if err != nil { + return nil, fmt.Errorf( + "failed to get eligible stake for staking provider %s: [%w]", + stakingProvider, + err, + ) + } + + return eligibleStake, nil +} + +// IsPoolLocked returns true if the sortition pool is locked and no state +// changes are allowed. +func (tc *TbtcChain) IsPoolLocked() (bool, error) { + return tc.sortitionPool.IsLocked() +} + +// IsOperatorInPool returns true if the operator is registered in +// the sortition pool. +func (tc *TbtcChain) IsOperatorInPool() (bool, error) { + return tc.walletRegistry.IsOperatorInPool(tc.key.Address) +} + +// IsOperatorUpToDate checks if the operator's authorized stake is in sync +// with operator's weight in the sortition pool. +// If the operator's authorized stake is not in sync with sortition pool +// weight, function returns false. +// If the operator is not in the sortition pool and their authorized stake +// is non-zero, function returns false. +func (tc *TbtcChain) IsOperatorUpToDate() (bool, error) { + return tc.walletRegistry.IsOperatorUpToDate(tc.key.Address) +} + +// JoinSortitionPool executes a transaction to have the operator join the +// sortition pool. +func (tc *TbtcChain) JoinSortitionPool() error { + _, err := tc.walletRegistry.JoinSortitionPool() + return err +} + +// UpdateOperatorStatus executes a transaction to update the operator's +// state in the sortition pool. +func (tc *TbtcChain) UpdateOperatorStatus() error { + _, err := tc.walletRegistry.UpdateOperatorStatus(tc.key.Address) + return err +} + +// IsEligibleForRewards checks whether the operator is eligible for rewards +// or not. +func (tc *TbtcChain) IsEligibleForRewards() (bool, error) { + return tc.sortitionPool.IsEligibleForRewards(tc.key.Address) +} + +// Checks whether the operator is able to restore their eligibility for +// rewards right away. +func (tc *TbtcChain) CanRestoreRewardEligibility() (bool, error) { + return tc.sortitionPool.CanRestoreRewardEligibility(tc.key.Address) +} + +// Restores reward eligibility for the operator. +func (tc *TbtcChain) RestoreRewardEligibility() error { + _, err := tc.sortitionPool.RestoreRewardEligibility(tc.key.Address) + return err +} + +// Returns true if the chaosnet phase is active, false otherwise. +func (tc *TbtcChain) IsChaosnetActive() (bool, error) { + return tc.sortitionPool.IsChaosnetActive() +} + +// Returns true if operator is a beta operator, false otherwise. +// Chaosnet status does not matter. +func (tc *TbtcChain) IsBetaOperator() (bool, error) { + return tc.sortitionPool.IsBetaOperator(tc.key.Address) +} + +// GetOperatorID returns the ID number of the given operator address. An ID +// number of 0 means the operator has not been allocated an ID number yet. +func (tc *TbtcChain) GetOperatorID( + operatorAddress chain.Address, +) (chain.OperatorID, error) { + return tc.sortitionPool.GetOperatorID( + common.HexToAddress(operatorAddress.String()), + ) +} + +// SelectGroup returns the group members selected for the current group +// selection. The function returns an error if the chain's state does not allow +// for group selection at the moment. +func (tc *TbtcChain) SelectGroup() (*tbtc.GroupSelectionResult, error) { + operatorsIDs, err := tc.walletRegistry.SelectGroup() + if err != nil { + return nil, fmt.Errorf( + "cannot select group in the sortition pool: [%v]", + err, + ) + } + + operatorsAddresses, err := tc.sortitionPool.GetIDOperators(operatorsIDs) + if err != nil { + return nil, fmt.Errorf( + "cannot convert operators' IDs to addresses: [%v]", + err, + ) + } + + // Should not happen as this is guaranteed by the contract but, just in case. + if len(operatorsIDs) != len(operatorsAddresses) { + return nil, fmt.Errorf("operators IDs and addresses mismatch") + } + + ids := make([]chain.OperatorID, len(operatorsIDs)) + addresses := make([]chain.Address, len(operatorsIDs)) + for i := range ids { + ids[i] = operatorsIDs[i] + addresses[i] = chain.Address(operatorsAddresses[i].String()) + } + + return &tbtc.GroupSelectionResult{ + OperatorsIDs: ids, + OperatorsAddresses: addresses, + }, nil +} diff --git a/pkg/chain/ethereum/tbtc_test.go b/pkg/chain/ethereum/tbtc_test.go index 1c9eef1be0..aac5f4c8e4 100644 --- a/pkg/chain/ethereum/tbtc_test.go +++ b/pkg/chain/ethereum/tbtc_test.go @@ -1,135 +1,13 @@ package ethereum import ( - "bytes" "crypto/ecdsa" - "encoding/hex" - "fmt" "math/big" - "reflect" "testing" - "github.com/keep-network/keep-core/pkg/bitcoin" - - "github.com/keep-network/keep-core/pkg/chain" - - "github.com/ethereum/go-ethereum/common" - "github.com/keep-network/keep-core/internal/testutils" - "github.com/keep-network/keep-core/pkg/chain/local_v1" - "github.com/keep-network/keep-core/pkg/protocol/group" ) -func TestComputeOperatorsIDsHash(t *testing.T) { - operatorIDs := []chain.OperatorID{ - 5, 1, 55, 45435534, 33, 345, 23, 235, 3333, 2, - } - - hash, err := computeOperatorsIDsHash(operatorIDs) - if err != nil { - t.Fatal(err) - } - - expectedHash := "8cd41effd4ee91b56d6b2f836efdcac11ab1ef2ae228e348814d0e6c2966d01e" - - testutils.AssertStringsEqual( - t, - "hash", - expectedHash, - hex.EncodeToString(hash[:]), - ) -} - -func TestConvertSignaturesToChainFormat(t *testing.T) { - signatureSize := 65 - - signature1 := common.LeftPadBytes([]byte{1, 2, 3}, signatureSize) - signature2 := common.LeftPadBytes([]byte{4, 5, 6}, signatureSize) - signature3 := common.LeftPadBytes([]byte{7}, signatureSize) - signature4 := common.LeftPadBytes([]byte{8, 9, 10}, signatureSize) - signature5 := common.LeftPadBytes([]byte{11, 12, 13}, signatureSize) - - invalidSignature := common.LeftPadBytes([]byte("invalid"), signatureSize-1) - - var tests = map[string]struct { - signaturesMap map[group.MemberIndex][]byte - expectedIndices []group.MemberIndex - expectedError error - }{ - "one valid signature": { - signaturesMap: map[uint8][]byte{ - 1: signature1, - }, - expectedIndices: []group.MemberIndex{1}, - }, - "five valid signatures": { - signaturesMap: map[group.MemberIndex][]byte{ - 3: signature3, - 1: signature1, - 4: signature4, - 5: signature5, - 2: signature2, - }, - expectedIndices: []group.MemberIndex{1, 2, 3, 4, 5}, - }, - "invalid signature": { - signaturesMap: map[group.MemberIndex][]byte{ - 1: signature1, - 2: invalidSignature, - }, - expectedError: fmt.Errorf("invalid signature size for member [2] got [64] bytes but [65] bytes required"), - }, - } - for testName, test := range tests { - t.Run(testName, func(t *testing.T) { - indicesSlice, signaturesSlice, err := - convertSignaturesToChainFormat(test.signaturesMap) - - if !reflect.DeepEqual(err, test.expectedError) { - t.Errorf( - "unexpected error\nexpected: [%v]\nactual: [%v]\n", - test.expectedError, - err, - ) - } - - if test.expectedError == nil { - if !reflect.DeepEqual(test.expectedIndices, indicesSlice) { - t.Errorf( - "unexpected indices\n"+ - "expected: [%v]\n"+ - "actual: [%v]\n", - test.expectedIndices, - indicesSlice, - ) - } - - testutils.AssertIntsEqual( - t, - "signatures slice length", - signatureSize*len(test.signaturesMap), - len(signaturesSlice), - ) - } - - for i, memberIndex := range indicesSlice { - actualSignature := signaturesSlice[signatureSize*i : signatureSize*(i+1)] - if !bytes.Equal( - test.signaturesMap[memberIndex], - actualSignature, - ) { - t.Errorf( - "invalid signatures for member %v\nexpected: %v\nactual: %v\n", - memberIndex, - test.signaturesMap[memberIndex], - actualSignature, - ) - } - } - }) - } -} - func TestConvertPubKeyToChainFormat(t *testing.T) { bytes30 := []byte{229, 19, 136, 216, 125, 157, 135, 142, 67, 130, 136, 13, 76, 188, 32, 218, 243, 134, 95, 73, 155, 24, 38, 73, 117, 90, @@ -168,368 +46,3 @@ func TestConvertPubKeyToChainFormat(t *testing.T) { actualResult[:], ) } - -func TestValidateMemberIndex(t *testing.T) { - one := big.NewInt(1) - maxMemberIndex := big.NewInt(255) - - var tests = map[string]struct { - chainMemberIndex *big.Int - expectedError error - }{ - "less than max member index": { - chainMemberIndex: new(big.Int).Sub(maxMemberIndex, one), - expectedError: nil, - }, - "max member index": { - chainMemberIndex: maxMemberIndex, - expectedError: nil, - }, - "greater than max member index": { - chainMemberIndex: new(big.Int).Add(maxMemberIndex, one), - expectedError: fmt.Errorf("invalid member index value: [256]"), - }, - } - - for testName, test := range tests { - t.Run(testName, func(t *testing.T) { - err := validateMemberIndex(test.chainMemberIndex) - - if !reflect.DeepEqual(err, test.expectedError) { - t.Errorf( - "unexpected error\nexpected: [%v]\nactual: [%v]\n", - test.expectedError, - err, - ) - } - }) - } -} - -func TestCalculateDKGResultSignatureHash(t *testing.T) { - chainID := big.NewInt(1) - - groupPublicKey, err := hex.DecodeString( - "989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d9d" + - "218b65e7d91c752f7b22eaceb771a9af3a6f3d3f010a5d471a1aeef7d7713af", - ) - if err != nil { - t.Fatal(err) - } - - misbehavedMembersIndexes := []group.MemberIndex{2, 55} - - startBlock := big.NewInt(2000) - - hash, err := calculateDKGResultSignatureHash( - chainID, - groupPublicKey, - misbehavedMembersIndexes, - startBlock, - ) - if err != nil { - t.Fatal(err) - } - - expectedHash := "25f917154586c2be0b6364f5c4758580e535bc01ed4881211000c9267aef3a3b" - - testutils.AssertStringsEqual( - t, - "hash", - expectedHash, - hex.EncodeToString(hash[:]), - ) -} - -func TestCalculateInactivityClaimHash(t *testing.T) { - chainID := big.NewInt(31337) - nonce := big.NewInt(3) - - walletPublicKey, err := hex.DecodeString( - "9a0544440cc47779235ccb76d669590c2cd20c7e431f97e17a1093faf03291c473e" + - "661a208a8a565ca1e384059bd2ff7ff6886df081ff1229250099d388c83df", - ) - if err != nil { - t.Fatal(err) - } - - inactiveMembersIndexes := []*big.Int{ - big.NewInt(1), big.NewInt(2), big.NewInt(30), - } - - heartbeatFailed := true - - hash, err := calculateInactivityClaimHash( - chainID, - nonce, - walletPublicKey, - inactiveMembersIndexes, - heartbeatFailed, - ) - if err != nil { - t.Fatal(err) - } - - expectedHash := "f3210008cba186e90386a1bd0c63b6f29a67666f632350be22ce63ab39fc506e" - - testutils.AssertStringsEqual( - t, - "hash", - expectedHash, - hex.EncodeToString(hash[:]), - ) -} - -func TestCalculateWalletID(t *testing.T) { - hexToByte32 := func(hexStr string) [32]byte { - if len(hexStr) != 64 { - t.Fatal("hex string length incorrect") - } - - decoded, err := hex.DecodeString(hexStr) - if err != nil { - t.Fatal(err) - } - - var result [32]byte - copy(result[:], decoded) - - return result - } - - xBytes := hexToByte32( - "9a0544440cc47779235ccb76d669590c2cd20c7e431f97e17a1093faf03291c4", - ) - - yBytes := hexToByte32( - "73e661a208a8a565ca1e384059bd2ff7ff6886df081ff1229250099d388c83df", - ) - - walletPublicKey := &ecdsa.PublicKey{ - Curve: local_v1.DefaultCurve, - X: new(big.Int).SetBytes(xBytes[:]), - Y: new(big.Int).SetBytes(yBytes[:]), - } - - actualWalletID, err := calculateWalletID(walletPublicKey) - if err != nil { - t.Fatal(err) - } - - expectedWalletID := hexToByte32( - "a6602e554b8cf7c23538fd040e4ff3520ec680e5e5ce9a075259e613a3e5aa79", - ) - - testutils.AssertBytesEqual(t, expectedWalletID[:], actualWalletID[:]) -} - -func TestParseDkgResultValidationOutcome(t *testing.T) { - isValid, err := parseDkgResultValidationOutcome( - &struct { - bool - string - }{ - true, - "", - }, - ) - if err != nil { - t.Fatal(err) - } - testutils.AssertBoolsEqual(t, "validation outcome", true, isValid) - - isValid, err = parseDkgResultValidationOutcome( - &struct { - bool - string - }{ - false, - "", - }, - ) - if err != nil { - t.Fatal(err) - } - testutils.AssertBoolsEqual(t, "validation outcome", false, isValid) - - _, err = parseDkgResultValidationOutcome( - struct { - bool - string - }{ - true, - "", - }, - ) - expectedErr := fmt.Errorf("result validation outcome is not a pointer") - if !reflect.DeepEqual(expectedErr, err) { - t.Errorf( - "unexpected error\n"+ - "expected: [%v]\n"+ - "actual: [%v]", - expectedErr, - err, - ) - } - - _, err = parseDkgResultValidationOutcome( - &struct { - string - bool - }{ - "", - true, - }, - ) - expectedErr = fmt.Errorf("cannot parse result validation outcome") - if !reflect.DeepEqual(expectedErr, err) { - t.Errorf( - "unexpected error\n"+ - "expected: [%v]\n"+ - "actual: [%v]", - expectedErr, - err, - ) - } -} - -func TestComputeMainUtxoHash(t *testing.T) { - transactionHash, err := bitcoin.NewHashFromString( - "089bd0671a4481c3584919b4b9b6751cb3f8586dab41cb157adec43fd10ccc00", - bitcoin.InternalByteOrder, - ) - if err != nil { - t.Fatal(err) - } - - mainUtxo := &bitcoin.UnspentTransactionOutput{ - Outpoint: &bitcoin.TransactionOutpoint{ - TransactionHash: transactionHash, - OutputIndex: 5, - }, - Value: 143565433, - } - - mainUtxoHash := computeMainUtxoHash(mainUtxo) - - expectedMainUtxoHash, err := hex.DecodeString( - "1216f8e993c4c57d3c4c971c0d2651140fc4ab09d41960d9ccd7b41fdcd270d6", - ) - if err != nil { - t.Fatal(err) - } - testutils.AssertBytesEqual(t, expectedMainUtxoHash, mainUtxoHash[:]) -} - -func TestComputeMovingFundsCommitmentHash(t *testing.T) { - toByte20 := func(s string) [20]byte { - bytes, err := hex.DecodeString(s) - if err != nil { - t.Fatal(err) - } - - if len(bytes) != 20 { - t.Fatal("incorrect hexstring length") - } - - var result [20]byte - copy(result[:], bytes[:]) - return result - } - - targetWallets := [][20]byte{ - toByte20("4b440cb29c80c3f256212d8fdd4f2125366f3c91"), - toByte20("888f01315e0268bfa05d5e522f8d63f6824d9a96"), - toByte20("b2a89e53a4227dbe530a52a1c419040735fa636c"), - } - - movingFundsCommitmentHash := computeMovingFundsCommitmentHash( - targetWallets, - ) - - expectedMovingFundsCommitmentHash, err := hex.DecodeString( - "8ba62d1d754a3429e2ff1fb4f523b5fad2b605c873a2968bb5985a625eb96202", - ) - if err != nil { - t.Fatal(err) - } - testutils.AssertBytesEqual( - t, - expectedMovingFundsCommitmentHash, - movingFundsCommitmentHash[:], - ) -} - -// Test data based on: https://etherscan.io/tx/0x97c7a293127a604da77f7ef8daf4b19da2bf04327dd891b6d717eaef89bd8bca -func TestBuildDepositKey(t *testing.T) { - fundingTxHash, err := bitcoin.NewHashFromString( - "585b6699f42291d1a9d0776b75f04c295ea203f83504349db11e94fdae7d1b2c", - bitcoin.InternalByteOrder, - ) - if err != nil { - t.Fatal(err) - } - - fundingOutputIndex := uint32(1) - - depositKey := buildDepositKey(fundingTxHash, fundingOutputIndex) - - expectedDepositKey := "3e84c1ea6aeaf2f45fb49623a88affe653b798ea6f675805acc0ec3965b6f317" - testutils.AssertStringsEqual( - t, - "deposit key", - expectedDepositKey, - depositKey.Text(16), - ) -} - -func TestBuildRedemptionKey(t *testing.T) { - fromHex := func(hexString string) []byte { - b, err := hex.DecodeString(hexString) - if err != nil { - t.Fatal(err) - } - return b - } - - walletPublicKeyHashBytes := fromHex("8db50eb52063ea9d98b3eac91489a90f738986f6") - var walletPublicKeyHash [20]byte - copy(walletPublicKeyHash[:], walletPublicKeyHashBytes) - - redeemerOutputScript := fromHex("76a9144130879211c54df460e484ddf9aac009cb38ee7488ac") - - redemptionKey, err := buildRedemptionKey(walletPublicKeyHash, redeemerOutputScript) - if err != nil { - t.Fatal(err) - } - - expectedRedemptionKey := "cb493004c645792101cfa4cc5da4c16aa3148065034371a6f1478b7df4b92d39" - testutils.AssertStringsEqual( - t, - "redemption key", - expectedRedemptionKey, - redemptionKey.Text(16), - ) -} - -func TestBuildMovedFundsKey(t *testing.T) { - fundingTxHash, err := bitcoin.NewHashFromString( - "7cff663e3e08847a5579913f6a66bc6c01f5f48c6ae1783be77418ed188021e6", - bitcoin.InternalByteOrder, - ) - if err != nil { - t.Fatal(err) - } - - fundingOutputIndex := uint32(2) - - movedFundsKey := buildMovedFundsKey(fundingTxHash, fundingOutputIndex) - - expectedMovedFundsKey := "24509b8a853476ebe77af3707bd7ce017d527680e941b6eeaac2d5b712df4f8d" - testutils.AssertStringsEqual( - t, - "moved funds key", - expectedMovedFundsKey, - movedFundsKey.Text(16), - ) -} diff --git a/pkg/chain/ethereum/tbtc_wallet.go b/pkg/chain/ethereum/tbtc_wallet.go new file mode 100644 index 0000000000..fc0eeb77e5 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_wallet.go @@ -0,0 +1,237 @@ +// tbtc_wallet.go: wallet registry read/write methods for the TbtcChain adapter. +package ethereum + +import ( + "crypto/ecdsa" + "encoding/binary" + "fmt" + "sort" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/keep-network/keep-core/pkg/bitcoin" + + tbtcabi "github.com/keep-network/keep-core/pkg/chain/ethereum/tbtc/gen/abi" + "github.com/keep-network/keep-core/pkg/subscription" + "github.com/keep-network/keep-core/pkg/tbtc" +) + +func (tc *TbtcChain) PastNewWalletRegisteredEvents( + filter *tbtc.NewWalletRegisteredEventFilter, +) ([]*tbtc.NewWalletRegisteredEvent, error) { + var startBlock uint64 + var endBlock *uint64 + var ecdsaWalletID [][32]byte + var walletPublicKeyHash [][20]byte + + if filter != nil { + startBlock = filter.StartBlock + endBlock = filter.EndBlock + ecdsaWalletID = filter.EcdsaWalletID + walletPublicKeyHash = filter.WalletPublicKeyHash + } + + events, err := tc.bridge.PastNewWalletRegisteredEvents( + startBlock, + endBlock, + ecdsaWalletID, + walletPublicKeyHash, + ) + if err != nil { + return nil, err + } + + convertedEvents := make([]*tbtc.NewWalletRegisteredEvent, 0) + for _, event := range events { + convertedEvent := &tbtc.NewWalletRegisteredEvent{ + EcdsaWalletID: event.EcdsaWalletID, + WalletPublicKeyHash: event.WalletPubKeyHash, + BlockNumber: event.Raw.BlockNumber, + } + + convertedEvents = append(convertedEvents, convertedEvent) + } + + sort.SliceStable( + convertedEvents, + func(i, j int) bool { + return convertedEvents[i].BlockNumber < convertedEvents[j].BlockNumber + }, + ) + + return convertedEvents, err +} + +func (tc *TbtcChain) CalculateWalletID( + walletPublicKey *ecdsa.PublicKey, +) ([32]byte, error) { + return calculateWalletID(walletPublicKey) +} + +func calculateWalletID(walletPublicKey *ecdsa.PublicKey) ([32]byte, error) { + walletPublicKeyBytes, err := convertPubKeyToChainFormat(walletPublicKey) + if err != nil { + return [32]byte{}, fmt.Errorf( + "error while converting wallet public key to chain format: [%v]", + err, + ) + } + + return crypto.Keccak256Hash(walletPublicKeyBytes[:]), nil +} + +func (tc *TbtcChain) IsWalletRegistered(EcdsaWalletID [32]byte) (bool, error) { + isWalletRegistered, err := tc.walletRegistry.IsWalletRegistered( + EcdsaWalletID, + ) + if err != nil { + return false, fmt.Errorf( + "cannot check if wallet with ECDSA ID [0x%x] is registered: [%v]", + EcdsaWalletID, + err, + ) + } + + return isWalletRegistered, nil +} + +func (tc *TbtcChain) GetWallet( + walletPublicKeyHash [20]byte, +) (*tbtc.WalletChainData, error) { + wallet, err := tc.bridge.Wallets(walletPublicKeyHash) + if err != nil { + return nil, fmt.Errorf( + "cannot get wallet for public key hash [0x%x]: [%v]", + walletPublicKeyHash, + err, + ) + } + + // Wallet not found. + if wallet.CreatedAt == 0 { + return nil, fmt.Errorf( + "no wallet for public key hash [0x%x]", + walletPublicKeyHash, + ) + } + + walletState, err := parseWalletState(wallet.State) + if err != nil { + return nil, fmt.Errorf("cannot parse wallet state: [%v]", err) + } + + return &tbtc.WalletChainData{ + EcdsaWalletID: wallet.EcdsaWalletID, + MainUtxoHash: wallet.MainUtxoHash, + PendingRedemptionsValue: wallet.PendingRedemptionsValue, + CreatedAt: time.Unix(int64(wallet.CreatedAt), 0), + MovingFundsRequestedAt: time.Unix(int64(wallet.MovingFundsRequestedAt), 0), + ClosingStartedAt: time.Unix(int64(wallet.ClosingStartedAt), 0), + PendingMovedFundsSweepRequestsCount: wallet.PendingMovedFundsSweepRequestsCount, + State: walletState, + MovingFundsTargetWalletsCommitmentHash: wallet.MovingFundsTargetWalletsCommitmentHash, + }, nil +} + +func (tc *TbtcChain) OnWalletClosed( + handler func(event *tbtc.WalletClosedEvent), +) subscription.EventSubscription { + onEvent := func( + walletID [32]byte, + blockNumber uint64, + ) { + handler(&tbtc.WalletClosedEvent{ + WalletID: walletID, + BlockNumber: blockNumber, + }) + } + return tc.walletRegistry.WalletClosedEvent(nil, nil).OnEvent(onEvent) +} + +func (tc *TbtcChain) ComputeMainUtxoHash( + mainUtxo *bitcoin.UnspentTransactionOutput, +) [32]byte { + return computeMainUtxoHash(mainUtxo) +} + +func computeMainUtxoHash(mainUtxo *bitcoin.UnspentTransactionOutput) [32]byte { + outputIndexBytes := make([]byte, 4) + binary.BigEndian.PutUint32(outputIndexBytes, mainUtxo.Outpoint.OutputIndex) + + valueBytes := make([]byte, 8) + binary.BigEndian.PutUint64(valueBytes, uint64(mainUtxo.Value)) + + mainUtxoHash := crypto.Keccak256Hash( + append( + append( + mainUtxo.Outpoint.TransactionHash[:], + outputIndexBytes..., + ), valueBytes..., + ), + ) + + return mainUtxoHash +} + +func (tc *TbtcChain) GetWalletParameters() (tbtc.WalletParameters, error) { + parameters, err := tc.bridge.WalletParameters() + if err != nil { + return tbtc.WalletParameters{}, err + } + + return tbtc.WalletParameters{ + CreationPeriod: parameters.WalletCreationPeriod, + CreationMinBtcBalance: parameters.WalletCreationMinBtcBalance, + CreationMaxBtcBalance: parameters.WalletCreationMaxBtcBalance, + ClosureMinBtcBalance: parameters.WalletClosureMinBtcBalance, + MaxAge: parameters.WalletMaxAge, + MaxBtcTransfer: parameters.WalletMaxBtcTransfer, + ClosingPeriod: parameters.WalletClosingPeriod, + }, nil +} + +func (tc *TbtcChain) GetLiveWalletsCount() (uint32, error) { + return tc.bridge.LiveWalletsCount() +} + +func parseWalletState(value uint8) (tbtc.WalletState, error) { + switch value { + case 0: + return tbtc.StateUnknown, nil + case 1: + return tbtc.StateLive, nil + case 2: + return tbtc.StateMovingFunds, nil + case 3: + return tbtc.StateClosing, nil + case 4: + return tbtc.StateClosed, nil + case 5: + return tbtc.StateTerminated, nil + default: + return 0, fmt.Errorf("unexpected wallet state value: [%v]", value) + } +} + +func (tc *TbtcChain) ValidateHeartbeatProposal( + walletPublicKeyHash [20]byte, + proposal *tbtc.HeartbeatProposal, +) error { + valid, err := tc.walletProposalValidator.ValidateHeartbeatProposal( + tbtcabi.WalletProposalValidatorHeartbeatProposal{ + WalletPubKeyHash: walletPublicKeyHash, + Message: proposal.Message[:], + }, + ) + if err != nil { + return fmt.Errorf("validation failed: [%v]", err) + } + + // Should never happen because `validateHeartbeatProposal` returns true + // or reverts (returns an error) but do the check just in case. + if !valid { + return fmt.Errorf("unexpected validation result") + } + + return nil +} diff --git a/pkg/chain/ethereum/tbtc_wallet_test.go b/pkg/chain/ethereum/tbtc_wallet_test.go new file mode 100644 index 0000000000..06f1a9b6e8 --- /dev/null +++ b/pkg/chain/ethereum/tbtc_wallet_test.go @@ -0,0 +1,83 @@ +package ethereum + +import ( + "crypto/ecdsa" + "encoding/hex" + "math/big" + "testing" + + "github.com/keep-network/keep-core/internal/testutils" + "github.com/keep-network/keep-core/pkg/bitcoin" + "github.com/keep-network/keep-core/pkg/chain/local_v1" +) + +func TestCalculateWalletID(t *testing.T) { + hexToByte32 := func(hexStr string) [32]byte { + if len(hexStr) != 64 { + t.Fatal("hex string length incorrect") + } + + decoded, err := hex.DecodeString(hexStr) + if err != nil { + t.Fatal(err) + } + + var result [32]byte + copy(result[:], decoded) + + return result + } + + xBytes := hexToByte32( + "9a0544440cc47779235ccb76d669590c2cd20c7e431f97e17a1093faf03291c4", + ) + + yBytes := hexToByte32( + "73e661a208a8a565ca1e384059bd2ff7ff6886df081ff1229250099d388c83df", + ) + + walletPublicKey := &ecdsa.PublicKey{ + Curve: local_v1.DefaultCurve, + X: new(big.Int).SetBytes(xBytes[:]), + Y: new(big.Int).SetBytes(yBytes[:]), + } + + actualWalletID, err := calculateWalletID(walletPublicKey) + if err != nil { + t.Fatal(err) + } + + expectedWalletID := hexToByte32( + "a6602e554b8cf7c23538fd040e4ff3520ec680e5e5ce9a075259e613a3e5aa79", + ) + + testutils.AssertBytesEqual(t, expectedWalletID[:], actualWalletID[:]) +} + +func TestComputeMainUtxoHash(t *testing.T) { + transactionHash, err := bitcoin.NewHashFromString( + "089bd0671a4481c3584919b4b9b6751cb3f8586dab41cb157adec43fd10ccc00", + bitcoin.InternalByteOrder, + ) + if err != nil { + t.Fatal(err) + } + + mainUtxo := &bitcoin.UnspentTransactionOutput{ + Outpoint: &bitcoin.TransactionOutpoint{ + TransactionHash: transactionHash, + OutputIndex: 5, + }, + Value: 143565433, + } + + mainUtxoHash := computeMainUtxoHash(mainUtxo) + + expectedMainUtxoHash, err := hex.DecodeString( + "1216f8e993c4c57d3c4c971c0d2651140fc4ab09d41960d9ccd7b41fdcd270d6", + ) + if err != nil { + t.Fatal(err) + } + testutils.AssertBytesEqual(t, expectedMainUtxoHash, mainUtxoHash[:]) +} diff --git a/pkg/clientinfo/clientinfo.go b/pkg/clientinfo/clientinfo.go index 7848aa0ec7..82f2efa871 100644 --- a/pkg/clientinfo/clientinfo.go +++ b/pkg/clientinfo/clientinfo.go @@ -2,6 +2,8 @@ package clientinfo import ( "context" + "net/http" + "net/http/pprof" "time" "github.com/ipfs/go-log" @@ -18,6 +20,10 @@ type Config struct { EthereumMetricsTick time.Duration BitcoinMetricsTick time.Duration RPCHealthCheckInterval time.Duration + // EnablePprof exposes Go runtime profiling endpoints at /debug/pprof/ on + // the clientinfo port. Requires Port != 0. Never expose to untrusted + // networks; bind behind a firewall or restrict with an SSH tunnel. + EnablePprof bool } // Registry wraps keep-common clientinfo registry and exposes additional @@ -32,15 +38,38 @@ type Registry struct { // diagnostics server. func Initialize( ctx context.Context, - port int, + cfg Config, ) (*Registry, bool) { - if port == 0 { + if cfg.Port == 0 { return nil, false } registry := &Registry{clientinfo.NewRegistry(), ctx} - registry.EnableServer(port) + if cfg.EnablePprof { + // Register the pprof handlers on http.DefaultServeMux, which is the + // mux that keep-common's EnableServer hands to the http.Server. + // Registering them explicitly here avoids the side-effecting blank + // import of net/http/pprof, which would otherwise register + // /debug/pprof/* unconditionally on DefaultServeMux regardless of + // this flag. + registerPprofHandlers() + logger.Infof("pprof profiling endpoints enabled at /debug/pprof/") + } + + registry.EnableServer(cfg.Port) return registry, true } + +// registerPprofHandlers registers the standard net/http/pprof handlers on +// http.DefaultServeMux. It is invoked explicitly from Initialize when +// EnablePprof is true, in place of the blank import of net/http/pprof that +// would otherwise register the endpoints at init time. +func registerPprofHandlers() { + http.HandleFunc("/debug/pprof/", pprof.Index) + http.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + http.HandleFunc("/debug/pprof/profile", pprof.Profile) + http.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + http.HandleFunc("/debug/pprof/trace", pprof.Trace) +} diff --git a/pkg/clientinfo/performance.go b/pkg/clientinfo/performance.go index bcc28137ca..06e43cdab4 100644 --- a/pkg/clientinfo/performance.go +++ b/pkg/clientinfo/performance.go @@ -37,15 +37,12 @@ type PerformanceMetrics struct { registry *Registry cancel context.CancelFunc - // Counters track cumulative counts of events countersMutex sync.RWMutex counters map[string]*counter - // Histograms track distributions of values (like durations) histogramsMutex sync.RWMutex histograms map[string]*histogram - // Gauges track current values (like queue sizes) gaugesMutex sync.RWMutex gauges map[string]*gauge } @@ -102,13 +99,34 @@ func (pm *PerformanceMetrics) Stop() { // registerAllMetrics registers all performance metrics with 0 values // so they appear in the /metrics endpoint even before operations occur. func (pm *PerformanceMetrics) registerAllMetrics() { - // Register all counter metrics with 0 initial value + // ----- counter metrics ----- counters := []string{ + // ----- DKG counters ----- MetricDKGJoinedTotal, MetricDKGFailedTotal, MetricDKGValidationTotal, MetricDKGChallengesSubmittedTotal, MetricDKGApprovalsSubmittedTotal, + + // ----- wallet action counters ----- + MetricWalletActionsTotal, + MetricWalletActionSuccessTotal, + MetricWalletActionFailedTotal, + MetricWalletHeartbeatFailuresTotal, + MetricStuckWalletTransactionsTotal, + MetricUnmonitoredWalletTransactionsTotal, + + // ----- SPV proof-skip counters ----- + MetricRedemptionProofSubmissionsTotal, + MetricRedemptionProofSubmissionsSuccessTotal, + MetricRedemptionProofSubmissionsFailedTotal, + MetricDepositSweepProofSubmissionsTotal, + MetricDepositSweepProofSubmissionsSuccessTotal, + MetricDepositSweepProofSubmissionsFailedTotal, + MetricSpvProofSkippedOutsideRelayRangeTotal, + MetricSpvProofSkippedExceededMaxHeadersTotal, + + // ----- on-chain action counters ----- MetricSigningOperationsTotal, MetricSigningSuccessTotal, MetricSigningFailedTotal, @@ -116,15 +134,6 @@ func (pm *PerformanceMetrics) registerAllMetrics() { MetricRedemptionExecutionsTotal, MetricRedemptionExecutionsSuccessTotal, MetricRedemptionExecutionsFailedTotal, - MetricRedemptionProofSubmissionsTotal, - MetricRedemptionProofSubmissionsSuccessTotal, - MetricRedemptionProofSubmissionsFailedTotal, - MetricWalletActionsTotal, - MetricWalletActionSuccessTotal, - MetricWalletActionFailedTotal, - MetricWalletHeartbeatFailuresTotal, - MetricStuckWalletTransactionsTotal, - MetricUnmonitoredWalletTransactionsTotal, MetricCoordinationWindowsDetectedTotal, MetricCoordinationProceduresExecutedTotal, MetricCoordinationFailedTotal, @@ -149,14 +158,12 @@ func (pm *PerformanceMetrics) registerAllMetrics() { counters = append(counters, NetworkJoinFailureMetricName(reason)) } - // First, initialize all counters in the map pm.countersMutex.Lock() for _, name := range counters { pm.counters[name] = &counter{value: 0} } pm.countersMutex.Unlock() - // Then, register observers (this prevents concurrent map read/write) for _, name := range counters { metricName := name // Capture for closure pm.registry.ObserveApplicationSource( @@ -177,7 +184,7 @@ func (pm *PerformanceMetrics) registerAllMetrics() { ) } - // Register per-action type wallet metrics + // ----- wallet action metrics ----- // For each action type, register: total, success_total, failed_total, duration_seconds for _, actionType := range GetAllWalletActionTypes() { actionCounters := []string{ @@ -238,8 +245,8 @@ func (pm *PerformanceMetrics) registerAllMetrics() { ) } - // Register all duration/histogram metrics with 0 initial values - // Note: These use the actual metric names as used in the codebase + // ----- histogram metrics ----- + // These use the actual metric names as used in the codebase. durationMetrics := []string{ MetricDKGDurationSeconds, MetricSigningDurationSeconds, @@ -251,7 +258,6 @@ func (pm *PerformanceMetrics) registerAllMetrics() { MetricNetworkHandshakeDurationSeconds, } - // First, initialize all histograms in the map pm.histogramsMutex.Lock() for _, name := range durationMetrics { pm.histograms[name] = &histogram{ @@ -260,7 +266,6 @@ func (pm *PerformanceMetrics) registerAllMetrics() { } pm.histogramsMutex.Unlock() - // Then, register observers (this prevents concurrent map read/write) for _, name := range durationMetrics { metricName := name sources := map[string]Source{ @@ -297,13 +302,12 @@ func (pm *PerformanceMetrics) registerAllMetrics() { pm.registry.ObserveApplicationSource("performance", sources) } - // Register all gauge metrics with 0 initial value + // ----- gauge metrics ----- gauges := []string{ MetricWalletDispatcherActiveActions, MetricIncomingMessageQueueSize, MetricMessageHandlerQueueSize, MetricSigningAttemptsPerOperation, - MetricCPUUtilization, MetricMemoryUsageMB, MetricGoroutineCount, MetricCPULoadPercent, @@ -311,14 +315,12 @@ func (pm *PerformanceMetrics) registerAllMetrics() { MetricSwapUtilizationPercent, } - // First, initialize all gauges in the map pm.gaugesMutex.Lock() for _, name := range gauges { pm.gauges[name] = &gauge{value: 0} } pm.gaugesMutex.Unlock() - // Then, register observers (this prevents concurrent map read/write) for _, name := range gauges { metricName := name // Capture for closure pm.registry.ObserveApplicationSource( @@ -338,7 +340,6 @@ func (pm *PerformanceMetrics) registerAllMetrics() { }, ) } - } // IncrementCounter increments a counter metric by the given value. @@ -346,33 +347,24 @@ func (pm *PerformanceMetrics) registerAllMetrics() { // only updates the counter value without re-registering observers. func (pm *PerformanceMetrics) IncrementCounter(name string, value float64) { pm.countersMutex.RLock() - c, exists := pm.counters[name] - pm.countersMutex.RUnlock() - - // Fast path: if counter exists, just increment it - if exists { - c.mutex.Lock() - c.value += value - c.mutex.Unlock() + defer pm.countersMutex.RUnlock() + + c, ok := pm.counters[name] + if !ok { + // Counter not pre-registered. Pre-registration is enforced by + // registerAllMetrics() and tested by the *_CountersRegistered + // tests. The original slow path lazily added the counter to + // pm.counters on first increment but never called + // ObserveApplicationSource, so the value lived in memory but + // never reached /metrics; the current code silently ignores + // the increment. Review the registration list if a counter + // appears here unexpectedly. return } - // Slow path: counter doesn't exist, need to create it - // Upgrade to write lock and check/create - pm.countersMutex.Lock() - c, exists = pm.counters[name] - if !exists { - c = &counter{value: value} - pm.counters[name] = c - pm.countersMutex.Unlock() - return - } - pm.countersMutex.Unlock() - - // Counter was created by another goroutine after our first check c.mutex.Lock() + defer c.mutex.Unlock() c.value += value - c.mutex.Unlock() } // RecordDuration records a duration value in a histogram. @@ -380,18 +372,22 @@ func (pm *PerformanceMetrics) IncrementCounter(name string, value float64) { // Observers are already registered in registerAllMetrics, so this method // only updates the histogram without re-registering observers. func (pm *PerformanceMetrics) RecordDuration(name string, duration time.Duration) { - pm.histogramsMutex.Lock() - h, exists := pm.histograms[name] - if !exists { - h = &histogram{ - buckets: make(map[float64]float64), - } - pm.histograms[name] = h + pm.histogramsMutex.RLock() + h, ok := pm.histograms[name] + pm.histogramsMutex.RUnlock() + + if !ok { + // Histogram not pre-registered. Pre-registration is enforced by + // registerAllMetrics() and tested by the *_CountersRegistered + // tests. Silently ignoring the duration is the original behavior + // of this slow path; review the registration list if a histogram + // appears here unexpectedly. + return } - pm.histogramsMutex.Unlock() seconds := duration.Seconds() h.mutex.Lock() + defer h.mutex.Unlock() // Simple histogram: increment bucket counts // Buckets: 0.001, 0.01, 0.1, 1, 10, 60, 300, 600, +Inf (overflow) buckets := []float64{0.001, 0.01, 0.1, 1, 10, 60, 300, 600} @@ -410,39 +406,36 @@ func (pm *PerformanceMetrics) RecordDuration(name string, duration time.Duration // Also track total count and sum for average calculation h.buckets[histogramCountKey]++ // count h.buckets[histogramSumKey] += seconds - h.mutex.Unlock() } // SetGauge sets a gauge metric to the given value. // Observers are already registered in registerAllMetrics, so this method // only updates the gauge value without re-registering observers. func (pm *PerformanceMetrics) SetGauge(name string, value float64) { - pm.gaugesMutex.Lock() - g, exists := pm.gauges[name] - if !exists { - g = &gauge{value: value} - pm.gauges[name] = g - pm.gaugesMutex.Unlock() + pm.gaugesMutex.RLock() + g, ok := pm.gauges[name] + pm.gaugesMutex.RUnlock() + + if !ok { + // Gauge not pre-registered. Pre-registration is enforced by + // registerAllMetrics() and tested by the *_CountersRegistered + // tests. Silently ignoring the value is the original behavior + // of this slow path; review the registration list if a gauge + // appears here unexpectedly. return } - pm.gaugesMutex.Unlock() g.mutex.Lock() + defer g.mutex.Unlock() g.value = value - g.mutex.Unlock() } // observeSystemMetrics periodically collects and updates system metrics // including CPU utilization, memory usage, and goroutine count. func (pm *PerformanceMetrics) observeSystemMetrics(ctx context.Context) { - ticker := time.NewTicker(60 * time.Second) // Update every 10 seconds + ticker := time.NewTicker(60 * time.Second) // Update every 60 seconds defer ticker.Stop() - var lastMemStats runtime.MemStats - var lastUpdateTime time.Time - runtime.ReadMemStats(&lastMemStats) - lastUpdateTime = time.Now() - for { select { case <-ticker.C: @@ -459,17 +452,6 @@ func (pm *PerformanceMetrics) observeSystemMetrics(ctx context.Context) { memoryUsageMB := float64(memStats.Sys) / (1024 * 1024) // Total memory in megabytes pm.SetGauge(MetricMemoryUsageMB, memoryUsageMB) - // Calculate CPU utilization using a more realistic heuristic - now := time.Now() - elapsed := now.Sub(lastUpdateTime) - if elapsed > 0 { - cpuUtilization := pm.calculateCPUUtilizationHeuristic(memStats, lastMemStats, elapsed) - pm.SetGauge(MetricCPUUtilization, cpuUtilization) - - lastMemStats = memStats - lastUpdateTime = now - } - // Update OS-level machine stats pm.updateMachineStats() case <-ctx.Done(): @@ -478,55 +460,6 @@ func (pm *PerformanceMetrics) observeSystemMetrics(ctx context.Context) { } } -// calculateCPUUtilizationHeuristic calculates CPU utilization using a heuristic -// based on goroutine count and GC activity. This provides a reasonable approximation. -// Note: For accurate CPU metrics, consider using OS-level process CPU time. -func (pm *PerformanceMetrics) calculateCPUUtilizationHeuristic( - currentMemStats runtime.MemStats, - lastMemStats runtime.MemStats, - elapsed time.Duration, -) float64 { - numCPU := float64(runtime.NumCPU()) - activeGoroutines := float64(runtime.NumGoroutine()) - - // Calculate GC rate (GCs per second) - gcDelta := float64(currentMemStats.NumGC - lastMemStats.NumGC) - gcRate := gcDelta / elapsed.Seconds() - - // Normalize goroutines: if we have more goroutines than CPU cores, - // we're likely using more CPU, but use a conservative multiplier - // Formula: (goroutines / CPU cores) * 10%, capped at 40% - goroutineContribution := (activeGoroutines / numCPU) * 10.0 - if goroutineContribution > 40.0 { - goroutineContribution = 40.0 - } - - // GC contribution: frequent GCs indicate CPU work, but use conservative multiplier - // Formula: GC rate * 1%, capped at 20% - gcContribution := gcRate * 1.0 - if gcContribution > 20.0 { - gcContribution = 20.0 - } - - // Total CPU utilization estimate - cpuUtilization := goroutineContribution + gcContribution - - // Add a small base load if there are active goroutines - if cpuUtilization < 1.0 && activeGoroutines > 0 { - cpuUtilization = 1.0 // Minimum 1% if there are active goroutines - } - - // Cap CPU utilization at 100% - if cpuUtilization > 100.0 { - cpuUtilization = 100.0 - } - if cpuUtilization < 0.0 { - cpuUtilization = 0.0 - } - - return cpuUtilization -} - // updateMachineStats collects and updates OS-level machine statistics // including CPU load, RAM utilization, and swapfile utilization. func (pm *PerformanceMetrics) updateMachineStats() { @@ -558,25 +491,6 @@ func (pm *PerformanceMetrics) updateMachineStats() { } } -// NoOpPerformanceMetrics is a no-op implementation of PerformanceMetricsRecorder -// that can be used when metrics are disabled. -type NoOpPerformanceMetrics struct{} - -// IncrementCounter is a no-op. -func (n *NoOpPerformanceMetrics) IncrementCounter(name string, value float64) {} - -// RecordDuration is a no-op. -func (n *NoOpPerformanceMetrics) RecordDuration(name string, duration time.Duration) {} - -// SetGauge is a no-op. -func (n *NoOpPerformanceMetrics) SetGauge(name string, value float64) {} - -// GetCounterValue always returns 0. -func (n *NoOpPerformanceMetrics) GetCounterValue(name string) float64 { return 0 } - -// GetGaugeValue always returns 0. -func (n *NoOpPerformanceMetrics) GetGaugeValue(name string) float64 { return 0 } - // GetCounterValue returns the current value of a counter. func (pm *PerformanceMetrics) GetCounterValue(name string) float64 { pm.countersMutex.RLock() @@ -636,6 +550,21 @@ const ( MetricRedemptionProofSubmissionsSuccessTotal = "redemption_proof_submissions_success_total" MetricRedemptionProofSubmissionsFailedTotal = "redemption_proof_submissions_failed_total" + // Deposit Sweep Proof Submission Metrics (SPV maintainer) + MetricDepositSweepProofSubmissionsTotal = "deposit_sweep_proof_submissions_total" + MetricDepositSweepProofSubmissionsSuccessTotal = "deposit_sweep_proof_submissions_success_total" + MetricDepositSweepProofSubmissionsFailedTotal = "deposit_sweep_proof_submissions_failed_total" + + // SPV Proof Skip Metrics (SPV maintainer) + // MetricSpvProofSkippedOutsideRelayRangeTotal counts the number of + // transactions whose SPV proofs were skipped because no relay range + // contained the transaction. + MetricSpvProofSkippedOutsideRelayRangeTotal = "spv_proof_skipped_outside_relay_range_total" + // MetricSpvProofSkippedExceededMaxHeadersTotal counts the number of + // transactions whose SPV proofs were skipped because the chain header + // count exceeded the configured maximum. + MetricSpvProofSkippedExceededMaxHeadersTotal = "spv_proof_skipped_exceeded_max_headers_total" + // Wallet Action Metrics (aggregate) MetricWalletActionsTotal = "wallet_actions_total" MetricWalletActionSuccessTotal = "wallet_action_success_total" @@ -690,7 +619,6 @@ const ( MetricWalletDispatcherRejectedTotal = "wallet_dispatcher_rejected_total" // System Metrics - MetricCPUUtilization = "cpu_utilization_percent" MetricMemoryUsageMB = "memory_usage_mb" MetricGoroutineCount = "goroutine_count" MetricCPULoadPercent = "cpu_load_percent" diff --git a/pkg/clientinfo/performance_test.go b/pkg/clientinfo/performance_test.go index 5ebf253288..2b040beaef 100644 --- a/pkg/clientinfo/performance_test.go +++ b/pkg/clientinfo/performance_test.go @@ -2,7 +2,9 @@ package clientinfo import ( "context" + "fmt" "math" + "strings" "sync" "testing" "time" @@ -284,6 +286,10 @@ func TestHistogramBucketPlacement(t *testing.T) { {1000 * time.Second, 0, false}, // > 600s (overflow) } + pm.histogramsMutex.Lock() + pm.histograms[metricName] = &histogram{buckets: make(map[float64]float64)} + pm.histogramsMutex.Unlock() + for _, tc := range testCases { pm.RecordDuration(metricName, tc.duration) } @@ -338,7 +344,6 @@ func TestMetricsInitialization(t *testing.T) { // Test gauges gauges := []string{ - MetricCPUUtilization, MetricMemoryUsageMB, MetricGoroutineCount, MetricCPULoadPercent, @@ -396,6 +401,29 @@ func TestNetworkJoinFailureMetricName(t *testing.T) { } } +// assertCounterExportedInRegistry verifies that counterName is actually +// exposed through the metrics registry (not just tracked in pm's internal +// counters map) by attempting to register the same gauge name again: a +// registration that was silently skipped (e.g. because +// ObserveApplicationSource was never called, or was called with the wrong +// metric name) would succeed here instead of failing with "already exists". +func assertCounterExportedInRegistry( + t *testing.T, + registry *Registry, + counterName string, +) { + t.Helper() + + metricName := fmt.Sprintf("performance_%s", counterName) + if _, err := registry.NewMetricGauge(metricName); err == nil || + !strings.Contains(err.Error(), "already exists") { + t.Errorf( + "counter %s should be exported in the metrics registry as %s", + counterName, metricName, + ) + } +} + // TestJoinFailureAndOnChainCountersRegistered tests that the per-reason join // failure counters and the firewall on-chain checks counter are registered // upfront so they appear in the metrics endpoint before any increment. @@ -420,6 +448,87 @@ func TestJoinFailureAndOnChainCountersRegistered(t *testing.T) { continue } + assertCounterExportedInRegistry(t, registry, counterName) + + if value := pm.GetCounterValue(counterName); value != 0 { + t.Errorf("counter %s should start at 0, got %v", counterName, value) + } + + pm.IncrementCounter(counterName, 1) + if value := pm.GetCounterValue(counterName); value != 1 { + t.Errorf("counter %s should increment to 1, got %v", counterName, value) + } + } +} + +// TestDepositSweepProofSubmissionCountersRegistered tests that the +// deposit-sweep proof-submission counters are registered upfront so they +// appear in the metrics endpoint before any increment. +func TestDepositSweepProofSubmissionCountersRegistered(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + registry := &Registry{keepclientinfo.NewRegistry(), ctx} + pm := NewPerformanceMetrics(ctx, registry) + + expectedCounters := []string{ + MetricDepositSweepProofSubmissionsTotal, + MetricDepositSweepProofSubmissionsSuccessTotal, + MetricDepositSweepProofSubmissionsFailedTotal, + } + + for _, counterName := range expectedCounters { + pm.countersMutex.RLock() + _, exists := pm.counters[counterName] + pm.countersMutex.RUnlock() + if !exists { + t.Errorf("counter %s should be registered upfront", counterName) + continue + } + + assertCounterExportedInRegistry(t, registry, counterName) + + if value := pm.GetCounterValue(counterName); value != 0 { + t.Errorf("counter %s should start at 0, got %v", counterName, value) + } + + pm.IncrementCounter(counterName, 1) + if value := pm.GetCounterValue(counterName); value != 1 { + t.Errorf("counter %s should increment to 1, got %v", counterName, value) + } + } +} + +// TestSpvProofSkipCountersRegistered tests that the SPV proof-skip counters +// are registered upfront so they appear in the metrics endpoint before any +// increment. The spv.go maintainer emits IncrementCounter calls for these +// counters from the relay-range and exceeded-max-headers skip branches; a +// missing upfront registration would cause the values to be silently dropped +// from /metrics because the lazy-create-without-register path in +// IncrementCounter never calls ObserveApplicationSource. +func TestSpvProofSkipCountersRegistered(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + registry := &Registry{keepclientinfo.NewRegistry(), ctx} + pm := NewPerformanceMetrics(ctx, registry) + + expectedCounters := []string{ + MetricSpvProofSkippedOutsideRelayRangeTotal, + MetricSpvProofSkippedExceededMaxHeadersTotal, + } + + for _, counterName := range expectedCounters { + pm.countersMutex.RLock() + _, exists := pm.counters[counterName] + pm.countersMutex.RUnlock() + if !exists { + t.Errorf("counter %s should be registered upfront", counterName) + continue + } + + assertCounterExportedInRegistry(t, registry, counterName) + if value := pm.GetCounterValue(counterName); value != 0 { t.Errorf("counter %s should start at 0, got %v", counterName, value) } diff --git a/pkg/crypto/ephemeral/private_key.go b/pkg/crypto/ephemeral/private_key.go index e75376cc45..2373e42388 100644 --- a/pkg/crypto/ephemeral/private_key.go +++ b/pkg/crypto/ephemeral/private_key.go @@ -1,11 +1,19 @@ package ephemeral import ( + "errors" "fmt" "github.com/btcsuite/btcd/btcec" ) +// ErrInvalidPublicKey is returned by UnmarshalPublicKey when the given bytes +// do not decode to a valid point on the curve. Wrapped into the returned +// error via %w so callers up the stack (including retry-policy code) can +// classify the failure with errors.Is regardless of the underlying decoder's +// error type. +var ErrInvalidPublicKey = errors.New("invalid ephemeral public key") + // PrivateKey is an ephemeral private elliptic curve key. type PrivateKey btcec.PrivateKey @@ -58,7 +66,7 @@ func UnmarshalPrivateKey(bytes []byte) *PrivateKey { func UnmarshalPublicKey(bytes []byte) (*PublicKey, error) { pubKey, err := btcec.ParsePubKey(bytes, curve()) if err != nil { - return nil, fmt.Errorf("could not parse ephemeral public key: [%v]", err) + return nil, fmt.Errorf("%w: [%w]", ErrInvalidPublicKey, err) } return (*PublicKey)(pubKey), nil diff --git a/pkg/maintainer/btcdiff/bitcoin_difficulty.go b/pkg/maintainer/btcdiff/bitcoin_difficulty.go index b33e391182..1a8c2257dd 100644 --- a/pkg/maintainer/btcdiff/bitcoin_difficulty.go +++ b/pkg/maintainer/btcdiff/bitcoin_difficulty.go @@ -42,9 +42,11 @@ var ( ) ) -// lightRelayMinDifficultyTarget matches LightRelay.MIN_DIFFICULTY_TARGET / -// BTCUtils.DIFF1_TARGET (compact bits 0x1d00ffff). -var lightRelayMinDifficultyTarget = blockchain.CompactToBig(0x1d00ffff) +// LightRelayMinDifficultyTarget matches LightRelay.MIN_DIFFICULTY_TARGET / +// BTCUtils.DIFF1_TARGET (compact bits 0x1d00ffff). Exported so other packages +// (e.g. pkg/maintainer/spv) can share the same decoded value rather than +// duplicating the compact-bits decode. +var LightRelayMinDifficultyTarget = blockchain.CompactToBig(0x1d00ffff) func Initialize( ctx context.Context, @@ -397,7 +399,7 @@ func relayAllowsPreRetargetHeaderTarget(oldEpochTarget, headerTarget *big.Int) b if oldEpochTarget.Cmp(headerTarget) == 0 { return true } - return lightRelayMinDifficultyTarget.Cmp(headerTarget) == 0 + return LightRelayMinDifficultyTarget.Cmp(headerTarget) == 0 } // getBlockHeaders returns block headers from the given range. diff --git a/pkg/maintainer/spv/config.go b/pkg/maintainer/spv/config.go index 49cdfe40d9..d9f3dfcf7f 100644 --- a/pkg/maintainer/spv/config.go +++ b/pkg/maintainer/spv/config.go @@ -29,6 +29,12 @@ const ( DefaultIdleBackOffTime = 10 * time.Minute ) +// DefaultMaxProofHeaders is the default value for the maximum number of +// block headers allowed in a single SPV proof. It caps the forward walk +// over headers when assembling a proof; see the documentation on the +// MaxProofHeaders config field and on getProofInfo in spv.go. +const DefaultMaxProofHeaders = 144 + // Config holds configurable properties. type Config struct { // Enabled indicates whether the SPV maintainer should be started. @@ -65,4 +71,13 @@ type Config struct { // IdleBackoffTime is a wait time which should be applied when there are no // more transaction proofs to submit. IdleBackoffTime time.Duration + + // MaxProofHeaders caps the forward walk over headers when assembling an + // SPV proof. The proof window is anchored at a fixed start block, so a + // run of leading minimum-difficulty (DIFF1) headers longer than this + // bound makes the transaction permanently unprovable rather than merely + // delayed. Raise the value on networks (e.g. testnet4 with extended + // BIP94 minimum-difficulty runs) where the default 144 headers is + // insufficient. + MaxProofHeaders uint } diff --git a/pkg/maintainer/spv/deposit_sweep.go b/pkg/maintainer/spv/deposit_sweep.go index f8405e1576..44be829242 100644 --- a/pkg/maintainer/spv/deposit_sweep.go +++ b/pkg/maintainer/spv/deposit_sweep.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-core/pkg/bitcoin" "github.com/keep-network/keep-core/pkg/chain" + "github.com/keep-network/keep-core/pkg/clientinfo" ) // SubmitDepositSweepProof prepares deposit sweep proof for the given @@ -26,7 +27,7 @@ func SubmitDepositSweepProof( btcChain, spvChain, bitcoin.AssembleSpvProof, - getGlobalMetricsRecorder(), + getMetricsRecorder(), ) } @@ -42,12 +43,12 @@ func submitDepositSweepProof( ) error { // Record proof submission attempt if metricsRecorder != nil { - metricsRecorder.IncrementCounter("deposit_sweep_proof_submissions_total", 1) + metricsRecorder.IncrementCounter(clientinfo.MetricDepositSweepProofSubmissionsTotal, 1) } if requiredConfirmations == 0 { if metricsRecorder != nil { - metricsRecorder.IncrementCounter("deposit_sweep_proof_submissions_failed_total", 1) + metricsRecorder.IncrementCounter(clientinfo.MetricDepositSweepProofSubmissionsFailedTotal, 1) } return fmt.Errorf( "provided required confirmations count must be greater than 0", @@ -61,7 +62,7 @@ func submitDepositSweepProof( ) if err != nil { if metricsRecorder != nil { - metricsRecorder.IncrementCounter("deposit_sweep_proof_submissions_failed_total", 1) + metricsRecorder.IncrementCounter(clientinfo.MetricDepositSweepProofSubmissionsFailedTotal, 1) } return fmt.Errorf( "failed to assemble transaction spv proof: [%v]", @@ -76,7 +77,7 @@ func submitDepositSweepProof( ) if err != nil { if metricsRecorder != nil { - metricsRecorder.IncrementCounter("deposit_sweep_proof_submissions_failed_total", 1) + metricsRecorder.IncrementCounter(clientinfo.MetricDepositSweepProofSubmissionsFailedTotal, 1) } return fmt.Errorf( "error while parsing transaction inputs: [%v]", @@ -91,7 +92,7 @@ func submitDepositSweepProof( vault, ); err != nil { if metricsRecorder != nil { - metricsRecorder.IncrementCounter("deposit_sweep_proof_submissions_failed_total", 1) + metricsRecorder.IncrementCounter(clientinfo.MetricDepositSweepProofSubmissionsFailedTotal, 1) } return fmt.Errorf( "failed to submit deposit sweep proof with reimbursement: [%v]", @@ -101,7 +102,7 @@ func submitDepositSweepProof( // Record successful proof submission if metricsRecorder != nil { - metricsRecorder.IncrementCounter("deposit_sweep_proof_submissions_success_total", 1) + metricsRecorder.IncrementCounter(clientinfo.MetricDepositSweepProofSubmissionsSuccessTotal, 1) } return nil @@ -118,17 +119,12 @@ func parseDepositSweepTransactionInputs( common.Address, error, ) { - // Represents the main UTXO of the deposit sweep transaction. Nil if there - // was no main UTXO. var mainUTXO *bitcoin.UnspentTransactionOutput = nil - // Stores the vault address of the deposits. Each deposit should have the - // same value of vault. The zero-filled value indicates there was no vault - // value set for the deposits. + // Each deposit must have the same vault value. The zero-filled value + // indicates there was no vault set for the deposits. var vault = common.Address{} - // This flag checks if at least one deposit input has been found during - // deposit processing. var depositAlreadyProcessed = false // Perform a sanity check: a deposit sweep transaction must have exactly one diff --git a/pkg/maintainer/spv/deposit_sweep_test.go b/pkg/maintainer/spv/deposit_sweep_test.go index dc61256ccf..ece12243de 100644 --- a/pkg/maintainer/spv/deposit_sweep_test.go +++ b/pkg/maintainer/spv/deposit_sweep_test.go @@ -96,7 +96,7 @@ func TestSubmitDepositSweepProof(t *testing.T) { btcChain, spvChain, mockSpvProofAssembler, - getGlobalMetricsRecorder(), + getMetricsRecorder(), ) if err != nil { t.Fatal(err) diff --git a/pkg/maintainer/spv/redemptions.go b/pkg/maintainer/spv/redemptions.go index e504860f81..dd0f42da49 100644 --- a/pkg/maintainer/spv/redemptions.go +++ b/pkg/maintainer/spv/redemptions.go @@ -9,13 +9,6 @@ import ( "github.com/keep-network/keep-core/pkg/tbtc" ) -// getGlobalMetricsRecorder returns the global metrics recorder if set. -func getGlobalMetricsRecorder() interface { - IncrementCounter(name string, value float64) -} { - return getMetricsRecorder() -} - // SubmitRedemptionProof prepares redemption proof for the given transaction // and submits it to the on-chain contract. If the number of required // confirmations is `0`, an error is returned. @@ -31,7 +24,7 @@ func SubmitRedemptionProof( btcChain, spvChain, bitcoin.AssembleSpvProof, - getGlobalMetricsRecorder(), + getMetricsRecorder(), ) } diff --git a/pkg/maintainer/spv/redemptions_test.go b/pkg/maintainer/spv/redemptions_test.go index 4f10a3a208..048dcb3080 100644 --- a/pkg/maintainer/spv/redemptions_test.go +++ b/pkg/maintainer/spv/redemptions_test.go @@ -78,7 +78,7 @@ func TestSubmitRedemptionProof(t *testing.T) { btcChain, spvChain, mockSpvProofAssembler, - getGlobalMetricsRecorder(), + getMetricsRecorder(), ) if err != nil { t.Fatal(err) diff --git a/pkg/maintainer/spv/spv.go b/pkg/maintainer/spv/spv.go index 6191a2144d..6275f5f83c 100644 --- a/pkg/maintainer/spv/spv.go +++ b/pkg/maintainer/spv/spv.go @@ -24,18 +24,35 @@ import ( "github.com/ipfs/go-log/v2" "github.com/keep-network/keep-core/pkg/bitcoin" + "github.com/keep-network/keep-core/pkg/clientinfo" "github.com/keep-network/keep-core/pkg/maintainer/btcdiff" ) var logger = log.Logger("keep-maintainer-spv") -// The length of the Bitcoin difficulty epoch in blocks. -const difficultyEpochLength = 2016 - -// The maximum number of block headers allowed in a single SPV proof. Bounds -// the forward walk over headers when computing required confirmations -// (relevant on testnet4 where long runs of minimum-difficulty blocks occur). -const maxProofHeaders = 144 +// proofSkipReason explains why an SPV proof cannot be assembled for a +// transaction in the current cycle. It lets callers log and record metrics with +// the specific cause instead of collapsing every skip into one generic message. +type proofSkipReason int + +const ( + // proofSkipNone means the proof is within the relay's difficulty range and + // should be assembled once enough confirmations accumulate. + proofSkipNone proofSkipReason = iota + // proofSkipOutsideRelayRange means the decisive header matched neither the + // current nor the previous relay epoch difficulty. The Bridge would revert + // with "Not at current or previous difficulty". This is usually transient - + // the transaction's epoch is not yet proven in the relay - and resolves as + // the relay advances. + proofSkipOutsideRelayRange + // proofSkipExceededMaxHeaders means no decisive header was found and not + // enough difficulty accumulated within the configured MaxProofHeaders + // bound. Because the proof window is anchored at a fixed start block, a + // run of leading minimum-difficulty (DIFF1) headers longer than the bound + // is permanently unprovable rather than merely delayed, hence it is + // signalled separately. + proofSkipExceededMaxHeaders +) func Initialize( ctx context.Context, @@ -218,27 +235,66 @@ func (sm *spvMaintainer) proveTransactions( transactionHashStr, ) - isProofWithinRelayRange, accumulatedConfirmations, requiredConfirmations, err := getProofInfo( + accumulatedConfirmations, requiredConfirmations, skipReason, err := getProofInfo( transaction.Hash(), sm.btcChain, sm.spvChain, sm.btcDiffChain, + sm.config.MaxProofHeaders, ) if err != nil { return fmt.Errorf("failed to get proof info: [%v]", err) } - if !isProofWithinRelayRange { + switch skipReason { + case proofSkipOutsideRelayRange: // The required proof goes outside the previous and current // difficulty epochs as seen by the relay. Skip the transaction. It - // will most likely be proven later. + // will most likely be proven later, once the relay advances. logger.Warnf( "skipped proving transaction [%s]; the range "+ "of the required proof goes outside the previous and "+ "current difficulty epochs as seen by the relay", transactionHashStr, ) + if recorder := getMetricsRecorder(); recorder != nil { + recorder.IncrementCounter( + clientinfo.MetricSpvProofSkippedOutsideRelayRangeTotal, + 1, + ) + } continue + case proofSkipExceededMaxHeaders: + // No decisive header was found and not enough difficulty + // accumulated within the configured MaxProofHeaders bound. Unlike + // the range skip above, this transaction may be permanently + // unprovable if it is buried under a run of minimum-difficulty + // blocks longer than the bound. + logger.Errorf( + "skipped proving transaction [%s]; could not find a decisive "+ + "header or accumulate enough difficulty within [%d] "+ + "headers; the transaction may be permanently unprovable", + transactionHashStr, + sm.config.MaxProofHeaders, + ) + if recorder := getMetricsRecorder(); recorder != nil { + recorder.IncrementCounter( + clientinfo.MetricSpvProofSkippedExceededMaxHeadersTotal, + 1, + ) + } + continue + case proofSkipNone: + // The proof is within range and assemblable; proceed to the + // confirmation check and submission below. + default: + // Defensive: a skip reason getProofInfo does not currently emit + // must never silently fall through to proof submission. + return fmt.Errorf( + "unexpected proof skip reason [%d] for transaction [%s]", + skipReason, + transactionHashStr, + ) } if accumulatedConfirmations < requiredConfirmations { @@ -316,21 +372,23 @@ func isInputCurrentWalletsMainUTXO( return bytes.Equal(mainUtxoHash[:], wallet.MainUtxoHash[:]), nil } -// getProofInfo returns information about the SPV proof. It includes the -// information whether the transaction proof range is within the previous and -// current difficulty epochs as seen by the relay, the accumulated number of -// confirmations and the required number of confirmations. +// getProofInfo returns information about the SPV proof: the accumulated number +// of confirmations, the required number of confirmations, and a proofSkipReason +// indicating whether the proof can be assembled (proofSkipNone) or why it must +// be skipped this cycle. The confirmation counts are meaningful only when the +// reason is proofSkipNone. func getProofInfo( transactionHash bitcoin.Hash, btcChain bitcoin.Chain, spvChain Chain, btcDiffChain btcdiff.Chain, + maxProofHeaders uint, ) ( - bool, uint, uint, error, + uint, uint, proofSkipReason, error, ) { latestBlockHeight, err := btcChain.GetLatestBlockHeight() if err != nil { - return false, 0, 0, fmt.Errorf( + return 0, 0, proofSkipNone, fmt.Errorf( "failed to get latest block height: [%v]", err, ) @@ -341,7 +399,7 @@ func getProofInfo( transactionHash, ) if err != nil { - return false, 0, 0, fmt.Errorf( + return 0, 0, proofSkipNone, fmt.Errorf( "failed to get transaction confirmations: [%v]", err, ) @@ -349,7 +407,7 @@ func getProofInfo( txProofDifficultyFactor, err := spvChain.TxProofDifficultyFactor() if err != nil { - return false, 0, 0, fmt.Errorf( + return 0, 0, proofSkipNone, fmt.Errorf( "failed to get transaction proof difficulty factor: [%v]", err, ) @@ -358,7 +416,7 @@ func getProofInfo( currentEpochDifficulty, previousEpochDifficulty, err := btcDiffChain.GetCurrentAndPrevEpochDifficulty() if err != nil { - return false, 0, 0, fmt.Errorf( + return 0, 0, proofSkipNone, fmt.Errorf( "failed to get Bitcoin epoch difficulties: [%v]", err, ) @@ -382,15 +440,17 @@ func getProofInfo( previousEpochDifficulty.Cmp(one) > 0 var requestedDiff *big.Int + var totalDifficultyRequired *big.Int observedDiff := big.NewInt(0) headerCount := uint(0) for { if headerCount >= maxProofHeaders { // Could not find a decisive header or accumulate enough - // difficulty within a sane number of headers. Skip the - // transaction; it may become provable later. - return false, 0, 0, nil + // difficulty within the header bound. Signal the distinct cause; + // with a fixed proof window this may be permanent rather than + // merely delayed. + return 0, 0, proofSkipExceededMaxHeaders, nil } blockHeight := proofStartBlock + uint64(headerCount) @@ -398,12 +458,12 @@ func getProofInfo( // Not enough mined blocks yet to assemble the proof. Report the // number of headers needed so far plus one more; the caller will // see accumulated < required and skip the transaction for now. - return true, accumulatedConfirmations, headerCount + 1, nil + return accumulatedConfirmations, headerCount + 1, proofSkipNone, nil } header, err := btcChain.GetBlockHeader(uint(blockHeight)) if err != nil { - return false, 0, 0, fmt.Errorf( + return 0, 0, proofSkipNone, fmt.Errorf( "failed to get block header at height [%v]: [%v]", blockHeight, err, @@ -415,8 +475,12 @@ func getProofInfo( observedDiff.Add(observedDiff, headerDiff) if requestedDiff == nil { - // Still looking for the decisive header. - if skipMinDifficulty && headerDiff.Cmp(one) == 0 { + // Still looking for the decisive header. Skip minimum-difficulty + // (DIFF1) headers by exact target equality, mirroring the Bridge's + // target == MIN_DIFFICULTY_TARGET predicate. Their work is still + // added to observedDiff above. + if skipMinDifficulty && + header.Target().Cmp(btcdiff.LightRelayMinDifficultyTarget) == 0 { continue } @@ -429,16 +493,17 @@ func getProofInfo( // difficulty". The transaction is either too fresh (its epoch // is not yet proven in the relay) or too old. Skip it; it may // be proven in the future. - return false, 0, 0, nil + return 0, 0, proofSkipOutsideRelayRange, nil } + + totalDifficultyRequired = new(big.Int).Mul( + requestedDiff, + txProofDifficultyFactor, + ) } - totalDifficultyRequired := new(big.Int).Mul( - requestedDiff, - txProofDifficultyFactor, - ) if observedDiff.Cmp(totalDifficultyRequired) >= 0 { - return true, accumulatedConfirmations, headerCount, nil + return accumulatedConfirmations, headerCount, proofSkipNone, nil } } } diff --git a/pkg/maintainer/spv/spv_test.go b/pkg/maintainer/spv/spv_test.go index 088c619883..3f404052ee 100644 --- a/pkg/maintainer/spv/spv_test.go +++ b/pkg/maintainer/spv/spv_test.go @@ -7,8 +7,10 @@ import ( "strings" "testing" + "github.com/btcsuite/btcd/blockchain" "github.com/keep-network/keep-core/internal/testutils" "github.com/keep-network/keep-core/pkg/bitcoin" + "github.com/keep-network/keep-core/pkg/maintainer/btcdiff" "github.com/keep-network/keep-core/pkg/tbtc" ) @@ -28,7 +30,7 @@ func TestGetProofInfo(t *testing.T) { previousEpochDifficulty *big.Int headerDifficultyAt func(uint) *big.Int headersFrom, headersTo uint - expectedIsProofWithinRelayRange bool + expectedSkipReason proofSkipReason expectedAccumulatedConfirmations uint expectedRequiredConfirmations uint }{ @@ -42,7 +44,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 19, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 20, expectedRequiredConfirmations: 6, }, @@ -55,7 +57,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 19, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 20, expectedRequiredConfirmations: 6, }, @@ -75,7 +77,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 30, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 31, expectedRequiredConfirmations: 10, }, @@ -93,7 +95,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 30, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 31, expectedRequiredConfirmations: 4, }, @@ -114,7 +116,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 30, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 31, expectedRequiredConfirmations: 8, }, @@ -128,7 +130,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 19, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 20, expectedRequiredConfirmations: 6, }, @@ -143,12 +145,13 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 19, - expectedIsProofWithinRelayRange: false, + expectedSkipReason: proofSkipOutsideRelayRange, expectedAccumulatedConfirmations: 0, expectedRequiredConfirmations: 0, }, - // A run of minimum-difficulty headers longer than maxProofHeaders + // A run of minimum-difficulty headers longer than DefaultMaxProofHeaders // never reaches a decisive header. + "minimum difficulty run exceeds header bound": { transactionConfirmations: 150, currentEpochDifficulty: diff(32), @@ -157,7 +160,7 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 149, - expectedIsProofWithinRelayRange: false, + expectedSkipReason: proofSkipExceededMaxHeaders, expectedAccumulatedConfirmations: 0, expectedRequiredConfirmations: 0, }, @@ -172,7 +175,112 @@ func TestGetProofInfo(t *testing.T) { headersFrom: proofStart, headersTo: proofStart + 2, - expectedIsProofWithinRelayRange: true, + expectedSkipReason: proofSkipNone, + expectedAccumulatedConfirmations: 3, + expectedRequiredConfirmations: 4, + }, + // The decisive header matches the current (not previous) epoch on an + // epoch-spanning proof. Complements the "difficulty drops/raises" cases + // (which bind to the previous epoch) by exercising the current-epoch + // binding branch on asymmetric difficulties. Proof starts in the current + // epoch (32) for two blocks, then drops to the previous epoch's value + // (16). Required total is 6*32=192; 2*32 + 8*16 = 192 -> 10 headers. + "decisive header binds current epoch": { + transactionConfirmations: 31, + currentEpochDifficulty: diff(32), + previousEpochDifficulty: diff(16), + headerDifficultyAt: func(h uint) *big.Int { + if h < proofStart+2 { + return diff(32) + } + return diff(16) + }, + headersFrom: proofStart, + headersTo: proofStart + 30, + + expectedSkipReason: proofSkipNone, + expectedAccumulatedConfirmations: 31, + expectedRequiredConfirmations: 10, + }, + // A minimum-difficulty (DIFF1) header appearing after the decisive + // header is accumulated like any other header and does not re-enter the + // skip/binding logic (that runs only until the decisive header is + // found). Decisive header 32 binds requestedDiff; the interior DIFF1 + // contributes its work to the observed difficulty. Required total is + // 6*32=192; 32 + 1 + 5*32 = 193 >= 192 -> 7 headers. + "minimum difficulty header after decisive header is counted": { + transactionConfirmations: 20, + currentEpochDifficulty: diff(32), + previousEpochDifficulty: diff(16), + headerDifficultyAt: func(h uint) *big.Int { + if h == proofStart+1 { + return diff(1) + } + return diff(32) + }, + headersFrom: proofStart, + headersTo: proofStart + 19, + + expectedSkipReason: proofSkipNone, + expectedAccumulatedConfirmations: 20, + expectedRequiredConfirmations: 7, + }, + // The decisive header sits exactly at the header bound: 143 leading + // DIFF1 headers (skipped for binding but contributing 1 each) followed + // by the decisive header at position DefaultMaxProofHeaders. Required + // total is 6*16=96; 143*1 + 16 = 159 >= 96 -> exactly 144 headers, at + // the bound. + "decisive header exactly at header bound is proven": { + transactionConfirmations: DefaultMaxProofHeaders, + currentEpochDifficulty: diff(16), + previousEpochDifficulty: diff(32), + headerDifficultyAt: func(h uint) *big.Int { + if h < proofStart+DefaultMaxProofHeaders-1 { + return diff(1) + } + return diff(16) + }, + headersFrom: proofStart, + headersTo: proofStart + DefaultMaxProofHeaders - 1, + + expectedSkipReason: proofSkipNone, + expectedAccumulatedConfirmations: DefaultMaxProofHeaders, + expectedRequiredConfirmations: DefaultMaxProofHeaders, + }, + // The decisive header sits one past the header bound: DefaultMaxProofHeaders + // leading DIFF1 headers exhaust the walk before the decisive header at + // position DefaultMaxProofHeaders+1 is ever examined. This is the off-by-one + // companion to the case above and must be signalled as exceeded. + "decisive header just past header bound is skipped": { + transactionConfirmations: DefaultMaxProofHeaders + 1, + currentEpochDifficulty: diff(16), + previousEpochDifficulty: diff(32), + headerDifficultyAt: func(h uint) *big.Int { + if h < proofStart+DefaultMaxProofHeaders { + return diff(1) + } + return diff(16) + }, + headersFrom: proofStart, + headersTo: proofStart + DefaultMaxProofHeaders, + + expectedSkipReason: proofSkipExceededMaxHeaders, + expectedAccumulatedConfirmations: 0, + expectedRequiredConfirmations: 0, + }, + // The chain tip is reached while still skipping leading DIFF1 headers, + // before any decisive header is bound (requestedDiff is still nil). The + // proof is within range and the caller is told to wait for one more + // header than currently exists. + "chain tip reached before decisive header": { + transactionConfirmations: 3, + currentEpochDifficulty: diff(32), + previousEpochDifficulty: diff(16), + headerDifficultyAt: func(uint) *big.Int { return diff(1) }, + headersFrom: proofStart, + headersTo: proofStart + 2, + + expectedSkipReason: proofSkipNone, expectedAccumulatedConfirmations: 3, expectedRequiredConfirmations: 4, }, @@ -206,30 +314,32 @@ func TestGetProofInfo(t *testing.T) { localChain.setTxProofDifficultyFactor(big.NewInt(6)) localChain.setCurrentEpoch(392) + // Note the setter's parameter order is (previous, current). localChain.setCurrentAndPrevEpochDifficulty( - test.currentEpochDifficulty, test.previousEpochDifficulty, + test.currentEpochDifficulty, ) - isProofWithinRelayRange, - accumulatedConfirmations, + accumulatedConfirmations, requiredConfirmations, + skipReason, err := getProofInfo( transactionHash, btcChain, localChain, localChain, + DefaultMaxProofHeaders, ) if err != nil { t.Fatal(err) } - testutils.AssertBoolsEqual( + testutils.AssertIntsEqual( t, - "is proof within range", - test.expectedIsProofWithinRelayRange, - isProofWithinRelayRange, + "skip reason", + int(test.expectedSkipReason), + int(skipReason), ) testutils.AssertUintsEqual( @@ -249,6 +359,251 @@ func TestGetProofInfo(t *testing.T) { } } +// TestGetProofInfo_MinDifficultyDetectedByExactTarget pins the DIFF1 skip +// predicate to exact target equality. The Bridge skips headers whose target +// equals MIN_DIFFICULTY_TARGET, not headers whose computed difficulty rounds +// to 1. These differ: any target in (maxTarget/2, maxTarget] yields +// Difficulty()==1, but only the exact maxTarget is the canonical +// minimum-difficulty target. A header with Difficulty()==1 yet a target below +// maxTarget must NOT be skipped - it is a decisive header. +// +// Here that decisive header matches neither relay epoch, so the Bridge would +// revert and getProofInfo must report proofSkipOutsideRelayRange. If the +// predicate regressed to Difficulty()==1, the header would be skipped as DIFF1 +// and the following current-epoch headers would prove the transaction +// (proofSkipNone) - so this case fails loudly on that regression. +func TestGetProofInfo_MinDifficultyDetectedByExactTarget(t *testing.T) { + const proofStart = 790270 + + // A target of 3/4 * maxTarget: Difficulty() floors to 1, but the target is + // strictly below the minimum-difficulty target. BigToCompact truncates + // toward zero, so the encoded target can never round up to maxTarget. + nonMinTarget := new(big.Int).Mul(btcdiff.LightRelayMinDifficultyTarget, big.NewInt(3)) + nonMinTarget.Div(nonMinTarget, big.NewInt(4)) + decisiveHeader := &bitcoin.BlockHeader{ + Bits: blockchain.BigToCompact(nonMinTarget), + } + + // Guard the construction; without both properties the test proves nothing. + if decisiveHeader.Difficulty().Cmp(big.NewInt(1)) != 0 { + t.Fatalf( + "test header must have difficulty 1, got [%v]", + decisiveHeader.Difficulty(), + ) + } + if decisiveHeader.Target().Cmp(btcdiff.LightRelayMinDifficultyTarget) == 0 { + t.Fatal( + "test header target must differ from the minimum-difficulty target", + ) + } + + transactionHash, err := bitcoin.NewHashFromString( + "44c568bc0eac07a2a9c2b46829be5b5d46e7d00e17bfb613f506a75ccf86a473", + bitcoin.InternalByteOrder, + ) + if err != nil { + t.Fatal(err) + } + + btcChain := newLocalBitcoinChain() + // The first (decisive) header carries Difficulty()==1 with a non-minimum + // target; the remaining headers carry the current epoch difficulty. + if err := btcChain.addBlockHeader(proofStart, decisiveHeader); err != nil { + t.Fatal(err) + } + if err := populateBlockHeaders( + btcChain, + proofStart+1, + proofStart+19, + func(uint) *big.Int { return big.NewInt(32) }, + ); err != nil { + t.Fatal(err) + } + btcChain.addTransactionConfirmations(transactionHash, 20) + + localChain := newLocalChain() + localChain.setTxProofDifficultyFactor(big.NewInt(6)) + localChain.setCurrentEpoch(392) + // Note the setter's parameter order is (previous, current). + localChain.setCurrentAndPrevEpochDifficulty(big.NewInt(16), big.NewInt(32)) + + _, _, skipReason, err := getProofInfo( + transactionHash, + btcChain, + localChain, + localChain, + DefaultMaxProofHeaders, + ) + if err != nil { + t.Fatal(err) + } + + testutils.AssertIntsEqual( + t, + "skip reason", + int(proofSkipOutsideRelayRange), + int(skipReason), + ) +} + +// recordingMetricsRecorder captures IncrementCounter calls for assertions. +// proveTransactions invokes it synchronously, so no locking is needed. +type recordingMetricsRecorder struct { + counters map[string]float64 +} + +func (r *recordingMetricsRecorder) IncrementCounter(name string, value float64) { + r.counters[name] += value +} + +// TestProveTransactions covers the caller-side handling of each proofSkipReason +// in proveTransactions. The safety property under test is that a skip reason +// never results in a proof submission, and that an assemblable proof is +// submitted; the per-reason metric counter is asserted as a secondary check. +func TestProveTransactions(t *testing.T) { + const proofStart = 790270 + + // A concrete transaction so proveTransactions can derive a real hash. + rawTransaction, err := hex.DecodeString( + "0100000000010110a15e879b7e8b07df62772579a64bf2b409409bbcc8bc2c7f6e39" + + "31dc615e920100000000ffffffff02042900000000000017a9143ec459d0f3c29286" + + "ae5df5fcc421e2786024277e87b4121600000000001600148db50eb52063ea9d98b3" + + "eac91489a90f738986f6024830450221009740ad12d2e74c00ccb4741d533d2ecd69" + + "02289144c4626508afb61eed790c97022006e67179e8e2a63dc4f1ab758867d8bbfe" + + "0a2b67682be6dadfa8e07d3b7ba04d012103989d253b17a6a0f41838b84ff0d20e88" + + "98f9d7b1a98f2564da4cc29dcf8581d900000000", + ) + if err != nil { + t.Fatal(err) + } + transaction := new(bitcoin.Transaction) + if err := transaction.Deserialize(rawTransaction); err != nil { + t.Fatal(err) + } + transactionHash := transaction.Hash() + + tests := map[string]struct { + headerDifficultyAt func(uint) *big.Int + headersTo uint + transactionConfirmations uint + expectSubmitted bool + expectedCounter string + }{ + // Decisive header (difficulty 8) matches neither epoch -> skipped. + "outside relay range is skipped and metered": { + headerDifficultyAt: func(uint) *big.Int { return big.NewInt(8) }, + headersTo: proofStart + 19, + transactionConfirmations: 20, + expectSubmitted: false, + expectedCounter: "spv_proof_skipped_outside_relay_range_total", + }, + // A run of DIFF1 headers longer than the bound never binds -> skipped. + "exceeded max headers is skipped and metered": { + headerDifficultyAt: func(uint) *big.Int { return big.NewInt(1) }, + headersTo: proofStart + 149, + transactionConfirmations: 150, + expectSubmitted: false, + expectedCounter: "spv_proof_skipped_exceeded_max_headers_total", + }, + // All headers at the current epoch difficulty -> proof is submitted. + "assemblable proof is submitted": { + headerDifficultyAt: func(uint) *big.Int { return big.NewInt(32) }, + headersTo: proofStart + 19, + transactionConfirmations: 20, + expectSubmitted: true, + expectedCounter: "", + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + btcChain := newLocalBitcoinChain() + if err := populateBlockHeaders( + btcChain, + proofStart, + test.headersTo, + test.headerDifficultyAt, + ); err != nil { + t.Fatal(err) + } + btcChain.addTransactionConfirmations( + transactionHash, + test.transactionConfirmations, + ) + + localChain := newLocalChain() + localChain.setTxProofDifficultyFactor(big.NewInt(6)) + localChain.setCurrentEpoch(392) + // Note the setter's parameter order is (previous, current). + localChain.setCurrentAndPrevEpochDifficulty( + big.NewInt(16), + big.NewInt(32), + ) + + recorder := &recordingMetricsRecorder{ + counters: make(map[string]float64), + } + SetMetricsRecorder(recorder) + defer SetMetricsRecorder(nil) + + sm := &spvMaintainer{ + config: Config{MaxProofHeaders: DefaultMaxProofHeaders}, + spvChain: localChain, + btcDiffChain: localChain, + btcChain: btcChain, + } + + var submitted []bitcoin.Hash + getter := func( + uint64, + int, + bitcoin.Chain, + Chain, + ) ([]*bitcoin.Transaction, error) { + return []*bitcoin.Transaction{transaction}, nil + } + submitter := func( + hash bitcoin.Hash, + _ uint, + _ bitcoin.Chain, + _ Chain, + ) error { + submitted = append(submitted, hash) + return nil + } + + if err := sm.proveTransactions(getter, submitter); err != nil { + t.Fatal(err) + } + + if test.expectSubmitted { + if len(submitted) != 1 || submitted[0] != transactionHash { + t.Errorf( + "expected the transaction to be submitted, "+ + "got submissions [%v]", + submitted, + ) + } + } else if len(submitted) != 0 { + t.Errorf( + "expected no submission on skip, got [%d]", + len(submitted), + ) + } + + if test.expectedCounter != "" { + if got := recorder.counters[test.expectedCounter]; got != 1 { + t.Errorf( + "expected counter [%s] to be 1, got [%v]", + test.expectedCounter, + got, + ) + } + } + }) + } +} + func TestUniqueWalletPublicKeyHashes(t *testing.T) { bytesFromHex := func(str string) []byte { value, err := hex.DecodeString(str) diff --git a/pkg/net/libp2p/channel_test.go b/pkg/net/libp2p/channel_test.go index 116c5da73d..916a337595 100644 --- a/pkg/net/libp2p/channel_test.go +++ b/pkg/net/libp2p/channel_test.go @@ -610,3 +610,62 @@ func (ms *mockSubscription) Next(ctx context.Context) (*pubsub.Message, error) { } func (ms *mockSubscription) Cancel() {} + +// --- Benchmarks --- + +// BenchmarkChannelDeliver_SingleHandler measures deliver() latency with a +// single registered handler. The handler's buffer fills after messageHandlerThrottle +// calls; subsequent iterations take the non-blocking default branch. Both paths +// exercise the same mutex lock and snapshot copy overhead. +func BenchmarkChannelDeliver_SingleHandler(b *testing.B) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ch := &channel{} + ch.messageHandlers = []*messageHandler{ + {ctx: ctx, channel: make(chan net.Message, messageHandlerThrottle)}, + } + msg := &mockNetMessage{} + b.ResetTimer() + for range b.N { + ch.deliver(msg) + } +} + +// BenchmarkChannelDeliver_10Handlers measures deliver() fan-out cost across 10 +// concurrent handlers -- representative of a node with multiple active protocol +// subscriptions on the same channel. +func BenchmarkChannelDeliver_10Handlers(b *testing.B) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ch := &channel{} + handlers := make([]*messageHandler, 10) + for i := range handlers { + handlers[i] = &messageHandler{ + ctx: ctx, + channel: make(chan net.Message, messageHandlerThrottle), + } + } + ch.messageHandlers = handlers + msg := &mockNetMessage{} + b.ResetTimer() + for range b.N { + ch.deliver(msg) + } +} + +// BenchmarkProcessPubsubMessage measures the raw throughput of +// processPubsubMessage with an empty message. proto.Unmarshal succeeds on empty +// input; the call returns early with "couldn't find unmarshaler", giving a +// baseline for the per-message overhead before any application logic runs. +func BenchmarkProcessPubsubMessage(b *testing.B) { + ch := &channel{ + unmarshalersByType: make(map[string]func() net.TaggedUnmarshaler), + } + msg := &pubsub.Message{Message: &pubsubpb.Message{}} + b.ResetTimer() + for range b.N { + _ = ch.processPubsubMessage(msg) + } +} diff --git a/pkg/net/retransmission/strategy_test.go b/pkg/net/retransmission/strategy_test.go index 476999d5ee..f0af9901dd 100644 --- a/pkg/net/retransmission/strategy_test.go +++ b/pkg/net/retransmission/strategy_test.go @@ -157,3 +157,25 @@ func TestBackoffStrategy_ConcurrentTick(t *testing.T) { ) } } + +// --- Benchmarks --- + +func BenchmarkBackoffStrategyTick(b *testing.B) { + strategy := WithBackoffStrategy() + noop := func() error { return nil } + b.ResetTimer() + for range b.N { + _ = strategy.Tick(noop) + } +} + +func BenchmarkStandardStrategyTick(b *testing.B) { + strategy := WithStandardStrategy() + var calls int + fn := func() error { calls++; return nil } + b.ResetTimer() + for range b.N { + _ = strategy.Tick(fn) + } + _ = calls +} diff --git a/pkg/protocol/inactivity/marshalling.go b/pkg/protocol/inactivity/marshaling.go similarity index 94% rename from pkg/protocol/inactivity/marshalling.go rename to pkg/protocol/inactivity/marshaling.go index f117f015a4..718d04a240 100644 --- a/pkg/protocol/inactivity/marshalling.go +++ b/pkg/protocol/inactivity/marshaling.go @@ -1,3 +1,4 @@ +// marshaling.go: protobuf (un)marshaling for the public types in this package. package inactivity import ( diff --git a/pkg/protocol/inactivity/marshalling_test.go b/pkg/protocol/inactivity/marshaling_test.go similarity index 100% rename from pkg/protocol/inactivity/marshalling_test.go rename to pkg/protocol/inactivity/marshaling_test.go diff --git a/pkg/protocol/state/sync_machine.go b/pkg/protocol/state/sync_machine.go index a6f2ec20ff..7d23fcc8ce 100644 --- a/pkg/protocol/state/sync_machine.go +++ b/pkg/protocol/state/sync_machine.go @@ -72,7 +72,7 @@ func (sm *SyncMachine) Execute(startBlockHeight uint64) (SyncState, uint64, erro err := sm.blockCounter.WaitForBlockHeight(startBlockHeight) if err != nil { cancelCtx() - return nil, 0, fmt.Errorf("failed to wait for the execution start block") + return nil, 0, fmt.Errorf("failed to wait for the execution start block: [%w]", err) } lastStateEndBlockHeight := startBlockHeight diff --git a/pkg/tbtc/coordination.go b/pkg/tbtc/coordination.go index 2dd75e9614..43e0b2d79f 100644 --- a/pkg/tbtc/coordination.go +++ b/pkg/tbtc/coordination.go @@ -380,9 +380,6 @@ func (ce *coordinationExecutor) coordinate( startTime := time.Now() - // Record duration metric once at the end using defer - var coordinationFailed bool - seed, err := ce.getSeed(window.coordinationBlock) if err != nil { return nil, fmt.Errorf("failed to compute coordination seed: [%v]", err) @@ -431,7 +428,6 @@ func (ce *coordinationExecutor) coordinate( // no point to keep the context active as retransmissions do not // occur anyway. cancelCtx() - coordinationFailed = true if ce.metricsRecorder != nil { ce.metricsRecorder.IncrementCounter(clientinfo.MetricCoordinationFailedTotal, 1) } @@ -455,7 +451,6 @@ func (ce *coordinationExecutor) coordinate( append(actionsChecklist, ActionNoop), ) if err != nil { - coordinationFailed = true // Record as leader timeout observation, not as a failure of this node. // The actual failure is on the leader's side. if ce.metricsRecorder != nil { @@ -498,7 +493,7 @@ func (ce *coordinationExecutor) coordinate( execLogger.Infof("coordination completed with result: [%s]", result) // Record successful coordination counter - if ce.metricsRecorder != nil && !coordinationFailed { + if ce.metricsRecorder != nil { ce.metricsRecorder.IncrementCounter(clientinfo.MetricCoordinationProceduresExecutedTotal, 1) ce.metricsRecorder.RecordDuration(clientinfo.MetricCoordinationDurationSeconds, time.Since(startTime)) } @@ -608,15 +603,12 @@ func (ce *coordinationExecutor) getActionsChecklist( // proposal generator performs a full-history chain scan. if coordinationBlock < DepositSweepEveryWindowActivationBlock { if windowIndex%frequencyWindows == 0 { - actions = append(actions, ActionDepositSweep) - } - - if windowIndex%frequencyWindows == 0 { - actions = append(actions, ActionMovedFundsSweep) - } - - if windowIndex%frequencyWindows == 0 { - actions = append(actions, ActionMovingFunds) + actions = append( + actions, + ActionDepositSweep, + ActionMovedFundsSweep, + ActionMovingFunds, + ) } } else { actions = append(actions, ActionDepositSweep) diff --git a/pkg/tbtc/coordination_window_metrics.go b/pkg/tbtc/coordination_window_metrics.go index 2b57fc4c52..dd510df0bf 100644 --- a/pkg/tbtc/coordination_window_metrics.go +++ b/pkg/tbtc/coordination_window_metrics.go @@ -2,6 +2,7 @@ package tbtc import ( "fmt" + "sort" "sync" "time" @@ -218,16 +219,13 @@ func (cwm *coordinationWindowMetrics) recordWalletCoordination( wm.WalletsFailed++ } - // Track leader leaderStr := leader.String() wm.Leaders[leaderStr]++ - // Track action type if actionType != "" { wm.ActionTypes[actionType]++ } - // Track faults faultDetails := make([]faultDetail, 0, len(faults)) for _, fault := range faults { faultTypeStr := fault.faultType.String() @@ -285,13 +283,9 @@ func (cwm *coordinationWindowMetrics) GetRecentWindows(limit int) []*windowMetri } // Sort in descending order (most recent first) - for i := 0; i < len(indices)-1; i++ { - for j := i + 1; j < len(indices); j++ { - if indices[i] < indices[j] { - indices[i], indices[j] = indices[j], indices[i] - } - } - } + sort.Slice(indices, func(i, j int) bool { + return indices[i] > indices[j] + }) // Limit results if limit > 0 && limit < len(indices) { @@ -321,13 +315,9 @@ func (cwm *coordinationWindowMetrics) cleanupOldWindows() { } // Sort in ascending order (oldest first) - for i := 0; i < len(indices)-1; i++ { - for j := i + 1; j < len(indices); j++ { - if indices[i] > indices[j] { - indices[i], indices[j] = indices[j], indices[i] - } - } - } + sort.Slice(indices, func(i, j int) bool { + return indices[i] < indices[j] + }) // Remove oldest windows windowsToRemove := len(cwm.windows) - int(cwm.maxWindowsToTrack) diff --git a/pkg/tbtc/coordination_window_metrics_test.go b/pkg/tbtc/coordination_window_metrics_test.go index 274613f765..8d5ceb92fc 100644 --- a/pkg/tbtc/coordination_window_metrics_test.go +++ b/pkg/tbtc/coordination_window_metrics_test.go @@ -346,3 +346,91 @@ func TestCoordinationWindowMetrics_Concurrent(t *testing.T) { _ = cwm.GetSummary() _ = cwm.GetRecentWindows(5) } + +// TestCleanupOldWindows_BoundsMapSize inserts 2000 windows into a store capped +// at 100 and asserts the map never exceeds the cap. This guards against a +// regression where cleanupOldWindows stops enforcing the bound, causing +// unbounded memory growth on long-running nodes. +func TestCleanupOldWindows_BoundsMapSize(t *testing.T) { + const maxWindows = 100 + cwm := newTestWindowMetrics(maxWindows) + leader := chain.Address("0xleader") + + for i := uint64(1); i <= 2000; i++ { + window := newCoordinationWindow(i * 900) + cwm.recordWalletCoordination(window, [20]byte{byte(i % 256)}, leader, "Heartbeat", true, 0, nil, nil) + } + + summary := cwm.GetSummary() + if int(summary.TotalWindows) > maxWindows { + t.Errorf( + "cleanupOldWindows not enforcing bound: got %d windows, want <= %d", + summary.TotalWindows, maxWindows, + ) + } +} + +// --- Benchmarks --- + +func populateWindowMetrics(b *testing.B, cwm *coordinationWindowMetrics, n int) { + b.Helper() + leader := chain.Address("0xleader") + for i := uint64(1); i <= uint64(n); i++ { + window := newCoordinationWindow(i * 900) + cwm.recordWalletCoordination(window, [20]byte{}, leader, "Heartbeat", true, 0, nil, nil) + } +} + +func BenchmarkGetRecentWindows_100Windows(b *testing.B) { + cwm := newTestWindowMetrics(200) + populateWindowMetrics(b, cwm, 100) + b.ResetTimer() + for range b.N { + _ = cwm.GetRecentWindows(100) + } +} + +func BenchmarkGetRecentWindows_1000Windows(b *testing.B) { + cwm := newTestWindowMetrics(2000) + populateWindowMetrics(b, cwm, 1000) + b.ResetTimer() + for range b.N { + _ = cwm.GetRecentWindows(1000) + } +} + +func BenchmarkGetSummary_100Windows(b *testing.B) { + cwm := newTestWindowMetrics(200) + populateWindowMetrics(b, cwm, 100) + b.ResetTimer() + for range b.N { + _ = cwm.GetSummary() + } +} + +func BenchmarkGetSummary_1000Windows(b *testing.B) { + cwm := newTestWindowMetrics(2000) + populateWindowMetrics(b, cwm, 1000) + b.ResetTimer() + for range b.N { + _ = cwm.GetSummary() + } +} + +// BenchmarkCleanupOldWindows_1000Windows measures the O(n^2) bubble-sort +// cleanup pass when the store holds 1000 windows and needs to evict down to +// 900. This catches regressions in the cleanup algorithm before they affect +// long-running nodes. +func BenchmarkCleanupOldWindows_1000Windows(b *testing.B) { + const maxWindows uint64 = 900 + + for range b.N { + b.StopTimer() + cwm := newTestWindowMetrics(maxWindows) + for i := uint64(1); i <= 1000; i++ { + cwm.windows[i] = &windowMetrics{WindowIndex: i} + } + b.StartTimer() + cwm.cleanupOldWindows() + } +} diff --git a/pkg/tbtc/deposit_sweep.go b/pkg/tbtc/deposit_sweep.go index e58ee88e25..53643fc7a4 100644 --- a/pkg/tbtc/deposit_sweep.go +++ b/pkg/tbtc/deposit_sweep.go @@ -50,15 +50,45 @@ const ( // the transaction is known on the Bitcoin chain. This delay is needed // as spreading the transaction over the Bitcoin network takes time. depositSweepBroadcastCheckDelay = 1 * time.Minute + // DepositScriptByteSize mirrors tbtcpg.DepositScriptByteSize, the worst-case + // deposit script size used to estimate the sweep transaction virtual size. + // Exported for the external tbtc_test package to compare it against the + // canonical tbtcpg value (guarded by TestSweepFeeConstantsMirrorTbtcpg). + DepositScriptByteSize = 126 ) +// DepositKey identifies a deposit by the outpoint of its funding transaction. +// +// Note: DepositKey is a named type; it replaced the anonymous struct +// previously used inline as the element type of +// DepositSweepProposal.DepositsKeys. Go does not allow assigning an +// anonymous-struct-typed slice literal to a named-struct-typed slice field, +// so code outside this module that builds a DepositSweepProposal from the +// old anonymous struct literal must switch to constructing []DepositKey +// values instead. +// +// Migrating from the old anonymous-struct literal: +// +// // Before: +// DepositsKeys: []struct{ +// FundingTxHash: chain.Hash(...), +// FundingOutputIndex: 0, +// }{...}, +// +// // After: +// DepositsKeys: []DepositKey{ +// {FundingTxHash: chain.Hash(...), FundingOutputIndex: 0}, +// ... +// }, +type DepositKey struct { + FundingTxHash bitcoin.Hash + FundingOutputIndex uint32 +} + // DepositSweepProposal represents a deposit sweep proposal issued by a // wallet's coordination leader. type DepositSweepProposal struct { - DepositsKeys []struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - } + DepositsKeys []DepositKey SweepTxFee *big.Int DepositsRevealBlocks []*big.Int } @@ -466,6 +496,42 @@ func ValidateDepositSweepProposal( "deposit sweep proposal is valid", ) + // Follower-side soft check on the proposed fee. The on-chain + // WalletProposalValidator only bounds the sweep fee from above, not below, + // so a misbehaving or unpatched leader can propose a fee at the ~1 sat/vByte + // relay floor that this node would otherwise sign - the same underpricing + // that jams the wallet (see threshold-network/keep-core#4171). We recompute + // the safe minimum (applying the 25% safety buffer that + // tbtcpg.applyWalletTxFeeFloor would also enforce on the leader side) and + // warn if the proposal is below it. + // + // This is intentionally log-only, not a rejection: rejecting a below-floor + // proposal here would, during a mixed-version rollout, split signers (patched + // nodes reject, unpatched nodes sign) and could stall signing. Hard + // enforcement belongs on-chain in the WalletProposalValidator, or behind a + // coordinated all-nodes upgrade. The threshold is recomputed in + // warnIfProposedWalletTxFeeBelowBufferedFloor (proposal_fee_check.go); keep + // the size estimator below in sync with the leader-side estimator in + // tbtcpg/deposit_sweep.go. + if sweepTxSize, sizeErr := bitcoin.NewTransactionSizeEstimator(). + AddPublicKeyHashInputs(1, true). + AddScriptHashInputs(len(proposal.DepositsKeys), DepositScriptByteSize, true). + AddPublicKeyHashOutputs(1, true). + VirtualSize(); sizeErr != nil { + validateProposalLogger.Warnf( + "cannot estimate sweep tx size for the fee sanity check: [%v]", + sizeErr, + ) + } else { + warnIfProposedWalletTxFeeBelowBufferedFloor( + validateProposalLogger, + MinWalletTxSatPerVByteFee, + sweepTxSize, + proposal.SweepTxFee, + "deposit sweep", + ) + } + deposits := make([]*Deposit, len(depositExtraInfo)) for i, dei := range depositExtraInfo { deposits[i] = dei.Deposit diff --git a/pkg/tbtc/deposit_sweep_test.go b/pkg/tbtc/deposit_sweep_test.go index 00f501b827..d390f040c9 100644 --- a/pkg/tbtc/deposit_sweep_test.go +++ b/pkg/tbtc/deposit_sweep_test.go @@ -42,10 +42,7 @@ func TestDepositSweepAction_Execute(t *testing.T) { } // depositsKeys will be needed to build the proposal instance. - depositsKeys := make([]struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }, len(scenario.Deposits)) + depositsKeys := make([]DepositKey, len(scenario.Deposits)) // depositsExtraInfo will be needed to perform on-chain proposal // validation. @@ -66,10 +63,7 @@ func TestDepositSweepAction_Execute(t *testing.T) { t.Fatal(err) } - depositsKeys[i] = struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }{ + depositsKeys[i] = DepositKey{ FundingTxHash: fundingTxHash, FundingOutputIndex: fundingOutputIndex, } @@ -319,3 +313,149 @@ func TestAssembleDepositSweepTransaction(t *testing.T) { }) } } + +// capturingLogger wraps testutils.MockLogger and records Warnf calls for +// assertions. +type capturingLogger struct { + testutils.MockLogger + warnings []string +} + +func (cl *capturingLogger) Warnf(format string, args ...interface{}) { + cl.warnings = append(cl.warnings, fmt.Sprintf(format, args...)) +} + +// depositSweepFeeCheckChain is a minimal stub satisfying the chain interface +// ValidateDepositSweepProposal requires. Its ValidateDepositSweepProposal +// unconditionally reports the proposal as valid, and its other two methods +// are never invoked for a proposal with no deposits. This isolates the +// follower-side sweep-fee soft check (deposit_sweep.go, below the +// "calling chain for proposal validation" log line) from on-chain proposal +// validation and deposit-lookup concerns that the soft check does not +// depend on. +type depositSweepFeeCheckChain struct{} + +func (depositSweepFeeCheckChain) PastDepositRevealedEvents( + *DepositRevealedEventFilter, +) ([]*DepositRevealedEvent, error) { + return nil, nil +} + +func (depositSweepFeeCheckChain) ValidateDepositSweepProposal( + [20]byte, + *DepositSweepProposal, + []struct { + *Deposit + FundingTx *bitcoin.Transaction + }, +) error { + return nil +} + +func (depositSweepFeeCheckChain) GetDepositRequest( + bitcoin.Hash, + uint32, +) (*DepositChainRequest, bool, error) { + return nil, false, nil +} + +// TestValidateDepositSweepProposal_SweepFeeSoftCheck exercises the +// follower-side soft check on the leader-proposed sweep fee. The check is +// log-only by design (see threshold-network/keep-core#4171): it must warn +// about an unsafe fee but must never fail proposal validation because of it. +func TestValidateDepositSweepProposal_SweepFeeSoftCheck(t *testing.T) { + var walletPublicKeyHash [20]byte + stubChain := depositSweepFeeCheckChain{} + btcChain := newLocalBitcoinChain() + + // Compute the exact safe-minimum fee for a proposal with no deposits + // using the same estimator call the soft check itself performs + // (deposit_sweep.go), so the boundary between "below" and "at/above" the + // floor is derived rather than hardcoded. The floor is the buffered + // minimum that warnIfProposedWalletTxFeeBelowBufferedFloor + // (proposal_fee_check.go) recomputes from the bare sweep floor using the + // WalletTxFeeBufferPercent mirror (the + // 25% safety buffer that tbtcpg.applyWalletTxFeeFloor also reapplies on + // the leader side). Keeping the formula here in sync with the helper is + // exactly the property this test exercises. + sweepTxSize, err := bitcoin.NewTransactionSizeEstimator(). + AddPublicKeyHashInputs(1, true). + AddScriptHashInputs(0, DepositScriptByteSize, true). + AddPublicKeyHashOutputs(1, true). + VirtualSize() + if err != nil { + t.Fatal(err) + } + bufferedRate := (MinWalletTxSatPerVByteFee*(100+WalletTxFeeBufferPercent) + + 99) / 100 + minBufferedSweepTxFee := big.NewInt(int64(bufferedRate) * sweepTxSize) + + scenarios := map[string]struct { + fee *big.Int + expectWarn bool + }{ + "fee below the safe buffered minimum": { + fee: new(big.Int).Sub(minBufferedSweepTxFee, big.NewInt(1)), + expectWarn: true, + }, + "fee at the safe buffered minimum": { + fee: minBufferedSweepTxFee, + expectWarn: false, + }, + "fee above the safe buffered minimum": { + fee: new(big.Int).Add(minBufferedSweepTxFee, big.NewInt(1000)), + expectWarn: false, + }, + // A nil SweepTxFee cannot occur on the real production path (see the + // comment on the nil case in deposit_sweep.go): the on-chain + // WalletProposalValidator call a few lines above the soft check + // already ABI-packs the fee and panics first, and wire + // deserialization always constructs a non-nil value. This scenario + // exists to lock in the defense-in-depth behavior for callers, like + // this test's stub chain, that can hand the soft check a nil fee + // directly. + "nil fee from a test/mock caller": { + fee: nil, + expectWarn: true, + }, + } + + for name, scenario := range scenarios { + t.Run(name, func(t *testing.T) { + proposal := &DepositSweepProposal{ + SweepTxFee: scenario.fee, + } + + logger := &capturingLogger{} + + _, err := ValidateDepositSweepProposal( + logger, + walletPublicKeyHash, + proposal, + 0, + stubChain, + btcChain, + ) + if err != nil { + t.Fatalf( + "expected the log-only soft check to never fail "+ + "validation; got error: [%v]", + err, + ) + } + + gotWarn := len(logger.warnings) > 0 + if gotWarn != scenario.expectWarn { + t.Errorf( + "unexpected warning presence for fee [%v]\n"+ + "expected warning: %v\nactual warning: %v\n"+ + "captured warnings: %v", + scenario.fee, + scenario.expectWarn, + gotWarn, + logger.warnings, + ) + } + }) + } +} diff --git a/pkg/tbtc/dkg.go b/pkg/tbtc/dkg.go index 4385f25fd1..a073cd47b8 100644 --- a/pkg/tbtc/dkg.go +++ b/pkg/tbtc/dkg.go @@ -506,7 +506,7 @@ func (de *dkgExecutor) registerSigner( de.groupParameters, ) if err != nil { - return nil, fmt.Errorf("failed to resolve final signing group members") + return nil, fmt.Errorf("failed to resolve final signing group members: [%w]", err) } // Just like the final and original group may differ, the diff --git a/pkg/tbtc/marshaling.go b/pkg/tbtc/marshaling.go index 02b5195e45..5483b43d0d 100644 --- a/pkg/tbtc/marshaling.go +++ b/pkg/tbtc/marshaling.go @@ -326,10 +326,7 @@ func (dsp *DepositSweepProposal) Unmarshal(bytes []byte) error { } depositsKeys := make( - []struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }, + []DepositKey, len(pbMsg.DepositsKeys), ) for i, depositKey := range pbMsg.DepositsKeys { @@ -344,10 +341,7 @@ func (dsp *DepositSweepProposal) Unmarshal(bytes []byte) error { ) } - depositsKeys[i] = struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }{ + depositsKeys[i] = DepositKey{ FundingTxHash: hash, FundingOutputIndex: depositKey.FundingOutputIndex, } diff --git a/pkg/tbtc/marshaling_test.go b/pkg/tbtc/marshaling_test.go index 32b6977f0a..6fcbdcf831 100644 --- a/pkg/tbtc/marshaling_test.go +++ b/pkg/tbtc/marshaling_test.go @@ -186,10 +186,7 @@ func TestCoordinationMessage_MarshalingRoundtrip(t *testing.T) { }, "with deposit sweep proposal": { proposal: &DepositSweepProposal{ - DepositsKeys: []struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }{ + DepositsKeys: []DepositKey{ { FundingTxHash: parseHash("709b55bd3da0f5a838125bd0ee20c5bfdd7caba173912d4281cae816b79a201b"), FundingOutputIndex: 0, diff --git a/pkg/tbtc/moving_funds.go b/pkg/tbtc/moving_funds.go index ef246d29aa..2b713edbdc 100644 --- a/pkg/tbtc/moving_funds.go +++ b/pkg/tbtc/moving_funds.go @@ -350,9 +350,60 @@ func ValidateMovingFundsProposal( validateProposalLogger.Infof("moving funds proposal is valid") + // Follower-side soft check on the proposed fee. The on-chain + // WalletProposalValidator only bounds the moving-funds fee from above, not + // below, so a misbehaving or unpatched leader can propose a fee at the + // ~1 sat/vByte relay floor that this node would otherwise sign - the same + // underpricing that jams the wallet (see + // threshold-network/keep-core#4171). We recompute the safe minimum + // (applying the 25% safety buffer that tbtcpg.applyWalletTxFeeFloor would + // also enforce on the leader side) and warn if the proposal is below it. + // + // This is intentionally log-only, not a rejection: rejecting a below-floor + // proposal here would, during a mixed-version rollout, split signers + // (patched nodes reject, unpatched nodes sign) and could stall signing. + // Hard enforcement belongs on-chain in the WalletProposalValidator, or + // behind a coordinated all-nodes upgrade. The threshold is recomputed in + // warnIfProposedWalletTxFeeBelowBufferedFloor (proposal_fee_check.go); + // keep the size estimator below in sync with the leader-side estimator + // in tbtcpg/moving_funds.go. + if movingFundsTxSize, sizeErr := bitcoin.NewTransactionSizeEstimator(). + AddPublicKeyHashInputs(1, true). + AddPublicKeyHashOutputs(len(proposal.TargetWallets), true). + VirtualSize(); sizeErr != nil { + validateProposalLogger.Warnf( + "cannot estimate moving funds tx size for the fee sanity "+ + "check: [%v]", + sizeErr, + ) + } else { + warnIfProposedWalletTxFeeBelowBufferedFloor( + validateProposalLogger, + MinWalletTxSatPerVByteFee, + movingFundsTxSize, + proposal.MovingFundsTxFee, + "moving funds", + ) + } + return nil } +// movingFundsSafetyMarginChain is the chain interface required to evaluate the +// moving funds safety margin and to determine whether a wallet is a pending +// moving funds target. +type movingFundsSafetyMarginChain interface { + BlockCounter() (chain.BlockCounter, error) + + GetWallet(walletPublicKeyHash [20]byte) (*WalletChainData, error) + + GetMovingFundsParameters() (MovingFundsParameters, error) + + PastMovingFundsCommitmentSubmittedEvents( + filter *MovingFundsCommitmentSubmittedEventFilter, + ) ([]*MovingFundsCommitmentSubmittedEvent, error) +} + // ValidateMovingFundsSafetyMargin checks if the moving funds safety margin // is in force. // @@ -370,17 +421,7 @@ func ValidateMovingFundsProposal( // wallets. In this case a longer safety margin should be used. func ValidateMovingFundsSafetyMargin( walletPublicKeyHash [20]byte, - chain interface { - BlockCounter() (chain.BlockCounter, error) - - GetWallet(walletPublicKeyHash [20]byte) (*WalletChainData, error) - - GetMovingFundsParameters() (MovingFundsParameters, error) - - PastMovingFundsCommitmentSubmittedEvents( - filter *MovingFundsCommitmentSubmittedEventFilter, - ) ([]*MovingFundsCommitmentSubmittedEvent, error) - }, + chain movingFundsSafetyMarginChain, ) error { // In most cases the safety margin of 24 hours should be enough. It will // allow the wallet to sweep the last deposits that were made before the @@ -447,17 +488,7 @@ func (mfa *movingFundsAction) actionType() WalletActionType { func isWalletPendingMovingFundsTarget( walletPublicKeyHash [20]byte, - chain interface { - BlockCounter() (chain.BlockCounter, error) - - GetWallet(walletPublicKeyHash [20]byte) (*WalletChainData, error) - - GetMovingFundsParameters() (MovingFundsParameters, error) - - PastMovingFundsCommitmentSubmittedEvents( - filter *MovingFundsCommitmentSubmittedEventFilter, - ) ([]*MovingFundsCommitmentSubmittedEvent, error) - }, + chain movingFundsSafetyMarginChain, ) (bool, error) { blockCounter, err := chain.BlockCounter() if err != nil { diff --git a/pkg/tbtc/proposal_fee_check.go b/pkg/tbtc/proposal_fee_check.go new file mode 100644 index 0000000000..85cb58b10c --- /dev/null +++ b/pkg/tbtc/proposal_fee_check.go @@ -0,0 +1,131 @@ +package tbtc + +import ( + "math/big" + + "github.com/ipfs/go-log/v2" +) + +// warnIfProposedWalletTxFeeBelowBufferedFloor is the follower-side soft +// (log-only, never rejects) check used by every wallet-tx proposal +// validator in this package: deposit sweep, redemption, and moving +// funds. It compares the leader's proposed total fee against the safe +// buffered minimum that tbtcpg.applyWalletTxFeeFloor would enforce for +// the same bare floor and vsize, so patched followers warn at the same +// threshold the leader was supposed to produce. The buffer is reapplied +// here because a leader proposing exactly-at-floor would otherwise slip +// past a bare-floor check and undersell the tx. +// +// The floor and the buffer percentage are read from the canonical +// package vars MinWalletTxSatPerVByteFee / WalletTxFeeBufferPercent +// (declared in tbtc.go), which Initialize populates from Config. +// tbtcpg.applyWalletTxFeeFloor reads the same vars, so a single source +// of truth is enforced - tuning the policy from the operator side +// automatically tunes both the leader-side floor application and the +// follower-side soft check. +// +// The threshold is computed with arbitrary-precision big.Int arithmetic +// (the proposed fee is already *big.Int, so this avoids both an int64 +// overflow on the buffered-rate product and a lossy conversion of +// minBufferedFee back into int64). The leader-side floor helper applies +// the same buffer formula with checked-arithmetic guards and returns +// ErrMaxFeeTooLow on implausible inputs; on such inputs the follower +// just sees a buffered fee above any reasonable proposed total and the +// check stays quiet, which is the same observable behavior as a leader +// that refused to broadcast. +// +// This is intentionally log-only, not a rejection: rejecting a +// below-floor proposal here would, during a mixed-version rollout, +// split signers (patched nodes reject, unpatched nodes sign) and could +// stall signing. Hard enforcement belongs on-chain in the +// WalletProposalValidator, or behind a coordinated all-nodes upgrade; +// see threshold-network/keep-core#4171. +// +// satPerVByteFloor is the bare minimum per-vByte fee rate (sat/vByte); +// pass MinWalletTxSatPerVByteFee for sweep/redemption/moving-funds +// validators. +// txVsize is the estimated transaction virtual size in vBytes, as +// returned by the caller-specific bitcoin.TransactionSizeEstimator. +// proposedFee is the leader's proposed total fee in satoshis; nil is +// treated as "no fee set" (defense-in-depth for test/mock chains; +// unreachable on the real production path where on-chain validation has +// already ABI-packed the fee and panicked on nil). +// actionLabel identifies the proposal type in the log message +// (e.g. "deposit sweep", "redemption", "moving funds"). +func warnIfProposedWalletTxFeeBelowBufferedFloor( + logger log.StandardLogger, + satPerVByteFloor int64, + txVsize int64, + proposedFee *big.Int, + actionLabel string, +) { + // Silently skip on degenerate inputs; the caller has already surfaced + // the underlying estimation error (size estimator failure, nil fee + // that panicked in on-chain validation, etc.). This helper never + // escalates failures; it only adds a warning when the inputs are + // usable. + if satPerVByteFloor <= 0 || txVsize <= 0 { + return + } + if WalletTxFeeBufferPercent < 0 { + return + } + + // Compute the buffered threshold with arbitrary-precision arithmetic + // so an operator-tuned policy (large satPerVByteFloor or buffer + // ratio) cannot overflow int64 in this helper. The leader-side + // tbtcpg.applyWalletTxFeeFloor applies the same buffer formula but + // with checked-arithmetic guards and returns an error on the same + // implausible inputs; here the threshold simply ends up large enough + // that no realistic proposed fee trips the warning. + satPerVByte := big.NewInt(satPerVByteFloor) + numerator := big.NewInt(100 + WalletTxFeeBufferPercent) + denominator := big.NewInt(100) + delta := big.NewInt(99) + + // bufferedRate = ceil(satPerVByteFloor * (100+Percent) / 100). + bufferedRate := new(big.Int).Mul(satPerVByte, numerator) + bufferedRate.Add(bufferedRate, delta) + bufferedRate.Quo(bufferedRate, denominator) + + // minBufferedFee = bufferedRate * txVsize. + minBufferedFee := new(big.Int).Mul(bufferedRate, big.NewInt(txVsize)) + + switch { + // This branch is defense-in-depth for test/mock chain implementations + // and is not expected to be reachable on the real production path: + // by the time control reaches the validator, on-chain + // WalletProposalValidator has already ABI-packed the fee and panics + // on a nil *big.Int before this code ever runs. Likewise, a proposal + // decoded off the wire (Unmarshal in marshaling.go) always + // constructs the fee via new(big.Int).SetBytes(...), which never + // yields nil. + case proposedFee == nil: + logger.Warnf( + "%s proposal has no tx fee set; expected at least the safe "+ + "buffered minimum [%v] ([%v] buffered sat/vByte = [%d] "+ + "bare floor * %d%% buffer * [%d] vByte)", + actionLabel, + minBufferedFee, + bufferedRate, + satPerVByteFloor, + WalletTxFeeBufferPercent, + txVsize, + ) + case proposedFee.Cmp(minBufferedFee) < 0: + logger.Warnf( + "proposed %s tx fee [%v] is below the safe buffered minimum "+ + "[%v] ([%v] buffered sat/vByte = [%d] bare floor * %d%% "+ + "buffer * [%d] vByte); the leader may be underpricing the "+ + "tx, which risks it getting stuck in the mempool and "+ + "jamming the wallet", + actionLabel, + proposedFee, + minBufferedFee, + bufferedRate, + satPerVByteFloor, + WalletTxFeeBufferPercent, + txVsize, + ) + } +} diff --git a/pkg/tbtc/proposal_fee_check_test.go b/pkg/tbtc/proposal_fee_check_test.go new file mode 100644 index 0000000000..5fe3cb2b8b --- /dev/null +++ b/pkg/tbtc/proposal_fee_check_test.go @@ -0,0 +1,172 @@ +package tbtc + +import ( + "fmt" + "math" + "math/big" + "testing" + + "github.com/ipfs/go-log/v2" +) + +// capturingFeeCheckLogger is a test double for log.StandardLogger that +// records every Warnf call so the follower-side soft check can be +// asserted on directly. +type capturingFeeCheckLogger struct { + warnings []string +} + +func (cl *capturingFeeCheckLogger) Warnf(format string, args ...interface{}) { + cl.warnings = append(cl.warnings, fmt.Sprintf(format, args...)) +} + +func (cl *capturingFeeCheckLogger) Errorf(format string, args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Infof(format string, args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Debugf(format string, args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Warn(args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Error(args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Info(args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Debug(args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Fatal(args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Fatalf(format string, args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Panic(args ...interface{}) {} +func (cl *capturingFeeCheckLogger) Panicf(format string, args ...interface{}) {} + +// TestWarnIfProposedWalletTxFeeBelowBufferedFloor exercises the +// follower-side soft check end-to-end. The buffered threshold is +// derived from the policy vars + txVsize the same way the helper does +// (with big.Int math) so the boundary between "warn" and "no warn" +// is computed, not hardcoded. +func TestWarnIfProposedWalletTxFeeBelowBufferedFloor(t *testing.T) { + const vsize = 200 + + expectedBufferedRate := new(big.Int).Mul( + big.NewInt(MinWalletTxSatPerVByteFee), + big.NewInt(100+WalletTxFeeBufferPercent), + ) + expectedBufferedRate.Add( + expectedBufferedRate, + big.NewInt(99), + ) + expectedBufferedRate.Quo( + expectedBufferedRate, + big.NewInt(100), + ) + expectedMinBufferedFee := new(big.Int).Mul( + expectedBufferedRate, + big.NewInt(vsize), + ) + + scenarios := map[string]struct { + fee *big.Int + expectWarn bool + }{ + "fee below the safe buffered minimum": { + fee: new(big.Int).Sub(expectedMinBufferedFee, big.NewInt(1)), + expectWarn: true, + }, + "fee at the safe buffered minimum": { + fee: expectedMinBufferedFee, + expectWarn: false, + }, + "fee above the safe buffered minimum": { + fee: new(big.Int).Add(expectedMinBufferedFee, big.NewInt(1000)), + expectWarn: false, + }, + "nil fee from a test/mock caller": { + fee: nil, + expectWarn: true, + }, + } + + for name, scenario := range scenarios { + t.Run(name, func(t *testing.T) { + logger := &capturingFeeCheckLogger{} + + warnIfProposedWalletTxFeeBelowBufferedFloor( + logger, + MinWalletTxSatPerVByteFee, + vsize, + scenario.fee, + "test", + ) + + gotWarn := len(logger.warnings) > 0 + if gotWarn != scenario.expectWarn { + t.Errorf( + "unexpected warning presence for fee [%v]\n"+ + "expected warning: %v\nactual warning: %v\n"+ + "captured warnings: %v", + scenario.fee, + scenario.expectWarn, + gotWarn, + logger.warnings, + ) + } + }) + } +} + +// TestWarnIfProposedWalletTxFeeBelowBufferedFloor_OverflowBoundary locks +// in the big.Int arithmetic path: with a policy that would overflow +// int64 in naive (rate*Numerator or bufferedRate*txVsize) math, the +// helper still computes a correct threshold and warns only for fees +// that are actually below it. The leader-side tbtcpg.applyWalletTxFeeFloor +// rejects the same implausible input with a checked-arithmetic error; +// the follower-side helper has no MaxInt64 ceiling, so a leader that +func TestWarnIfProposedWalletTxFeeBelowBufferedFloor_OverflowBoundary(t *testing.T) { + const vsize = 200 + + originalFloor := MinWalletTxSatPerVByteFee + originalPercent := WalletTxFeeBufferPercent + t.Cleanup(func() { + MinWalletTxSatPerVByteFee = originalFloor + WalletTxFeeBufferPercent = originalPercent + }) + + // Percent set so numerator (100+Percent) is MaxInt64, so + // rate * numerator would overflow int64 in naive math. The big.Int + // path should compute a threshold of satPerVByteFloor * MaxInt64 + // sat/vByte / 100 * vsize vByte = 5 * MaxInt64 / 100 * 200 total, + // which no int64 fee can ever reach, so every realistic proposal + // trips the warning. + MinWalletTxSatPerVByteFee = 5 + WalletTxFeeBufferPercent = math.MaxInt64 - 100 + + logger := &capturingFeeCheckLogger{} + warnIfProposedWalletTxFeeBelowBufferedFloor( + logger, + MinWalletTxSatPerVByteFee, + vsize, + big.NewInt(1_000_000_000), // 1e9 sat, normal fee + "test", + ) + + if len(logger.warnings) == 0 { + t.Errorf( + "expected a warning for a normal fee under a MaxInt64-buffered " + + "threshold (the buffered minimum exceeds any int64 fee, so " + + "every realistic proposal trips the warning); got no warnings", + ) + } + + // And a nil fee still warns ... + logger = &capturingFeeCheckLogger{} + warnIfProposedWalletTxFeeBelowBufferedFloor( + logger, + MinWalletTxSatPerVByteFee, + vsize, + nil, + "test", + ) + if len(logger.warnings) == 0 { + t.Errorf( + "expected a warning for nil proposed fee regardless of " + + "buffered threshold", + ) + } +} + +// Compile-time check that capturingFeeCheckLogger satisfies the +// log.StandardLogger interface used by warnIfProposedWalletTxFeeBelowBufferedFloor. +var _ log.StandardLogger = (*capturingFeeCheckLogger)(nil) diff --git a/pkg/tbtc/redemption.go b/pkg/tbtc/redemption.go index 8be91350c9..6037092ec1 100644 --- a/pkg/tbtc/redemption.go +++ b/pkg/tbtc/redemption.go @@ -348,6 +348,66 @@ func ValidateRedemptionProposal( "redemption proposal is valid", ) + // Follower-side soft check on the proposed fee. The on-chain + // WalletProposalValidator only bounds the redemption fee from above, not + // below, so a misbehaving or unpatched leader can propose a fee at the + // ~1 sat/vByte relay floor that this node would otherwise sign - the same + // underpricing that jams the wallet (see + // threshold-network/keep-core#4171). We recompute the safe minimum + // (applying the 25% safety buffer that tbtcpg.applyWalletTxFeeFloor would + // also enforce on the leader side) and warn if the proposal is below it. + // + // This is intentionally log-only, not a rejection: rejecting a below-floor + // proposal here would, during a mixed-version rollout, split signers + // (patched nodes reject, unpatched nodes sign) and could stall signing. + // Hard enforcement belongs on-chain in the WalletProposalValidator, or + // behind a coordinated all-nodes upgrade. The threshold is recomputed in + // warnIfProposedWalletTxFeeBelowBufferedFloor (proposal_fee_check.go); + // keep the size estimator below in sync with the leader-side estimator + // in tbtcpg/redemptions.go. + sizeEstimator := bitcoin.NewTransactionSizeEstimator(). + AddPublicKeyHashInputs(1, true). + AddPublicKeyHashOutputs(1, true) + canEstimate := true + for _, script := range proposal.RedeemersOutputScripts { + switch bitcoin.GetScriptType(script) { + case bitcoin.P2PKHScript: + sizeEstimator.AddPublicKeyHashOutputs(1, false) + case bitcoin.P2WPKHScript: + sizeEstimator.AddPublicKeyHashOutputs(1, true) + case bitcoin.P2SHScript: + sizeEstimator.AddScriptHashOutputs(1, false) + case bitcoin.P2WSHScript: + sizeEstimator.AddScriptHashOutputs(1, true) + default: + validateProposalLogger.Warnf( + "cannot estimate redemption tx size for the fee sanity " + + "check: non-standard redeemer output script type", + ) + canEstimate = false + } + if !canEstimate { + break + } + } + if canEstimate { + if redemptionTxSize, sizeErr := sizeEstimator.VirtualSize(); sizeErr != nil { + validateProposalLogger.Warnf( + "cannot estimate redemption tx size for the fee sanity "+ + "check: [%v]", + sizeErr, + ) + } else { + warnIfProposedWalletTxFeeBelowBufferedFloor( + validateProposalLogger, + MinWalletTxSatPerVByteFee, + redemptionTxSize, + proposal.RedemptionTxFee, + "redemption", + ) + } + } + requests := make([]*RedemptionRequest, len(proposal.RedeemersOutputScripts)) for i, script := range proposal.RedeemersOutputScripts { requestDisplayIndex := fmt.Sprintf( diff --git a/pkg/tbtc/sweep_fee_sync_test.go b/pkg/tbtc/sweep_fee_sync_test.go new file mode 100644 index 0000000000..fcfa901c68 --- /dev/null +++ b/pkg/tbtc/sweep_fee_sync_test.go @@ -0,0 +1,41 @@ +package tbtc_test + +import ( + "testing" + + "github.com/keep-network/keep-core/pkg/tbtc" + "github.com/keep-network/keep-core/pkg/tbtcpg" +) + +// TestSweepFeeConstantsMirrorTbtcpg guards the cross-package mirrors of +// constants that pkg/tbtc duplicates from pkg/tbtcpg. The follower-side +// soft check (threshold-network/keep-core#4171) uses the same +// DepositScriptByteSize as the leader-side estimator, so a drift would +// produce a sweep tx estimate that disagrees with what the leader built. +// +// The minimum-floor and buffer-percent constants are NOT mirrored: they +// live as canonical exported vars in pkg/tbtc (MinWalletTxSatPerVByteFee, +// WalletTxFeeBufferPercent), which pkg/tbtcpg reads directly via +// tbtc.X. The operator-tunable runtime policy has a single source of +// truth, so a drift here would be a build error rather than a silent +// inconsistency. +// +// This test lives in the external tbtc_test package precisely because +// that package can import both pkg/tbtc and pkg/tbtcpg without forming +// the cycle. It compares the two actual constants directly - not +// against hand-copied literals - so it fails whenever the pkg/tbtc +// mirror and the canonical tbtcpg value drift apart, regardless of +// which side was changed. A literal-based guard could be defeated by +// updating tbtcpg and the literal together while forgetting the +// pkg/tbtc mirror; comparing the live values closes that gap. +func TestSweepFeeConstantsMirrorTbtcpg(t *testing.T) { + if tbtc.DepositScriptByteSize != tbtcpg.DepositScriptByteSize { + t.Errorf( + "tbtc.DepositScriptByteSize [%d] has drifted from the canonical "+ + "tbtcpg.DepositScriptByteSize [%d]; the follower soft check would "+ + "estimate the sweep tx size incorrectly", + tbtc.DepositScriptByteSize, + tbtcpg.DepositScriptByteSize, + ) + } +} diff --git a/pkg/tbtc/tbtc.go b/pkg/tbtc/tbtc.go index fa009348b9..715066fd7c 100644 --- a/pkg/tbtc/tbtc.go +++ b/pkg/tbtc/tbtc.go @@ -80,10 +80,56 @@ const ( DefaultPreParamsGenerationTimeout = 2 * time.Minute DefaultPreParamsGenerationDelay = 10 * time.Second DefaultPreParamsGenerationConcurrency = 1 + + // DefaultWalletTxSatPerVByteFloor is the default minimum fee rate, in + // sat/vByte, applied to wallet Bitcoin transactions (deposit sweeps, + // redemptions, moving funds, moved funds sweeps). The default keeps + // the fee safely above the 1 sat/vByte relay floor while remaining + // far below the Bridge's maximum fee. See + // threshold-network/keep-core#4171. + DefaultWalletTxSatPerVByteFloor = 5 + // DefaultWalletTxFeeBufferPercent is the default safety-buffer + // percentage applied over the per-vByte fee rate. + DefaultWalletTxFeeBufferPercent = 25 ) var DefaultKeyGenerationConcurrency = runtime.GOMAXPROCS(0) +// MinWalletTxSatPerVByteFee and WalletTxFeeBufferPercent are the +// canonical runtime policy applied to every wallet Bitcoin transaction: +// both the leader-side floor application in +// tbtcpg.applyWalletTxFeeFloor and the follower-side soft check in +// tbtc.warnIfProposedWalletTxFeeBelowBufferedFloor read from these +// vars, so a single source of truth is enforced - tuning one side +// automatically tunes the other. +// +// They are vars (not consts) so operators can tune them via Config / +// Viper flags at startup, and so tests can override them via t.Cleanup. +// Initialize applies the Config values if non-zero; otherwise the +// DefaultWalletTx* constants above are kept. MinWalletTxSatPerVByteFee +// must be positive and WalletTxFeeBufferPercent must be non-negative; +// the helpers return an error if a runtime value violates this. +// +// A fee oracle can return an unusably low estimate (down to the +// 1 sat/vByte relay floor enforced by the Electrum client) in an +// uncongested mempool. Because these transactions spend or consolidate +// significant wallet value and are not RBF-enabled, they cannot be +// replaced once broadcast, so a floor-rate transaction can get stuck in +// the mempool and jam the wallet: no new wallet transaction can be +// built while the previous one is unconfirmed. The static floor and the +// 25% buffer are a stopgap for the current fire-and-forget, non-RBF +// wallet transaction path: because a stuck transaction cannot be +// fee-bumped, the fee must be right on the first broadcast. Once RBF / +// fee-bumping lands (Part B, tracked in #4171) the safety net shifts to +// monitor-and-bump, and this policy should be revisited rather than +// carried forward unchanged: the defensive buffer can be dropped and the +// floor relaxed toward the live estimate, keeping only a small +// relay-propagation minimum. +var ( + MinWalletTxSatPerVByteFee int64 = DefaultWalletTxSatPerVByteFloor + WalletTxFeeBufferPercent int64 = DefaultWalletTxFeeBufferPercent +) + // Config carries the config for tBTC protocol. type Config struct { // The size of the pre-parameters pool for tECDSA. @@ -96,6 +142,30 @@ type Config struct { PreParamsGenerationConcurrency int // Concurrency level for key-generation for tECDSA. KeyGenerationConcurrency int + // WalletTxSatPerVByteFloor is the minimum fee rate (sat/vByte) applied + // to wallet Bitcoin transactions. Zero means use + // DefaultWalletTxSatPerVByteFloor. Maps to the + // tbtc.walletTxSatPerVByteFloor flag / viper key. + WalletTxSatPerVByteFloor int + // WalletTxFeeBufferPercent is the safety-buffer percentage applied + // over the per-vByte fee rate. The buffered rate is + // ceil(rawRate * (100 + Percent) / 100). Zero means use + // DefaultWalletTxFeeBufferPercent. Maps to the + // tbtc.walletTxFeeBufferPercent flag / viper key. + WalletTxFeeBufferPercent int +} + +// applyWalletTxFeePolicy applies the operator-tunable wallet-tx fee-floor +// policy from Config to the package-level policy vars. Zero-valued Config +// fields are skipped so a direct Config{} in tests retains the +// DefaultWalletTx* constants. +func applyWalletTxFeePolicy(config Config) { + if config.WalletTxSatPerVByteFloor > 0 { + MinWalletTxSatPerVByteFee = int64(config.WalletTxSatPerVByteFloor) + } + if config.WalletTxFeeBufferPercent > 0 { + WalletTxFeeBufferPercent = int64(config.WalletTxFeeBufferPercent) + } } // Initialize kicks off the TBTC by initializing internal state, ensuring @@ -115,6 +185,8 @@ func Initialize( perfMetrics *clientinfo.PerformanceMetrics, ethereumNetwork ethereum.Network, ) error { + applyWalletTxFeePolicy(config) + groupParameters := defaultGroupParameters(ethereumNetwork) if ethChain, ok := chain.(interface { diff --git a/pkg/tbtc/tbtc_test.go b/pkg/tbtc/tbtc_test.go new file mode 100644 index 0000000000..209ea29cc2 --- /dev/null +++ b/pkg/tbtc/tbtc_test.go @@ -0,0 +1,90 @@ +package tbtc + +import ( + "testing" +) + +func TestApplyWalletTxFeePolicy(t *testing.T) { + originalFloor := MinWalletTxSatPerVByteFee + originalPercent := WalletTxFeeBufferPercent + t.Cleanup(func() { + MinWalletTxSatPerVByteFee = originalFloor + WalletTxFeeBufferPercent = originalPercent + }) + + // A zero-valued Config (e.g. a test that constructs Config{}) must + // keep the package defaults so the leader-side and follower-side + // fee-floor logic keeps working without a CLI override. + t.Run("zero-valued config keeps defaults", func(t *testing.T) { + MinWalletTxSatPerVByteFee = DefaultWalletTxSatPerVByteFloor + WalletTxFeeBufferPercent = DefaultWalletTxFeeBufferPercent + + applyWalletTxFeePolicy(Config{}) + + if MinWalletTxSatPerVByteFee != DefaultWalletTxSatPerVByteFloor { + t.Errorf( + "expected default floor [%d], got [%d]", + DefaultWalletTxSatPerVByteFloor, + MinWalletTxSatPerVByteFee, + ) + } + if WalletTxFeeBufferPercent != DefaultWalletTxFeeBufferPercent { + t.Errorf( + "expected default buffer percent [%d], got [%d]", + DefaultWalletTxFeeBufferPercent, + WalletTxFeeBufferPercent, + ) + } + }) + + // An operator-supplied config (e.g. via a Viper flag) overrides + // every field that is non-zero. The leader-side floor application + // in tbtcpg.applyWalletTxFeeFloor and the follower-side soft check + // in warnIfProposedWalletTxFeeBelowBufferedFloor both read the + // same package vars, so a single tuning here propagates to both. + t.Run("non-zero config overrides defaults", func(t *testing.T) { + applyWalletTxFeePolicy(Config{ + WalletTxSatPerVByteFloor: 7, + WalletTxFeeBufferPercent: 30, + }) + + if MinWalletTxSatPerVByteFee != 7 { + t.Errorf( + "expected floor [7], got [%d]", + MinWalletTxSatPerVByteFee, + ) + } + if WalletTxFeeBufferPercent != 30 { + t.Errorf( + "expected buffer percent [30], got [%d]", + WalletTxFeeBufferPercent, + ) + } + }) + + // Partial config: only the floor is tuned, the buffer percentage + // keeps the default. This is the realistic operator path where one + // knob is changed at a time during a rollout. + t.Run("partial config keeps unset defaults", func(t *testing.T) { + MinWalletTxSatPerVByteFee = DefaultWalletTxSatPerVByteFloor + WalletTxFeeBufferPercent = DefaultWalletTxFeeBufferPercent + + applyWalletTxFeePolicy(Config{ + WalletTxSatPerVByteFloor: 9, + }) + + if MinWalletTxSatPerVByteFee != 9 { + t.Errorf( + "expected floor [9], got [%d]", + MinWalletTxSatPerVByteFee, + ) + } + if WalletTxFeeBufferPercent != DefaultWalletTxFeeBufferPercent { + t.Errorf( + "expected default buffer percent [%d], got [%d]", + DefaultWalletTxFeeBufferPercent, + WalletTxFeeBufferPercent, + ) + } + }) +} diff --git a/pkg/tbtc/wallet.go b/pkg/tbtc/wallet.go index b5d1edc311..eb4bce52f5 100644 --- a/pkg/tbtc/wallet.go +++ b/pkg/tbtc/wallet.go @@ -36,18 +36,18 @@ const ( // ParseWalletActionType parses the given value into a WalletActionType. func ParseWalletActionType(value uint8) (WalletActionType, error) { - switch value { - case 0: + switch WalletActionType(value) { + case ActionNoop: return ActionNoop, nil - case 1: + case ActionHeartbeat: return ActionHeartbeat, nil - case 2: + case ActionDepositSweep: return ActionDepositSweep, nil - case 3: + case ActionRedemption: return ActionRedemption, nil - case 4: + case ActionMovingFunds: return ActionMovingFunds, nil - case 5: + case ActionMovedFundsSweep: return ActionMovedFundsSweep, nil default: return 0, fmt.Errorf("unknown wallet action type [%v]", value) diff --git a/pkg/tbtcpg/chain.go b/pkg/tbtcpg/chain.go index af939852e5..a1bead42c5 100644 --- a/pkg/tbtcpg/chain.go +++ b/pkg/tbtcpg/chain.go @@ -133,7 +133,8 @@ type Chain interface { proposal *tbtc.MovingFundsProposal, ) error - // Submits the moving funds target wallets commitment. + // SubmitMovingFundsCommitment submits the moving funds target wallets + // commitment. SubmitMovingFundsCommitment( walletPublicKeyHash [20]byte, walletMainUTXO bitcoin.UnspentTransactionOutput, @@ -150,8 +151,8 @@ type Chain interface { proposal *tbtc.MovedFundsSweepProposal, ) error - // Computes the moving funds commitment hash from the provided public key - // hashes of target wallets. + // ComputeMovingFundsCommitmentHash computes the moving funds commitment hash + // from the provided public key hashes of target wallets. ComputeMovingFundsCommitmentHash(targetWallets [][20]byte) [32]byte // GetRedemptionDelay returns the processing delay for the given redemption. diff --git a/pkg/tbtcpg/chain_test.go b/pkg/tbtcpg/chain_test.go index cdff0f01e3..48754b3972 100644 --- a/pkg/tbtcpg/chain_test.go +++ b/pkg/tbtcpg/chain_test.go @@ -57,6 +57,7 @@ type LocalChain struct { operatorIDs map[chain.Address]uint32 redemptionDelays map[[32]byte]time.Duration depositMinAge uint32 + depositSweepMaxSizeErr error } func NewLocalChain() *LocalChain { @@ -870,9 +871,26 @@ func (lc *LocalChain) SetRedemptionRequestMinAge(redemptionRequestMinAge uint32) } func (lc *LocalChain) GetDepositSweepMaxSize() (uint16, error) { + lc.mutex.Lock() + defer lc.mutex.Unlock() + + if lc.depositSweepMaxSizeErr != nil { + return 0, lc.depositSweepMaxSizeErr + } + panic("unsupported") } +// SetDepositSweepMaxSizeError configures the error GetDepositSweepMaxSize +// returns, allowing tests to exercise the max-size-lookup failure path +// without a real chain implementation. +func (lc *LocalChain) SetDepositSweepMaxSizeError(err error) { + lc.mutex.Lock() + defer lc.mutex.Unlock() + + lc.depositSweepMaxSizeErr = err +} + func (lc *LocalChain) BlockCounter() (chain.BlockCounter, error) { lc.mutex.Lock() defer lc.mutex.Unlock() diff --git a/pkg/tbtcpg/deposit_sweep.go b/pkg/tbtcpg/deposit_sweep.go index 42b36de377..b8bf84749f 100644 --- a/pkg/tbtcpg/deposit_sweep.go +++ b/pkg/tbtcpg/deposit_sweep.go @@ -18,9 +18,10 @@ import ( "github.com/keep-network/keep-core/pkg/tbtc" ) -// Use the worst-case 126-byte deposit script with embedded extra data for estimation. -// This will ensure that deposit sweep transaction fees are not underestimated. -const depositScriptByteSize = 126 +// DepositScriptByteSize is the worst-case 126-byte deposit script with embedded +// extra data used for transaction size estimation. This ensures that deposit +// sweep transaction fees are not underestimated. +const DepositScriptByteSize = 126 // DepositSweepLookBackBlocks is the look-back period in blocks used // when searching for submitted deposit-related events. It's equal to @@ -147,7 +148,7 @@ func FindDeposits( // The filterStartBlock parameter controls the earliest block from which // deposit-revealed events are queried. func findDeposits( - fnLogger log.StandardLogger, + taskLogger log.StandardLogger, chain Chain, btcChain bitcoin.Chain, walletPublicKeyHash [20]byte, @@ -156,7 +157,7 @@ func findDeposits( skipUnconfirmed bool, filterStartBlock uint64, ) ([]*Deposit, error) { - fnLogger.Infof("reading revealed deposits from chain") + taskLogger.Infof("reading revealed deposits from chain") depositMinAgeSeconds, err := chain.GetDepositMinAge() if err != nil { @@ -182,14 +183,14 @@ func findDeposits( ) } - fnLogger.Infof("found [%d] DepositRevealed events", len(depositRevealedEvents)) + taskLogger.Infof("found [%d] DepositRevealed events", len(depositRevealedEvents)) // Take the oldest first sort.SliceStable(depositRevealedEvents, func(i, j int) bool { return depositRevealedEvents[i].BlockNumber < depositRevealedEvents[j].BlockNumber }) - fnLogger.Infof("getting deposits details") + taskLogger.Infof("getting deposits details") resultSliceCapacity := len(depositRevealedEvents) if maxNumberOfDeposits > 0 { @@ -208,7 +209,7 @@ func findDeposits( depositKey := chain.BuildDepositKey(event.FundingTxHash, event.FundingOutputIndex) depositKeyStr := depositKey.Text(16) - fnLogger.Debugf("getting details of deposit [%s]", depositKeyStr) + taskLogger.Debugf("getting details of deposit [%s]", depositKeyStr) depositRequest, found, err := chain.GetDepositRequest( event.FundingTxHash, @@ -230,13 +231,13 @@ func findDeposits( matureAt := depositRequest.RevealedAt.Add(depositMinAge) if !timeNow.After(matureAt) { - fnLogger.Infof("deposit [%s] is not old enough", depositKeyStr) + taskLogger.Infof("deposit [%s] is not old enough", depositKeyStr) continue } isSwept := depositRequest.SweptAt.Unix() != 0 if skipSwept && isSwept { - fnLogger.Debugf("deposit [%s] is already swept", depositKeyStr) + taskLogger.Debugf("deposit [%s] is already swept", depositKeyStr) continue } @@ -245,14 +246,14 @@ func findDeposits( event.FundingTxHash, ) if err != nil { - fnLogger.Errorf( + taskLogger.Errorf( "failed to get bitcoin transaction confirmations: [%v]", err, ) } if skipUnconfirmed && confirmations < tbtc.DepositSweepRequiredFundingTxConfirmations { - fnLogger.Debugf( + taskLogger.Debugf( "deposit [%s] funding transaction doesn't have enough confirmations: [%d/%d]", depositKeyStr, confirmations, @@ -495,7 +496,7 @@ func (dst *DepositSweepTask) ProposeDepositsSweep( // the deposits stay unswept. Log it distinctly at WARN so operators // can tell this apart from a benign "no deposits to sweep" outcome; // in particular, a safe-minimum-fee abort (see - // minWalletTxSatPerVByteFee) can indicate a misconfigured, too-low + // MinWalletTxSatPerVByteFee) can indicate a misconfigured, too-low // per-deposit maximum fee that will strand deposits until governance // raises it. taskLogger.Warnf("cannot estimate sweep transaction fee: [%v]", err) @@ -507,18 +508,12 @@ func (dst *DepositSweepTask) ProposeDepositsSweep( taskLogger.Infof("sweep transaction fee: [%d]", fee) - depositsKeys := make([]struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }, len(deposits)) + depositsKeys := make([]tbtc.DepositKey, len(deposits)) depositsRevealBlocks := make([]*big.Int, len(deposits)) for i, deposit := range deposits { - depositsKeys[i] = struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }{ + depositsKeys[i] = tbtc.DepositKey{ FundingTxHash: deposit.FundingTxHash, FundingOutputIndex: deposit.FundingOutputIndex, } @@ -565,7 +560,7 @@ func (dst *DepositSweepTask) ProposeDepositsSweep( // - 1 P2WPKH output // // An error is returned if any estimated fee exceeds the maximum fee allowed by -// the Bridge contract, or if the minimum safe fee (see minWalletTxSatPerVByteFee) +// the Bridge contract, or if the minimum safe fee (see MinWalletTxSatPerVByteFee) // required to avoid a stuck, unbumpable sweep would itself exceed that Bridge // maximum. func EstimateDepositsSweepFee( @@ -596,7 +591,7 @@ func EstimateDepositsSweepFee( } else { sweepMaxSize, err := chain.GetDepositSweepMaxSize() if err != nil { - return nil, fmt.Errorf("cannot get sweep max size: [%v]", sweepMaxSize) + return nil, fmt.Errorf("cannot get sweep max size: [%w]", err) } for i := 1; i <= int(sweepMaxSize); i++ { @@ -639,7 +634,7 @@ func estimateDepositsSweepFee( // 1 P2WPKH main UTXO input. AddPublicKeyHashInputs(1, true). // depositsCount P2WSH deposit inputs. - AddScriptHashInputs(depositsCount, depositScriptByteSize, true). + AddScriptHashInputs(depositsCount, DepositScriptByteSize, true). // 1 P2WPKH output. AddPublicKeyHashOutputs(1, true). VirtualSize() diff --git a/pkg/tbtcpg/deposit_sweep_fee_test.go b/pkg/tbtcpg/deposit_sweep_fee_test.go index 185da44781..e935d99806 100644 --- a/pkg/tbtcpg/deposit_sweep_fee_test.go +++ b/pkg/tbtcpg/deposit_sweep_fee_test.go @@ -1,6 +1,7 @@ package tbtcpg_test import ( + "errors" "strings" "testing" @@ -12,7 +13,7 @@ import ( // with the given number of deposit inputs, mirroring the sizing that // EstimateDepositsSweepFee performs internally: 1 P2WPKH main-UTXO input, // depositsCount P2WSH deposit inputs, and 1 P2WPKH output. 126 == -// depositScriptByteSize. +// DepositScriptByteSize. func sweepVirtualSize(t *testing.T, depositsCount int) int64 { t.Helper() size, err := bitcoin.NewTransactionSizeEstimator(). @@ -34,7 +35,9 @@ func sweepVirtualSize(t *testing.T, depositsCount int) int64 { // the informational SatPerVByteFee and the TotalFee actually broadcast on-chain // are asserted, and multi-deposit sweeps (where transactionSize grows // sub-linearly while totalMaxFee grows linearly) are exercised on both the happy -// path and the floor-exceeds-cap error branch. +// path and the floor-exceeds-cap error branch. A depositsCount of 0 is also +// covered, verifying the max-size-lookup failure is reported with its real +// underlying cause rather than the zero-value size. func TestEstimateDepositsSweepFee_MinimumFloorAndBuffer(t *testing.T) { // Virtual sizes used to pin the cap and the expected total fee (the on-chain // value) relative to the fee rate. The cap and expected-total expectations @@ -48,6 +51,7 @@ func TestEstimateDepositsSweepFee_MinimumFloorAndBuffer(t *testing.T) { depositsCount int estimateSatPerVByte int64 perDepositMaxFee uint64 + sweepMaxSizeErr error expectedSatPerVByteFee int64 expectedTotalFee int64 expectErrorContains string @@ -113,12 +117,25 @@ func TestEstimateDepositsSweepFee_MinimumFloorAndBuffer(t *testing.T) { perDepositMaxFee: uint64(size3), expectErrorContains: "minimum safe transaction fee", }, + "depositsCount of 0 with a failing max size lookup returns the wrapped error": { + depositsCount: 0, + // A depositsCount of 0 takes the "estimate for every count" branch, + // which looks up the max sweep size first. Inject a distinctive + // underlying error so the assertion below fails if that cause is + // ever dropped again (the bug this guards against formatted the + // zero-value max size instead of the real error). + sweepMaxSizeErr: errors.New("boom"), + expectErrorContains: "cannot get sweep max size: [boom]", + }, } for name, test := range tests { t.Run(name, func(t *testing.T) { tbtcChain := tbtcpg.NewLocalChain() tbtcChain.SetDepositParameters(0, 0, test.perDepositMaxFee, 0) + if test.sweepMaxSizeErr != nil { + tbtcChain.SetDepositSweepMaxSizeError(test.sweepMaxSizeErr) + } btcChain := tbtcpg.NewLocalBitcoinChain() btcChain.SetEstimateSatPerVByteFee(1, test.estimateSatPerVByte) @@ -137,6 +154,13 @@ func TestEstimateDepositsSweepFee_MinimumFloorAndBuffer(t *testing.T) { test.expectErrorContains, err, ) } + if test.sweepMaxSizeErr != nil && + !errors.Is(err, test.sweepMaxSizeErr) { + t.Fatalf( + "expected error to wrap [%v]; got [%v]", + test.sweepMaxSizeErr, err, + ) + } return } if err != nil { diff --git a/pkg/tbtcpg/fee.go b/pkg/tbtcpg/fee.go index b1e2acc43e..e6d33338a6 100644 --- a/pkg/tbtcpg/fee.go +++ b/pkg/tbtcpg/fee.go @@ -3,6 +3,9 @@ package tbtcpg import ( "errors" "fmt" + "math" + + "github.com/keep-network/keep-core/pkg/tbtc" ) // ErrMaxFeeTooLow indicates that the Bridge maximum total fee is too low to @@ -14,60 +17,72 @@ var ErrMaxFeeTooLow = errors.New( "minimum safe transaction fee exceeds the maximum fee", ) -// minWalletTxSatPerVByteFee is the minimum fee rate, in sat/vByte, applied to -// wallet Bitcoin transactions (deposit sweeps, redemptions, moving funds, moved -// funds sweeps). A fee oracle can return an unusably low estimate (down to the -// 1 sat/vByte relay floor enforced by the Electrum client) in an uncongested -// mempool. Because these transactions spend or consolidate significant wallet -// value and are not RBF-enabled, they cannot be replaced once broadcast, so a -// floor-rate transaction can get stuck in the mempool and jam the wallet: no -// new wallet transaction can be built while the previous one is unconfirmed. -// This minimum keeps the fee safely above the relay floor while remaining far -// below the Bridge's maximum fee. The value is intentionally conservative and -// could be made configurable; see threshold-network/keep-core#4171. -// -// NOTE: this static floor and the 25% buffer applied in applyWalletTxFeeFloor -// are a stopgap for the current fire-and-forget, non-RBF wallet transaction -// path: because a stuck transaction cannot be fee-bumped, the fee must be right -// on the first broadcast. Once RBF / fee-bumping lands (Part B, tracked in -// #4171) the safety net shifts to monitor-and-bump, and this policy should be -// revisited rather than carried forward unchanged: the defensive buffer can be -// dropped and the floor relaxed toward the live estimate, keeping only a small -// relay-propagation minimum. -const minWalletTxSatPerVByteFee = 5 +// maxWalletTxVsize and maxWalletTxEstimatedFee are sanity bounds on the +// applyWalletTxFeeFloor inputs. They are intentionally far above any +// realistic Bitcoin transaction (block weight caps vsize at ~4M weight +// units; a wallet tx fee over a few BTC is itself implausible) so +// legitimate callers never trip them. They are also defense-in-depth for +// the checked-arithmetic overflow guards below: a value within these +// bounds is guaranteed (modulo the explicit checks) to keep the internal +// int64 multiplications in range. +const ( + maxWalletTxVsize int64 = 10_000_000 // 10M vbytes; ~2x Bitcoin block weight. + maxWalletTxEstimatedFee int64 = 1_000_000_000 // 1e9 satoshis = 10 BTC. +) // applyWalletTxFeeFloor raises a raw oracle fee estimate to a safe value for a // non-RBF wallet transaction. It: -// - adds a 25% buffer over the oracle estimate so there is margin during the -// estimate-to-broadcast delay and the fee stays adaptive under congestion, -// - enforces a floor of minWalletTxSatPerVByteFee sat/vByte, and -// - bounds the result by maxTotalFee (the Bridge maximum total fee for the -// transaction). +// - applies a safety buffer (default 25%, controlled by +// tbtc.WalletTxFeeBufferPercent) over the per-vByte fee rate so +// there is margin during the estimate-to-broadcast delay and the +// fee stays adaptive under congestion, +// - enforces a floor of tbtc.MinWalletTxSatPerVByteFee sat/vByte, and +// - bounds the result by maxTotalFee (the Bridge maximum total fee for +// the transaction). // // It returns ErrMaxFeeTooLow if the minimum floor alone would exceed -// maxTotalFee - a safe transaction cannot be built, so the caller must not -// broadcast an underpriced one. estimatedFee is the raw oracle fee in satoshis -// and txVsize is the estimated transaction virtual size in vBytes. +// maxTotalFee - a safe transaction cannot be built, so the caller must +// not broadcast an underpriced one. estimatedFee is the raw oracle fee +// in satoshis and txVsize is the estimated transaction virtual size in +// vBytes. Both inputs are sanity-bounded against maxWalletTxEstimatedFee +// / maxWalletTxVsize to prevent int64 overflow in the internal +// multiplications when the oracle or size-estimator returns an +// implausible value; an input outside the bound is rejected with an +// error rather than silently overflowing. The buffer multiplication +// and the final totalFee multiplication additionally have +// checked-arithmetic overflow guards so an operator-tuned +// tbtc.WalletTxFeeBufferPercent / tbtc.MinWalletTxSatPerVByteFee +// cannot bypass the bound by exceeding the int64 limit on its own. +// +// The policy values (the floor and the buffer percentage) live in +// pkg/tbtc as exported vars so the leader-side floor application +// (here) and the follower-side soft check +// (pkg/tbtc.warnIfProposedWalletTxFeeBelowBufferedFloor, used by every +// wallet-tx proposal validator) consume a single source of truth. +// Operator tuning one side automatically tunes the other. // -// The 25% buffer is applied to the truncated per-vByte rate -// (estimatedFee / txVsize). This is lossless only because EstimateFee returns -// the fee as satPerVByteFee * txVsize (an exact multiple of the vsize), so the -// integer division recovers the exact rate. If that contract ever changes so -// estimatedFee is no longer an exact multiple of txVsize, apply the buffer to -// estimatedFee directly instead of to the truncated rate; otherwise up to -// txVsize-1 sat is silently dropped before buffering and the tx is underpriced. +// The buffer is applied to the truncated per-vByte rate +// (estimatedFee / txVsize). This is lossless only because EstimateFee +// returns the fee as satPerVByteFee * txVsize (an exact multiple of the +// vsize), so the integer division recovers the exact rate. If that +// contract ever changes so estimatedFee is no longer an exact multiple +// of txVsize, apply the buffer to estimatedFee directly instead of to +// the truncated rate; otherwise up to txVsize-1 sat is silently +// dropped before buffering and the tx is underpriced. // -// maxTotalFee bounds only the total transaction fee. Where the Bridge also -// enforces a per-request cap (e.g. the redemption TxMaxFee), satisfying that -// cap is the caller's or on-chain validation's responsibility; this helper is -// unaware of it. Callers are expected to reject a raw estimate already above -// maxTotalFee before calling (all current callers do); the result is in any -// case clamped down to maxTotalFee. +// maxTotalFee bounds only the total transaction fee. Where the Bridge +// also enforces a per-request cap (e.g. the redemption TxMaxFee), +// satisfying that cap is the caller's or on-chain validation's +// responsibility; this helper is unaware of it. Callers are expected to +// reject a raw estimate already above maxTotalFee before calling (all +// current callers do); the result is in any case clamped down to +// maxTotalFee. // -// The buffer and floor are applied to the estimated vsize; a transaction whose -// real on-wire vsize is larger than estimated (e.g. a deposit sweep containing -// legacy P2SH inputs) can land slightly below the intended rate, but still far -// above the relay floor this guards against. +// The buffer and floor are applied to the estimated vsize; a +// transaction whose real on-wire vsize is larger than estimated (e.g. +// a deposit sweep containing legacy P2SH inputs) can land slightly +// below the intended rate, but still far above the relay floor this +// guards against. func applyWalletTxFeeFloor( estimatedFee int64, txVsize int64, @@ -76,27 +91,104 @@ func applyWalletTxFeeFloor( if txVsize <= 0 { return 0, fmt.Errorf("invalid transaction virtual size [%d]", txVsize) } + if txVsize > maxWalletTxVsize { + return 0, fmt.Errorf( + "implausible transaction virtual size [%d]; expected at most [%d]", + txVsize, maxWalletTxVsize, + ) + } + if estimatedFee < 0 { + return 0, fmt.Errorf("invalid estimated fee [%d]", estimatedFee) + } + if estimatedFee > maxWalletTxEstimatedFee { + return 0, fmt.Errorf( + "implausible estimated fee [%d]; expected at most [%d]", + estimatedFee, maxWalletTxEstimatedFee, + ) + } + if tbtc.MinWalletTxSatPerVByteFee <= 0 { + return 0, fmt.Errorf( + "implausible minimum fee rate [%d]; expected positive", + tbtc.MinWalletTxSatPerVByteFee, + ) + } + if tbtc.WalletTxFeeBufferPercent < 0 { + return 0, fmt.Errorf( + "invalid wallet tx fee buffer percent [%d]; must be non-negative", + tbtc.WalletTxFeeBufferPercent, + ) + } + bufferNumerator := 100 + tbtc.WalletTxFeeBufferPercent + const bufferDenominator = 100 - // If even the minimum floor exceeds the Bridge maximum, a safe transaction - // cannot be constructed; error rather than silently broadcast underpriced. - if uint64(minWalletTxSatPerVByteFee*txVsize) > maxTotalFee { + // Checked-arithmetic guard: floor * txVsize must fit in int64 to + // display correctly in the error message below and to keep the int64 + // product in range. Both operands are positive int64. + if tbtc.MinWalletTxSatPerVByteFee > math.MaxInt64/txVsize { + return 0, fmt.Errorf( + "implausible minimum fee rate [%d] for vsize [%d]; "+ + "product would overflow", + tbtc.MinWalletTxSatPerVByteFee, txVsize, + ) + } + floorProduct := tbtc.MinWalletTxSatPerVByteFee * txVsize + if uint64(floorProduct) > maxTotalFee { return 0, fmt.Errorf( "%w: minimum fee [%d], maximum fee [%d]", ErrMaxFeeTooLow, - minWalletTxSatPerVByteFee*txVsize, + floorProduct, maxTotalFee, ) } rate := estimatedFee / txVsize - rate = (rate*5 + 3) / 4 // ceil(rate * 1.25) - if rate < minWalletTxSatPerVByteFee { - rate = minWalletTxSatPerVByteFee + // Checked-arithmetic guard for the buffer multiplication. Inputs + // are also bounded (maxWalletTxVsize / maxWalletTxEstimatedFee), so + // this is defense in depth: even if an operator tunes + // tbtc.WalletTxFeeBufferPercent to a huge value, we reject rate + // values whose product with bufferNumerator (plus + // bufferDenominator-1 for the ceiling) cannot fit in int64. rate == + // 0 never overflows. + if rate > 0 { + maxRateForBuffer := (math.MaxInt64 - (bufferDenominator - 1)) / + bufferNumerator + if rate > maxRateForBuffer { + return 0, fmt.Errorf( + "implausible per-vByte rate [%d] would overflow when "+ + "applied with buffer percent [%d]; expected at most [%d]", + rate, + tbtc.WalletTxFeeBufferPercent, + maxRateForBuffer, + ) + } + } + // ceil(rate * (100+Percent) / 100). Both rate and bufferNumerator + // are positive (or rate is zero), so the multiplication cannot + // overflow; see the input-bounds check and the rate-vs-Numerator + // check above. + rate = (rate*bufferNumerator + bufferDenominator - 1) / bufferDenominator + if rate < tbtc.MinWalletTxSatPerVByteFee { + rate = tbtc.MinWalletTxSatPerVByteFee + } + + // Checked-arithmetic guard for the total-fee multiplication: rate * + // txVsize must fit in int64. rate == 0 never overflows. txVsize is + // bounded above by maxWalletTxVsize, so this is defense in depth: an + // operator-tuned tbtc.MinWalletTxSatPerVByteFee (e.g. set to + // MaxInt64) would otherwise push rate past MaxInt64 / txVsize. + if rate > 0 && rate > math.MaxInt64/txVsize { + return 0, fmt.Errorf( + "implausible buffered rate [%d] would overflow when multiplied "+ + "by txVsize [%d]; expected at most [%d]", + rate, txVsize, math.MaxInt64/txVsize, + ) } - // Clamp down to the Bridge maximum total fee. This can never drop the fee - // below the floor: the floor-vs-cap guard above already guaranteed - // maxTotalFee is at least the minimum floor total. + // Clamp down to the Bridge maximum total fee. This can never drop + // the fee below the floor: the floor-vs-cap guard above already + // guaranteed maxTotalFee is at least the minimum floor total. The + // product is now guaranteed to fit in int64 by the rate*txVsize + // guard above. totalFee := rate * txVsize if uint64(totalFee) > maxTotalFee { totalFee = int64(maxTotalFee) diff --git a/pkg/tbtcpg/fee_test.go b/pkg/tbtcpg/fee_test.go index cdb522c53e..d6249dd23c 100644 --- a/pkg/tbtcpg/fee_test.go +++ b/pkg/tbtcpg/fee_test.go @@ -1,10 +1,27 @@ package tbtcpg import ( + "math" "strings" "testing" + + "github.com/keep-network/keep-core/pkg/tbtc" ) +// withWalletTxFeePolicy saves the current wallet-tx fee-floor policy +// (floor, buffer percent) and registers a t.Cleanup that restores it. +// Tests overriding the canonical pkg/tbtc vars MUST use this helper so +// later tests in the same package see the production defaults. +func withWalletTxFeePolicy(t *testing.T) { + t.Helper() + originalFloor := tbtc.MinWalletTxSatPerVByteFee + originalPercent := tbtc.WalletTxFeeBufferPercent + t.Cleanup(func() { + tbtc.MinWalletTxSatPerVByteFee = originalFloor + tbtc.WalletTxFeeBufferPercent = originalPercent + }) +} + func TestApplyWalletTxFeeFloor(t *testing.T) { const vsize = 200 @@ -57,6 +74,24 @@ func TestApplyWalletTxFeeFloor(t *testing.T) { maxTotalFee: 100000, expectErrorContains: "invalid transaction virtual size", }, + "negative estimated fee returns an error": { + estimatedFee: -1, + txVsize: vsize, + maxTotalFee: 100000, + expectErrorContains: "invalid estimated fee", + }, + "implausibly large virtual size returns an error": { + estimatedFee: 1000, + txVsize: maxWalletTxVsize + 1, + maxTotalFee: 100000, + expectErrorContains: "implausible transaction virtual size", + }, + "implausibly large estimated fee returns an error": { + estimatedFee: maxWalletTxEstimatedFee + 1, + txVsize: vsize, + maxTotalFee: 100000, + expectErrorContains: "implausible estimated fee", + }, } for name, tc := range tests { @@ -91,3 +126,124 @@ func TestApplyWalletTxFeeFloor(t *testing.T) { }) } } + +// TestApplyWalletTxFeeFloor_BufferOverride verifies that the safety buffer +// is driven by the canonical pkg/tbtc WalletTxFeeBufferPercent var, not +// a hardcoded constant. A test that overrides the var MUST restore it +// via t.Cleanup so other tests see the production defaults. +func TestApplyWalletTxFeeFloor_BufferOverride(t *testing.T) { + const vsize = 200 + withWalletTxFeePolicy(t) + + // 50% buffer. At rate 20 sat/vByte the buffered rate becomes + // ceil(20 * 150 / 100) = 30 sat/vByte, total 6000. + tbtc.WalletTxFeeBufferPercent = 50 + + fee, err := applyWalletTxFeeFloor( + 4000, // rate 20 sat/vByte + vsize, + 100000, // well above the buffered 6000 + ) + if err != nil { + t.Fatalf("unexpected error: [%v]", err) + } + if fee != 6000 { + t.Errorf( + "unexpected fee with 50%% buffer\nexpected: [6000]\nactual: [%d]", + fee, + ) + } + + // Disable the buffer (Percent=0). The buffered rate equals the raw + // rate, so a 20 sat/vByte estimate stays at 20 sat/vByte (above the + // floor), total 4000. + tbtc.WalletTxFeeBufferPercent = 0 + + fee, err = applyWalletTxFeeFloor( + 4000, + vsize, + 100000, + ) + if err != nil { + t.Fatalf("unexpected error: [%v]", err) + } + if fee != 4000 { + t.Errorf( + "unexpected fee with buffer disabled\nexpected: [4000]\nactual: [%d]", + fee, + ) + } + + // Negative buffer values are rejected so the helper cannot apply a + // sub-floor buffer (a percent below 0 would make the multiplier + // less than 1x, undermining the safety margin). + tbtc.WalletTxFeeBufferPercent = -1 + _, err = applyWalletTxFeeFloor(4000, vsize, 100000) + if err == nil { + t.Fatalf("expected an error for Percent=-1") + } + if !strings.Contains(err.Error(), "invalid wallet tx fee buffer percent") { + t.Fatalf( + "expected error containing [invalid wallet tx fee buffer percent]; got [%v]", + err, + ) + } +} + +// TestApplyWalletTxFeeFloor_OverflowGuard verifies that the helper rejects +// configurations whose internal multiplications (rate * bufferNumerator, +// rate * txVsize, floor * txVsize) would overflow int64. The overflow +// guards are checked-arithmetic and are the hard guarantee; the +// input-cap (maxWalletTxVsize / maxWalletTxEstimatedFee) is +// defense-in-depth that can never be reached for sane operator-tuned +// values, so this test exercises the checked-arithmetic path +// explicitly. +func TestApplyWalletTxFeeFloor_OverflowGuard(t *testing.T) { + const vsize = 200 + withWalletTxFeePolicy(t) + + // Buffer percent set so the derived numerator (100+Percent) is + // close to MaxInt64: rate * numerator overflows for any + // non-trivial rate. The helper rejects this rather than silently + // wrapping around into the buffer math. + tbtc.WalletTxFeeBufferPercent = math.MaxInt64 - 100 + + _, err := applyWalletTxFeeFloor( + 4000, // rate 20 sat/vByte + vsize, + 100000, + ) + if err == nil { + t.Fatalf("expected overflow error for Percent=MaxInt64-100") + } + if !strings.Contains(err.Error(), "would overflow when applied with buffer") { + t.Fatalf( + "expected error containing [would overflow when applied with buffer]; got [%v]", + err, + ) + } + + // Restore sane buffer. + tbtc.WalletTxFeeBufferPercent = tbtc.DefaultWalletTxFeeBufferPercent + + // Floor so high that floor * txVsize would overflow int64. With + // estimatedFee=0 the raw rate is 0, but the floor forces rate to + // tbtc.MinWalletTxSatPerVByteFee, which the checked-arithmetic + // guard catches before any multiplication happens. + tbtc.MinWalletTxSatPerVByteFee = math.MaxInt64 / 2 + + _, err = applyWalletTxFeeFloor( + 0, // raw rate 0 + vsize, + math.MaxUint64, + ) + if err == nil { + t.Fatalf("expected overflow error for huge MinWalletTxSatPerVByteFee") + } + if !strings.Contains(err.Error(), "would overflow") { + t.Fatalf( + "expected error containing [would overflow]; got [%v]", + err, + ) + } +} diff --git a/pkg/tbtcpg/internal/test/marshaling.go b/pkg/tbtcpg/internal/test/marshaling.go index 91c390df6e..57ee86bd88 100644 --- a/pkg/tbtcpg/internal/test/marshaling.go +++ b/pkg/tbtcpg/internal/test/marshaling.go @@ -170,10 +170,7 @@ func (dsp *depositSweepProposal) convert() ( copy(walletPublicKeyHash[:], hexToSlice(dsp.WalletPublicKeyHash)) } - result.DepositsKeys = make([]struct { - FundingTxHash bitcoin.Hash - FundingOutputIndex uint32 - }, len(dsp.DepositsKeys)) + result.DepositsKeys = make([]tbtc.DepositKey, len(dsp.DepositsKeys)) for i, depositKey := range dsp.DepositsKeys { fundingTxHash, err := bitcoin.NewHashFromString(depositKey.FundingTxHash, bitcoin.ReversedByteOrder) if err != nil { @@ -274,6 +271,8 @@ func (psts *ProposeSweepTestScenario) UnmarshalJSON(data []byte) error { // Unmarshal expected error if len(unmarshaled.ExpectedErr) > 0 { + // fmt.Errorf requires a constant format string; ExpectedErr is a + // plain string so use errors.New to avoid formatting interpretation. psts.ExpectedErr = errors.New(unmarshaled.ExpectedErr) } diff --git a/pkg/tbtcpg/redemptions.go b/pkg/tbtcpg/redemptions.go index d4d845ee6c..720a2afdbb 100644 --- a/pkg/tbtcpg/redemptions.go +++ b/pkg/tbtcpg/redemptions.go @@ -294,7 +294,7 @@ func (rt *RedemptionTask) ProposeRedemption( } func findPendingRedemptions( - fnLogger log.StandardLogger, + taskLogger log.StandardLogger, chain Chain, walletPublicKeyHash [20]byte, currentBlockNumber uint64, @@ -364,9 +364,9 @@ func findPendingRedemptions( eventsSet[hexutils.Encode(redemptionKey.Bytes())] = event } - fnLogger.Infof("found [%d] RedemptionRequested events", len(eventsSet)) + taskLogger.Infof("found [%d] RedemptionRequested events", len(eventsSet)) - fnLogger.Infof("checking pending redemptions details") + taskLogger.Infof("checking pending redemptions details") pendingRedemptions := make([]*RedemptionRequest, 0) @@ -375,7 +375,7 @@ redemptionRequestedLoop: for redemptionKey, event := range eventsSet { eventIndex++ - fnLogger.Debugf( + taskLogger.Debugf( "getting pending redemption details [%s]", redemptionKey, ) @@ -393,7 +393,7 @@ redemptionRequestedLoop: ) } if !found { - fnLogger.Infof( + taskLogger.Infof( "redemption request [%s] is no longer pending", redemptionKey, ) @@ -452,7 +452,7 @@ redemptionRequestedLoop: minAge = delay } - fnLogger.Infof( + taskLogger.Infof( "minimum age for redemption request [%s] is [%v]", redemption.RedemptionKey, minAge, @@ -469,7 +469,7 @@ redemptionRequestedLoop: // Check if timeout passed for the redemption request. if pendingRedemption.RequestedAt.Before(redemptionRequestsRangeStartTimestamp) { - fnLogger.Infof( + taskLogger.Infof( "redemption request [%s] has already timed out", pendingRedemption.RedemptionKey, ) @@ -489,7 +489,7 @@ redemptionRequestedLoop: // Check if enough time elapsed since the redemption request. if pendingRedemption.RequestedAt.After(rangeEndTimestamp) { - fnLogger.Infof( + taskLogger.Infof( "redemption request [%s] is not old enough", pendingRedemption.RedemptionKey, ) diff --git a/pkg/tbtcpg/redemptions_test.go b/pkg/tbtcpg/redemptions_test.go index 2aef02fa73..6f9fecb298 100644 --- a/pkg/tbtcpg/redemptions_test.go +++ b/pkg/tbtcpg/redemptions_test.go @@ -316,16 +316,31 @@ func TestRedemptionAction_ProposeRedemption_PerRequestFeeWarning(t *testing.T) { var tests = map[string]struct { txMaxFee uint64 + txMaxTotalFee uint64 + expectedFee int64 expectWarning bool }{ "worst-case share within the per-request cap": { - txMaxFee: 3000, // 2166 <= 3000 + // Aggregate cap = txMaxFee*count = 3000*3 = 9000, looser than + // txMaxTotalFee (8000), so the total-fee cap governs and the fee + // is unclamped; 2166 <= 3000 so no warning either. + txMaxFee: 3000, + txMaxTotalFee: 8000, + expectedFee: 6496, expectWarning: false, }, - "even share at the cap but last-request share exceeds it": { - // The even share 2165 equals the cap (a floor-division check would - // not warn), but the last request pays 2166 and would be rejected. + "total-fee cap clamps to a value whose remainder exceeds the per-request cap": { + // The aggregate per-request ceiling (txMaxFee*count = 2165*3 = + // 6495) is looser than txMaxTotalFee (6494), so txMaxTotalFee + // governs and the buffered fee (6496) is clamped down to 6494 - + // not a multiple of count, so the remainder still lands + // unevenly. The even share is floor(6494/3) = 2164 and the last + // request pays 2164 + 6494%3 = 2166, which exceeds txMaxFee + // (2165) even though the aggregate cap alone would not have + // forced an uneven split. txMaxFee: 2165, + txMaxTotalFee: 6494, + expectedFee: 6494, expectWarning: true, }, } @@ -337,9 +352,8 @@ func TestRedemptionAction_ProposeRedemption_PerRequestFeeWarning(t *testing.T) { btcChain.SetEstimateSatPerVByteFee(1, 25) - // txMaxFee at index 2; txMaxTotalFee at index 3, set comfortably - // above the estimated total (6496) so it does not bound the fee. - tbtcChain.SetRedemptionParameters(0, 0, test.txMaxFee, 8000, 0, nil, 0) + // txMaxFee at index 2; txMaxTotalFee at index 3. + tbtcChain.SetRedemptionParameters(0, 0, test.txMaxFee, test.txMaxTotalFee, 0, nil, 0) for _, script := range redeemersOutputScripts { tbtcChain.SetPendingRedemptionRequest( @@ -352,7 +366,7 @@ func TestRedemptionAction_ProposeRedemption_PerRequestFeeWarning(t *testing.T) { expectedProposal := &tbtc.RedemptionProposal{ RedeemersOutputScripts: redeemersOutputScripts, - RedemptionTxFee: big.NewInt(6496), + RedemptionTxFee: big.NewInt(test.expectedFee), } err := tbtcChain.SetRedemptionProposalValidationResult( diff --git a/pkg/tecdsa/dkg/marshaling.go b/pkg/tecdsa/dkg/marshaling.go index 4e2815d62e..e0bb3942b8 100644 --- a/pkg/tecdsa/dkg/marshaling.go +++ b/pkg/tecdsa/dkg/marshaling.go @@ -1,3 +1,4 @@ +// marshaling.go: protobuf (un)marshaling for the public types in this package. package dkg import ( @@ -9,7 +10,6 @@ import ( "google.golang.org/protobuf/proto" timestamppb "google.golang.org/protobuf/types/known/timestamppb" - "github.com/keep-network/keep-core/pkg/crypto/ephemeral" "github.com/keep-network/keep-core/pkg/protocol/group" "github.com/keep-network/keep-core/pkg/tecdsa/dkg/gen/pb" ) @@ -17,14 +17,9 @@ import ( // Marshal converts this ephemeralPublicKeyMessage to a byte array suitable for // network communication. func (epkm *ephemeralPublicKeyMessage) Marshal() ([]byte, error) { - ephemeralPublicKeys, err := marshalPublicKeyMap(epkm.ephemeralPublicKeys) - if err != nil { - return nil, err - } - return proto.Marshal(&pb.EphemeralPublicKeyMessage{ SenderID: uint32(epkm.senderID), - EphemeralPublicKeys: ephemeralPublicKeys, + EphemeralPublicKeys: marshalPublicKeyMap(epkm.ephemeralPublicKeys), SessionID: epkm.sessionID, }) } @@ -190,37 +185,28 @@ func validateMemberIndex(protoIndex uint32) error { } func marshalPublicKeyMap( - publicKeys map[group.MemberIndex]*ephemeral.PublicKey, -) (map[uint32][]byte, error) { + publicKeys map[group.MemberIndex][]byte, +) map[uint32][]byte { marshalled := make(map[uint32][]byte, len(publicKeys)) - for id, publicKey := range publicKeys { - if publicKey == nil { - return nil, fmt.Errorf("nil public key for member [%v]", id) - } - - marshalled[uint32(id)] = publicKey.Marshal() + for id, keyBytes := range publicKeys { + marshalled[uint32(id)] = keyBytes } - return marshalled, nil + return marshalled } +// unmarshalPublicKeyMap converts the wire-format map to an internal byte map, +// validating member indices but deferring EC point parsing to use-time so that +// only the one key per message actually needed for ECDH is ever parsed. func unmarshalPublicKeyMap( publicKeys map[uint32][]byte, -) (map[group.MemberIndex]*ephemeral.PublicKey, error) { - var unmarshalled = make(map[group.MemberIndex]*ephemeral.PublicKey, len(publicKeys)) +) (map[group.MemberIndex][]byte, error) { + unmarshalled := make(map[group.MemberIndex][]byte, len(publicKeys)) for memberID, publicKeyBytes := range publicKeys { if err := validateMemberIndex(memberID); err != nil { return nil, err } - - publicKey, err := ephemeral.UnmarshalPublicKey(publicKeyBytes) - if err != nil { - return nil, fmt.Errorf("could not unmarshal public key [%v]", err) - } - - unmarshalled[group.MemberIndex(memberID)] = publicKey - + unmarshalled[group.MemberIndex(memberID)] = publicKeyBytes } - return unmarshalled, nil } diff --git a/pkg/tecdsa/dkg/marshaling_test.go b/pkg/tecdsa/dkg/marshaling_test.go index 314f19376c..adfe771c0d 100644 --- a/pkg/tecdsa/dkg/marshaling_test.go +++ b/pkg/tecdsa/dkg/marshaling_test.go @@ -23,9 +23,10 @@ func TestEphemeralPublicKeyMessage_MarshalingRoundtrip(t *testing.T) { t.Fatal(err) } - publicKeys := make(map[group.MemberIndex]*ephemeral.PublicKey) - publicKeys[group.MemberIndex(211)] = keyPair1.PublicKey - publicKeys[group.MemberIndex(19)] = keyPair2.PublicKey + publicKeys := map[group.MemberIndex][]byte{ + group.MemberIndex(211): keyPair1.PublicKey.Marshal(), + group.MemberIndex(19): keyPair2.PublicKey.Marshal(), + } msg := &ephemeralPublicKeyMessage{ senderID: group.MemberIndex(38), @@ -48,7 +49,7 @@ func TestFuzzEphemeralPublicKeyMessage_MarshalingRoundtrip(t *testing.T) { for i := 0; i < 10; i++ { var ( senderID group.MemberIndex - ephemeralPublicKeys map[group.MemberIndex]*ephemeral.PublicKey + ephemeralPublicKeys map[group.MemberIndex][]byte sessionID string ) @@ -351,3 +352,119 @@ func TestPreParamsMarshalling(t *testing.T) { t.Errorf("unmarshaled pre params data are invalid") } } + +// --- Benchmarks --- + +func BenchmarkMarshalEphemeralPublicKeyMessage(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex][]byte{ + group.MemberIndex(211): kp1.PublicKey.Marshal(), + group.MemberIndex(19): kp2.PublicKey.Marshal(), + }, + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +func BenchmarkUnmarshalEphemeralPublicKeyMessage(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex][]byte{ + group.MemberIndex(211): kp1.PublicKey.Marshal(), + group.MemberIndex(19): kp2.PublicKey.Marshal(), + }, + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(ephemeralPublicKeyMessage).Unmarshal(data) + } +} + +// buildEphemeralKeyMap generates n key pairs and returns the serialized public +// key map as it would appear in a real EphemeralPublicKeyMessage (one entry per peer). +func buildEphemeralKeyMap(b *testing.B, n int) map[group.MemberIndex][]byte { + b.Helper() + m := make(map[group.MemberIndex][]byte, n) + for i := 0; i < n; i++ { + kp, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + m[group.MemberIndex(i+1)] = kp.PublicKey.Marshal() + } + return m +} + +// BenchmarkMarshalEphemeralPublicKeyMessage_100Keys benchmarks marshaling with +// a realistic group size (100 members = 99 peer keys per message). +func BenchmarkMarshalEphemeralPublicKeyMessage_100Keys(b *testing.B) { + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(1), + ephemeralPublicKeys: buildEphemeralKeyMap(b, 99), + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +// Benchmarks unmarshaling the wire-format bytes. EC point parsing is +// deferred to use-time in generateSymmetricKeys (protocol.go). +func BenchmarkUnmarshalEphemeralPublicKeyMessage_100Keys(b *testing.B) { + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(1), + ephemeralPublicKeys: buildEphemeralKeyMap(b, 99), + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(ephemeralPublicKeyMessage).Unmarshal(data) + } +} + +func BenchmarkRoundTripDKGMessage(b *testing.B) { + msg := &tssRoundTwoMessage{ + senderID: group.MemberIndex(50), + broadcastPayload: []byte{1, 2, 3, 4, 5}, + peersPayload: map[group.MemberIndex][]byte{ + 1: {6, 7, 8, 9, 10}, + 2: {11, 12, 13, 14, 15}, + }, + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + data, _ := msg.Marshal() + _ = new(tssRoundTwoMessage).Unmarshal(data) + } +} diff --git a/pkg/tecdsa/dkg/message.go b/pkg/tecdsa/dkg/message.go index ca9364ac57..fd87992e03 100644 --- a/pkg/tecdsa/dkg/message.go +++ b/pkg/tecdsa/dkg/message.go @@ -1,7 +1,6 @@ package dkg import ( - "github.com/keep-network/keep-core/pkg/crypto/ephemeral" "github.com/keep-network/keep-core/pkg/protocol/group" ) @@ -26,7 +25,7 @@ type message interface { type ephemeralPublicKeyMessage struct { senderID group.MemberIndex - ephemeralPublicKeys map[group.MemberIndex]*ephemeral.PublicKey + ephemeralPublicKeys map[group.MemberIndex][]byte sessionID string } diff --git a/pkg/tecdsa/dkg/protocol.go b/pkg/tecdsa/dkg/protocol.go index de333b62e7..e24f38a800 100644 --- a/pkg/tecdsa/dkg/protocol.go +++ b/pkg/tecdsa/dkg/protocol.go @@ -17,7 +17,7 @@ func (ekpgm *ephemeralKeyPairGeneratingMember) generateEphemeralKeyPair() ( *ephemeralPublicKeyMessage, error, ) { - ephemeralKeys := make(map[group.MemberIndex]*ephemeral.PublicKey) + ephemeralKeys := make(map[group.MemberIndex][]byte) // Calculate ephemeral key pair for every other group member for _, member := range ekpgm.group.MemberIndexes() { @@ -34,8 +34,8 @@ func (ekpgm *ephemeralKeyPairGeneratingMember) generateEphemeralKeyPair() ( // save the generated ephemeral key to our state ekpgm.ephemeralKeyPairs[member] = ephemeralKeyPair - // store the public key to the map for the message - ephemeralKeys[member] = ephemeralKeyPair.PublicKey + // store the serialized public key to the map for the message + ephemeralKeys[member] = ephemeralKeyPair.PublicKey.Marshal() } return &ephemeralPublicKeyMessage{ @@ -78,9 +78,30 @@ func (skgm *symmetricKeyGeneratingMember) generateSymmetricKeys( thisMemberEphemeralPrivateKey := ephemeralKeyPair.PrivateKey // Get the ephemeral public key broadcasted by the other group member, - // which was intended for this group member. - otherMemberEphemeralPublicKey := - ephemeralPubKeyMessage.ephemeralPublicKeys[skgm.id] + // which was intended for this group member, and parse it. Only this + // one key per message is needed for ECDH; the rest are validated for + // presence in isValidEphemeralPublicKeyMessage but never parsed. + otherMemberEphemeralPublicKey, err := ephemeral.UnmarshalPublicKey( + ephemeralPubKeyMessage.ephemeralPublicKeys[skgm.id], + ) + if err != nil { + // A single member's malformed key must not abort this member's + // entire round. Before the deferred-parse optimization, an + // unparseable key failed message unmarshaling at the network + // layer, so the whole message was dropped and the sender was + // simply treated as absent. Preserve that behavior here: skip + // the sender and mark it inactive instead of returning a fatal + // error that aborts this member's async state. + skgm.logger.Warnf( + "[member:%v] could not unmarshal ephemeral public key "+ + "from member [%v]: [%v]; marking member as inactive", + skgm.id, + otherMember, + err, + ) + skgm.group.MarkMemberAsInactive(otherMember) + continue + } // Create symmetric key for the current group member and the other // group member by ECDH'ing the public and private key. diff --git a/pkg/tecdsa/dkg/protocol_test.go b/pkg/tecdsa/dkg/protocol_test.go index c997a29b02..0925fe66e0 100644 --- a/pkg/tecdsa/dkg/protocol_test.go +++ b/pkg/tecdsa/dkg/protocol_test.go @@ -166,16 +166,16 @@ func TestGenerateSymmetricKeys(t *testing.T) { // Assert all symmetric keys stored by this member are correct. for otherMemberID, actualKey := range member.symmetricKeys { - var otherMemberEphemeralPublicKey *ephemeral.PublicKey + var otherMemberEphemeralPublicKeyBytes []byte for _, message := range messages { if message.senderID == otherMemberID { - if ephemeralPublicKey, ok := message.ephemeralPublicKeys[member.id]; ok { - otherMemberEphemeralPublicKey = ephemeralPublicKey + if keyBytes, ok := message.ephemeralPublicKeys[member.id]; ok { + otherMemberEphemeralPublicKeyBytes = keyBytes } } } - if otherMemberEphemeralPublicKey == nil { + if otherMemberEphemeralPublicKeyBytes == nil { t.Errorf( "[member:%v] no ephemeral public key from member [%v]", member.id, @@ -183,6 +183,13 @@ func TestGenerateSymmetricKeys(t *testing.T) { ) } + otherMemberEphemeralPublicKey, err := ephemeral.UnmarshalPublicKey( + otherMemberEphemeralPublicKeyBytes, + ) + if err != nil { + t.Fatalf("could not unmarshal ephemeral public key: %v", err) + } + expectedKey := ephemeral.SymmetricKey( member.ephemeralKeyPairs[otherMemberID].PrivateKey.Ecdh( otherMemberEphemeralPublicKey, @@ -248,6 +255,78 @@ func TestGenerateSymmetricKeys_InvalidEphemeralPublicKeyMessage(t *testing.T) { } } +func TestGenerateSymmetricKeys_CorruptEphemeralPublicKeyBytes(t *testing.T) { + members, messages, err := initializeSymmetricKeyGeneratingMembersGroup( + dishonestThreshold, + groupSize, + ) + if err != nil { + t.Fatal(err) + } + + // Replace member 2's ephemeral public key for member 1 with garbage. + // The key is still present so isValidEphemeralPublicKeyMessage passes; + // only member 1 encounters the parse error during ECDH. + misbehavingMemberID := group.MemberIndex(2) + victimMemberID := group.MemberIndex(1) + messages[misbehavingMemberID-1].ephemeralPublicKeys[victimMemberID] = []byte{0x00, 0x01, 0x02} + + for _, member := range members { + var receivedMessages []*ephemeralPublicKeyMessage + for _, message := range messages { + if message.senderID != member.id { + receivedMessages = append(receivedMessages, message) + } + } + + err := member.generateSymmetricKeys(receivedMessages) + + // A corrupt key from one member must never abort another member's + // entire round: the sender is skipped instead. + if err != nil { + t.Errorf("[member:%v] unexpected error: %v", member.id, err) + } + + expectedKeysCount := groupSize - 1 + if member.id == victimMemberID { + // The victim skips the misbehaving sender, so it stores one + // fewer symmetric key than everyone else. + expectedKeysCount-- + + if _, ok := member.symmetricKeys[misbehavingMemberID]; ok { + t.Errorf( + "[member:%v] expected no symmetric key stored for "+ + "misbehaving member [%v]", + member.id, + misbehavingMemberID, + ) + } + } + + testutils.AssertIntsEqual( + t, + fmt.Sprintf("number of stored symmetric keys for member [%v]", member.id), + expectedKeysCount, + len(member.symmetricKeys), + ) + } + + // All members in this test share a single *group.Group instance (see + // initializeEphemeralKeyPairGeneratingMembersGroup), so the effect of + // the victim marking the misbehaving member inactive is visible from + // any member's reference to it. + if !reflect.DeepEqual( + []group.MemberIndex{misbehavingMemberID}, + members[0].group.InactiveMemberIndexes(), + ) { + t.Errorf( + "expected member [%v] to be marked inactive, got inactive members: %v", + misbehavingMemberID, + members[0].group.InactiveMemberIndexes(), + ) + } +} + func TestTssRoundOne(t *testing.T) { members, err := initializeTssRoundOneMembersGroup( dishonestThreshold, diff --git a/pkg/tecdsa/signing/marshaling.go b/pkg/tecdsa/signing/marshaling.go index 98040ca91c..b55bc43f89 100644 --- a/pkg/tecdsa/signing/marshaling.go +++ b/pkg/tecdsa/signing/marshaling.go @@ -1,3 +1,4 @@ +// marshaling.go: protobuf (un)marshaling for the public types in this package. package signing import ( @@ -5,7 +6,6 @@ import ( "google.golang.org/protobuf/proto" - "github.com/keep-network/keep-core/pkg/crypto/ephemeral" "github.com/keep-network/keep-core/pkg/protocol/group" "github.com/keep-network/keep-core/pkg/tecdsa/signing/gen/pb" ) @@ -13,14 +13,9 @@ import ( // Marshal converts this ephemeralPublicKeyMessage to a byte array suitable for // network communication. func (epkm *ephemeralPublicKeyMessage) Marshal() ([]byte, error) { - ephemeralPublicKeys, err := marshalPublicKeyMap(epkm.ephemeralPublicKeys) - if err != nil { - return nil, err - } - return proto.Marshal(&pb.EphemeralPublicKeyMessage{ SenderID: uint32(epkm.senderID), - EphemeralPublicKeys: ephemeralPublicKeys, + EphemeralPublicKeys: marshalPublicKeyMap(epkm.ephemeralPublicKeys), SessionID: epkm.sessionID, }) } @@ -341,36 +336,27 @@ func validateMemberIndex(protoIndex uint32) error { } func marshalPublicKeyMap( - publicKeys map[group.MemberIndex]*ephemeral.PublicKey, -) (map[uint32][]byte, error) { + publicKeys map[group.MemberIndex][]byte, +) map[uint32][]byte { marshalled := make(map[uint32][]byte, len(publicKeys)) - for id, publicKey := range publicKeys { - if publicKey == nil { - return nil, fmt.Errorf("nil public key for member [%v]", id) - } - - marshalled[uint32(id)] = publicKey.Marshal() + for id, keyBytes := range publicKeys { + marshalled[uint32(id)] = keyBytes } - return marshalled, nil + return marshalled } +// unmarshalPublicKeyMap converts the wire-format map to an internal byte map, +// validating member indices but deferring EC point parsing to use-time so that +// only the one key per message actually needed for ECDH is ever parsed. func unmarshalPublicKeyMap( publicKeys map[uint32][]byte, -) (map[group.MemberIndex]*ephemeral.PublicKey, error) { - var unmarshalled = make(map[group.MemberIndex]*ephemeral.PublicKey, len(publicKeys)) +) (map[group.MemberIndex][]byte, error) { + unmarshalled := make(map[group.MemberIndex][]byte, len(publicKeys)) for memberID, publicKeyBytes := range publicKeys { if err := validateMemberIndex(memberID); err != nil { return nil, err } - - publicKey, err := ephemeral.UnmarshalPublicKey(publicKeyBytes) - if err != nil { - return nil, fmt.Errorf("could not unmarshal public key [%v]", err) - } - - unmarshalled[group.MemberIndex(memberID)] = publicKey - + unmarshalled[group.MemberIndex(memberID)] = publicKeyBytes } - return unmarshalled, nil } diff --git a/pkg/tecdsa/signing/marshaling_test.go b/pkg/tecdsa/signing/marshaling_test.go index 19dd00f858..6535d631b1 100644 --- a/pkg/tecdsa/signing/marshaling_test.go +++ b/pkg/tecdsa/signing/marshaling_test.go @@ -20,9 +20,10 @@ func TestEphemeralPublicKeyMessage_MarshalingRoundtrip(t *testing.T) { t.Fatal(err) } - publicKeys := make(map[group.MemberIndex]*ephemeral.PublicKey) - publicKeys[group.MemberIndex(211)] = keyPair1.PublicKey - publicKeys[group.MemberIndex(19)] = keyPair2.PublicKey + publicKeys := map[group.MemberIndex][]byte{ + group.MemberIndex(211): keyPair1.PublicKey.Marshal(), + group.MemberIndex(19): keyPair2.PublicKey.Marshal(), + } msg := &ephemeralPublicKeyMessage{ senderID: group.MemberIndex(38), @@ -45,7 +46,7 @@ func TestFuzzEphemeralPublicKeyMessage_MarshalingRoundtrip(t *testing.T) { for i := 0; i < 10; i++ { var ( senderID group.MemberIndex - ephemeralPublicKeys map[group.MemberIndex]*ephemeral.PublicKey + ephemeralPublicKeys map[group.MemberIndex][]byte sessionID string ) @@ -512,3 +513,165 @@ func TestFuzzTssRoundNineMessage_MarshalingRoundtrip(t *testing.T) { func TestFuzzTssRoundNineMessage_Unmarshaler(t *testing.T) { pbutils.FuzzUnmarshaler(&tssRoundNineMessage{}) } + +// --- Benchmarks --- + +func BenchmarkMarshalEphemeralPublicKeyMessage(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex][]byte{ + group.MemberIndex(211): kp1.PublicKey.Marshal(), + group.MemberIndex(19): kp2.PublicKey.Marshal(), + }, + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +func BenchmarkUnmarshalEphemeralPublicKeyMessage(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex][]byte{ + group.MemberIndex(211): kp1.PublicKey.Marshal(), + group.MemberIndex(19): kp2.PublicKey.Marshal(), + }, + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(ephemeralPublicKeyMessage).Unmarshal(data) + } +} + +// buildEphemeralKeyMap generates n key pairs and returns the serialized public +// key map as it would appear in a real EphemeralPublicKeyMessage (one entry per peer). +func buildEphemeralKeyMap(b *testing.B, n int) map[group.MemberIndex][]byte { + b.Helper() + m := make(map[group.MemberIndex][]byte, n) + for i := 0; i < n; i++ { + kp, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + m[group.MemberIndex(i+1)] = kp.PublicKey.Marshal() + } + return m +} + +// BenchmarkMarshalEphemeralPublicKeyMessage_100Keys benchmarks marshaling with +// a realistic group size (100 members = 99 peer keys per message). +func BenchmarkMarshalEphemeralPublicKeyMessage_100Keys(b *testing.B) { + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(1), + ephemeralPublicKeys: buildEphemeralKeyMap(b, 99), + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +// Benchmarks unmarshaling the wire-format bytes. EC point parsing is +// deferred to use-time in generateSymmetricKeys (protocol.go). +func BenchmarkUnmarshalEphemeralPublicKeyMessage_100Keys(b *testing.B) { + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(1), + ephemeralPublicKeys: buildEphemeralKeyMap(b, 99), + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(ephemeralPublicKeyMessage).Unmarshal(data) + } +} + +// BenchmarkMarshalSigningShareMessage benchmarks the heaviest per-member +// message in a signing round: round-one carries both broadcast and peer +// payloads. +func BenchmarkMarshalSigningShareMessage(b *testing.B) { + msg := &tssRoundOneMessage{ + senderID: group.MemberIndex(50), + broadcastPayload: []byte{1, 2, 3, 4, 5}, + peersPayload: map[group.MemberIndex][]byte{ + 1: {6, 7, 8, 9, 10}, + 2: {11, 12, 13, 14, 15}, + }, + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + _, _ = msg.Marshal() + } +} + +func BenchmarkUnmarshalSigningShareMessage(b *testing.B) { + msg := &tssRoundOneMessage{ + senderID: group.MemberIndex(50), + broadcastPayload: []byte{1, 2, 3, 4, 5}, + peersPayload: map[group.MemberIndex][]byte{ + 1: {6, 7, 8, 9, 10}, + 2: {11, 12, 13, 14, 15}, + }, + sessionID: "session-1", + } + data, err := msg.Marshal() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for range b.N { + _ = new(tssRoundOneMessage).Unmarshal(data) + } +} + +func BenchmarkRoundTripEphemeralKey(b *testing.B) { + kp1, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + kp2, err := ephemeral.GenerateKeyPair() + if err != nil { + b.Fatal(err) + } + msg := &ephemeralPublicKeyMessage{ + senderID: group.MemberIndex(38), + ephemeralPublicKeys: map[group.MemberIndex][]byte{ + group.MemberIndex(211): kp1.PublicKey.Marshal(), + group.MemberIndex(19): kp2.PublicKey.Marshal(), + }, + sessionID: "session-1", + } + b.ResetTimer() + for range b.N { + data, _ := msg.Marshal() + _ = new(ephemeralPublicKeyMessage).Unmarshal(data) + } +} diff --git a/pkg/tecdsa/signing/message.go b/pkg/tecdsa/signing/message.go index df2980e4bf..7b7c6d6d38 100644 --- a/pkg/tecdsa/signing/message.go +++ b/pkg/tecdsa/signing/message.go @@ -1,7 +1,6 @@ package signing import ( - "github.com/keep-network/keep-core/pkg/crypto/ephemeral" "github.com/keep-network/keep-core/pkg/protocol/group" ) @@ -26,7 +25,7 @@ type message interface { type ephemeralPublicKeyMessage struct { senderID group.MemberIndex - ephemeralPublicKeys map[group.MemberIndex]*ephemeral.PublicKey + ephemeralPublicKeys map[group.MemberIndex][]byte sessionID string } diff --git a/pkg/tecdsa/signing/protocol.go b/pkg/tecdsa/signing/protocol.go index 9814a0c1a9..2c13ff641d 100644 --- a/pkg/tecdsa/signing/protocol.go +++ b/pkg/tecdsa/signing/protocol.go @@ -17,7 +17,7 @@ func (ekpgm *ephemeralKeyPairGeneratingMember) generateEphemeralKeyPair() ( *ephemeralPublicKeyMessage, error, ) { - ephemeralKeys := make(map[group.MemberIndex]*ephemeral.PublicKey) + ephemeralKeys := make(map[group.MemberIndex][]byte) // Calculate ephemeral key pair for every other group member for _, member := range ekpgm.group.MemberIndexes() { @@ -34,8 +34,8 @@ func (ekpgm *ephemeralKeyPairGeneratingMember) generateEphemeralKeyPair() ( // save the generated ephemeral key to our state ekpgm.ephemeralKeyPairs[member] = ephemeralKeyPair - // store the public key to the map for the message - ephemeralKeys[member] = ephemeralKeyPair.PublicKey + // store the serialized public key to the map for the message + ephemeralKeys[member] = ephemeralKeyPair.PublicKey.Marshal() } return &ephemeralPublicKeyMessage{ @@ -78,9 +78,30 @@ func (skgm *symmetricKeyGeneratingMember) generateSymmetricKeys( thisMemberEphemeralPrivateKey := ephemeralKeyPair.PrivateKey // Get the ephemeral public key broadcasted by the other group member, - // which was intended for this group member. - otherMemberEphemeralPublicKey := - ephemeralPubKeyMessage.ephemeralPublicKeys[skgm.id] + // which was intended for this group member, and parse it. Only this + // one key per message is needed for ECDH; the rest are validated for + // presence in isValidEphemeralPublicKeyMessage but never parsed. + otherMemberEphemeralPublicKey, err := ephemeral.UnmarshalPublicKey( + ephemeralPubKeyMessage.ephemeralPublicKeys[skgm.id], + ) + if err != nil { + // A single member's malformed key must not abort this member's + // entire round. Before the deferred-parse optimization, an + // unparseable key failed message unmarshaling at the network + // layer, so the whole message was dropped and the sender was + // simply treated as absent. Preserve that behavior here: skip + // the sender and mark it inactive instead of returning a fatal + // error that aborts this member's async state. + skgm.logger.Warnf( + "[member:%v] could not unmarshal ephemeral public key "+ + "from member [%v]: [%v]; marking member as inactive", + skgm.id, + otherMember, + err, + ) + skgm.group.MarkMemberAsInactive(otherMember) + continue + } // Create symmetric key for the current group member and the other // group member by ECDH'ing the public and private key. diff --git a/pkg/tecdsa/signing/protocol_test.go b/pkg/tecdsa/signing/protocol_test.go index d5bc520379..d5533bdd64 100644 --- a/pkg/tecdsa/signing/protocol_test.go +++ b/pkg/tecdsa/signing/protocol_test.go @@ -179,16 +179,16 @@ func TestGenerateSymmetricKeys(t *testing.T) { // Assert all symmetric keys stored by this member are correct. for otherMemberID, actualKey := range member.symmetricKeys { - var otherMemberEphemeralPublicKey *ephemeral.PublicKey + var otherMemberEphemeralPublicKeyBytes []byte for _, message := range messages { if message.senderID == otherMemberID { - if ephemeralPublicKey, ok := message.ephemeralPublicKeys[member.id]; ok { - otherMemberEphemeralPublicKey = ephemeralPublicKey + if keyBytes, ok := message.ephemeralPublicKeys[member.id]; ok { + otherMemberEphemeralPublicKeyBytes = keyBytes } } } - if otherMemberEphemeralPublicKey == nil { + if otherMemberEphemeralPublicKeyBytes == nil { t.Errorf( "[member:%v] no ephemeral public key from member [%v]", member.id, @@ -196,6 +196,13 @@ func TestGenerateSymmetricKeys(t *testing.T) { ) } + otherMemberEphemeralPublicKey, err := ephemeral.UnmarshalPublicKey( + otherMemberEphemeralPublicKeyBytes, + ) + if err != nil { + t.Fatalf("could not unmarshal ephemeral public key: %v", err) + } + expectedKey := ephemeral.SymmetricKey( member.ephemeralKeyPairs[otherMemberID].PrivateKey.Ecdh( otherMemberEphemeralPublicKey, @@ -261,6 +268,78 @@ func TestGenerateSymmetricKeys_InvalidEphemeralPublicKeyMessage(t *testing.T) { } } +func TestGenerateSymmetricKeys_CorruptEphemeralPublicKeyBytes(t *testing.T) { + members, messages, err := initializeSymmetricKeyGeneratingMembersGroup( + dishonestThreshold, + groupSize, + ) + if err != nil { + t.Fatal(err) + } + + // Replace member 2's ephemeral public key for member 1 with garbage. + // The key is still present so isValidEphemeralPublicKeyMessage passes; + // only member 1 encounters the parse error during ECDH. + misbehavingMemberID := group.MemberIndex(2) + victimMemberID := group.MemberIndex(1) + messages[misbehavingMemberID-1].ephemeralPublicKeys[victimMemberID] = []byte{0x00, 0x01, 0x02} + + for _, member := range members { + var receivedMessages []*ephemeralPublicKeyMessage + for _, message := range messages { + if message.senderID != member.id { + receivedMessages = append(receivedMessages, message) + } + } + + err := member.generateSymmetricKeys(receivedMessages) + + // A corrupt key from one member must never abort another member's + // entire round: the sender is skipped instead. + if err != nil { + t.Errorf("[member:%v] unexpected error: %v", member.id, err) + } + + expectedKeysCount := groupSize - 1 + if member.id == victimMemberID { + // The victim skips the misbehaving sender, so it stores one + // fewer symmetric key than everyone else. + expectedKeysCount-- + + if _, ok := member.symmetricKeys[misbehavingMemberID]; ok { + t.Errorf( + "[member:%v] expected no symmetric key stored for "+ + "misbehaving member [%v]", + member.id, + misbehavingMemberID, + ) + } + } + + testutils.AssertIntsEqual( + t, + fmt.Sprintf("number of stored symmetric keys for member [%v]", member.id), + expectedKeysCount, + len(member.symmetricKeys), + ) + } + + // All members in this test share a single *group.Group instance (see + // initializeEphemeralKeyPairGeneratingMembersGroup), so the effect of + // the victim marking the misbehaving member inactive is visible from + // any member's reference to it. + if !reflect.DeepEqual( + []group.MemberIndex{misbehavingMemberID}, + members[0].group.InactiveMemberIndexes(), + ) { + t.Errorf( + "expected member [%v] to be marked inactive, got inactive members: %v", + misbehavingMemberID, + members[0].group.InactiveMemberIndexes(), + ) + } +} + func TestTssRoundOne(t *testing.T) { members, err := initializeTssRoundOneMembersGroup( dishonestThreshold, diff --git a/tools.go b/tools.go index e0dacdde1c..a594b4bc48 100644 --- a/tools.go +++ b/tools.go @@ -1,8 +1,10 @@ //go:build tools -// tools.go: Build-time dependencies required for Ethereum bindings generation -// These are imported to ensure they remain in go.mod and go.sum even though -// they're not directly used in the runtime code. +// tools.go pins dependencies that `go mod tidy` would otherwise drop +// because they are only referenced under the `tools` build tag (or are +// no longer referenced at all). They remain in go.mod / go.sum so version +// resolution stays reproducible for codegen and tooling that does pull +// them in. package tools import (