From 50ef8994931fc5d6540b5cd65e803ff583e98f37 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 15 Oct 2024 12:13:09 +0200 Subject: [PATCH 001/272] chore(testnet): holesky launch folder stub --- testnets/holesky/README.md | 106 +++ testnets/holesky/cb-bolt-config.toml | 158 ++++ .../grafana/dashboards/bolt_dashboard.json | 238 +++++ .../holesky/grafana/dashboards/dashboard.json | 803 +++++++++++++++++ .../holesky/grafana/dashboards/dashboards.yml | 13 + .../grafana/dashboards/system_metrics.json | 853 ++++++++++++++++++ .../grafana/datasources/datasources.yml | 11 + testnets/holesky/keys.json | 4 + testnets/holesky/prometheus.yml | 8 + testnets/holesky/targets.json | 34 + testnets/holesky/update-grafana.sh | 5 + 11 files changed, 2233 insertions(+) create mode 100644 testnets/holesky/README.md create mode 100644 testnets/holesky/cb-bolt-config.toml create mode 100644 testnets/holesky/grafana/dashboards/bolt_dashboard.json create mode 100644 testnets/holesky/grafana/dashboards/dashboard.json create mode 100644 testnets/holesky/grafana/dashboards/dashboards.yml create mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json create mode 100644 testnets/holesky/grafana/datasources/datasources.yml create mode 100644 testnets/holesky/keys.json create mode 100644 testnets/holesky/prometheus.yml create mode 100644 testnets/holesky/targets.json create mode 100755 testnets/holesky/update-grafana.sh diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md new file mode 100644 index 000000000..c031285f3 --- /dev/null +++ b/testnets/holesky/README.md @@ -0,0 +1,106 @@ +# Holesky Launch Instructions + +## Components + +The components that need to run to test Bolt on Holesky are: + +- A synced execution client +- A synced beacon node +- Active validators +- Commit-Boost with Bolt configuration + +## Setup + +### Commit-Boost + +#### Installation + +To install the `commit-boost` CLI with `cargo`: + +```bash +# Use specific commit hash to ensure compatibility +cargo install --locked --git https://github.com/Commit-Boost/commit-boost-client --rev aed00e8 commit-boost + +# Test installation +commit-boost --version +``` + +#### Configuration + +A commit-boost configuration file with Bolt support is provided at +[`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the +custom PBS module ([bolt-boost](../../bolt-boost)) that implements the +[constraints-API](https://chainbound.github.io/bolt-docs/api/builder), as well +as the [bolt-sidecar](../../bolt-sidecar) module. This file can be used as a +template for your own configuration. + +The important fields to configure are under the `[modules.env]` section of the +`BOLT` module, which contain the environment variables to configure the bolt +sidecar: + +```toml +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" + +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module (static) +BOLT_SIDECAR_BEACON_API = "" +BOLT_SIDECAR_EXECUTION_API = "" +BOLT_SIDECAR_ENGINE_API = "" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "" # The engine JWT used to authenticate with the engine API +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "" # The active validator indexes (can be defined as a comma-separated list, or a range) + # e.g. "0,1,2,3,4" or "0..4", or a combination of both +``` + +To initialize commit-boost, run the following command: + +```bash +commit-boost init --config cb-bolt-config.toml +``` + +This will create 3 files: + +- `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services +- `.cb.env`: with local env variables, including JWTs for modules +- `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus + +#### Running + +The final step is to run the Commit-Boost services. This can be done with the following command: + +```bash +commit-boost start --docker cb.docker-compose.yml --env .cb.env +``` + +This will run all modules in Docker containers. + +> [!IMPORTANT] +> bolt-boost will be exposed at `pbs.port` (18551 by default, set with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be configured +> to point the `builder-api` to this port for Bolt to work. + +### Bolt Sidecar + +WIP + +### Observability + +commit-boost comes with various observability tools, such as Prometheus, cadvisor, and Grafana. It also comes with some pre-built dashboards, +which can be found in the `grafana` directory. + +To update these dashboards, run the following command: + +```bash +./update-grafana.sh +``` + +In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. + +### Validators + +Validators must be configured to always prefer builder proposals over their own. Refer to client documentation for the specific configuration flags. +**If this is not set, it could lead to commitment faults**. + +#### Registration + +WIP diff --git a/testnets/holesky/cb-bolt-config.toml b/testnets/holesky/cb-bolt-config.toml new file mode 100644 index 000000000..be4e35002 --- /dev/null +++ b/testnets/holesky/cb-bolt-config.toml @@ -0,0 +1,158 @@ +# The main configuration file for the Commit-Boost sidecar. +# Some fields are optional and can be omitted, in which case the default value, if present, will be used. + +# Chain spec id. Supported values: Mainnet, Holesky, Helder +chain = "Holesky" + +# Configuration for the PBS module +[pbs] +# Docker image to use for the PBS module. +# BOLT: We use the bolt-boost PBS module here. +docker_image = "ghcr.io/chainbound/bolt-boost:v0.3.0-alpha-rc.1" +# Whether to enable the PBS module to request signatures from the Signer module (not used in the default PBS image) +# OPTIONAL, DEFAULT: false +with_signer = false +# Port to receive BuilderAPI calls from beacon node +port = 18550 +# Whether to forward `status` calls to relays or skip and return 200 +# OPTIONAL, DEFAULT: true +relay_check = true +# Timeout in milliseconds for the `get_header` call to relays. Note that the CL has also a timeout (e.g. 1 second) so +# this should be lower than that, leaving some margin for overhead +# OPTIONAL, DEFAULT: 950 +timeout_get_header_ms = 950 +# Timeout in milliseconds for the `submit_blinded_block` call to relays. +# OPTIONAL, DEFAULT: 4000 +timeout_get_payload_ms = 4000 +# Timeout in milliseconds for the `register_validator` call to relays. +# OPTIONAL, DEFAULT: 3000 +timeout_register_validator_ms = 3000 +# Whether to skip signature verification of headers against the relay pubkey +# OPTIONAL, DEFAULT: false +skip_sigverify = false +# Minimum bid in ETH that will be accepted from `get_header` +# OPTIONAL, DEFAULT: 0.0 +min_bid_eth = 0.0 +# List of URLs of relay monitors to send registrations to +# OPTIONAL +relay_monitors = [] +# How late in milliseconds in the slot is "late". This impacts the `get_header` requests, by shortening timeouts for `get_header` calls to +# relays and make sure a header is returned within this deadline. If the request from the CL comes later in the slot, then fetching headers is skipped +# to force local building and miniminzing the risk of missed slots. See also the timing games section below +# OPTIONAL, DEFAULT: 2000 +late_in_slot_time_ms = 1000 + +# The PBS module needs one or more [[relays]] as defined below. +[[relays]] +# Relay ID to use in telemetry +# OPTIONAL, DEFAULT: URL hostname +id = "example-relay" +# Relay URL in the format scheme://pubkey@host +url = "http://0xa1cec75a3f0661e99299274182938151e8433c61a19222347ea1313d839229cb4ce4e3e5aa2bdeb71c8fcf1b084963c2@abc.xyz" +# Headers to send with each request for this relay +# OPTIONAL +# headers = { X-MyCustomHeader = "MyCustomValue" } +# Whether to enable timing games, as tuned by `target_first_request_ms` and `frequency_get_header_ms`. +# These values should be carefully chosen for each relay, as each relay has different latency and timing games setups. +# They should only be used by advanced users, and if mis-configured can result in unforeseen effects, e.g. fetching a lower header value, +# or getting a temporary IP ban. +# +# EXAMPLES +# Assuming: timeout_get_header_ms = 950, frequency_get_header_ms = 300, target_first_request_ms = 200, late_in_slot_time_ms = 2000 +# +# 1) CL request comes at 100ms in the slot (max timeout 1050ms in the slot), then: +# - sleep for 100ms +# - send request at 200ms with 850ms timeout +# - send request at 500ms with 550ms timeout +# - send request at 800ms with 250ms timeout +# 2) CL request comes at 1500ms in the slot (max timeout 2000ms in the slot), then: +# - send request at 1500ms with 500ms timeout +# - send request at 1800ms with 200ms timeout +# 3) CL request comes 2500ms in the slot then: +# - return 204 and force local build +# +# OPTIONAL, DEFAULT: false +enable_timing_games = false +# Target time in slot when to send the first header request +# OPTIONAL +target_first_request_ms = 200 +# Frequency in ms to send get_header requests +# OPTIONAL +frequency_get_header_ms = 300 + +# Configuration for the Signer Module, only required if any `commit` module is present, or if `pbs.with_signer = true` +# OPTIONAL +[signer] +# Docker image to use for the Signer module. +# OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest +docker_image = "commitboost_signer" +# Configuration for how the Signer module should load validator keys. Currently two types of loaders are supported: +# - File: load keys from a plain text file (unsafe, use only for testing purposes) +# - ValidatorsDir: load keys from a `keys` and `secrets` folder (ERC-2335 style keystores as used in Lighthouse) +[signer.loader] +# File: path to the keys file +key_path = "./keys.json" +# ValidatorsDir: path to the keys directory +# keys_path = "" +# ValidatorsDir: path to the secrets directory +# secrets_path = "" + +# Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. +# Currently, two types of modules are supported: +# - "commit": modules which request commitment signatures from the validator keys +# - "events": modules which callback to BuilderAPI events as triggered from the PBS modules, used e.g. for monitoring +# If any "commit" module is present, then the [signer] section should also be configured +# OPTIONAL +[[modules]] +# Unique ID of the module +id = "BOLT" +# Type of the module. Supported values: commit, events +type = "commit" +# Docker image of the module +docker_image = "ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1" + +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" + +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module +BOLT_SIDECAR_BEACON_API = "http://100.85.200.41:4400" +BOLT_SIDECAR_EXECUTION_API = "http://100.85.200.41:4485" +BOLT_SIDECAR_ENGINE_API = "http://100.85.200.41:4451" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "89732cef77d7e9a20021ee8f419dbbb51bdf7f60586932c272ddef02e70cb755" # The engine JWT +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "0x0000000000000000000000000000000000000000" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "1..2" # The active validator indexes + +BOLT_SIDECAR_METRICS_PORT = "10000" + +# Configuration for how metrics should be collected and scraped +# OPTIONAL, skip metrics collection if missing +[metrics] +# Path to a `prometheus.yml` file to use in Prometheus. If using a custom config file, be sure to add a +# file discovery section as follows: +# ```yml +# file_sd_configs: +# - files: +# - /etc/prometheus/targets.json +# ``` +# and use the `targets.json` file generated by `commit-boost init` +prometheus_config = "./prometheus.yml" +# Whether to start Grafana with built-in dashboards +# OPTIONAL, DEFAULT: true +use_grafana = true +# Whether to start cadvisor for system monitoring +# OPTIONAL, DEFAULT: true +use_cadvisor = true + +# Configuration for how logs should be collected and stored +# OPTIONAL, info to stdout if missing +[logs] +# Path to the log directory +# OPTIONAL, DEFAULT: /var/logs/commit-boost +log_dir_path = "./logs" +# Log level. Supported values: trace, debug, info, warn, error +# OPTIONAL, DEFAULT: debug to file, info to stdout +log_level = "debug" +# Maximum number of log files to keep +# OPTIONAL +max_log_files = 30 diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json new file mode 100644 index 000000000..886ddfe02 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/bolt_dashboard.json @@ -0,0 +1,238 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics related to the bolt-sidecar and bolt-boost.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Bolt sidecar", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Latest head", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 4, + "panels": [], + "title": "Bolt boost", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "cb_pbs_constraints_cache_size", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Constraints cache size", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Bolt Metrics", + "uid": "edxnwlpgaw934c", + "version": 3, + "weekStart": "" +} diff --git a/testnets/holesky/grafana/dashboards/dashboard.json b/testnets/holesky/grafana/dashboards/dashboard.json new file mode 100644 index 000000000..f903affb2 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/dashboard.json @@ -0,0 +1,803 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": true, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 12, + "panels": [], + "repeat": "endpoint", + "repeatDirection": "h", + "title": "$endpoint calls", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P50", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P90", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 12 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P99", + "type": "timeseries" + } + ], + "refresh": "5m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "description": "BuilderAPI endpoint", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "endpoint", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "get_header", + "value": "get_header" + }, + { + "selected": false, + "text": "submit_blinded_block", + "value": "submit_blinded_block" + }, + { + "selected": false, + "text": "register_validator", + "value": "register_validator" + } + ], + "query": "register_validator, get_header, submit_blinded_block", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PBS Metrics", + "uid": "cb_prometheus", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/testnets/holesky/grafana/dashboards/dashboards.yml b/testnets/holesky/grafana/dashboards/dashboards.yml new file mode 100644 index 000000000..db50699f9 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/dashboards.yml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: "default" + orgId: 1 + folder: "" + folderUid: "" + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..93b649d80 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/system_metrics.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus as the datasource is obligatory", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.4.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 14282, + "graphTooltip": 0, + "id": null, + "iteration": 1617715580880, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Cached", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Received Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:674", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:675", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Sent Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:832", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:833", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": [ + "cadvisor", + "docker" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Commit-Boost System Metrics", + "uid": "pMEd7m0Mz", + "version": 1, + "description": "Simple exporter for cadvisor only" +} \ No newline at end of file diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml new file mode 100644 index 000000000..6d29d617b --- /dev/null +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: cb-prometheus + type: prometheus + uid: cb_prometheus + access: proxy + orgId: 1 + url: http://cb_prometheus:9090 + isDefault: true + editable: true diff --git a/testnets/holesky/keys.json b/testnets/holesky/keys.json new file mode 100644 index 000000000..4689c5d75 --- /dev/null +++ b/testnets/holesky/keys.json @@ -0,0 +1,4 @@ +[ + "0088e364a5396a81b50febbdc8784663fb9089b5e67cbdc173991a00c587673f", + "0x16f3bec1b7f4f1b87c5e1930f944a6dda76ad211a89bc98e8c8e88b5069f8a04" +] diff --git a/testnets/holesky/prometheus.yml b/testnets/holesky/prometheus.yml new file mode 100644 index 000000000..91b5d5905 --- /dev/null +++ b/testnets/holesky/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "commit-boost" + file_sd_configs: + - files: + - /etc/prometheus/targets.json diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json new file mode 100644 index 000000000..6eb23eea1 --- /dev/null +++ b/testnets/holesky/targets.json @@ -0,0 +1,34 @@ +[ + { + "targets": [ + "cb_bolt:10000" + ], + "labels": { + "job": "cb_bolt" + } + }, + { + "targets": [ + "cb_pbs:10000" + ], + "labels": { + "job": "pbs" + } + }, + { + "targets": [ + "cb_signer:10000" + ], + "labels": { + "job": "signer" + } + }, + { + "targets": [ + "cb_cadvisor:8080" + ], + "labels": { + "job": "cadvisor" + } + } +] \ No newline at end of file diff --git a/testnets/holesky/update-grafana.sh b/testnets/holesky/update-grafana.sh new file mode 100755 index 000000000..4e5a16695 --- /dev/null +++ b/testnets/holesky/update-grafana.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# Fetches the latest dashboards from commit-boost main +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json From beb91dad3554c52dc2b86ae67926e3aa397cdf59 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:25:01 +0200 Subject: [PATCH 002/272] git(sidecar): add bolt-sidecar binaries to .gitignore --- bolt-sidecar/.gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 55509a805..2e73cb821 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -1,4 +1,4 @@ target/ -.env -.env.* +.env* !.env.example +bolt-sidecar* From 9440b845655a76d629ade6abf871b1855b1a55f6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:25:35 +0200 Subject: [PATCH 003/272] docker(sidecar): remove redundant dependencies during build --- bolt-sidecar/Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bolt-sidecar/Dockerfile b/bolt-sidecar/Dockerfile index 99c0f839c..fd3e55d37 100644 --- a/bolt-sidecar/Dockerfile +++ b/bolt-sidecar/Dockerfile @@ -23,10 +23,7 @@ FROM base AS builder RUN apt-get update && apt-get install -y \ pkg-config \ libssl-dev \ - build-essential \ - perl \ - gcc \ - make + build-essential # Copy the generated recipe from the planner stage COPY --from=planner /app/recipe.json recipe.json From 1f37749aed91c3cb38ea4cd04865652f19a33ca9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:12:48 +0200 Subject: [PATCH 004/272] chore!(sidecar): rename env for telemetry options --- bolt-sidecar/src/config/telemetry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index 33a2f3ac8..b0ef87da4 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -4,9 +4,9 @@ use serde::Deserialize; #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics - #[clap(short, long, env = "METRICS_PORT", default_value_t = 3300)] + #[clap(short, long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] metrics_port: u16, - #[clap(short, long, env = "DISABLE_METRICS", default_value_t = false)] + #[clap(short, long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] disable_metrics: bool, } From 8b72e2000cfe6401ac70a479d4c5029cc0d9d5a4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:17:20 +0200 Subject: [PATCH 005/272] chore(sidecar): update .env.example --- bolt-sidecar/.env.example | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index cb234a599..98bf60852 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,33 +1,34 @@ -# Ethereum node connections +# Ethereum Node Connections + PBS URLs +BOLT_SIDECAR_PORT=8000 BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485 BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400 BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 -BOLT_SIDECAR_ENGINE_JWT_HEX= - -# Constraint URL: should point to the constraint API sidecar. -# Usually this corresponds to `mev-boost` or `bolt-boost` BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 - -# Commit-boost specific options (optional) -BOLT_SIDECAR_CB_SIGNER_URL=http://localhost:19551 -BOLT_SIDECAR_CB_JWT_HEX= - -# server ports -BOLT_SIDECAR_PORT=8000 BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 +BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 +BOLT_SIDECAR_JWT_HEX= +BOLT_SIDECAR_FEE_RECIPIENT= +BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -# commitment limits +# Commitments configs BOLT_SIDECAR_MAX_COMMITMENTS=128 -BOLT_SIDECAR_MAX_COMMITTED_GAS=10000000 - -# chain configs -BOLT_SIDECAR_CHAIN=holesky +BOLT_SIDECAR_MAX_COMMITTED_GAS= +BOLT_SIDECAR_MIN_PRIORITY_FEE= BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 + +# Chain configs +BOLT_SIDECAR_CHAIN=Holesky BOLT_SIDECAR_SLOT_TIME=12 -# sidecar security configs -BOLT_SIDECAR_VALIDATOR_INDEXES= -BOLT_SIDECAR_FEE_RECIPIENT= -BOLT_SIDECAR_BUILDER_PRIVATE_KEY= +# Signing options BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= + +# Metrics +BOLT_SIDECAR_METRICS_PORT= +BOLT_SIDECAR_DISABLE_METRICS= From 68be0b093ecf6e6d24a0bbd64e04a4a665c79ff6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:46:16 +0200 Subject: [PATCH 006/272] chore(sidecar): update Config.example.toml --- bolt-sidecar/Config.example.toml | 51 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index b73e925da..e6caad16e 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -1,34 +1,37 @@ -# ports +# Ethereum Node Connections + PBS URLs port = 8000 -metrics_port = 3300 - -# node urls -execution_api_url = "http://localhost:8545" -beacon_api_url = "http://localhost:5052" -engine_api_url = "http://localhost:8551" -engine_jwt_hex = "0x573300d0cd9a8e253429998a3ceecf358aa4868d96772d1344a80144d3b7b593" - -# constraints options -constraints_api_url = "http://localhost:3030" +execution_api_url = "http://localhost:4485" +beacon_api_url = "http://localhost:4400" +engine_api_url = "http://localhost:4451" +constraints_url = "http://localhost:19550" constraints_proxy_port = 18551 - validator_indexes = "0..64" -fee_recipient = "0x0155ef0C0fE550C297c1216585e0DE1478EA30e4" +jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +fee_recipient = "0x0000000000000000000000000000000000000000" +builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -builder_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" -commitment_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +# Commitments configs +max_commitments = 128 +max_committed_gas = 10000000 +min_priority_fee = 5000000 -# chain options +# Chain configs [chain] -chain = "Kurtosis" -slot_time = 2 +chain = "Holesky" +slot_time = 12 commitment_deadline = 8000 -[telemetry] -metrics_port = 3300 -disable_metrics = false - -# signing options +# Signing options [constraint_signing] -constraint_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +cb_signer_url = "http://localhost:18550" +cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +keystore_password = "password" +keystore_path = "./keys" delegations_path = "./delegations.json" + +# Metrics +[telemetry] +metrics_port = 8001 +disable_metrics = false From ed565011afca89ba4e2cde8f7d7926ff0df3c987 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 14:03:17 +0200 Subject: [PATCH 007/272] chore!(sidecar): remove short options from telemetry config --- bolt-sidecar/src/config/telemetry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index b0ef87da4..01bc179d6 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -4,9 +4,9 @@ use serde::Deserialize; #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics - #[clap(short, long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] + #[clap(long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] metrics_port: u16, - #[clap(short, long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] + #[clap(long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] disable_metrics: bool, } From a2592203d8d5780cb8ab16044ea93d55fbd47b36 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 10:38:24 +0200 Subject: [PATCH 008/272] chore(holesky): add .gitignore --- testnets/holesky/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testnets/holesky/.gitignore diff --git a/testnets/holesky/.gitignore b/testnets/holesky/.gitignore new file mode 100644 index 000000000..ef456d924 --- /dev/null +++ b/testnets/holesky/.gitignore @@ -0,0 +1,2 @@ +*.env* +!*.env.example From dba39b5eaa6f2211bd591b35df8118dbca7ed506 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:00:17 +0200 Subject: [PATCH 009/272] chore(mev-boost): add .env.example --- mev-boost/.env.example | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 mev-boost/.env.example diff --git a/mev-boost/.env.example b/mev-boost/.env.example new file mode 100644 index 000000000..447b2fab8 --- /dev/null +++ b/mev-boost/.env.example @@ -0,0 +1,32 @@ +# Logging settings +LOG_JSON=false # Set to true to log in JSON format +LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic +DEBUG=false # Set to true to enable debug mode +LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=false # Set to true to disable logging the version + +# Server settings +BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup + +# Relay settings +RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) +RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) +MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) + +# Relay timeout settings (in ms) +RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay +RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay +RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests + +# Genesis settings +GENESIS_FORK_VERSION= # Custom genesis fork version +GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) + +# Network settings +SEPOLIA=false # Set to true to use Sepolia network +GOERLI=false # Set to true to use Goerli network +HOLESKY=false # Set to true to use Holesky network + +# Retry settings +REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From 6f3766933399c34f94d456f0a682426db18e34fe Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:23:12 +0200 Subject: [PATCH 010/272] chore(mev-boost): update .gitignore --- mev-boost/.gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mev-boost/.gitignore b/mev-boost/.gitignore index 5a0fe0294..c919b8e0d 100644 --- a/mev-boost/.gitignore +++ b/mev-boost/.gitignore @@ -27,4 +27,7 @@ .vscode/ /README.internal.md /validator_data.json -/build/ \ No newline at end of file +/build/ + +*.env +!.env.example From cecbdcfc3cf844f5fb7175157e43aeda29d962b4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:57:00 +0200 Subject: [PATCH 011/272] chore(holesky): update docker compose setup --- testnets/holesky/docker-compose.yml | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 testnets/holesky/docker-compose.yml diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml new file mode 100644 index 000000000..52ef4da0c --- /dev/null +++ b/testnets/holesky/docker-compose.yml @@ -0,0 +1,58 @@ +services: + bolt-sidecar: + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1 + container_name: bolt-sidecar + restart: unless-stopped + ports: + - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) + - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" + env_file: ./bolt-sidecar.env + entrypoint: /bin/sh -c /bolt-sidecar + + mev-boost: + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha-rc.1 + container_name: mev-boost + restart: unless-stopped + env_file: ./mev-boost.env + entrypoint: /bin/sh -c '/app/mev-boost' + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - 9090:9090 + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - ./targets.json:/etc/prometheus/targets.json + - prometheus-data:/prometheus + networks: + - monitoring_network + + grafana: + image: grafana/grafana:latest + container_name: cb_grafana + ports: + - 3000:3000 + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - ./grafana/dashboards:/etc/grafana/provisioning/dashboards + - ./grafana/datasources:/etc/grafana/provisioning/datasources + - grafana-data:/var/lib/grafana + networks: + - monitoring_network + depends_on: + - prometheus + logging: + driver: none + +volumes: + prometheus-data: + driver: local + grafana-data: + driver: local +networks: + monitoring_network: + driver: bridge + signer_network: + driver: bridge From 039a4c716eec0cf6864d2c5258aa30591113f488 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:57:36 +0200 Subject: [PATCH 012/272] chore(holesky): move commit-boost config in separate directory --- testnets/holesky/{ => commit-boost}/cb-bolt-config.toml | 2 +- testnets/holesky/commit-boost/cb-prometheus.yml | 8 ++++++++ testnets/holesky/{ => commit-boost}/keys.json | 0 testnets/holesky/{ => commit-boost}/targets.json | 0 testnets/holesky/{ => commit-boost}/update-grafana.sh | 4 ++-- testnets/holesky/prometheus.yml | 4 ++-- 6 files changed, 13 insertions(+), 5 deletions(-) rename testnets/holesky/{ => commit-boost}/cb-bolt-config.toml (99%) create mode 100644 testnets/holesky/commit-boost/cb-prometheus.yml rename testnets/holesky/{ => commit-boost}/keys.json (100%) rename testnets/holesky/{ => commit-boost}/targets.json (100%) rename testnets/holesky/{ => commit-boost}/update-grafana.sh (51%) diff --git a/testnets/holesky/cb-bolt-config.toml b/testnets/holesky/commit-boost/cb-bolt-config.toml similarity index 99% rename from testnets/holesky/cb-bolt-config.toml rename to testnets/holesky/commit-boost/cb-bolt-config.toml index be4e35002..f78b0fc8d 100644 --- a/testnets/holesky/cb-bolt-config.toml +++ b/testnets/holesky/commit-boost/cb-bolt-config.toml @@ -136,7 +136,7 @@ BOLT_SIDECAR_METRICS_PORT = "10000" # - /etc/prometheus/targets.json # ``` # and use the `targets.json` file generated by `commit-boost init` -prometheus_config = "./prometheus.yml" +prometheus_config = "./cb-prometheus.yml" # Whether to start Grafana with built-in dashboards # OPTIONAL, DEFAULT: true use_grafana = true diff --git a/testnets/holesky/commit-boost/cb-prometheus.yml b/testnets/holesky/commit-boost/cb-prometheus.yml new file mode 100644 index 000000000..91b5d5905 --- /dev/null +++ b/testnets/holesky/commit-boost/cb-prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "commit-boost" + file_sd_configs: + - files: + - /etc/prometheus/targets.json diff --git a/testnets/holesky/keys.json b/testnets/holesky/commit-boost/keys.json similarity index 100% rename from testnets/holesky/keys.json rename to testnets/holesky/commit-boost/keys.json diff --git a/testnets/holesky/targets.json b/testnets/holesky/commit-boost/targets.json similarity index 100% rename from testnets/holesky/targets.json rename to testnets/holesky/commit-boost/targets.json diff --git a/testnets/holesky/update-grafana.sh b/testnets/holesky/commit-boost/update-grafana.sh similarity index 51% rename from testnets/holesky/update-grafana.sh rename to testnets/holesky/commit-boost/update-grafana.sh index 4e5a16695..1f832d32b 100755 --- a/testnets/holesky/update-grafana.sh +++ b/testnets/holesky/commit-boost/update-grafana.sh @@ -1,5 +1,5 @@ #!/bin/bash # Fetches the latest dashboards from commit-boost main -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ../grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ../grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/prometheus.yml b/testnets/holesky/prometheus.yml index 91b5d5905..bce41074a 100644 --- a/testnets/holesky/prometheus.yml +++ b/testnets/holesky/prometheus.yml @@ -1,8 +1,8 @@ global: - scrape_interval: 15s + scrape_interval: 5s scrape_configs: - - job_name: "commit-boost" + - job_name: "bolt-sidecar" file_sd_configs: - files: - /etc/prometheus/targets.json From a87a3588186e67b4612fb93df1a294250e05fba7 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 12:34:06 +0200 Subject: [PATCH 013/272] fix(delegations-cli): non-descriptive error message --- bolt-cli/src/utils/keystore.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bolt-cli/src/utils/keystore.rs b/bolt-cli/src/utils/keystore.rs index 26810e11b..464a1e5cd 100644 --- a/bolt-cli/src/utils/keystore.rs +++ b/bolt-cli/src/utils/keystore.rs @@ -51,7 +51,9 @@ impl KeystoreSecret { /// Load the keystore passwords from a directory containing individual password files. pub fn from_directory(root_dir: &str) -> Result { let mut secrets = HashMap::new(); - for entry in fs::read_dir(root_dir)? { + for entry in fs::read_dir(&root_dir) + .wrap_err(format!("failed to read secrets directory. path: {}", &root_dir))? + { let entry = entry.wrap_err("Failed to read secrets directory entry")?; let path = entry.path(); @@ -108,12 +110,14 @@ impl Drop for KeystoreSecret { /// -- ... /// Reference: https://github.com/chainbound/bolt/blob/4634ff905561009e4e74f9921dfdabf43717010f/bolt-sidecar/src/signer/keystore.rs#L109 pub fn keystore_paths(keys_path: &str) -> Result> { - let keys_path = Path::new(keys_path).to_path_buf(); + let keys_path_buf = Path::new(keys_path).to_path_buf(); let json_extension = OsString::from("json"); let mut keystores_paths = vec![]; // Iter over the `keys` directory - for entry in read_dir(keys_path)? { + for entry in read_dir(keys_path_buf) + .wrap_err(format!("failed to read keys directory. path: {keys_path}"))? + { let path = read_path(entry)?; if path.is_dir() { for entry in read_dir(path)? { From a678f4e19fdd2e781cdf022566fb2d22a58e793d Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:45:07 +0200 Subject: [PATCH 014/272] wip: pbs configs --- testnets/holesky/docker-compose.pbs.yml | 69 +++++++++++++++++++++++++ testnets/holesky/docker-compose.yml | 4 +- testnets/holesky/helix-config.yml | 17 ++++++ testnets/holesky/scripts/run-bn.sh | 28 ++++++++++ testnets/holesky/scripts/run-builder.sh | 32 ++++++++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 testnets/holesky/docker-compose.pbs.yml create mode 100644 testnets/holesky/helix-config.yml create mode 100644 testnets/holesky/scripts/run-bn.sh create mode 100644 testnets/holesky/scripts/run-builder.sh diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml new file mode 100644 index 000000000..be3a3a003 --- /dev/null +++ b/testnets/holesky/docker-compose.pbs.yml @@ -0,0 +1,69 @@ +volumes: + psql_data: + driver: local + chaindata: + driver: local + +services: + redis: + image: redis + restart: unless-stopped + + db: + image: timescale/timescaledb-ha:pg16 + restart: unless-stopped + volumes: + - "psql_data:/var/lib/postgresql/data" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: helixdb + + adminer: + image: adminer + restart: unless-stopped + depends_on: + - db + ports: + - "48093:8080" + environment: + ADMINER_PLUGINS: tables-filter tinymce + + builder: + image: ghcr.io/chainbound/bolt-builder:v0.3.0-alpha.rc1 + restart: unless-stopped + volumes: + - "chaindata:/var/lib/chaindata" + - "./shared:/var/lib/shared" + - "./scripts/run-builder.sh:/scripts/run-builder.sh" + environment: + BUILDER_TX_SIGNING_KEY: "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710" + ports: + - "30367:30303/tcp" + - "30367:30303/udp" + # entrypoint is builder + entrypoint: /scripts/run-builder.sh + + beacon: + image: sigp/lighthouse:latest + restart: unless-stopped + volumes: + - "chaindata:/var/lib/chaindata" + - "./shared:/var/lib/shared" + - "./scripts/run-bn.sh:/scripts/run-bn.sh" + ports: + - "41050:50050/tcp" + - "41050:50050/udp" + entrypoint: /scripts/run-bn.sh + + helix-relay: + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 + restart: unless-stopped + volumes: + - "./helix-config.yml:/helix-config.yml" + ports: + - "44040:4040" + environment: + - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 + - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + command: --config /helix-config.yml diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 52ef4da0c..2751212df 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,6 +1,6 @@ services: bolt-sidecar: - image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1 + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 container_name: bolt-sidecar restart: unless-stopped ports: @@ -10,7 +10,7 @@ services: entrypoint: /bin/sh -c /bolt-sidecar mev-boost: - image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha-rc.1 + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 container_name: mev-boost restart: unless-stopped env_file: ./mev-boost.env diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml new file mode 100644 index 000000000..edeff326e --- /dev/null +++ b/testnets/holesky/helix-config.yml @@ -0,0 +1,17 @@ +postgres: + hostname: db + port: 5432 + db_name: helixdb + user: postgres + password: postgres + +redis: + url: redis://redis:6379 + +simulator: + url: http://builder:8545 + +beacon_clients: + - url: http://beacon:50050 + +network_config: !Holesky diff --git a/testnets/holesky/scripts/run-bn.sh b/testnets/holesky/scripts/run-bn.sh new file mode 100644 index 000000000..5fe326129 --- /dev/null +++ b/testnets/holesky/scripts/run-bn.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +lighthouse beacon_node \ + --network=holesky \ + --debug-level=info \ + --datadir=/var/lib/chaindata \ + --disable-enr-auto-update \ + --enr-udp-port=50050 \ + --enr-tcp-port=50050 \ + --listen-address=0.0.0.0 \ + --port=50050 \ + --http \ + --http-address=0.0.0.0 \ + --http-port=4000 \ + --http-allow-sync-stalled \ + --always-prepare-payload \ + --prepare-payload-lookahead=12000 \ + --slots-per-restore-point=32 \ + --disable-packet-filter \ + --checkpoint-sync-url=https://holesky.beaconstate.info \ + --execution-endpoints=http://builder:8551 \ + --subscribe-all-subnets \ + --metrics \ + --metrics-address=0.0.0.0 \ + --metrics-allow-origin=* \ + --metrics-port=5054 \ + --enable-private-discovery \ + --jwt-secrets=/var/lib/shared/jwtsecret diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh new file mode 100644 index 000000000..d6894ac4e --- /dev/null +++ b/testnets/holesky/scripts/run-builder.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +geth --datadir=/var/lib/chaindata/geth \ + --network=holesky \ + --syncmode=full \ + --gcmode=archive \ + --state.scheme=hash \ + --verbosity=3 \ + --http \ + --http.port=8545 \ + --http.addr=0.0.0.0 \ + --http.vhosts=* \ + --http.corsdomain=* \ + --http.api=admin,engine,net,eth,web3,debug,flashbots,txpool \ + --ws \ + --ws.addr=0.0.0.0 \ + --ws.port=8546 \ + --ws.api=admin,engine,net,eth,web3,debug,flashbots,txpool \ + --ws.origins=* \ + --authrpc.port=8551 \ + --authrpc.addr=0.0.0.0 \ + --authrpc.vhosts=* \ + --authrpc.jwtsecret=/var/lib/shared/jwtsecret \ + --metrics \ + --metrics.addr=0.0.0.0 \ + --metrics.port=6060 \ + --port=30303 \ + --builder \ + --builder.remote_relay_endpoint=http://relay-api:9062 \ + --builder.beacon_endpoints=http://beacon:4000 \ + --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ + --miner.extradata="Bolt Builder" From c7255431e49f437d075a8423597716a1ca35d7c7 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:31:30 +0200 Subject: [PATCH 015/272] feat: working pbs docker compose --- testnets/holesky/docker-compose.pbs.yml | 23 ++++++++++++++++++----- testnets/holesky/helix-config.yml | 2 +- testnets/holesky/scripts/run-bn.sh | 0 testnets/holesky/scripts/run-builder.sh | 4 ++-- 4 files changed, 21 insertions(+), 8 deletions(-) mode change 100644 => 100755 testnets/holesky/scripts/run-bn.sh mode change 100644 => 100755 testnets/holesky/scripts/run-builder.sh diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index be3a3a003..362dc1c2e 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -3,6 +3,8 @@ volumes: driver: local chaindata: driver: local + shared: + driver: local services: redis: @@ -34,31 +36,41 @@ services: restart: unless-stopped volumes: - "chaindata:/var/lib/chaindata" - - "./shared:/var/lib/shared" + - "shared:/var/lib/shared" - "./scripts/run-builder.sh:/scripts/run-builder.sh" environment: BUILDER_TX_SIGNING_KEY: "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710" ports: - "30367:30303/tcp" - "30367:30303/udp" - # entrypoint is builder - entrypoint: /scripts/run-builder.sh + entrypoint: + [ + "/bin/sh", + "-c", + "chmod +x /scripts/run-builder.sh && /scripts/run-builder.sh", + ] beacon: image: sigp/lighthouse:latest restart: unless-stopped volumes: - "chaindata:/var/lib/chaindata" - - "./shared:/var/lib/shared" + - "shared:/var/lib/shared" - "./scripts/run-bn.sh:/scripts/run-bn.sh" ports: - "41050:50050/tcp" - "41050:50050/udp" - entrypoint: /scripts/run-bn.sh + entrypoint: + ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 restart: unless-stopped + depends_on: + - db + - redis + - builder + - beacon volumes: - "./helix-config.yml:/helix-config.yml" ports: @@ -66,4 +78,5 @@ services: environment: - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + - RUST_BACKTRACE=1 command: --config /helix-config.yml diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml index edeff326e..3305a335b 100644 --- a/testnets/holesky/helix-config.yml +++ b/testnets/holesky/helix-config.yml @@ -12,6 +12,6 @@ simulator: url: http://builder:8545 beacon_clients: - - url: http://beacon:50050 + - url: http://beacon:4000 network_config: !Holesky diff --git a/testnets/holesky/scripts/run-bn.sh b/testnets/holesky/scripts/run-bn.sh old mode 100644 new mode 100755 diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh old mode 100644 new mode 100755 index d6894ac4e..c1bc12035 --- a/testnets/holesky/scripts/run-builder.sh +++ b/testnets/holesky/scripts/run-builder.sh @@ -1,7 +1,7 @@ #!/bin/sh geth --datadir=/var/lib/chaindata/geth \ - --network=holesky \ + --holesky \ --syncmode=full \ --gcmode=archive \ --state.scheme=hash \ @@ -26,7 +26,7 @@ geth --datadir=/var/lib/chaindata/geth \ --metrics.port=6060 \ --port=30303 \ --builder \ - --builder.remote_relay_endpoint=http://relay-api:9062 \ + --builder.remote_relay_endpoint=http://helix-relay:4040 \ --builder.beacon_endpoints=http://beacon:4000 \ --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ --miner.extradata="Bolt Builder" From c671e6d32e5ab8640755c0557864fb44cbf03a50 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 11:14:27 +0200 Subject: [PATCH 016/272] chore(sidecar): more precise docs for config --- bolt-sidecar/src/config/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index af3256000..fcade3aee 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -33,7 +33,8 @@ pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18551; #[derive(Debug, Parser, Deserialize)] #[clap(trailing_var_arg = true)] pub struct Opts { - /// Port to listen on for incoming JSON-RPC requests + /// Port to listen on for incoming JSON-RPC requests of the Commitments API. + /// This port should be open on your firewall in order to receive external requests! #[clap(long, env = "BOLT_SIDECAR_PORT", default_value_t = DEFAULT_RPC_PORT)] pub port: u16, /// Execution client API URL @@ -42,7 +43,8 @@ pub struct Opts { /// URL for the beacon client #[clap(long, env = "BOLT_SIDECAR_BEACON_API_URL", default_value = "http://localhost:5052")] pub beacon_api_url: Url, - /// Execution client Engine API URL + /// Execution client Engine API URL. This is needed for fallback block building and must be a + /// synced Geth node. #[clap(long, env = "BOLT_SIDECAR_ENGINE_API_URL", default_value = "http://localhost:8551")] pub engine_api_url: Url, /// URL to forward the constraints produced by the Bolt sidecar to a server supporting the @@ -77,10 +79,11 @@ pub struct Opts { /// The fee recipient address for fallback blocks #[clap(long, env = "BOLT_SIDECAR_FEE_RECIPIENT")] pub fee_recipient: Address, - /// Secret BLS key to sign fallback payloads with (If not provided, a random key will be used) + /// Secret BLS key to sign fallback payloads with #[clap(long, env = "BOLT_SIDECAR_BUILDER_PRIVATE_KEY")] pub builder_private_key: BlsSecretKeyWrapper, - /// Secret ECDSA key to sign commitment messages with + /// Secret ECDSA key to sign commitment messages with. The public key associated to it must be + /// then used when registering the operator in the `BoltManager` contract. #[clap(long, env = "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY")] pub commitment_private_key: EcdsaSecretKeyWrapper, /// Operating limits for the sidecar From bf58ba5e9595d660359c9fd67c7e40b5403266fb Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:26:23 +0200 Subject: [PATCH 017/272] docs(holesky): README launch wip update --- testnets/holesky/README.md | 828 +++++++++++++++++++++++++++++++++++-- 1 file changed, 797 insertions(+), 31 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index c031285f3..526089c56 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -1,31 +1,159 @@ -# Holesky Launch Instructions +This document provides instructions for running the Bolt sidecar on the Holesky testnet. + +# Table of Contents + + + +* [Prerequisites](#prerequisites) +* [Setup](#setup) + * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Commit-Boost Mode](#commit-boost-mode) + * [Native Mode (advanced)](#native-mode-(advanced)) + * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) + * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) + * [Configuration file](#configuration-file) + * [Observability](#observability) +* [Register your validators on-chain on the Bolt Registry](#register-your-validators-on-chain-on-the-bolt-registry) + * [Validator Registration](#validator-registration) + * [Bolt Network Entrypoint](#bolt-network-entrypoint) + * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) + * [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) + * [EigenLayer Integration Guide for Node Operators and Solo Stakers](#eigenlayer-integration-guide-for-node-operators-and-solo-stakers) +* [Reference](#reference) + * [Command-line options](#command-line-options) + * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) + * [`bolt-delegations-cli`](#`bolt-delegations-cli`) + * [Installation and usage](#installation-and-usage) + * [Delegations CLI Example](#delegations-cli-example) + * [Using a private key directly](#using-a-private-key-directly) + * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) + * [Avoid restarting the beacon node](#avoid-restarting-the-beacon-node) + + + +# Prerequisites + +In order to run Bolt you need some components already installed and running in +your system. + +**A synced Geth client:** + +Bolt is fully trustless since it is able to produce a fallback block with the +commitments issued in case builders do not return a valid bid. In order to do so +it relies on a synced execution client, configured via the `--execution-api-url` +flag. At the moment only Geth is supported; with more +clients to be supported in the future. + +Using the sidecar with a different execution client could lead to commitment +faults because fallback block building is not supported yet. You can download +Geth from [the official website](https://geth.ethereum.org/downloads). + +**A synced beacon node:** + +Bolt is compatible with every beacon client. Please refer to the various beacon +client implementations to download and run them. -## Components +> [!IMPORTANT] +> In order to correctly run the Bolt sidecar and avoid commitment faults the +> beacon node must be configured so that: +> +> 1. the node's `builder-api` (or equivalent flag) must point to the Bolt +> Sidecar API. +> 2. the node will always prefer the builder payload. For instance, in +> Lighthouse this can be achieved by providing the following flags: +> +> ```text +> --always-prefer-builder-payload +> --builder-fallback-disable-checks +> ``` +> +> It might be necessary to restart your beacon node depending on your existing +> setup. See the [Avoid Restarting the Beacon +> Node](#avoid-restarting-the-beacon-node) for more details. + +**Active validators:** + +The Bolt sidecar requires signing keys from active Ethereum validators, or +authorized delegates acting on their behalf, to issue and sign preconfirmations. + +**LST collateral:** + +For Holesky in order to provide credible proposer commitments it is necessary to +restake 1 ether worth of ETH derivatives per validator in either the Symbiotic +or the EigenLayer protocol. + +# Setup + +There are various way to run the Bolt Sidecar depending on what infrastructure +you want to use and your preferred signing methods: -The components that need to run to test Bolt on Holesky are: +- Docker mode (recommended); +- [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode + (requires Docker). +- Native mode (advanced, requires building everything from source); -- A synced execution client -- A synced beacon node -- Active validators -- Commit-Boost with Bolt configuration +Running the Bolt sidecar as a standalone binary requires building it from +source. Both the standalone binary and the Docker container requires reading +signing keys from [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores, +while the Commit-Boost module relies on an internal signer and a custom PBS +module instead of regular [MEV-Boost](https://boost.flashbots.net/). -## Setup +In this section we're going to explore each of these options and its +requirements. -### Commit-Boost +## Docker Mode (recommended) -#### Installation +First, make sure to have both [Docker](https://docs.docker.com/engine/install/), +[Docker Compose](https://docs.docker.com/compose/install/) and +[git](https://git-scm.com/downloads) installed in your machine. -To install the `commit-boost` CLI with `cargo`: +Then clone the Bolt repository by running: ```bash -# Use specific commit hash to ensure compatibility -cargo install --locked --git https://github.com/Commit-Boost/commit-boost-client --rev aed00e8 commit-boost +git clone --branch v0.3.0 htts://github.com/chainbound/bolt.git && cd bolt +``` + +The Docker Compose setup will spin up the Bolt sidecar along with the Bolt +MEV-Boost fork which includes supports the [Constraints +API](https://docs.boltprotocol.xyz/api/builder). + +Before starting the services, you'll need to provide configuration files +containing the necessary environment variables: + +1. **Bolt Sidecar Configuration:** + + Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you + need a reference, you can use the `.env.example` file in the `bolt-sidecar` + directory as a starting point. For proper configuration of the signing + options, please refer to the [Delegations and + Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) + section of this guide. + +2. **MEV-Boost Configuration:** + + Similarly, create a `mev-boost.env` file in the + `testnets/holesky` folder to configure the MEV-Boost service. If you need a + reference, you can use the `.env.example` file in the `mev-boost` directory as a + starting point. -# Test installation -commit-boost --version +If you prefer not to restart your beacon node, follow the instructions in the +[Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. + +Once the configuration files are in place, you can start the Docker containers +by running: + +```bash +cd testnets/holesky && docker compose up -d ``` -#### Configuration +The docker compose setup comes with various observability tools, such as +Prometheus and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. + +## Commit-Boost Mode + +First download the `commit-boost-cli` binary from the Commit-Boost [official +releases page](https://github.com/Commit-Boost/commit-boost-client/releases) A commit-boost configuration file with Bolt support is provided at [`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the @@ -59,13 +187,13 @@ To initialize commit-boost, run the following command: commit-boost init --config cb-bolt-config.toml ``` -This will create 3 files: +This will create three files: - `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services - `.cb.env`: with local env variables, including JWTs for modules - `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus -#### Running +**Running** The final step is to run the Commit-Boost services. This can be done with the following command: @@ -76,31 +204,669 @@ commit-boost start --docker cb.docker-compose.yml --env .cb.env This will run all modules in Docker containers. > [!IMPORTANT] -> bolt-boost will be exposed at `pbs.port` (18551 by default, set with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be configured -> to point the `builder-api` to this port for Bolt to work. +> The `bolt-boost` service will be exposed at `pbs.port` (18551 by default, set +> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be +> configured to point the `builder-api` to this port for Bolt to work. + +## Native Mode (advanced) + +For running the Bolt Sidecar as a standalone binary you need to have the +following dependencies installed: + +- [git](https://git-scm.com/downloads); +- [Rust](https://www.rust-lang.org/tools/install). +- [Golang](https://golang.org/doc/install). + +Depending on your platform you may need to install additional dependencies. + +
+Linux + +Debian-based distributions: + +```bash +sudo apt update && sudo apt install -y git build-essential libssl-dev build-essential ca-certificates +``` + +Fedora/Red Hat/CentOS distributions: + +```bash +sudo dnf groupinstall "Development Tools" && sudo dnf install -y git openssl-devel ca-certificates pkgconfig +``` + +Arch/Manjaro-based distributions: + +```bash +sudo pacman -Syu --needed base-devel git openssl ca-certificates pkgconf +``` + +Alpine Linux + +```bash +sudo apk add git build-base openssl-dev ca-certificates pkgconf +``` + +
+ +
+ +
+ MacOS + +On MacOS after installing XCode Command Line tools (equivalent to `build-essential` on Linux) you can install the other dependencies with [Homebew](https://brew.sh/): + +```zsh +xcode-select --install +brew install pkg-config openssl +``` + +
+ +--- + +After having installed the dependencies you can clone the Bolt repository by +running: + +```bash +git clone --branch v0.3.0 https://github.com/chainbound/bolt.git && cd bolt +``` + +### Building and running the MEV-Boost fork binary -### Bolt Sidecar +The Bolt protocol relies on a modified version of +[MEV-Boost](https://boost.flashbots.net/) that supports the [Constraints +API](https://docs.boltprotocol.xyz/api/builder). This modified version is +available in the `mev-boost` directory of the project and can be built by +running -WIP +```bash +make build +``` + +in the `mev-boost` directory. The output of the command is a `mev-boost` binary. +To run the `mev-boost` binary please read the official [documentation](https://boost.flashbots.net/). + +If you're already running MEV-Boost along with your beacon client it is +recommended to choose another port this service in order to [avoid restarting +your beacon client](#avoid-restarting-the-beacon-node). Check out the linked +section for more details. + +### Building and running the Bolt sidecar binary + +Then you can build the Bolt sidecar by running: + +```bash +cargo build --release && mv target/release/bolt-sidecar . +``` + +In order to run correctly the sidecar you need to provide either a list command +line options or a configuration file (recommended). All the options available +can be found by running `./bolt-sidecar --help`, or you can find them in the +[reference](#command-line-options) section of this guide. + +#### Configuration file + +A configuration file can be either a `.env` file or a `.toml` file. If you use +`.env` file you can find a `.env.example` file in the repository that you can +use as a template. + +For a `.toml` file you can use the template in the `Config.example.toml`. Lastly +you need to specify the path of the configuration file by setting the +`BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. + +Please read the section on [delegations and signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +to configure such sidecar options properly. + +After you've set up the configuration file you can run the Bolt sidecar with + +```bash +./bolt-sidecar-cli +``` ### Observability -commit-boost comes with various observability tools, such as Prometheus, cadvisor, and Grafana. It also comes with some pre-built dashboards, -which can be found in the `grafana` directory. +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. To update these dashboards, run the following command: +`bash ./update-grafana.sh ` + +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. + +# Register your validators on-chain on the Bolt Registry + +Once you are successfully running the Bolt sidecar you need to register on-chain +on the Bolt Registry to successfully receive preconfirmation requests from users +and RPCs. This step is needed to provide economic security to your +commitments. + +In order to do that you need some collateral in the form of whitelisted Liquid +Staking Token (LST) that needs to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with ETH derivatives on Holesky. Here +are references to the supported tokens on both restaking protocols: + +- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) + - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) +- [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) + - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wETH`](https://holesky.etherscan.io/address/0x94373a4919B3240D86eA41593D5eBa789FEF3848) + - [`cbETH`](https://holesky.etherscan.io/address/0x8720095Fa5739Ab051799211B146a2EEE4Dd8B37) + - [`mETH`](https://holesky.etherscan.io/address/0xe3C063B1BEe9de02eb28352b55D49D85514C67FF) + +Then you need to interact with two contracts on Holesky: +`BoltValidators` and `BoltManager`. The former is used to register your +active validators into the protocol, while the latter is used to manage to +register as an operator into the system and integrate with the restaking +protocols. + +> [!IMPORTANT] +> When registering your operator in the `BoltManager` contract you must use the +> public key associated to the private key used to sign commitments with the +> Bolt Sidecar (the `--commitment-private-key` flag). + +## Validator Registration + +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for +validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. + +The registration process includes the following steps: + +1. Validator signs a message with their BLS private key. This is required to prove that the + validator private key is under their control and that they are indeed its owner. +2. Validator calls the `registerValidator` function providing: + 1. Their BLS public key + 2. The BLS signature of the registration message + 3. The address of the authorized collateral provider + 4. The address of the authorized operator + +Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function +that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and +will allow us to test the registration flow in a controlled environment. + +## Bolt Network Entrypoint + +The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +coordination of validators, operators, and vaults within the Bolt network. + +Key features include: + +1. Retrieval of operator stake and proposer status from their pubkey +2. Integration with Symbiotic +3. Integration with Eigenlayer + +Specific functionalities about the restaking protocols are handled inside +the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. + +### Symbiotic Integration guide for Staking Pools + +As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. +If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) +on how to spin up a Vault and start receiving stake from your node operators. + +Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: + +1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. +2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. +3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. +4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. +5. You can now start approving operators that opt in to your vault directly in Symbiotic. +6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. + +### Symbiotic Integration guide for Operators + +As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide +commitments on their behalf. + +The opt-in process requires the following steps: + +1. register in Symbiotic with `OperatorRegistry.registerOperator()`. +2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. +5. get approved by the vault. +6. start providing commitments with the stake provided by the vault. + +### EigenLayer Integration Guide for Node Operators and Solo Stakers + +> [!NOTE] +> Without loss of generality, we will assume the reader of this guide is a Node +> Operator (NO), since the same steps apply to solo stakers. +> As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) +> in the Bolt AVS built on top of EigenLayer. This requires +> running an Ethereum validator and the Bolt sidecar in order issue +> preconfirmations. + +The Operator will be represented by an Ethereum address that needs +to follow the standard procedure outlined in the +[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: + +1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + +2. As an Ethereum validator offering precofirmations a NO needs some collateral in + order to be economically credible. In order to do that, some entities known as a "stakers" + need to deposit whitelisted Liquid Staking Tokens (LSTs) + into an appropriate "Strategy" associated to the LST via the + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), + so that the Operator has a `min_amount` (for Holesky 1 ether) of collateral associated to it. + Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract + in the `getWhitelistedCollaterals` function. + Note that NOs and stakers can be two different entities + _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. + +3. After the stakers have deposited their collateral into a strategy they need + to choose you as their operator. To do that, they need to call the function + [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). + +4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. + This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. + The payload is a signature whose digest consists of: + + 1. your operator address + 2. the `BoltEigenLayerMiddleware` contract address + 3. a salt + 4. an expiry 2. + + The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) + with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, + the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. + +Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` +contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a +registered operator and so that they can forward you preconfirmation requests. + +The steps required are the following: + +1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. + If you own more than one validator public key, + you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. + The `authorizedOperator` argument must be the same Ethereum address used for + opting into EigenLayer as an Operator. + +2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling + the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network + and allows you to manage operations effectively, such as pausing or resuming + your service. + +3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. + This ensures that your restaked assets are correctly integrated with Bolt’s system. + +# Reference + +## Command-line options + +For completeness, here are all the command-line options available for the Bolt +sidecar. You can see them in your terminal by running the Bolt sidecar binary +with the `--help` flag: + +``` +Command-line options for the Bolt sidecar + +Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > + +Options: + --port + Port to listen on for incoming JSON-RPC requests of the Commitments API + This port should be open on your firewall in order to receive external requests! + + [env: BOLT_SIDECAR_PORT=] + [default: 8000] + + --execution-api-url + Execution client API URL + + [env: BOLT_SIDECAR_EXECUTION_API_URL=] + [default: http://localhost:8545] + + --beacon-api-url + URL for the beacon client + + [env: BOLT_SIDECAR_BEACON_API_URL=] + [default: http://localhost:5052] + + --engine-api-url + Execution client Engine API URL. This is needed for fallback block + building and must be a synced Geth node + + [env: BOLT_SIDECAR_ENGINE_API_URL=] + [default: http://localhost:8551] + + --constraints-api-url + URL to forward the constraints produced by the Bolt sidecar to a + server supporting the Constraints API, such as an MEV-Boost fork + + [env: BOLT_SIDECAR_CONSTRAINTS_API_URL=] + [default: http://localhost:3030] + + --constraints-proxy-port + The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client + + [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] + [default: 18551] + + --validator-indexes + Validator indexes of connected validators that the sidecar should + accept commitments on behalf of. Accepted values: + - a comma-separated list of indexes (e.g. "1,2,3,4") + - a contiguous range of indexes (e.g. "1..4") + - a mix of the above (e.g. "1,2..4,6..8") + + [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] + [default: ] + + --engine-jwt-hex + The JWT secret token to authenticate calls to the engine API. + + It can either be a hex-encoded string or a file path to a file containing the hex-encoded secret. + + [env: BOLT_SIDECAR_ENGINE_JWT_HEX=] + + --fee-recipient + The fee recipient address for fallback blocks + + [env: BOLT_SIDECAR_FEE_RECIPIENT=] + + --builder-private-key + Secret BLS key to sign fallback payloads with + + [env: BOLT_SIDECAR_BUILDER_PRIVATE_KEY=] + + --commitment-private-key + Secret ECDSA key to sign commitment messages with. The public key + associated to it must be then used when registering the operator in the + `BoltManager` contract + + [env: BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY=] + + --max-commitments-per-slot + Max number of commitments to accept per block + + [env: BOLT_SIDECAR_MAX_COMMITMENTS=] + [default: 128] + + --max-committed-gas-per-slot + Max committed gas per slot + + [env: BOLT_SIDECAR_MAX_COMMITTED_GAS=] + [default: 10000000] + + --min-priority-fee + Min priority fee to accept for a commitment + + [env: BOLT_SIDECAR_MIN_PRIORITY_FEE=] + [default: 1000000000] + + --chain + Chain on which the sidecar is running + + [env: BOLT_SIDECAR_CHAIN=] + [default: mainnet] + [possible values: mainnet, holesky, helder, kurtosis] + + --commitment-deadline + The deadline in the slot at which the sidecar will stop accepting new commitments for the next block (parsed as milliseconds) + + [env: BOLT_SIDECAR_COMMITMENT_DEADLINE=] + [default: 8000] + + --slot-time + The slot time duration in seconds. If provided, it overrides the default for the selected [Chain] + + [env: BOLT_SIDECAR_SLOT_TIME=] + [default: 12] + + --constraint-private-key + Private key to use for signing constraint messages + + [env: BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY=] + + --commit-boost-signer-url + URL for the commit-boost sidecar + + [env: BOLT_SIDECAR_CB_SIGNER_URL=] + + --commit-boost-jwt-hex + JWT in hexadecimal format for authenticating with the commit-boost service + + [env: BOLT_SIDECAR_CB_JWT_HEX=] + + --keystore-password + The password for the ERC-2335 keystore. Reference: https://eips.ethereum.org/EIPS/eip-2335 + + [env: BOLT_SIDECAR_KEYSTORE_PASSWORD=] + + --keystore-secrets-path + The path to the ERC-2335 keystore secret passwords Reference: https://eips.ethereum.org/EIPS/eip-2335 + + [env: BOLT_SIDECAR_KEYSTORE_SECRETS_PATH=] + + --keystore-path + Path to the keystores folder. If not provided, the default path is used + + [env: BOLT_SIDECAR_KEYSTORE_PATH=] + + --delegations-path + Path to the delegations file. If not provided, the default path is used + + [env: BOLT_SIDECAR_DELEGATIONS_PATH=] + + --metrics-port + The port on which to expose Prometheus metrics + + [env: BOLT_SIDECAR_METRICS_PORT=] + [default: 3300] + + --disable-metrics + [env: BOLT_SIDECAR_DISABLE_METRICS=] + + -h, --help + Print help (see a summary with '-h') +``` + +## Delegations and signing options for Native and Docker Compose Mode + +As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar +can sign commitments with a delegated set of private keys on behalf of active +Ethereum validators. + +> [!IMPORTANT] +> This is the recommended way to run the Bolt sidecar as it +> doesn't expose the active validator signing keys to any additional risk. + +In order to create these delegation you can use the `bolt-delegations-cli` binary. +If you don't want to use it you can skip the following section. + +### `bolt-delegations-cli` + +`bolt-delegations-cli` is an offline command-line tool for safely generating +delegation and revocation messages signed with a BLS12-381 key for the +[Constraints API](https://docs.boltprotocol.xyz/api/builder) in +[Bolt](https://docs.boltprotocol.xyz/). + +The tool supports two key sources: + +- Local: A BLS private key provided directly from a file. +- Keystore: A keystore file that contains an encrypted BLS private key. + +and outputs a JSON file with the delegation/revocation messages to the provided +`` for the given chain + +Features: + +- Offline usage: Safely generate delegation messages in an offline environment. +- Flexible key source: Support for both direct local BLS private keys and + Ethereum keystore files (ERC-2335 format). +- BLS delegation signing: Sign delegation messages using a BLS secret key and + output the signed delegation in JSON format. + +#### Installation and usage + +Go to the root of the Bolt project you've previously cloned using Git. Enter in +the `bolt-delegations-cli` directory by running `cd bolt-delegations-cli`. + +If you're using the Docker container setup make sure you have +[Rust](https://www.rust-lang.org/tools/install) installed in your system as +well. Then you can build the `bolt-delegations-cli` binary by running: + ```bash -./update-grafana.sh +cargo build --release && mv target/release/bolt-delegations-cli . +``` + +Now you can run the binary by running: + +```bash +./bolt-delegations-cli +``` + +The binary exposes a single `generate` command, which accepts the following +options and subcommands (use `./bolt-delegations-cli generate --help` to see +them): + +```text +Usage: bolt-delegations-cli generate [OPTIONS] --delegatee-pubkey + +Commands: + local Use local private keys to generate the signed messages + keystore Use an EIP-2335 keystore folder to generate the signed messages + help Print this message or the help of the given subcommand(s) + +Options: + --delegatee-pubkey The BLS public key to which the delegation message should be signed [env: DELEGATEE_PUBKEY=] + --out The output file for the delegations [env: OUTPUT_FILE_PATH=] [default: delegations.json] + --chain The chain for which the delegation message is intended [env: CHAIN=] [default: mainnet] [possible values: mainnet, holesky, helder, kurtosis] + --action The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) [env: ACTION=] [default: delegate] [possible values: delegate, revoke] + -h, --help Print help (see more with '--help') +``` + +The environment variables can be also set in a `.env` file. For a reference +example you can check out the `.env.local.example` and the +`.env.keystore.example` + +In the section below you can see a usage example of the binary. + +#### Delegations CLI Example + +1. Using a local BLS private key: + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + local \ + --secret-keys 0xabc123...,0xdef456.. + ``` + +2. Using a Ethereum keystores files and raw password: + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + keystore \ + --path /keys \ + --password myS3cr3tP@ssw0rd + ``` + +3. Using an Ethereum keystores files and secrets folder + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + keystore \ + --path /keys \ + --password-path /secrets + ``` + +When using the `keystore` key source, the `--path` flag should point to the +directory containing the encrypted keypair directories. + +The keystore folder must adhere to the following structure: + +```text +${KEYSTORE_PATH} +|-- 0x81b676591b823270a3284ace7d81cbce2d6cdce55bb0e053874d7e3a08f729453009d3e662ec3130379f43c0f3210b6d +| `-- voting-keystore.json +|-- 0x81ea9f74ef7d935b807474e38954ae3934856219a23e074954b2e860c5a3c400f9aedb42cd27cb4ceb697ca36d1e58cb +| `-- voting-keystore.json +|-- ... + `-- ... +``` + +where the folder names are the public keys and inside every +folder there is a single JSON file containing the keystore file. + +In case of validator-specific passwords (e.g. Lighthouse format) the +`--password-path` flag must be used instead of `--password`, pointing to the +directory containing the password files. + +The passwords folder must adhere to a certain structure as well, as shown below. + ``` +${KEYSTORE_PATH} +|-- 0x81b676591b823270a3284ace7d81cbce2d6cdce55bb0e053874d7e3a08f729453009d3e662ec3130379f43c0f3210b6d +|-- 0x81ea9f74ef7d935b807474e38954ae3934856219a23e074954b2e860c5a3c400f9aedb42cd27cb4ceb697ca36d1e58cb +|-- ... + `-- ... +``` + +That is, the password files should be named after the public key and each file +should just contain one line with the password in plain text. The files +themselves don't need a particular file extension. + +--- + +Now that you have generated the delegation messages you can provide them to the +sidecar using the `--delegations-path` flag (see the +[options](#command-line-options) section). When doing so the sidecar will check if +they're indeed valid messages and will keep in memory the association between +the delegator and the delegatee. + +However in order to sign the commitments you still need to provide the signing +key of the delegatee. There are two ways to do so, as explored in the sections +below. + +### Using a private key directly + +As you can see in the [command line options](#command-line-options) section you +can pass directly the private key as a hex-encoded string to the Bolt sidecar +using the `--private-key` flag. This is the simplest setup and can be used in +case if all the delegations messages point to the same delegatee or if you're +running the sidecar with a single active validator. + +### Using a ERC-2335 Keystore + +The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. +In order to use them you need to provide the `--keystore-path` pointing to the +folder containing the keystore files and the `--keystore-password` or +`keystore-secrets-path` flag pointing to the folder containing the password +file. -In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. +Both the `keys` and `passwords` folders must adhere to the structure outlined +in the [Delegations CLI example](#delegations-cli-example) section. -### Validators +## Avoid restarting the beacon node -Validators must be configured to always prefer builder proposals over their own. Refer to client documentation for the specific configuration flags. -**If this is not set, it could lead to commitment faults**. +As mentioned in the [prerequisites](#prerequisites) section, in order to run the +sidecar correctly it might be necessary to restart your beacon client. That is +because you need to configure the `--builder` flag (or equivalent) to point to +the Bolt sidecar endpoint. -#### Registration +However if you're already running a PBS sidecar like +[MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid +the restart by following this steps when starting the Bolt sidecar: -WIP +1. Set the `--constraints-proxy-port` flag or the + `BOLT_SIDECAR_BUILDER_PROXY_PORT` environment variable to the port previously occupied by + MEV-Boost. +2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it + using another port +3. Set the `--constraints-url` flag or the `BOLT_SIDECAR_CONSTRAINTS_URL` to point to the Bolt MEV-Boost instance. From 7d32c6ec4b3ce33e6f67c7a197e6cacda41569df Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 16:49:18 +0200 Subject: [PATCH 018/272] fix(holesky): add bolt prefix to all services and containers to avoid conflicts --- testnets/holesky/docker-compose.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 2751212df..f88549f40 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,7 +1,7 @@ services: - bolt-sidecar: + bolt-sidecar-holesky: image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 - container_name: bolt-sidecar + container_name: bolt-sidecar-holesky restart: unless-stopped ports: - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) @@ -9,18 +9,18 @@ services: env_file: ./bolt-sidecar.env entrypoint: /bin/sh -c /bolt-sidecar - mev-boost: + bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 - container_name: mev-boost + container_name: bolt-mev-boost-holesky restart: unless-stopped env_file: ./mev-boost.env entrypoint: /bin/sh -c '/app/mev-boost' - prometheus: + bolt-prometheus-holesky: image: prom/prometheus:latest - container_name: prometheus + container_name: bolt-prometheus-holesky ports: - - 9090:9090 + - 49090:49090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json @@ -28,11 +28,11 @@ services: networks: - monitoring_network - grafana: + bolt-grafana-holesky: image: grafana/grafana:latest - container_name: cb_grafana + container_name: bolt-grafana-holesky ports: - - 3000:3000 + - 33000:33000 environment: - GF_SECURITY_ADMIN_PASSWORD=admin volumes: @@ -42,7 +42,7 @@ services: networks: - monitoring_network depends_on: - - prometheus + - bolt-prometheus-holesky logging: driver: none From d783918dc57f3e0b35058701c1dd7054e77c7182 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 17:34:40 +0200 Subject: [PATCH 019/272] fix(holesky): docker-compose sidecar entrypoint --- testnets/holesky/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index f88549f40..7687aa77e 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -7,7 +7,7 @@ services: - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" env_file: ./bolt-sidecar.env - entrypoint: /bin/sh -c /bolt-sidecar + entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 6dc4e31a676ea221c62bd21d2037b628a7907e68 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 17:35:01 +0200 Subject: [PATCH 020/272] fix(sidecar): .env.example --- bolt-sidecar/.env.example | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index 98bf60852..7621489a6 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -6,7 +6,7 @@ BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 -BOLT_SIDECAR_JWT_HEX= +BOLT_SIDECAR_ENGINE_JWT_HEX= BOLT_SIDECAR_FEE_RECIPIENT= BOLT_SIDECAR_BUILDER_PRIVATE_KEY= @@ -17,17 +17,17 @@ BOLT_SIDECAR_MIN_PRIORITY_FEE= BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 # Chain configs -BOLT_SIDECAR_CHAIN=Holesky +BOLT_SIDECAR_CHAIN=holesky BOLT_SIDECAR_SLOT_TIME=12 -# Signing options -BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= -BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= -BOLT_SIDECAR_CB_SIGNER_URL= -BOLT_SIDECAR_CB_JWT_HEX= -BOLT_SIDECAR_KEYSTORE_PASSWORD= -BOLT_SIDECAR_KEYSTORE_PATH= -BOLT_SIDECAR_DELEGATIONS_PATH= +# Signing options. Uncomment only what you'll use +#BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +#BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +#BOLT_SIDECAR_CB_SIGNER_URL= +#BOLT_SIDECAR_CB_JWT_HEX= +#BOLT_SIDECAR_KEYSTORE_PASSWORD= +#BOLT_SIDECAR_KEYSTORE_PATH= +#BOLT_SIDECAR_DELEGATIONS_PATH= # Metrics BOLT_SIDECAR_METRICS_PORT= From 94b6b874c7c1be152412d2b5a09b1ada60eabd40 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 18:20:14 +0200 Subject: [PATCH 021/272] chore(holesky): add simple bash script to create .env stub --- testnets/holesky/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 526089c56..4ea5dc1f0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -124,7 +124,13 @@ containing the necessary environment variables: Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you need a reference, you can use the `.env.example` file in the `bolt-sidecar` - directory as a starting point. For proper configuration of the signing + directory as a starting point. + + ```bash + cat ./bolt-sidecar/.env.example > ./testnets/holesky/bolt-sidecar.env + ``` + + For proper configuration of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) section of this guide. @@ -136,6 +142,10 @@ containing the necessary environment variables: reference, you can use the `.env.example` file in the `mev-boost` directory as a starting point. + ```bash + cat ./mev-boost/.env.example > ./testnets/holesky/mev-boost.env + ``` + If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. From ed058bf904f8e23d42baf12a059de476f8400af9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:40:16 +0200 Subject: [PATCH 022/272] chore(sidecar): update Config.example.toml --- bolt-sidecar/Config.example.toml | 87 +++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index e6caad16e..b76ead584 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -1,37 +1,78 @@ # Ethereum Node Connections + PBS URLs -port = 8000 -execution_api_url = "http://localhost:4485" -beacon_api_url = "http://localhost:4400" -engine_api_url = "http://localhost:4451" -constraints_url = "http://localhost:19550" -constraints_proxy_port = 18551 + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +port = 8017 +# Execution client API URL +execution_api_url = "http://localhost:8545" +# URL for the beacon client +beacon_api_url = "http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +engine_api_url = "http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +constraints_proxy_port = 18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +constraints_api_url = "http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") validator_indexes = "0..64" -jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. +engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# The fee recipient address for fallback blocks fee_recipient = "0x0000000000000000000000000000000000000000" +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +# Secret BLS key to sign fallback payloads with builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# Commitments configs -max_commitments = 128 -max_committed_gas = 10000000 -min_priority_fee = 5000000 +# Commitments limits +[limits] +# Max number of commitments to accept per block +max_commitments_per_slot = 128 +# Max committed gas per slot +max_committed_gas_per_slot = 10_000_000 +# Min priority fee to accept for a commitment +min_priority_fee = 4_000_000_000 # 4 Gwei = 4 * 10^9 wei -# Chain configs +# Chain configuration [chain] -chain = "Holesky" +# Chain on which the sidecar is running +chain = "holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] slot_time = 12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) commitment_deadline = 8000 -# Signing options +# Signing options. Uncomment only the signing setup you'll use: +# - single private key -> `constraints_private_key` +# - commit-boost -> `cb_signer_url`, `cb_jwt_hex` +# - keystores -> `keystore_path`, `keystore_password` or `keystore_secrets_path` +# (depending on whether all keystores have the same passwords or not) +# +# If you plan to use delegations, uncomment the option `delegations_path` as +# well. [constraint_signing] -constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -cb_signer_url = "http://localhost:18550" -cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -keystore_password = "password" -keystore_path = "./keys" -delegations_path = "./delegations.json" +# Private key to use for signing constraint messages +# constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +# cb_signer_url = "http://localhost:18551" +# cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# keystore_password = "password" +# keystore_path = "./keys" +# delegations_path = "./delegations.json" -# Metrics +# Telemetry and Metrics [telemetry] -metrics_port = 8001 +metrics_port = 3300 disable_metrics = false From db6a419f573bb67019d4a3335507857ecd4e41ca Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:40:51 +0200 Subject: [PATCH 023/272] chore(sidecar): review CLI options and their defaults --- bolt-sidecar/src/config/chain.rs | 30 ++++++++++++++++++++++-------- bolt-sidecar/src/config/mod.rs | 14 ++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 4ce4f88a6..98edb7248 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -1,4 +1,5 @@ -use std::time::Duration; +use core::fmt; +use std::{fmt::Display, time::Duration}; use clap::{Args, ValueEnum}; use ethereum_consensus::deneb::{compute_fork_data_root, Root}; @@ -25,7 +26,7 @@ pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67]; #[derive(Debug, Clone, Copy, Args, Deserialize)] pub struct ChainConfig { /// Chain on which the sidecar is running - #[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value = "mainnet")] + #[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value_t = Chain::Mainnet)] chain: Chain, /// The deadline in the slot at which the sidecar will stop accepting /// new commitments for the next block (parsed as milliseconds). @@ -65,6 +66,24 @@ pub enum Chain { Kurtosis, } +impl Chain { + /// Get the chain name for the given chain. + pub fn name(&self) -> &'static str { + match self { + Chain::Mainnet => "mainnet", + Chain::Holesky => "holesky", + Chain::Helder => "helder", + Chain::Kurtosis => "kurtosis", + } + } +} + +impl Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name()) + } +} + impl ChainConfig { /// Get the chain ID for the given chain. pub fn chain_id(&self) -> u64 { @@ -78,12 +97,7 @@ impl ChainConfig { /// Get the chain name for the given chain. pub fn name(&self) -> &'static str { - match self.chain { - Chain::Mainnet => "mainnet", - Chain::Holesky => "holesky", - Chain::Helder => "helder", - Chain::Kurtosis => "kurtosis", - } + self.chain.name() } /// Get the slot time for the given chain in seconds. diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index fcade3aee..1b9e64b76 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -23,11 +23,13 @@ use limits::LimitsOpts; use crate::common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}; -/// Default port for the JSON-RPC server exposed by the sidecar. -pub const DEFAULT_RPC_PORT: u16 = 8000; +/// Default port for the JSON-RPC server exposed by the sidecar supporting the Commitments API. +/// +/// 8017 -> BOLT :) +pub const DEFAULT_RPC_PORT: u16 = 8017; -/// Default port for the Constraints proxy server. -pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18551; +/// Default port for the Constraints proxy server, binded to the default port used by MEV-Boost. +pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18550; /// Command-line options for the Bolt sidecar #[derive(Debug, Parser, Deserialize)] @@ -52,7 +54,7 @@ pub struct Opts { #[clap( long, env = "BOLT_SIDECAR_CONSTRAINTS_API_URL", - default_value = "http://localhost:3030" + default_value = "http://localhost:18551" )] pub constraints_api_url: Url, /// The port from which the Bolt sidecar will receive Builder-API requests from the @@ -68,7 +70,7 @@ pub struct Opts { /// - a comma-separated list of indexes (e.g. "1,2,3,4") /// - a contiguous range of indexes (e.g. "1..4") /// - a mix of the above (e.g. "1,2..4,6..8") - #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES", default_value_t)] + #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES")] pub validator_indexes: ValidatorIndexes, /// The JWT secret token to authenticate calls to the engine API. /// From b1e1da835e33a20c935942af86b8f5b79391945c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:51:34 +0200 Subject: [PATCH 024/272] chore(sidecar): add Config.toml to .gitignore --- bolt-sidecar/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 2e73cb821..8fd11d943 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -2,3 +2,4 @@ target/ .env* !.env.example bolt-sidecar* +Config.toml From 58dd289cfd2d0ac4f114304254402fc45bc9540f Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:51:52 +0200 Subject: [PATCH 025/272] feat(sidecar): allow reading TOML config from default path --- bolt-sidecar/bin/sidecar.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index c7fbf60cc..6903fffbd 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,13 +1,19 @@ +use std::fs; + use clap::Parser; use eyre::{bail, Result}; use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; +pub const TOML_CONFIG_DEFAULT_PATH: &str = "./Config.toml"; + #[tokio::main] async fn main() -> Result<()> { let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") { Opts::parse_from_toml(config_path.as_str())? + } else if fs::exists(TOML_CONFIG_DEFAULT_PATH).is_ok_and(|exists| exists) { + Opts::parse_from_toml(TOML_CONFIG_DEFAULT_PATH)? } else { Opts::parse() }; From e202895db115ff6a149256d3ed2cc81224a622f6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:00:43 +0200 Subject: [PATCH 026/272] chore(holesky): add *.toml to .gitignore --- testnets/holesky/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/.gitignore b/testnets/holesky/.gitignore index ef456d924..71c6e0c11 100644 --- a/testnets/holesky/.gitignore +++ b/testnets/holesky/.gitignore @@ -1,2 +1,3 @@ *.env* !*.env.example +*.toml From 2d3774c1b44d6fe4d4ed3d6987e5135622b9f005 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:03:14 +0200 Subject: [PATCH 027/272] fix(holesky): broken links in README, use TOML config file instead of .env for the sidecar --- testnets/holesky/README.md | 49 +++++++++++++++----------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4ea5dc1f0..47c849756 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -122,17 +122,17 @@ containing the necessary environment variables: 1. **Bolt Sidecar Configuration:** - Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you - need a reference, you can use the `.env.example` file in the `bolt-sidecar` + Create a `bolt-sidecar.toml` file in the `testnets/holesky` directory. If you + need a reference, you can use the `Config.example.toml` file in the `bolt-sidecar` directory as a starting point. ```bash - cat ./bolt-sidecar/.env.example > ./testnets/holesky/bolt-sidecar.env + cat ./bolt-sidecar/Config.example.toml > ./testnets/holesky/bolt-sidecar.toml ``` For proper configuration of the signing options, please refer to the [Delegations and - Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) + Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. 2. **MEV-Boost Configuration:** @@ -316,15 +316,13 @@ can be found by running `./bolt-sidecar --help`, or you can find them in the #### Configuration file -A configuration file can be either a `.env` file or a `.toml` file. If you use -`.env` file you can find a `.env.example` file in the repository that you can -use as a template. - -For a `.toml` file you can use the template in the `Config.example.toml`. Lastly -you need to specify the path of the configuration file by setting the +You can use a `Config.toml` file to configure the sidecar, for which you can +find a template in the `Config.example.toml` file. +If you wish to place the configuration file in another folder you need to +specify the path of the configuration file by setting the `BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. -Please read the section on [delegations and signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) to configure such sidecar options properly. After you've set up the configuration file you can run the Bolt sidecar with @@ -517,15 +515,14 @@ with the `--help` flag: ``` Command-line options for the Bolt sidecar -Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > +Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: --port - Port to listen on for incoming JSON-RPC requests of the Commitments API - This port should be open on your firewall in order to receive external requests! + Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] - [default: 8000] + [default: 8017] --execution-api-url Execution client API URL @@ -540,34 +537,28 @@ Options: [default: http://localhost:5052] --engine-api-url - Execution client Engine API URL. This is needed for fallback block - building and must be a synced Geth node + Execution client Engine API URL. This is needed for fallback block building and must be a synced Geth node [env: BOLT_SIDECAR_ENGINE_API_URL=] [default: http://localhost:8551] --constraints-api-url - URL to forward the constraints produced by the Bolt sidecar to a - server supporting the Constraints API, such as an MEV-Boost fork + URL to forward the constraints produced by the Bolt sidecar to a server supporting the Constraints API, such as an MEV-Boost fork [env: BOLT_SIDECAR_CONSTRAINTS_API_URL=] - [default: http://localhost:3030] + [default: http://localhost:18551] --constraints-proxy-port The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] - [default: 18551] + [default: 18550] --validator-indexes - Validator indexes of connected validators that the sidecar should - accept commitments on behalf of. Accepted values: - - a comma-separated list of indexes (e.g. "1,2,3,4") - - a contiguous range of indexes (e.g. "1..4") - - a mix of the above (e.g. "1,2..4,6..8") + Validator indexes of connected validators that the sidecar should accept commitments on behalf of. Accepted values: - a comma-separated list of indexes (e.g. "1,2,3,4") - a contiguous range of indexes (e.g. "1..4") - a mix of the + above (e.g. "1,2..4,6..8") [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] - [default: ] --engine-jwt-hex The JWT secret token to authenticate calls to the engine API. @@ -587,9 +578,7 @@ Options: [env: BOLT_SIDECAR_BUILDER_PRIVATE_KEY=] --commitment-private-key - Secret ECDSA key to sign commitment messages with. The public key - associated to it must be then used when registering the operator in the - `BoltManager` contract + Secret ECDSA key to sign commitment messages with. The public key associated to it must be then used when registering the operator in the `BoltManager` contract [env: BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY=] From 8f2d9a253befa19e8671b9e6fa4551eeeae166f8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:13:11 +0200 Subject: [PATCH 028/272] chore(holesky): update docker-compose.yml --- testnets/holesky/docker-compose.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 7687aa77e..830402af8 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -4,10 +4,11 @@ services: container_name: bolt-sidecar-holesky restart: unless-stopped ports: - - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) - - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" - env_file: ./bolt-sidecar.env - entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar + - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) + - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" + entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" + volumes: + - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From feaa28d418f5c5c6c28d4d88abad074bcb8c998d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:09:17 +0200 Subject: [PATCH 029/272] fix(sidecar): min_priority_fee is now u128 and not NonZero This caused issue when parsing from TOML string. Also if that value is zero is not a problem actually --- bolt-sidecar/src/config/limits.rs | 4 ++-- bolt-sidecar/src/state/execution.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bolt-sidecar/src/config/limits.rs b/bolt-sidecar/src/config/limits.rs index b33561072..7e50d24b8 100644 --- a/bolt-sidecar/src/config/limits.rs +++ b/bolt-sidecar/src/config/limits.rs @@ -31,7 +31,7 @@ pub struct LimitsOpts { env = "BOLT_SIDECAR_MIN_PRIORITY_FEE", default_value_t = LimitsOpts::default().min_priority_fee )] - pub min_priority_fee: NonZero, + pub min_priority_fee: u128, } impl Default for LimitsOpts { @@ -41,7 +41,7 @@ impl Default for LimitsOpts { .expect("Valid non-zero"), max_committed_gas_per_slot: NonZero::new(DEFAULT_MAX_COMMITTED_GAS) .expect("Valid non-zero"), - min_priority_fee: NonZero::new(DEFAULT_MIN_PRIORITY_FEE).expect("Valid non-zero"), + min_priority_fee: DEFAULT_MIN_PRIORITY_FEE, } } } diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index b82418317..f6e4262d9 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -299,7 +299,7 @@ impl ExecutionState { } // Ensure max_priority_fee_per_gas is greater than or equal to min_priority_fee - if !req.validate_min_priority_fee(max_basefee, self.limits.min_priority_fee.get()) { + if !req.validate_min_priority_fee(max_basefee, self.limits.min_priority_fee) { return Err(ValidationError::MaxPriorityFeePerGasTooLow); } @@ -764,7 +764,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(200000000).unwrap(), // 0.2 gwei + min_priority_fee: 200000000, // 0.2 gwei }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -803,7 +803,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2000000000).unwrap(), + min_priority_fee: 2000000000, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -834,7 +834,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -876,7 +876,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -923,7 +923,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -1061,7 +1061,7 @@ mod tests { let limits: LimitsOpts = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(1000000000).unwrap(), + min_priority_fee: 1000000000, }; let mut state = ExecutionState::new(client.clone(), limits).await?; From 2db3c98ccab40c1347487ca6472f184c849f633c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:08 +0200 Subject: [PATCH 030/272] fix(holesky): cat -> cp in README --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 47c849756..1e75c3cb9 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -127,7 +127,7 @@ containing the necessary environment variables: directory as a starting point. ```bash - cat ./bolt-sidecar/Config.example.toml > ./testnets/holesky/bolt-sidecar.toml + cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml ``` For proper configuration of the signing @@ -143,7 +143,7 @@ containing the necessary environment variables: starting point. ```bash - cat ./mev-boost/.env.example > ./testnets/holesky/mev-boost.env + cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env ``` If you prefer not to restart your beacon node, follow the instructions in the From a63cc154298da03a10533ec9921d6851a449c3ab Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:34 +0200 Subject: [PATCH 031/272] chore(holesky): delegations flag in README --- testnets/holesky/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 1e75c3cb9..9d91de16e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -742,6 +742,11 @@ Options: -h, --help Print help (see more with '--help') ``` +> [!TIP] +> If you're using the Docker Compose Mode please don't set the `--out` flag and +> provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` +> file. + The environment variables can be also set in a `.env` file. For a reference example you can check out the `.env.local.example` and the `.env.keystore.example` From bb71349e9778aee49fcc959b079d01eb733c8d06 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:52 +0200 Subject: [PATCH 032/272] fix(holesky): provide delegations cli volume --- testnets/holesky/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 830402af8..86d2c3dce 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -9,6 +9,7 @@ services: entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" volumes: - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" + - "../../bolt-delegations-cli/delegations.json:/etc/delegations.json" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From cfb978b6f1ae5ba2d915f817d9f82fbcd32b6207 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 17:17:35 +0200 Subject: [PATCH 033/272] docs(holesky): update instructions --- testnets/holesky/README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 9d91de16e..4b80c8241 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -5,7 +5,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) -* [Setup](#setup) +* [Off-Chain Setup](#off-chain-setup) * [Docker Mode (recommended)](#docker-mode-(recommended)) * [Commit-Boost Mode](#commit-boost-mode) * [Native Mode (advanced)](#native-mode-(advanced)) @@ -13,7 +13,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) * [Observability](#observability) -* [Register your validators on-chain on the Bolt Registry](#register-your-validators-on-chain-on-the-bolt-registry) +* [On-Chain Registration](#on-chain-registration) * [Validator Registration](#validator-registration) * [Bolt Network Entrypoint](#bolt-network-entrypoint) * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) @@ -33,7 +33,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky # Prerequisites -In order to run Bolt you need some components already installed and running in +In order to run Bolt you need some components already installed and running on your system. **A synced Geth client:** @@ -76,13 +76,7 @@ client implementations to download and run them. The Bolt sidecar requires signing keys from active Ethereum validators, or authorized delegates acting on their behalf, to issue and sign preconfirmations. -**LST collateral:** - -For Holesky in order to provide credible proposer commitments it is necessary to -restake 1 ether worth of ETH derivatives per validator in either the Symbiotic -or the EigenLayer protocol. - -# Setup +# Off-Chain Setup There are various way to run the Bolt Sidecar depending on what infrastructure you want to use and your preferred signing methods: @@ -157,8 +151,7 @@ cd testnets/holesky && docker compose up -d ``` The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards, which can -be found in the `grafana` directory. +Prometheus and Grafana. It also comes with some pre-built dashboards which you can find at `http://localhost:3000`. ## Commit-Boost Mode @@ -344,7 +337,7 @@ To update these dashboards, run the following command: In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. -# Register your validators on-chain on the Bolt Registry +# On-Chain Registration Once you are successfully running the Bolt sidecar you need to register on-chain on the Bolt Registry to successfully receive preconfirmation requests from users From 0b7a8818848997dffe918a9dd4e03884aed10057 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 17:46:17 +0200 Subject: [PATCH 034/272] docs(holesky): smol chagnes --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4b80c8241..b4fea395d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -104,7 +104,7 @@ First, make sure to have both [Docker](https://docs.docker.com/engine/install/), Then clone the Bolt repository by running: ```bash -git clone --branch v0.3.0 htts://github.com/chainbound/bolt.git && cd bolt +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git && cd bolt ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt From b37f13347f6aeb4887e7230e337528c3048816a0 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 18:26:05 +0200 Subject: [PATCH 035/272] docs(holesky): smol changes --- testnets/holesky/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b4fea395d..af896fa11 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -67,6 +67,9 @@ client implementations to download and run them. > --builder-fallback-disable-checks > ``` > +> In other clients like Vouch, the same can be achieved by setting the `builder-boost-factor` to a large value +> like 18446744073709551615. +> > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon > Node](#avoid-restarting-the-beacon-node) for more details. @@ -124,17 +127,20 @@ containing the necessary environment variables: cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml ``` - For proper configuration of the signing - options, please refer to the [Delegations and + Next up, fill out all the values that are required. For proper configuration + of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. 2. **MEV-Boost Configuration:** - Similarly, create a `mev-boost.env` file in the - `testnets/holesky` folder to configure the MEV-Boost service. If you need a - reference, you can use the `.env.example` file in the `mev-boost` directory as a - starting point. + Copy over the example environment file: + + ```bash + cp ../../mev-boost/.env.example mev-boost.env + ``` + + Then configure the file accordingly. ```bash cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env From 80f1482dc720ce504b7549cc9bc190db52e875c9 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 18:45:07 +0200 Subject: [PATCH 036/272] docs(holesky): validator registration script --- testnets/holesky/README.md | 55 +++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index af896fa11..34e41aaff 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -350,10 +350,10 @@ on the Bolt Registry to successfully receive preconfirmation requests from users and RPCs. This step is needed to provide economic security to your commitments. -In order to do that you need some collateral in the form of whitelisted Liquid -Staking Token (LST) that needs to be restaked in either the Symbiotic or -EigenLayer protocol. Bolt is compatible with ETH derivatives on Holesky. Here -are references to the supported tokens on both restaking protocols: +In order to do that you need some collateral in the form of whitelisted ETH derivative +tokens that need to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens +on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) @@ -376,6 +376,24 @@ protocols. > public key associated to the private key used to sign commitments with the > Bolt Sidecar (the `--commitment-private-key` flag). +## Prerequisites + +- Install the Foundry tools: + +```bash +curl -L https://foundry.paradigm.xyz | bash +source $HOME/.bashrc +foundryup +``` + +- Clone the Bolt repo and navigate to the [contracts](https://github.com/chainbound/bolt/tree/unstable/bolt-contracts) directory: + +```bash +git clone https://github.com/chainbound/bolt +cd bolt-contracts +forge install +``` + ## Validator Registration The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for @@ -395,6 +413,35 @@ Until the Pectra hard-fork will be activated, the contract will also expose a `r that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and will allow us to test the registration flow in a controlled environment. +Note that the account initiating the registration will be the `controller` account for those validators. Only the `controller` can then +deregister validator or change any preferences. + +### Registration Steps + +> [!NOTE] +> All of these scripts can be simulated on a Holesky fork using Anvil with the following command: +> ```bash +> anvil --fork-url https://holesky.drpc.org +> ``` +> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in all of the `forge` commands below. + +To register your validators, we provide the following Foundry script: [`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` directory. + +- First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that +both `maxCommittedGasLimit` and `authorizedOperator` must reflect the values specified in previous steps, during the configuration of the sidecar. +`pubkeys` should be configured with all of the validator public keys that you wish to register. + +- Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. +This controller key will be used to run the script and will mark the corresponding account as the controller account for these validators. + +- Finally, run the script: +```bash +forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast +``` + +If the script executed succesfully, your validators were registered. + ## Bolt Network Entrypoint The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that From 9a41144d576889f081cf6ac44c9bc6d724a6e4a5 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Tue, 22 Oct 2024 10:25:18 +0200 Subject: [PATCH 037/272] docs(holesky): operator registration --- testnets/holesky/README.md | 115 +++++++++++++------------------------ 1 file changed, 41 insertions(+), 74 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 34e41aaff..72988c64d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -444,7 +444,7 @@ If the script executed succesfully, your validators were registered. ## Bolt Network Entrypoint -The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. @@ -457,44 +457,46 @@ Key features include: Specific functionalities about the restaking protocols are handled inside the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. -### Symbiotic Integration guide for Staking Pools +## Operator Registration +In this section we outline how to register as an operator, i.e. an entity uniquely identified by an Ethereum address and responsible for +duties like signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in +the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically +connect validators to an on-chain address associated with some stake, which is what the operator is. -As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. -If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) -on how to spin up a Vault and start receiving stake from your node operators. +**In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will +be read by the Forge scripts for registering operators and needs to be set correctly. You also have to invoke the scripts from the +[`bolt-contracts`](../../bolt-contracts) directory. -Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: - -1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. -2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. -3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. -4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. -5. You can now start approving operators that opt in to your vault directly in Symbiotic. -6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. - -### Symbiotic Integration guide for Operators +### Symbiotic Registration Steps As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide -commitments on their behalf. +commitments on their behalf. The opt-in process requires the following steps: +#### External Steps + +> [!NOTE] The network and supported vault addresses can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). + 1. register in Symbiotic with `OperatorRegistry.registerOperator()`. 2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. 3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. -4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. -5. get approved by the vault. -6. start providing commitments with the stake provided by the vault. -### EigenLayer Integration Guide for Node Operators and Solo Stakers +#### Internal Steps -> [!NOTE] -> Without loss of generality, we will assume the reader of this guide is a Node -> Operator (NO), since the same steps apply to solo stakers. -> As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) -> in the Bolt AVS built on top of EigenLayer. This requires -> running an Ethereum validator and the Bolt sidecar in order issue -> preconfirmations. +Run the provided Forge script to register a Symbiotic operator: + +```bash +forge script script/holesky/validators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +``` + +If all goes well, your Symbiotic operator was registered into Bolt. + +### EigenLayer Registration Steps + +#### External Steps + +> [!NOTE] The supported strategies can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). The Operator will be represented by an Ethereum address that needs to follow the standard procedure outlined in the @@ -502,53 +504,18 @@ to follow the standard procedure outlined in the 1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). -2. As an Ethereum validator offering precofirmations a NO needs some collateral in - order to be economically credible. In order to do that, some entities known as a "stakers" - need to deposit whitelisted Liquid Staking Tokens (LSTs) - into an appropriate "Strategy" associated to the LST via the - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), - so that the Operator has a `min_amount` (for Holesky 1 ether) of collateral associated to it. - Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract - in the `getWhitelistedCollaterals` function. - Note that NOs and stakers can be two different entities - _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. - -3. After the stakers have deposited their collateral into a strategy they need - to choose you as their operator. To do that, they need to call the function - [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). - -4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. - This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. - The payload is a signature whose digest consists of: - - 1. your operator address - 2. the `BoltEigenLayerMiddleware` contract address - 3. a salt - 4. an expiry 2. - - The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) - with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, - the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. - -Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` -contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a -registered operator and so that they can forward you preconfirmation requests. - -The steps required are the following: - -1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. - If you own more than one validator public key, - you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. - The `authorizedOperator` argument must be the same Ethereum address used for - opting into EigenLayer as an Operator. - -2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling - the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network - and allows you to manage operations effectively, such as pausing or resuming - your service. - -3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. - This ensures that your restaked assets are correctly integrated with Bolt’s system. +2. You can then use the same account to deposit into a supported EigenLayer strategy using [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). This will add the deposit into the collateral of the operator +so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. + +#### Internal Steps + +Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: + +```bash +forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +``` + +If all goes well, your EigenLayer operator was registered into Bolt. # Reference From 8406eb735581419c8b97f9f2328054cb5c5cc662 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 13:24:53 +0200 Subject: [PATCH 038/272] chore(holesky): README fmt --- testnets/holesky/README.md | 160 ++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 64 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 72988c64d..f3fe42e53 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -15,10 +15,11 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Observability](#observability) * [On-Chain Registration](#on-chain-registration) * [Validator Registration](#validator-registration) + * [Registration Steps](#registration-steps) * [Bolt Network Entrypoint](#bolt-network-entrypoint) - * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) - * [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) - * [EigenLayer Integration Guide for Node Operators and Solo Stakers](#eigenlayer-integration-guide-for-node-operators-and-solo-stakers) + * [Operator Registration](#operator-registration) + * [Symbiotic Registration Steps](#symbiotic-registration-steps) + * [EigenLayer Registration Steps](#eigenlayer-registration-steps) * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) @@ -67,8 +68,9 @@ client implementations to download and run them. > --builder-fallback-disable-checks > ``` > -> In other clients like Vouch, the same can be achieved by setting the `builder-boost-factor` to a large value -> like 18446744073709551615. +> In other clients like Vouch, the same can be achieved by setting the +> `builder-boost-factor` to a large value like `18446744073709551615` (`2**64 - +1`). > > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon @@ -134,18 +136,14 @@ containing the necessary environment variables: 2. **MEV-Boost Configuration:** - Copy over the example environment file: - - ```bash - cp ../../mev-boost/.env.example mev-boost.env - ``` - - Then configure the file accordingly. + Copy over the example configuration file: ```bash cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env ``` + Then configure it accordingly. + If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. @@ -376,7 +374,7 @@ protocols. > public key associated to the private key used to sign commitments with the > Bolt Sidecar (the `--commitment-private-key` flag). -## Prerequisites +**Prerequisites** - Install the Foundry tools: @@ -396,46 +394,60 @@ forge install ## Validator Registration -The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for -validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only +point of entry for validators to signal their intent to participate in Bolt +Protocol and authenticate with their BLS private key. The registration process includes the following steps: -1. Validator signs a message with their BLS private key. This is required to prove that the - validator private key is under their control and that they are indeed its owner. +1. Validator signs a message with their BLS private key. This is required to + prove that the validator private key is under their control and that they are + indeed its owner. 2. Validator calls the `registerValidator` function providing: 1. Their BLS public key 2. The BLS signature of the registration message 3. The address of the authorized collateral provider 4. The address of the authorized operator -Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function -that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and -will allow us to test the registration flow in a controlled environment. +Until the Pectra hard-fork will be activated, the contract will also expose a +`registerValidatorUnsafe` function that will not check the BLS signature. This +is gated by a feature flag that will be turned off post-Pectra and will allow us +to test the registration flow in a controlled environment. -Note that the account initiating the registration will be the `controller` account for those validators. Only the `controller` can then -deregister validator or change any preferences. +Note that the account initiating the registration will be the `controller` +account for those validators. Only the `controller` can then deregister +validator or change any preferences. ### Registration Steps > [!NOTE] -> All of these scripts can be simulated on a Holesky fork using Anvil with the following command: -> ```bash -> anvil --fork-url https://holesky.drpc.org -> ``` -> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in all of the `forge` commands below. - -To register your validators, we provide the following Foundry script: [`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). -Note that in order to run these scripts, you must be in the `bolt-contracts` directory. - -- First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that -both `maxCommittedGasLimit` and `authorizedOperator` must reflect the values specified in previous steps, during the configuration of the sidecar. -`pubkeys` should be configured with all of the validator public keys that you wish to register. - -- Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. -This controller key will be used to run the script and will mark the corresponding account as the controller account for these validators. +> All of these scripts can be simulated on a Holesky fork using Anvil with the +> following command: +> +> `bash anvil --fork-url https://holesky.drpc.org ` +> +> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in +> all of the `forge` commands below. + +To register your validators, we provide the following Foundry script: +[`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` +directory. + +- First, configure + [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) + to your requirements. Note that both `maxCommittedGasLimit` and + `authorizedOperator` must reflect the values specified in previous steps, during + the configuration of the sidecar. `pubkeys` should be configured with all of the + validator public keys that you wish to register. + +- Next up, decide on a controller account and save the key in an environment + variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run + the script and will mark the corresponding account as the controller account for + these validators. - Finally, run the script: + ```bash forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast ``` @@ -444,8 +456,9 @@ If the script executed succesfully, your validators were registered. ## Bolt Network Entrypoint -The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that -integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) +contract is a crucial component of Bolt that integrates with restaking +ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. Key features include: @@ -454,35 +467,45 @@ Key features include: 2. Integration with Symbiotic 3. Integration with Eigenlayer -Specific functionalities about the restaking protocols are handled inside -the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. +Specific functionalities about the restaking protocols are handled inside the +`IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and +`BoltEigenlayerMiddleware`. ## Operator Registration -In this section we outline how to register as an operator, i.e. an entity uniquely identified by an Ethereum address and responsible for -duties like signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in -the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically -connect validators to an on-chain address associated with some stake, which is what the operator is. -**In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will -be read by the Forge scripts for registering operators and needs to be set correctly. You also have to invoke the scripts from the -[`bolt-contracts`](../../bolt-contracts) directory. +In this section we outline how to register as an operator, i.e. an entity +uniquely identified by an Ethereum address and responsible for duties like +signing commitments. Note that in Bolt, there is no real separation between +validators and an operator. An operator is only real in the sense that its +private key will be used to sign commitments on the corresponding validators' +sidecars. However, we need a way to logically connect validators to an on-chain +address associated with some stake, which is what the operator is. + +**In the next sections we assume you have saved the private key corresponding to +the operator address in `$OPERATOR_SK`.** This private key will be read by the +Forge scripts for registering operators and needs to be set correctly. You also +have to invoke the scripts from the [`bolt-contracts`](../../bolt-contracts) +directory. ### Symbiotic Registration Steps -As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide -commitments on their behalf. +As an operator, you will need to opt-in to the Bolt Network and any Vault that +trusts you to provide commitments on their behalf. The opt-in process requires the following steps: -#### External Steps +**External Steps** -> [!NOTE] The network and supported vault addresses can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +> [!NOTE] +> The network and supported vault addresses can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). 1. register in Symbiotic with `OperatorRegistry.registerOperator()`. -2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +2. opt-in to the Bolt network with + `OperatorNetworkOptInService.optIn(networkAddress)`. 3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. -#### Internal Steps +**Internal Steps** Run the provided Forge script to register a Symbiotic operator: @@ -494,22 +517,31 @@ If all goes well, your Symbiotic operator was registered into Bolt. ### EigenLayer Registration Steps -#### External Steps +**External Steps** -> [!NOTE] The supported strategies can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +> [!NOTE] +> The supported strategies can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -The Operator will be represented by an Ethereum address that needs -to follow the standard procedure outlined in the -[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: +The Operator will be represented by an Ethereum address that needs to follow the +standard procedure outlined in the [EigenLayer +documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go +through the steps: -1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). +1. As an Operator, you register into EigenLayer using + [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). -2. You can then use the same account to deposit into a supported EigenLayer strategy using [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). This will add the deposit into the collateral of the operator -so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. +2. You can then use the same account to deposit into a supported EigenLayer + strategy using + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). + This will add the deposit into the collateral of the operator so that Bolt can + read it. Note that you need to deposit a minimum of `1 ether` of the strategies + underlying token in order to opt in. -#### Internal Steps +**Internal Steps** -Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: +Set the operator private key to an `OPERATOR_SK` environment variable, and then +run the following Forge script from the `bolt-contracts` directory: ```bash forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast From 3862bda4c255b9ccd7df504eef503fa951a0ce49 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 13:51:03 +0200 Subject: [PATCH 039/272] chore(holesky): validator controller reference on README --- testnets/holesky/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index f3fe42e53..531f3d982 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -443,8 +443,9 @@ directory. - Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run - the script and will mark the corresponding account as the controller account for - these validators. + the script and will mark the corresponding account as the [controller + account](https://github.com/chainbound/bolt/blob/06bdd8e75d759d91f6178ad73f962b1f4ad43fd8/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol#L18-L19) + for these validators. - Finally, run the script: From f1d01c67708666ce6d55f0bbd194f8d6a6ad5d1d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 14:04:01 +0200 Subject: [PATCH 040/272] fix(holesky): stale flags in README --- testnets/holesky/README.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 531f3d982..fb6594a4e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -888,17 +888,21 @@ below. As you can see in the [command line options](#command-line-options) section you can pass directly the private key as a hex-encoded string to the Bolt sidecar -using the `--private-key` flag. This is the simplest setup and can be used in +using the `--constraint-private-key` flag (or `constraint_private_key` in the +TOML file). + +This is the simplest setup and can be used in case if all the delegations messages point to the same delegatee or if you're running the sidecar with a single active validator. ### Using a ERC-2335 Keystore -The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. -In order to use them you need to provide the `--keystore-path` pointing to the -folder containing the keystore files and the `--keystore-password` or -`keystore-secrets-path` flag pointing to the folder containing the password -file. +The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) +keystores for loading signing keypairs. In order to use them you need to provide +the `--keystore-path` pointing to the folder containing the keystore files and +the `--keystore-password` or `keystore-secrets-path` flag pointing to the folder +containing the password file (in the TOML configuration file these are the +`keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). Both the `keys` and `passwords` folders must adhere to the structure outlined in the [Delegations CLI example](#delegations-cli-example) section. @@ -914,9 +918,9 @@ However if you're already running a PBS sidecar like [MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid the restart by following this steps when starting the Bolt sidecar: -1. Set the `--constraints-proxy-port` flag or the - `BOLT_SIDECAR_BUILDER_PROXY_PORT` environment variable to the port previously occupied by - MEV-Boost. +1. Set the `--constraints-proxy-port` flag (the `constraints_proxy_port` option + in the TOML file) to the port previously occupied by MEV-Boost. 2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it using another port -3. Set the `--constraints-url` flag or the `BOLT_SIDECAR_CONSTRAINTS_URL` to point to the Bolt MEV-Boost instance. +3. Set the `--constraints-api-url` flag (or the `constraints_api_url` in the + TOML file) to point to the Bolt MEV-Boost instance. From 56ffd6e635de7a0b29f4a7c43ea2c4a30e09856d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 14:04:19 +0200 Subject: [PATCH 041/272] chore(sidecar): mark required fields in the Config.example.toml --- bolt-sidecar/Config.example.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index b76ead584..2bb86003b 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -21,18 +21,23 @@ constraints_api_url = "http://localhost:18551" # - a comma-separated list of indexes (e.g. "1,2,3,4") # - a contiguous range of indexes (e.g. "1..4") # - a mix of the above (e.g. "1,2..4,6..8") +# REQUIRED validator_indexes = "0..64" # The JWT secret token to authenticate calls to the engine API. It can be # either be a hex-encoded string or a file path to a file containing the # hex-encoded secret. +# REQUIRED engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" # The fee recipient address for fallback blocks +# REQUIRED fee_recipient = "0x0000000000000000000000000000000000000000" # Secret ECDSA key to sign commitment messages with. The public key associated # to it must be then used when registering the operator in the `BoltManager` # contract +# REQUIRED commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" # Secret BLS key to sign fallback payloads with +# REQUIRED builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" # Commitments limits @@ -69,6 +74,7 @@ commitment_deadline = 8000 # cb_signer_url = "http://localhost:18551" # cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" # keystore_password = "password" +# keystore_secrets_path = "./secrets" # keystore_path = "./keys" # delegations_path = "./delegations.json" From d035e47a4178ba6593d82bbb76d45e4482c1e080 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 10:31:36 +0200 Subject: [PATCH 042/272] fix(holesky): wrong code links and snippets in README --- testnets/holesky/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index fb6594a4e..fa1a31cfd 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -424,9 +424,9 @@ validator or change any preferences. > All of these scripts can be simulated on a Holesky fork using Anvil with the > following command: > -> `bash anvil --fork-url https://holesky.drpc.org ` +> `anvil --fork-url https://holesky.drpc.org --port 8545` > -> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in +> In order to use this local fork, replace `$HOLESKY_RPC` with `localhost:8545` in > all of the `forge` commands below. To register your validators, we provide the following Foundry script: @@ -530,11 +530,11 @@ documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: 1. As an Operator, you register into EigenLayer using - [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/DelegationManager.sol#L107-L119). 2. You can then use the same account to deposit into a supported EigenLayer strategy using - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). This will add the deposit into the collateral of the operator so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. From 83e2243427ec8ad7b257517737e6d9269bb4811a Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 17:41:11 +0200 Subject: [PATCH 043/272] feat(holesky): test commit grafana dashboard and prometheus setup --- .../grafana/dashboards/bolt_dashboard.json | 740 ++++++++++++++++-- .../grafana/datasources/datasources.yml | 6 +- testnets/holesky/targets.json | 14 + 3 files changed, 686 insertions(+), 74 deletions(-) create mode 100644 testnets/holesky/targets.json diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json index 886ddfe02..dce85acb0 100644 --- a/testnets/holesky/grafana/dashboards/bolt_dashboard.json +++ b/testnets/holesky/grafana/dashboards/bolt_dashboard.json @@ -15,36 +15,144 @@ } ] }, - "description": "Metrics related to the bolt-sidecar and bolt-boost.", "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 3, + "id": 2, "links": [], + "liveNow": false, "panels": [ { - "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 1, - "w": 24, + "h": 8, + "w": 12, "x": 0, "y": 0 }, - "id": 2, - "panels": [], - "title": "Bolt sidecar", - "type": "row" + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transactions Preconfirmed", + "type": "timeseries" }, { "datasource": { - "default": true, "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { - "mode": "thresholds" + "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", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } }, "mappings": [], "thresholds": { @@ -53,6 +161,10 @@ { "color": "green", "value": null + }, + { + "color": "red", + "value": 80 } ] } @@ -62,67 +174,130 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, - "y": 1 + "x": 12, + "y": 0 }, - "id": 1, + "id": 10, "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + "tooltip": { + "mode": "single", + "sort": "none" + } }, - "pluginVersion": "11.2.0", "targets": [ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, - "disableTextWrap": false, "editorMode": "builder", - "expr": "bolt_sidecar_latest_head", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "expr": "bolt_sidecar_validation_errors", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "Latest head", - "type": "stat" + "title": "Invalid Transactions Reasons", + "type": "timeseries" }, { - "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 1, - "w": 24, + "h": 8, + "w": 12, "x": 0, - "y": 9 + "y": 8 }, - "id": 4, - "panels": [], - "title": "Bolt boost", - "type": "row" + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Remote Blocks Proposed", + "type": "timeseries" }, { "datasource": { - "default": true, "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { @@ -130,13 +305,11 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, - "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -145,8 +318,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -182,10 +354,10 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, - "y": 10 + "x": 12, + "y": 8 }, - "id": 3, + "id": 4, "options": { "legend": { "calcs": [], @@ -202,25 +374,451 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, - "disableTextWrap": false, "editorMode": "builder", - "expr": "cb_pbs_constraints_cache_size", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "{{__name__}}", + "expr": "bolt_sidecar_inclusion_commitments_received", + "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "Constraints cache size", + "title": "Inclusion Commitments Received", "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Local Blocks Proposed", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Inclusion Commitments Accepted", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Total HTTP Requests", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_duration_seconds", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "HTTP Requests Durations in ms", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Head Slot", + "type": "stat" } ], - "schemaVersion": 39, + "refresh": "", + "schemaVersion": 38, + "style": "dark", "tags": [], "templating": { "list": [] @@ -230,9 +828,9 @@ "to": "now" }, "timepicker": {}, - "timezone": "browser", - "title": "Bolt Metrics", - "uid": "edxnwlpgaw934c", + "timezone": "", + "title": "Bolt Sidecar", + "uid": "e5960f6d-a1ed-4538-9c7c-3ecba4d4b4b1", "version": 3, "weekStart": "" } diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml index 6d29d617b..91e10e2b4 100644 --- a/testnets/holesky/grafana/datasources/datasources.yml +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -1,11 +1,11 @@ apiVersion: 1 datasources: - - name: cb-prometheus + - name: bolt-prometheus-holesky type: prometheus - uid: cb_prometheus + uid: bolt-prometheus-holesky access: proxy orgId: 1 - url: http://cb_prometheus:9090 + url: http://bolt-prometheus-holesky:49090 isDefault: true editable: true diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json new file mode 100644 index 000000000..6cd87cbc6 --- /dev/null +++ b/testnets/holesky/targets.json @@ -0,0 +1,14 @@ +[ + { + "targets": ["bolt-sidecar-holesky:8017"], + "labels": { + "job": "bolt-sidecar-rpc" + } + }, + { + "targets": ["bolt-sidecar-holesky:18550"], + "labels": { + "job": "bolt-sidecar-builder-proxy" + } + } +] From b1db09bd9dcbd845132badd7f371090b5d00dc38 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 10:53:16 +0200 Subject: [PATCH 044/272] fix(contracts): move operators scripts into new operators folder --- .../RegisterEigenLayerOperator.s.sol | 0 .../{validators => operators}/RegisterSymbioticOperator.s.sol | 0 testnets/holesky/README.md | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename bolt-contracts/script/holesky/{validators => operators}/RegisterEigenLayerOperator.s.sol (100%) rename bolt-contracts/script/holesky/{validators => operators}/RegisterSymbioticOperator.s.sol (100%) diff --git a/bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol similarity index 100% rename from bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol rename to bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol diff --git a/bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol similarity index 100% rename from bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol rename to bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index fa1a31cfd..4a6f6df60 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -511,7 +511,7 @@ The opt-in process requires the following steps: Run the provided Forge script to register a Symbiotic operator: ```bash -forge script script/holesky/validators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast ``` If all goes well, your Symbiotic operator was registered into Bolt. @@ -545,7 +545,7 @@ Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: ```bash -forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast ``` If all goes well, your EigenLayer operator was registered into Bolt. From 6c5d3e1bc680d1a57a1cb6b6993585c53324da6e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 11:46:03 +0200 Subject: [PATCH 045/272] feat(holesky): EL script for StrategyManager.depositIntoStrategy --- .../config/holesky/deployments.json | 74 +++++++++--------- bolt-contracts/config/holesky/operator.json | 9 ++- .../eigenlayer/depositIntoStrategy.json | 5 ++ .../RegisterEigenLayerOperator.s.sol | 59 +++++++++++++- testnets/holesky/README.md | 77 ++++++++++++++----- 5 files changed, 159 insertions(+), 65 deletions(-) create mode 100644 bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json diff --git a/bolt-contracts/config/holesky/deployments.json b/bolt-contracts/config/holesky/deployments.json index a5a41415d..2866f9e27 100644 --- a/bolt-contracts/config/holesky/deployments.json +++ b/bolt-contracts/config/holesky/deployments.json @@ -1,38 +1,38 @@ { - "bolt": { - "validators": "0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8", - "parameters": "0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12", - "manager": "0x440202829b493F9FF43E730EB5e8379EEa3678CF" - }, - "symbiotic": { - "network": "0xb017002D8024d8c8870A5CECeFCc63887650D2a4", - "operatorRegistry": "0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548", - "networkOptInService": "0x58973d16FFA900D11fC22e5e2B6840d9f7e13401", - "vaultFactory": "0x407A039D94948484D356eFB765b3c74382A050B4", - "vaultConfigurator": "0xD2191FE92987171691d552C219b8caEf186eb9cA", - "networkRegistry": "0x7d03b7343BF8d5cEC7C0C27ecE084a20113D15C9", - "networkMiddlewareService": "0x62a1ddfD86b4c1636759d9286D3A0EC722D086e3", - "middleware": "0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8", - "supportedVaults": [ - "0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78", - "0xe5708788c90e971f73D928b7c5A8FD09137010e0", - "0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd", - "0xC56Ba584929c6f381744fA2d7a028fA927817f2b", - "0xcDdeFfcD2bA579B8801af1d603812fF64c301462", - "0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f" - ] - }, - "eigenLayer": { - "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", - "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", - "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", - "middleware": "0xa632a3e652110Bb2901D5cE390685E6a9838Ca04", - "supportedStrategies": [ - "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", - "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", - "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", - "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", - "0xaccc5A86732BE85b5012e8614AF237801636F8e5" - ] - } -} \ No newline at end of file + "bolt": { + "validators": "0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8", + "parameters": "0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12", + "manager": "0x440202829b493F9FF43E730EB5e8379EEa3678CF" + }, + "symbiotic": { + "network": "0xb017002D8024d8c8870A5CECeFCc63887650D2a4", + "operatorRegistry": "0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548", + "networkOptInService": "0x58973d16FFA900D11fC22e5e2B6840d9f7e13401", + "vaultFactory": "0x407A039D94948484D356eFB765b3c74382A050B4", + "vaultConfigurator": "0xD2191FE92987171691d552C219b8caEf186eb9cA", + "networkRegistry": "0x7d03b7343BF8d5cEC7C0C27ecE084a20113D15C9", + "networkMiddlewareService": "0x62a1ddfD86b4c1636759d9286D3A0EC722D086e3", + "middleware": "0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8", + "supportedVaults": [ + "0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78", + "0xe5708788c90e971f73D928b7c5A8FD09137010e0", + "0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd", + "0xC56Ba584929c6f381744fA2d7a028fA927817f2b", + "0xcDdeFfcD2bA579B8801af1d603812fF64c301462", + "0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f" + ] + }, + "eigenLayer": { + "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", + "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", + "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", + "middleware": "0xa632a3e652110Bb2901D5cE390685E6a9838Ca04", + "supportedStrategies": [ + "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", + "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", + "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", + "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", + "0xaccc5A86732BE85b5012e8614AF237801636F8e5" + ] + } +} diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operator.json index 0b6373c92..7e1d43c10 100644 --- a/bolt-contracts/config/holesky/operator.json +++ b/bolt-contracts/config/holesky/operator.json @@ -1,5 +1,6 @@ { - "rpc": "localhost:50051", - "salt": "0x000000000000000abc0000000000000000000000000000000000000000000000", - "expiry": null -} \ No newline at end of file + "rpc": ":", + "salt": "0x0000000000000000000_salt_value_0000000000000000000000000000000000", + "expiry": "0x00000000000000000_expiry_value_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} + diff --git a/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json new file mode 100644 index 000000000..f500598aa --- /dev/null +++ b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json @@ -0,0 +1,5 @@ +{ + "strategy": "", + "token": "", + "amount": "" +} diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 3acbe2d81..109f70c73 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -4,13 +4,14 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; +import {IDelegationManager} from "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy, IERC20} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; import {IBoltMiddlewareV1} from "../../../src/interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; contract RegisterEigenLayerOperator is Script { struct OperatorConfig { @@ -19,9 +20,27 @@ contract RegisterEigenLayerOperator is Script { uint256 expiry; } - function run() public { + function S01_depositIntoStrategy() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); + IStrategyManager strategyManager = _readStrategyManager(); + + string memory json = vm.readFile("config/holesky/operators/eigenlayer/depositIntoStrategy.json"); + + IStrategy strategy = IStrategy(vm.parseJsonAddress(json, ".strategy")); + IERC20 token = IERC20(vm.parseJsonAddress(json, ".token")); + uint256 amount = vm.parseJsonUint(json, ".amount"); + + vm.startBroadcast(operatorSk); + // Allowance must be set before depositing + token.approve(address(strategyManager), amount); + strategyManager.depositIntoStrategy(strategy, token, amount); + console.log("Successfully run StrategyManager.depositIntoStrategy"); + vm.stopBroadcast(); + } + + function S02_registerIntoBoltAVS() public { + uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); @@ -53,6 +72,16 @@ contract RegisterEigenLayerOperator is Script { vm.stopBroadcast(); } + function S03_checkOperatorRegistration() public view { + address operatorPublicKey = vm.envAddress("OPERATOR_PK"); + console.log("Checking operator registration for address", operatorPublicKey); + + IBoltManagerV1 boltManager = _readBoltManager(); + bool isRegistered = boltManager.isOperator(operatorPublicKey); + console.log("Operator is registered:", isRegistered); + require(isRegistered, "Operator is not registered"); + } + function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -69,6 +98,28 @@ contract RegisterEigenLayerOperator is Script { return IAVSDirectory(vm.parseJsonAddress(json, ".eigenLayer.avsDirectory")); } + function _readDelegationManager() public view returns (IDelegationManager) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return IDelegationManager(vm.parseJsonAddress(json, ".eigenLayer.delegationManager")); + } + + function _readStrategyManager() public view returns (IStrategyManager) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IStrategyManager(vm.parseJsonAddress(json, ".eigenLayer.strategyManager")); + } + + function _readBoltManager() public view returns (IBoltManagerV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + } + function _readConfig( string memory path ) public view returns (OperatorConfig memory) { diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4a6f6df60..cfdf062da 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -524,31 +524,66 @@ If all goes well, your Symbiotic operator was registered into Bolt. > The supported strategies can be found in > [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -The Operator will be represented by an Ethereum address that needs to follow the -standard procedure outlined in the [EigenLayer -documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go -through the steps: +If you're not registered as an operator in EigenLayer yet, you need to do so by +following [the official +guide](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-introduction). +This requires installing the EigenLayer CLI and opt into the protocol by +registering via the +[`DelegationManager.registerAsOperator`](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-installation) +function. + +After that you need to deposit into a supported EigenLayer +strategy using +[`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). +This will add the deposit into the collateral of the operator so that Bolt can +read it. Note that you need to deposit a minimum of `1 ether` of the strategies +underlying token in order to opt in. + +We've provided a script to facilitate the procedure. If you want to use it, +please set the operator private key to an `OPERATOR_SK` environment variable. + +First, you need to first configure the deposit details in this JSON +file: -1. As an Operator, you register into EigenLayer using - [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/DelegationManager.sol#L107-L119). +```bash +$EDITOR ./config/holesky/operators/eigenlayer/depositIntoStrategy.json +``` + +Then you can run the following Forge script: -2. You can then use the same account to deposit into a supported EigenLayer - strategy using - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). - This will add the deposit into the collateral of the operator so that Bolt can - read it. Note that you need to deposit a minimum of `1 ether` of the strategies - underlying token in order to opt in. +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S01_depositIntoStrategy()" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast +``` **Internal Steps** -Set the operator private key to an `OPERATOR_SK` environment variable, and then -run the following Forge script from the `bolt-contracts` directory: +After having deposited collateral into a strategy you need to register into the +Bolt AVS. We've provided a script to facilitate the procedure. If you want to +use it, please set the operator private key to an `OPERATOR_SK` environment +variable, and then run the following Forge script from the `bolt-contracts` +directory: ```bash -forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S02_registerIntoBoltAVS" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -If all goes well, your EigenLayer operator was registered into Bolt. +To check if your operator is correctly registered, set the operator public key +in the `OPERATOR_PK` environment variable and run the following script: + +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S03_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` # Reference @@ -559,13 +594,14 @@ sidecar. You can see them in your terminal by running the Bolt sidecar binary with the `--help` flag: ``` + Command-line options for the Bolt sidecar Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: - --port - Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! +--port +Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] [default: 8017] @@ -709,8 +745,9 @@ Options: --disable-metrics [env: BOLT_SIDECAR_DISABLE_METRICS=] - -h, --help - Print help (see a summary with '-h') +-h, --help +Print help (see a summary with '-h') + ``` ## Delegations and signing options for Native and Docker Compose Mode From e262f2cb24900daf8c3189c8eab61c2ed758d0fe Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 10:22:06 +0200 Subject: [PATCH 046/272] fix(holesky): formatting in README --- testnets/holesky/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index cfdf062da..6089342c1 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -600,8 +600,8 @@ Command-line options for the Bolt sidecar Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: ---port -Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! + --port + Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] [default: 8017] @@ -637,8 +637,11 @@ Port to listen on for incoming JSON-RPC requests of the Commitments API. This po [default: 18550] --validator-indexes - Validator indexes of connected validators that the sidecar should accept commitments on behalf of. Accepted values: - a comma-separated list of indexes (e.g. "1,2,3,4") - a contiguous range of indexes (e.g. "1..4") - a mix of the - above (e.g. "1,2..4,6..8") + Validator indexes of connected validators that the sidecar should accept commitments on behalf of. + Accepted values: + - a comma-separated list of indexes (e.g. "1,2,3,4") + - a contiguous range of indexes (e.g. "1..4") + - a mix of the above (e.g. "1,2..4,6..8") [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] @@ -745,8 +748,8 @@ Port to listen on for incoming JSON-RPC requests of the Commitments API. This po --disable-metrics [env: BOLT_SIDECAR_DISABLE_METRICS=] --h, --help -Print help (see a summary with '-h') + -h, --help + Print help (see a summary with '-h') ``` From a567c668b99943ba4eb66fdcaa4741d6d015d5d6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 11:04:20 +0200 Subject: [PATCH 047/272] chore(holesky): better explanation of operator config steps for EL --- .../eigenlayer/registerIntoBoltAVS.json} | 5 ++- .../RegisterEigenLayerOperator.s.sol | 2 +- testnets/holesky/README.md | 33 +++++++++++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) rename bolt-contracts/config/holesky/{operator.json => operators/eigenlayer/registerIntoBoltAVS.json} (68%) diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json similarity index 68% rename from bolt-contracts/config/holesky/operator.json rename to bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json index 7e1d43c10..dd38cee99 100644 --- a/bolt-contracts/config/holesky/operator.json +++ b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json @@ -1,6 +1,5 @@ { "rpc": ":", - "salt": "0x0000000000000000000_salt_value_0000000000000000000000000000000000", - "expiry": "0x00000000000000000_expiry_value_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "salt": "0x0000000000000000000_salt_value_000000000000000000000000000000000", + "expiry": "0x00000000000000000_expiry_value_000000000000000000000000000000000" } - diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 109f70c73..c67b7ef06 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -45,7 +45,7 @@ contract RegisterEigenLayerOperator is Script { BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); IAVSDirectory avsDirectory = _readAvsDirectory(); - OperatorConfig memory config = _readConfig("config/holesky/operator.json"); + OperatorConfig memory config = _readConfig("config/holesky/operators/eigenlayer/registerIntoBoltAVS.json"); console.log("Registering EigenLayer operator"); console.log("Operator address:", operator); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 6089342c1..524d64335 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -563,9 +563,36 @@ forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ After having deposited collateral into a strategy you need to register into the Bolt AVS. We've provided a script to facilitate the procedure. If you want to -use it, please set the operator private key to an `OPERATOR_SK` environment -variable, and then run the following Forge script from the `bolt-contracts` -directory: +use it, please set follow these steps: + +1. configure the operator details in this JSON file + + ```bash + $EDITOR ./config/holesky/operators/eigenlayer/registerIntoBoltAVS.json + ``` + + In there you'll need to set the the following fields: + + - `rpc` -- the RPC URL of your operator which supports the Commitments API + - `salt` -- an unique 32 bytes value to avoid replay attacks. To generate it on + both Linux and MacOS you can run: + + ```bash + echo -n "0x"; head -c 32 /dev/urandom | hexdump -e '32/1 "%02x" "\n"' + ``` + + - `expiry` -- the timestamp of the signature expiry in seconds. To generate it + on both Linux and MacOS run the following command, replacing + `` with the desired timestamp: + + ```bash + echo -n "0x"; printf "%064x\n" + ``` + +2. set the operator private key to an `OPERATOR_SK` environment + variable; +3. run the following Forge script from the `bolt-contracts` + directory: ```bash forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ From 9b98a46940081ac9e02d5c105a1f48ee5628e1ce Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 16:08:38 +0200 Subject: [PATCH 048/272] feat(holesky): update scripts + README for Symbiotic integration --- .../operators/RegisterSymbioticOperator.s.sol | 37 +++++++++++----- testnets/holesky/README.md | 43 +++++++++++++++---- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index 0089432c0..f2728c85b 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -4,7 +4,10 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; + import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; +import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { @@ -14,7 +17,7 @@ contract RegisterSymbioticOperator is Script { address symbioticNetwork; } - function run() public { + function S01_registerIntoBolt() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); @@ -22,15 +25,12 @@ contract RegisterSymbioticOperator is Script { Config memory config = _readConfig(); // First, make sure the operator is opted into the network - if (!config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork)) { - console.log("Operator is not opted into the network yet. Opting in..."); - vm.startBroadcast(operatorSk); - config.symbioticNetworkOptInService.optIn(config.symbioticNetwork); - vm.stopBroadcast(); - console.log("Operator successfully opted into the network"); - } - - console.log("Registering Symbiotic operator"); + require( + config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork), + "Operator must be opted in into Bolt Network" + ); + + console.log("Registering Symbiotic operator into Bolt"); console.log("Operator address:", operator); console.log("Operator RPC:", config.rpc); @@ -41,6 +41,16 @@ contract RegisterSymbioticOperator is Script { vm.stopBroadcast(); } + function S02_checkOperatorRegistration() public view { + address operatorPublicKey = vm.envAddress("OPERATOR_PK"); + console.log("Checking operator registration for address", operatorPublicKey); + + IBoltManagerV1 boltManager = _readBoltManager(); + bool isRegistered = boltManager.isOperator(operatorPublicKey); + console.log("Operator is registered:", isRegistered); + require(isRegistered, "Operator is not registered"); + } + function _readConfig() public view returns (Config memory) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -56,4 +66,11 @@ contract RegisterSymbioticOperator is Script { symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) }); } + + function _readBoltManager() public view returns (IBoltManagerV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + } } diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 524d64335..b15e25fc0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -493,28 +493,53 @@ directory. As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide commitments on their behalf. -The opt-in process requires the following steps: - **External Steps** > [!NOTE] > The network and supported vault addresses can be found in > [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -1. register in Symbiotic with `OperatorRegistry.registerOperator()`. -2. opt-in to the Bolt network with - `OperatorNetworkOptInService.optIn(networkAddress)`. -3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +Make sure you have installed the [Symbiotic +CLI](https://docs.symbiotic.fi/guides/cli/). + +The opt-in process requires the following steps: + +1. if you haven't done it already, register as a Symbiotic Operator with the + [`register-operator`](https://docs.symbiotic.fi/guides/cli/#register-operator) + command; +2. opt-in to the Bolt network with the + [`opt-in-network`](https://docs.symbiotic.fi/guides/cli/#opt-in-network) + command; +3. opt-in to any vault using the + [`opt-in-vault`](https://docs.symbiotic.fi/guides/cli/#opt-in-vault) command; +4. deposit collateral into the vault using the + [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. **Internal Steps** -Run the provided Forge script to register a Symbiotic operator: +After having deposited collateral into a vault you need to register into +Bolt as a Symbiotic operator. We've provided a script to facilitate the +procedure. If you want to use it, please set the operator private key to an +`OPERATOR_SK` environment variable, and then run the following Forge script from +the `bolt-contracts` directory: ```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -If all goes well, your Symbiotic operator was registered into Bolt. +To check if your operator is correctly registered, set the operator public key +in the `OPERATOR_PK` environment variable and run the following script: + +```bash +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S02_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` ### EigenLayer Registration Steps From 36eee882cb4eff89b61b0490e4ce6e6c1a4976cd Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 11:13:17 +0200 Subject: [PATCH 049/272] fix(holesky): config options for Symbiotic guide Don't use operators.json file but provide the RPC URL using an enviroment value --- .../operators/RegisterSymbioticOperator.s.sol | 5 ++-- testnets/holesky/README.md | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index f2728c85b..b5a6d46d8 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -19,6 +19,7 @@ contract RegisterSymbioticOperator is Script { function S01_registerIntoBolt() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); + string memory rpc = vm.envString("OPERATOR_RPC"); address operator = vm.addr(operatorSk); @@ -32,10 +33,10 @@ contract RegisterSymbioticOperator is Script { console.log("Registering Symbiotic operator into Bolt"); console.log("Operator address:", operator); - console.log("Operator RPC:", config.rpc); + console.log("Operator RPC:", rpc); vm.startBroadcast(operatorSk); - config.symbioticMiddleware.registerOperator(config.rpc); + config.symbioticMiddleware.registerOperator(rpc); console.log("Successfully registered Symbiotic operator"); vm.stopBroadcast(); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b15e25fc0..154328429 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -519,17 +519,20 @@ The opt-in process requires the following steps: After having deposited collateral into a vault you need to register into Bolt as a Symbiotic operator. We've provided a script to facilitate the -procedure. If you want to use it, please set the operator private key to an -`OPERATOR_SK` environment variable, and then run the following Forge script from -the `bolt-contracts` directory: +procedure. If you want to use it, please follow these steps: -```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ - --sig "S01_registerIntoBolt" \ - --rpc-url $HOLESKY_RPC \ - -vvvv \ - --broadcast -``` +1. set the operator private key to the `OPERATOR_SK` environment variable; +2. set the operator RPC URL which supports the Commitments API to the + `OPERATOR_RPC` environment variable; +3. run the following Forge script from the `bolt-contracts` directory: + + ```bash + forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast + ``` To check if your operator is correctly registered, set the operator public key in the `OPERATOR_PK` environment variable and run the following script: From 4f577641ccd386e8dfe6dfce0070baee96b98065 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 13:41:41 +0200 Subject: [PATCH 050/272] chore(holesky): upgrade vault addresses for Symbiotic --- testnets/holesky/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 154328429..6574cb06d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -354,8 +354,12 @@ EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) - - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) + - [`rETH`](https://holesky.etherscan.io/address/0xe5708788c90e971f73D928b7c5A8FD09137010e0) + - [`stETH`](https://holesky.etherscan.io/address/0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd) + - [`WETH`](https://holesky.etherscan.io/address/0xC56Ba584929c6f381744fA2d7a028fA927817f2b) + - [`cbETH`](https://holesky.etherscan.io/address/0xcDdeFfcD2bA579B8801af1d603812fF64c301462) + - [`mETH`](https://holesky.etherscan.io/address/0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f) - [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) From dbb7ee66f21de07375d413f08bb1520b9e9ec2d9 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Thu, 24 Oct 2024 14:45:07 +0200 Subject: [PATCH 051/272] fix(contracts/scripts): register Symbiotic operator script --- .../operators/RegisterSymbioticOperator.s.sol | 25 +++++++++++-------- testnets/holesky/README.md | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index b5a6d46d8..d57902e9b 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -11,7 +11,6 @@ import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { - string rpc; BoltSymbioticMiddlewareV1 symbioticMiddleware; IOptInService symbioticNetworkOptInService; address symbioticNetwork; @@ -25,14 +24,15 @@ contract RegisterSymbioticOperator is Script { Config memory config = _readConfig(); + console.log("Registering Symbiotic operator into Bolt"); + console.log("Operator address:", operator); + // First, make sure the operator is opted into the network require( config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork), "Operator must be opted in into Bolt Network" ); - console.log("Registering Symbiotic operator into Bolt"); - console.log("Operator address:", operator); console.log("Operator RPC:", rpc); vm.startBroadcast(operatorSk); @@ -40,14 +40,23 @@ contract RegisterSymbioticOperator is Script { console.log("Successfully registered Symbiotic operator"); vm.stopBroadcast(); + + (address[] memory tokens, uint256[] memory amounts) = + config.symbioticMiddleware.getOperatorCollaterals(operator); + + console.log("Operator collateral:"); + for (uint256 i; i < tokens.length; ++i) { + console.log("Collateral:", tokens[i], "Amount:", amounts[i]); + } } function S02_checkOperatorRegistration() public view { - address operatorPublicKey = vm.envAddress("OPERATOR_PK"); - console.log("Checking operator registration for address", operatorPublicKey); + address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); + console.log("Checking operator registration for address", operatorAddress); IBoltManagerV1 boltManager = _readBoltManager(); - bool isRegistered = boltManager.isOperator(operatorPublicKey); + bool isRegistered = boltManager.isOperator(operatorAddress); + console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); } @@ -57,11 +66,7 @@ contract RegisterSymbioticOperator is Script { string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - string memory operatorPath = string.concat(root, "/config/holesky/operator.json"); - string memory operatorJson = vm.readFile(operatorPath); - return Config({ - rpc: vm.parseJsonString(operatorJson, ".rpc"), symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), symbioticMiddleware: BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")), symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 6574cb06d..5ddfb7a3f 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -538,8 +538,8 @@ procedure. If you want to use it, please follow these steps: --broadcast ``` -To check if your operator is correctly registered, set the operator public key -in the `OPERATOR_PK` environment variable and run the following script: +To check if your operator is correctly registered, set the operator address +in the `OPERATOR_ADDRESS` environment variable and run the following script: ```bash forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ From e0376aa476ba2779d32bd01881838a7d522b0699 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:32:12 +0200 Subject: [PATCH 052/272] chore(holesky): updated bolt-cli usage guide --- testnets/holesky/README.md | 235 ++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 95 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 5ddfb7a3f..5ad5016ba 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -23,9 +23,8 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt-delegations-cli`](#`bolt-delegations-cli`) + * [`bolt-cli`](#bolt-cli) * [Installation and usage](#installation-and-usage) - * [Delegations CLI Example](#delegations-cli-example) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) * [Avoid restarting the beacon node](#avoid-restarting-the-beacon-node) @@ -42,8 +41,8 @@ your system. Bolt is fully trustless since it is able to produce a fallback block with the commitments issued in case builders do not return a valid bid. In order to do so it relies on a synced execution client, configured via the `--execution-api-url` -flag. At the moment only Geth is supported; with more -clients to be supported in the future. +flag. **At the moment only Geth is supported; with more +clients to be supported in the future.** Using the sidecar with a different execution client could lead to commitment faults because fallback block building is not supported yet. You can download @@ -74,22 +73,25 @@ client implementations to download and run them. > > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon -> Node](#avoid-restarting-the-beacon-node) for more details. +> Node](#avoid-restarting-the-beacon-node) section for more details. **Active validators:** -The Bolt sidecar requires signing keys from active Ethereum validators, or -authorized delegates acting on their behalf, to issue and sign preconfirmations. +The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, +or **authorized delegates** acting on their behalf, to issue and sign preconfirmations. + +To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +section. # Off-Chain Setup There are various way to run the Bolt Sidecar depending on what infrastructure you want to use and your preferred signing methods: -- Docker mode (recommended); +- Docker mode (recommended) - [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode - (requires Docker). -- Native mode (advanced, requires building everything from source); + (requires Docker) +- Native mode (advanced, requires building everything from source) Running the Bolt sidecar as a standalone binary requires building it from source. Both the standalone binary and the Docker container requires reading @@ -102,19 +104,19 @@ requirements. ## Docker Mode (recommended) -First, make sure to have both [Docker](https://docs.docker.com/engine/install/), +First, make sure to have [Docker](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/) and [git](https://git-scm.com/downloads) installed in your machine. Then clone the Bolt repository by running: ```bash -git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git && cd bolt +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git +cd bolt/testnets/holesky ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt -MEV-Boost fork which includes supports the [Constraints -API](https://docs.boltprotocol.xyz/api/builder). +MEV-Boost fork which includes supports the [Constraints API](https://docs.boltprotocol.xyz/api/builder). Before starting the services, you'll need to provide configuration files containing the necessary environment variables: @@ -652,7 +654,10 @@ For completeness, here are all the command-line options available for the Bolt sidecar. You can see them in your terminal by running the Bolt sidecar binary with the `--help` flag: -``` +
+CLI help Reference + +```text Command-line options for the Bolt sidecar @@ -812,6 +817,9 @@ Options: ``` +
+ + ## Delegations and signing options for Native and Docker Compose Mode As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar @@ -825,115 +833,150 @@ Ethereum validators. In order to create these delegation you can use the `bolt-delegations-cli` binary. If you don't want to use it you can skip the following section. -### `bolt-delegations-cli` +### `bolt` CLI -`bolt-delegations-cli` is an offline command-line tool for safely generating -delegation and revocation messages signed with a BLS12-381 key for the -[Constraints API](https://docs.boltprotocol.xyz/api/builder) in -[Bolt](https://docs.boltprotocol.xyz/). +`bolt` CLI is an offline tool for safely generating delegation and revocation messages +signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) +in [Bolt](https://docs.boltprotocol.xyz/). -The tool supports two key sources: +The tool supports three key sources: -- Local: A BLS private key provided directly from a file. -- Keystore: A keystore file that contains an encrypted BLS private key. +- **Secret Keys**: A list of BLS private keys provided directly as hex-strings. +- **Local Keystore**: A EIP-2335 keystore that contains an encrypted BLS private keys. +- **Dirk**: A remote Dirk server that provides the BLS signatures for the delegation messages. and outputs a JSON file with the delegation/revocation messages to the provided -`` for the given chain +`` for the given chain. -Features: +#### Installation and usage -- Offline usage: Safely generate delegation messages in an offline environment. -- Flexible key source: Support for both direct local BLS private keys and - Ethereum keystore files (ERC-2335 format). -- BLS delegation signing: Sign delegation messages using a BLS secret key and - output the signed delegation in JSON format. +Prerequisites: -#### Installation and usage +- [Rust toolchain][rust] +- [Protoc][protoc] -Go to the root of the Bolt project you've previously cloned using Git. Enter in -the `bolt-delegations-cli` directory by running `cd bolt-delegations-cli`. +Once you have the necessary prerequisites, you can build the binary +in the following way: -If you're using the Docker container setup make sure you have -[Rust](https://www.rust-lang.org/tools/install) installed in your system as -well. Then you can build the `bolt-delegations-cli` binary by running: +```shell +# clone the Bolt repository if you haven't already +git clone git@github.com:chainbound/bolt.git -```bash -cargo build --release && mv target/release/bolt-delegations-cli . -``` +# navigate to the Bolt CLI package directory +cd bolt-cli -Now you can run the binary by running: +# build and install the binary on your machine +cargo install --path . --force -```bash -./bolt-delegations-cli +# test the installation +bolt --version ``` -The binary exposes a single `generate` command, which accepts the following -options and subcommands (use `./bolt-delegations-cli generate --help` to see -them): +The binary can be used with the following command: -```text -Usage: bolt-delegations-cli generate [OPTIONS] --delegatee-pubkey +```shell +bolt delegate --delegate-pubkey + --out + --chain + + +``` -Commands: - local Use local private keys to generate the signed messages - keystore Use an EIP-2335 keystore folder to generate the signed messages - help Print this message or the help of the given subcommand(s) +where: -Options: - --delegatee-pubkey The BLS public key to which the delegation message should be signed [env: DELEGATEE_PUBKEY=] - --out The output file for the delegations [env: OUTPUT_FILE_PATH=] [default: delegations.json] - --chain The chain for which the delegation message is intended [env: CHAIN=] [default: mainnet] [possible values: mainnet, holesky, helder, kurtosis] - --action The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) [env: ACTION=] [default: delegate] [possible values: delegate, revoke] - -h, --help Print help (see more with '--help') -``` +- `` is the public key of the delegatee. +- `` is the path to the file where the delegation JSON messages will be written. +- `` is the chain for which the delegations are being generated (e.g. Holesky). +- `` is the key source to use for generating the delegations. It can be one of: + - `secret-keys`: A list of BLS private keys provided directly as hex-strings. + - `local-keystore`: A EIP-2335 keystore that contains an encrypted BLS private keys. + - `dirk`: A remote Dirk server that provides the BLS signatures for the delegation messages. + +You can also find more information about the available key source +options by running `bolt delegate --help`. > [!TIP] > If you're using the Docker Compose Mode please don't set the `--out` flag and > provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` > file. -The environment variables can be also set in a `.env` file. For a reference -example you can check out the `.env.local.example` and the -`.env.keystore.example` +Here you can see usage examples for each key source: + +
+Usage + +```text +❯ bolt-cli delegate --help +Generate BLS delegation or revocation messages +Usage: bolt-cli delegate [OPTIONS] --delegatee-pubkey +Commands: +secret-keys Use local secret keys to generate the signed messages +local-keystore Use an EIP-2335 filesystem keystore directory to generate the signed messages +dirk Use a remote DIRK keystore to generate the signed messages +help Print this message or the help of the given subcommand(s) +Options: + --delegatee-pubkey + The BLS public key to which the delegation message should be signed + [env: DELEGATEE_PUBKEY=] + --out + The output file for the delegations + [env: OUTPUT_FILE_PATH=] + [default: delegations.json] + --chain + The chain for which the delegation message is intended + [env: CHAIN=] + [default: mainnet] + [possible values: mainnet, holesky, helder, kurtosis] + --action + The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) + [env: ACTION=] + [default: delegate] + Possible values: + - delegate: Create a delegation message + - revoke: Create a revocation message +-h, --help + Print help (see a summary with '-h') +``` -In the section below you can see a usage example of the binary. +
-#### Delegations CLI Example +
+Examples -1. Using a local BLS private key: +1. Generating a delegation using a local BLS secret key - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - local \ - --secret-keys 0xabc123...,0xdef456.. - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x8d0edf4fe9c80cd640220ca7a68a48efcbc56a13536d6b274bf3719befaffa13688ebee9f37414b3dddc8c7e77233ce8 \ + --chain holesky \ + secret-keys --secret-keys 642e0d33fde8968a48b5f560c1b20143eb82036c1aa6c7f4adc4beed919a22e3 +``` -2. Using a Ethereum keystores files and raw password: +2. Generating a delegation using an ERC-2335 keystore directory - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - keystore \ - --path /keys \ - --password myS3cr3tP@ssw0rd - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x8d0edf4fe9c80cd640220ca7a68a48efcbc56a13536d6b274bf3719befaffa13688ebee9f37414b3dddc8c7e77233ce8 \ + --chain holesky \ + local-keystore --path test_data/lighthouse/validators --password-path test_data/lighthouse/secrets +``` -3. Using an Ethereum keystores files and secrets folder +3. Generating a delegation using a remote DIRK keystore - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - keystore \ - --path /keys \ - --password-path /secrets - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x83eeddfac5e60f8fe607ee8713efb8877c295ad9f8ca075f4d8f6f2ae241a30dd57f78f6f3863a9fe0d5b5db9d550b93 \ + dirk --url https://localhost:9091 \ + --client-cert-path ./test_data/dirk/client1.crt \ + --client-key-path ./test_data/dirk/client1.key \ + --ca-cert-path ./test_data/dirk/security/ca.crt \ + --wallet-path wallet1 --passphrases secret +``` + +
+ +
+Keystore-specific instructions When using the `keystore` key source, the `--path` flag should point to the directory containing the encrypted keypair directories. @@ -971,6 +1014,8 @@ That is, the password files should be named after the public key and each file should just contain one line with the password in plain text. The files themselves don't need a particular file extension. +
+ --- Now that you have generated the delegation messages you can provide them to the @@ -990,9 +1035,9 @@ can pass directly the private key as a hex-encoded string to the Bolt sidecar using the `--constraint-private-key` flag (or `constraint_private_key` in the TOML file). -This is the simplest setup and can be used in -case if all the delegations messages point to the same delegatee or if you're -running the sidecar with a single active validator. +This is the simplest setup and can be used in case if all the delegations messages +point to the same delegatee or if you're running the sidecar with a single active +validator. ### Using a ERC-2335 Keystore From 6a901b97a91f32e5ec88a52c4bac1b327e50373d Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:36:54 +0200 Subject: [PATCH 053/272] fix: broken links --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 5ad5016ba..45a2fd339 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -852,8 +852,8 @@ and outputs a JSON file with the delegation/revocation messages to the provided Prerequisites: -- [Rust toolchain][rust] -- [Protoc][protoc] +- [Rust toolchain](https://www.rust-lang.org/tools/install) +- [Protoc](https://grpc.io/docs/protoc-installation/) Once you have the necessary prerequisites, you can build the binary in the following way: From 7a9f75359a3593cbaa9d8a46f88b1f9f7da554b1 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:09:56 +0200 Subject: [PATCH 054/272] chore!(sidecar,holesky): drop support for TOML, use .env everywhere --- bolt-sidecar/.env.example | 83 +++++++++++++++------- bolt-sidecar/Config.example.toml | 84 ----------------------- bolt-sidecar/bin/sidecar.rs | 12 +--- bolt-sidecar/src/config/mod.rs | 23 ------- bolt-sidecar/src/primitives/delegation.rs | 2 +- testnets/holesky/bolt-sidecar.env.example | 67 ++++++++++++++++++ testnets/holesky/mev-boost.env.example | 32 +++++++++ 7 files changed, 159 insertions(+), 144 deletions(-) delete mode 100644 bolt-sidecar/Config.example.toml create mode 100644 testnets/holesky/bolt-sidecar.env.example create mode 100644 testnets/holesky/mev-boost.env.example diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index 7621489a6..cf7785bb4 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,34 +1,67 @@ # Ethereum Node Connections + PBS URLs -BOLT_SIDECAR_PORT=8000 -BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485 -BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400 -BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 -BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 -BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 -BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +BOLT_SIDECAR_PORT=8017 +# Execution client API URL +BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +# URL for the beacon client +BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") +BOLT_SIDECAR_VALIDATOR_INDEXES= +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. BOLT_SIDECAR_ENGINE_JWT_HEX= +# The fee recipient address for fallback blocks BOLT_SIDECAR_FEE_RECIPIENT= +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +# Secret BLS key to sign fallback payloads with BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -# Commitments configs -BOLT_SIDECAR_MAX_COMMITMENTS=128 -BOLT_SIDECAR_MAX_COMMITTED_GAS= -BOLT_SIDECAR_MIN_PRIORITY_FEE= -BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 +# Commitments limits +# Max number of commitments to accept per block +BOLT_SIDECAR_MAX_COMMITMENTS_PER_SLOT=128 +# Max committed gas per slot +BOLT_SIDECAR_MAX_COMMITTED_GAS_PER_SLOT=10_000_000 +# Min priority fee to accept for a commitment +BOLT_SIDECAR_MIN_PRIORITY_FEE=4_000_000_000 # 4 Gwei = 4 * 10^9 wei -# Chain configs -BOLT_SIDECAR_CHAIN=holesky +# Chain configuration +# Chain on which the sidecar is running +BOLT_SIDECAR_CHAIN="holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] BOLT_SIDECAR_SLOT_TIME=12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) +BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 -# Signing options. Uncomment only what you'll use -#BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= -#BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= -#BOLT_SIDECAR_CB_SIGNER_URL= -#BOLT_SIDECAR_CB_JWT_HEX= -#BOLT_SIDECAR_KEYSTORE_PASSWORD= -#BOLT_SIDECAR_KEYSTORE_PATH= -#BOLT_SIDECAR_DELEGATIONS_PATH= +# Signing options. +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_SECRETS_PATH= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= -# Metrics -BOLT_SIDECAR_METRICS_PORT= -BOLT_SIDECAR_DISABLE_METRICS= +# Telemetry and Metrics +BOLT_SIDECAR_METRICS_PORT=9091 +BOLT_SIDECAR_DISABLE_METRICS=false diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml deleted file mode 100644 index 2bb86003b..000000000 --- a/bolt-sidecar/Config.example.toml +++ /dev/null @@ -1,84 +0,0 @@ -# Ethereum Node Connections + PBS URLs - -# Port to listen on for incoming JSON-RPC requests of the Commitments API. This -# port should be open on your firewall in order to receive external requests! -port = 8017 -# Execution client API URL -execution_api_url = "http://localhost:8545" -# URL for the beacon client -beacon_api_url = "http://localhost:5052" -# Execution client Engine API URL. This is needed for fallback block building -# and must be a synced Geth node -engine_api_url = "http://localhost:8551" -# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client -constraints_proxy_port = 18550 -# URL to forward the constraints produced by the Bolt sidecar to a server -# supporting the Constraints API, such as an MEV-Boost fork -constraints_api_url = "http://localhost:18551" -# Validator indexes of connected validators that the sidecar should accept -# commitments on behalf of. -# Accepted values: -# - a comma-separated list of indexes (e.g. "1,2,3,4") -# - a contiguous range of indexes (e.g. "1..4") -# - a mix of the above (e.g. "1,2..4,6..8") -# REQUIRED -validator_indexes = "0..64" -# The JWT secret token to authenticate calls to the engine API. It can be -# either be a hex-encoded string or a file path to a file containing the -# hex-encoded secret. -# REQUIRED -engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -# The fee recipient address for fallback blocks -# REQUIRED -fee_recipient = "0x0000000000000000000000000000000000000000" -# Secret ECDSA key to sign commitment messages with. The public key associated -# to it must be then used when registering the operator in the `BoltManager` -# contract -# REQUIRED -commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# Secret BLS key to sign fallback payloads with -# REQUIRED -builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" - -# Commitments limits -[limits] -# Max number of commitments to accept per block -max_commitments_per_slot = 128 -# Max committed gas per slot -max_committed_gas_per_slot = 10_000_000 -# Min priority fee to accept for a commitment -min_priority_fee = 4_000_000_000 # 4 Gwei = 4 * 10^9 wei - -# Chain configuration -[chain] -# Chain on which the sidecar is running -chain = "holesky" -# The slot time duration in seconds. If provided, it overrides the default for -# the selected [chain] -slot_time = 12 -# The deadline in the slot at which the sidecar will stop accepting new -# commitments for the next block (parsed as milliseconds) -commitment_deadline = 8000 - -# Signing options. Uncomment only the signing setup you'll use: -# - single private key -> `constraints_private_key` -# - commit-boost -> `cb_signer_url`, `cb_jwt_hex` -# - keystores -> `keystore_path`, `keystore_password` or `keystore_secrets_path` -# (depending on whether all keystores have the same passwords or not) -# -# If you plan to use delegations, uncomment the option `delegations_path` as -# well. -[constraint_signing] -# Private key to use for signing constraint messages -# constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# cb_signer_url = "http://localhost:18551" -# cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -# keystore_password = "password" -# keystore_secrets_path = "./secrets" -# keystore_path = "./keys" -# delegations_path = "./delegations.json" - -# Telemetry and Metrics -[telemetry] -metrics_port = 3300 -disable_metrics = false diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 6903fffbd..fbf0ce31c 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,22 +1,12 @@ -use std::fs; - use clap::Parser; use eyre::{bail, Result}; use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; -pub const TOML_CONFIG_DEFAULT_PATH: &str = "./Config.toml"; - #[tokio::main] async fn main() -> Result<()> { - let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") { - Opts::parse_from_toml(config_path.as_str())? - } else if fs::exists(TOML_CONFIG_DEFAULT_PATH).is_ok_and(|exists| exists) { - Opts::parse_from_toml(TOML_CONFIG_DEFAULT_PATH)? - } else { - Opts::parse() - }; + let opts = Opts::parse(); if let Err(err) = init_telemetry_stack(opts.telemetry.metrics_port()) { bail!("Failed to initialize telemetry stack: {:?}", err) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 1b9e64b76..7d05e1171 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -1,8 +1,5 @@ -use std::fs; - use alloy::primitives::Address; use clap::Parser; -use eyre::Context; use reqwest::Url; use serde::Deserialize; @@ -110,14 +107,6 @@ pub struct Opts { pub extra_args: Vec, } -impl Opts { - /// Parse the configuration from a TOML file. - pub fn parse_from_toml(file_path: &str) -> eyre::Result { - let contents = fs::read_to_string(file_path).wrap_err("Unable to read file")?; - toml::from_str(&contents).wrap_err("Error parsing the TOML file") - } -} - #[cfg(test)] mod tests { use super::*; @@ -136,16 +125,4 @@ mod tests { let localhost_socket = "0.0.0.0:3030".parse().unwrap(); assert_eq!(socket_addr, localhost_socket); } - - #[test] - fn test_parse_config_from_toml() { - let path = env!("CARGO_MANIFEST_DIR").to_string() + "/Config.example.toml"; - - let config = Opts::parse_from_toml(&path).expect("Failed to parse config from TOML"); - assert_eq!(config.execution_api_url, Url::parse("http://localhost:8545").unwrap()); - assert_eq!(config.beacon_api_url, Url::parse("http://localhost:5052").unwrap()); - assert_eq!(config.engine_api_url, Url::parse("http://localhost:8551").unwrap()); - assert_eq!(config.constraints_api_url, Url::parse("http://localhost:3030").unwrap()); - assert_eq!(config.constraints_proxy_port, 18551); - } } diff --git a/bolt-sidecar/src/primitives/delegation.rs b/bolt-sidecar/src/primitives/delegation.rs index 20b689bbd..3d41f275f 100644 --- a/bolt-sidecar/src/primitives/delegation.rs +++ b/bolt-sidecar/src/primitives/delegation.rs @@ -47,7 +47,7 @@ impl SignableBLS for DelegationMessage { } } -/// read the delegaitons from disk if they exist and add them to the constraints client +/// read the delegations from disk if they exist and add them to the constraints client pub fn read_signed_delegations_from_file( file_path: &PathBuf, ) -> eyre::Result> { diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example new file mode 100644 index 000000000..a094ec52d --- /dev/null +++ b/testnets/holesky/bolt-sidecar.env.example @@ -0,0 +1,67 @@ +# Ethereum Node Connections + PBS URLs + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +BOLT_SIDECAR_PORT=8017 +# Execution client API URL +BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +# URL for the beacon client +BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") +BOLT_SIDECAR_VALIDATOR_INDEXES= +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. +BOLT_SIDECAR_ENGINE_JWT_HEX= +# The fee recipient address for fallback blocks +BOLT_SIDECAR_FEE_RECIPIENT= +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +# Secret BLS key to sign fallback payloads with +BOLT_SIDECAR_BUILDER_PRIVATE_KEY= + +# Commitments limits +# Max number of commitments to accept per block +BOLT_SIDECAR_MAX_COMMITMENTS_PER_SLOT=128 +# Max committed gas per slot +BOLT_SIDECAR_MAX_COMMITTED_GAS_PER_SLOT=10_000_000 +# Min priority fee to accept for a commitment +BOLT_SIDECAR_MIN_PRIORITY_FEE=4_000_000_000 # 4 Gwei = 4 * 10^9 wei + +# Chain configuration +# Chain on which the sidecar is running +BOLT_SIDECAR_CHAIN="holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] +BOLT_SIDECAR_SLOT_TIME=12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) +BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 + +# Signing options. +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_SECRETS_PATH= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= + +# Telemetry and Metrics +BOLT_SIDECAR_METRICS_PORT=9091 # Changing this requires also changing the `target.json` file +BOLT_SIDECAR_DISABLE_METRICS=false diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example new file mode 100644 index 000000000..8815677dc --- /dev/null +++ b/testnets/holesky/mev-boost.env.example @@ -0,0 +1,32 @@ +# Logging settings +LOG_JSON=false # Set to true to log in JSON format +LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic +DEBUG=false # Set to true to enable debug mode +LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=false # Set to true to disable logging the version + +# Server settings +BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup + +# Relay settings +RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) +RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) +MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) + +# Relay timeout settings (in ms) +RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay +RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay +RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests + +# Genesis settings -- Not needed if using one of the predefined networks +# GENESIS_FORK_VERSION= # Custom genesis fork version +# GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) + +# Network settings +SEPOLIA=false # Set to true to use Sepolia network +GOERLI=false # Set to true to use Goerli network +HOLESKY=true # Set to true to use Holesky network + +# Retry settings +REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From ad40f0623a70e6f9280f5963ccbd59cc96481299 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:35:32 +0200 Subject: [PATCH 055/272] chore(holesky): update README after dropping TOML support --- testnets/holesky/README.md | 91 +++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 45a2fd339..7da86a4d7 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -23,7 +23,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt-cli`](#bolt-cli) + * [`bolt` CLI](#`bolt`-cli) * [Installation and usage](#installation-and-usage) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) @@ -77,10 +77,10 @@ client implementations to download and run them. **Active validators:** -The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, +The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, or **authorized delegates** acting on their behalf, to issue and sign preconfirmations. -To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section. # Off-Chain Setup @@ -123,41 +123,48 @@ containing the necessary environment variables: 1. **Bolt Sidecar Configuration:** - Create a `bolt-sidecar.toml` file in the `testnets/holesky` directory. If you - need a reference, you can use the `Config.example.toml` file in the `bolt-sidecar` - directory as a starting point. + Change directory to the `testnets/holesky` folder and create a + `bolt-sidecar.env` file starting from the reference template: ```bash - cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml + cd testnets/holesky + cp bolt-sidecar.env.example bolt-sidecar.env ``` - Next up, fill out all the values that are required. For proper configuration - of the signing options, please refer to the [Delegations and + Next up, fill out the values that are left blank. Please also review the + default values and see that they work for your setup. For proper + configuration of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. + If you've generated a `delegation.json` file using the Bolt CLI please + place it in the `testnets/holesky` directory by replacing the existing empty + one. + 2. **MEV-Boost Configuration:** - Copy over the example configuration file: + Change directory to the `testnets/holesky` folder if you haven't already and + copy over the example configuration file: ```bash - cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env + cp ./mev-boost.env.example ./mev-boost.env ``` - Then configure it accordingly. +Then configure it accordingly and review the default values chosen. If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. -Once the configuration files are in place, you can start the Docker containers -by running: +Once the configuration files are in place, make sure you are in the +`testnets/holesky` directory and then run: ```bash -cd testnets/holesky && docker compose up -d +docker compose up -d --env-file bolt-sidecar.env ``` The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards which you can find at `http://localhost:3000`. +Prometheus and Grafana. It also comes with some pre-built dashboards which you +can find at `http://localhost:28017`. ## Commit-Boost Mode @@ -315,11 +322,8 @@ can be found by running `./bolt-sidecar --help`, or you can find them in the #### Configuration file -You can use a `Config.toml` file to configure the sidecar, for which you can -find a template in the `Config.example.toml` file. -If you wish to place the configuration file in another folder you need to -specify the path of the configuration file by setting the -`BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. +You can use a `.env` file to configure the sidecar, for which you can +find a template in the `.env.example` file. Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) to configure such sidecar options properly. @@ -332,16 +336,22 @@ After you've set up the configuration file you can run the Bolt sidecar with ### Observability -Commit-Boost comes with various observability tools, such as Prometheus, -cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +The bolt sidecar comes with various observability tools, such as Prometheus +and Grafana. It also comes with some pre-built dashboards, which can be found in the `grafana` directory. -To update these dashboards, run the following command: +To run these dashboards change directory to the `bolt-sidecar/infra` folder and +run: + +```bash +docker compose -f telemetry.compose.yml up -d +``` -`bash ./update-grafana.sh ` +To stop the services run: -In this directory, you can also find a Bolt dashboard, which will be launched -alongside the other dashboards. +```bash +docker compose -f telemetry.compose.yml down +``` # On-Chain Registration @@ -819,7 +829,6 @@ Options: - ## Delegations and signing options for Native and Docker Compose Mode As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar @@ -830,13 +839,13 @@ Ethereum validators. > This is the recommended way to run the Bolt sidecar as it > doesn't expose the active validator signing keys to any additional risk. -In order to create these delegation you can use the `bolt-delegations-cli` binary. +In order to create these delegation you can use the `bolt` CLI binary. If you don't want to use it you can skip the following section. ### `bolt` CLI -`bolt` CLI is an offline tool for safely generating delegation and revocation messages -signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) +`bolt` CLI is an offline tool for safely generating delegation and revocation messages +signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) in [Bolt](https://docs.boltprotocol.xyz/). The tool supports three key sources: @@ -855,7 +864,7 @@ Prerequisites: - [Rust toolchain](https://www.rust-lang.org/tools/install) - [Protoc](https://grpc.io/docs/protoc-installation/) -Once you have the necessary prerequisites, you can build the binary +Once you have the necessary prerequisites, you can build the binary in the following way: ```shell @@ -875,10 +884,10 @@ bolt --version The binary can be used with the following command: ```shell -bolt delegate --delegate-pubkey - --out - --chain - +bolt delegate --delegate-pubkey + --out + --chain + ``` @@ -891,8 +900,8 @@ where: - `secret-keys`: A list of BLS private keys provided directly as hex-strings. - `local-keystore`: A EIP-2335 keystore that contains an encrypted BLS private keys. - `dirk`: A remote Dirk server that provides the BLS signatures for the delegation messages. - -You can also find more information about the available key source + +You can also find more information about the available key source options by running `bolt delegate --help`. > [!TIP] @@ -1035,8 +1044,8 @@ can pass directly the private key as a hex-encoded string to the Bolt sidecar using the `--constraint-private-key` flag (or `constraint_private_key` in the TOML file). -This is the simplest setup and can be used in case if all the delegations messages -point to the same delegatee or if you're running the sidecar with a single active +This is the simplest setup and can be used in case if all the delegations messages +point to the same delegatee or if you're running the sidecar with a single active validator. ### Using a ERC-2335 Keystore @@ -1049,7 +1058,7 @@ containing the password file (in the TOML configuration file these are the `keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). Both the `keys` and `passwords` folders must adhere to the structure outlined -in the [Delegations CLI example](#delegations-cli-example) section. +in the [Installation and Usage](#installation-and-usage) section. ## Avoid restarting the beacon node From 98812c41b0e5b025c15676377f2f8ad68fb5dded Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Thu, 24 Oct 2024 16:55:06 +0200 Subject: [PATCH 056/272] Update testnets/holesky/README.md Co-authored-by: nicolas <48695862+merklefruit@users.noreply.github.com> --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 7da86a4d7..b78a75acb 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -884,7 +884,7 @@ bolt --version The binary can be used with the following command: ```shell -bolt delegate --delegate-pubkey +bolt delegate --delegatee-pubkey --out --chain From ba88cc93396a8354ea726ca42e3f7823f52b9e38 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:11:47 +0200 Subject: [PATCH 057/272] fix(holesky): docker compose setup -- grafana, prometheus --- testnets/holesky/delegations.json | 1 + testnets/holesky/docker-compose.yml | 22 +- .../grafana/dashboards/bolt_dashboard.json | 836 ----------------- .../holesky/grafana/dashboards/dashboard.json | 473 +++++----- .../grafana/dashboards/system_metrics.json | 853 ------------------ .../grafana/datasources/datasources.yml | 2 +- testnets/holesky/targets.json | 10 +- 7 files changed, 262 insertions(+), 1935 deletions(-) create mode 100644 testnets/holesky/delegations.json delete mode 100644 testnets/holesky/grafana/dashboards/bolt_dashboard.json delete mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/delegations.json b/testnets/holesky/delegations.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/testnets/holesky/delegations.json @@ -0,0 +1 @@ +[] diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 86d2c3dce..e0c54cbc7 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -6,10 +6,11 @@ services: ports: - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" - entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" + entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar + env_file: ./bolt-sidecar.env volumes: - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" - - "../../bolt-delegations-cli/delegations.json:/etc/delegations.json" + - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 @@ -22,39 +23,26 @@ services: image: prom/prometheus:latest container_name: bolt-prometheus-holesky ports: - - 49090:49090 + - 18017:9090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus - networks: - - monitoring_network bolt-grafana-holesky: image: grafana/grafana:latest container_name: bolt-grafana-holesky ports: - - 33000:33000 - environment: - - GF_SECURITY_ADMIN_PASSWORD=admin + - 28017:3000 volumes: - ./grafana/dashboards:/etc/grafana/provisioning/dashboards - ./grafana/datasources:/etc/grafana/provisioning/datasources - grafana-data:/var/lib/grafana - networks: - - monitoring_network depends_on: - bolt-prometheus-holesky - logging: - driver: none volumes: prometheus-data: driver: local grafana-data: driver: local -networks: - monitoring_network: - driver: bridge - signer_network: - driver: bridge diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json deleted file mode 100644 index dce85acb0..000000000 --- a/testnets/holesky/grafana/dashboards/bolt_dashboard.json +++ /dev/null @@ -1,836 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 2, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_transactions_preconfirmed", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Transactions Preconfirmed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_validation_errors", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Invalid Transactions Reasons", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_remote_blocks_proposed", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Remote Blocks Proposed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_inclusion_commitments_received", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Inclusion Commitments Received", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 16 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.5.12", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "bolt_sidecar_local_blocks_proposed", - "instant": false, - "interval": "", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Local Blocks Proposed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 16 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_inclusion_commitments_accepted", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Inclusion Commitments Accepted", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_http_requests_total", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Total HTTP Requests", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 7, - "options": { - "bucketOffset": 0, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_http_requests_duration_seconds", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "HTTP Requests Durations in ms", - "type": "histogram" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 0, - "y": 32 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.12", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_latest_head", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Latest Head Slot", - "type": "stat" - } - ], - "refresh": "", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Bolt Sidecar", - "uid": "e5960f6d-a1ed-4538-9c7c-3ecba4d4b4b1", - "version": 3, - "weekStart": "" -} diff --git a/testnets/holesky/grafana/dashboards/dashboard.json b/testnets/holesky/grafana/dashboards/dashboard.json index f903affb2..8823158a8 100644 --- a/testnets/holesky/grafana/dashboards/dashboard.json +++ b/testnets/holesky/grafana/dashboards/dashboard.json @@ -18,28 +18,14 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, + "id": 2, "links": [], - "liveNow": true, + "liveNow": false, "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 12, - "panels": [], - "repeat": "endpoint", - "repeatDirection": "h", - "title": "$endpoint calls", - "type": "row" - }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -47,7 +33,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -61,11 +46,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -82,7 +63,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -100,12 +80,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, + "h": 8, + "w": 12, "x": 0, - "y": 1 + "y": 0 }, - "id": 11, + "id": 9, "options": { "legend": { "calcs": [], @@ -122,23 +102,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Relay Success QPH", + "title": "Transactions Preconfirmed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -146,7 +125,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -160,11 +138,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -181,7 +155,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -199,12 +172,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 6, - "y": 1 + "h": 8, + "w": 12, + "x": 12, + "y": 0 }, - "id": 13, + "id": 10, "options": { "legend": { "calcs": [], @@ -221,23 +194,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_validation_errors", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Relay Error QPH", + "title": "Invalid Transactions Reasons", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -245,7 +217,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -259,11 +230,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -280,17 +247,12 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -298,12 +260,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 12, - "y": 1 + "h": 8, + "w": 12, + "x": 0, + "y": 8 }, - "id": 43, + "id": 2, "options": { "legend": { "calcs": [], @@ -320,23 +282,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Beacon Node Success QPH", + "title": "Remote Blocks Proposed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -344,7 +305,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -358,11 +318,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -379,7 +335,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -397,12 +352,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 18, - "y": 1 + "h": 8, + "w": 12, + "x": 12, + "y": 8 }, - "id": 44, + "id": 4, "options": { "legend": { "calcs": [], @@ -419,23 +374,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_received", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Beacon Node Error QPH", + "title": "Inclusion Commitments Received", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -443,7 +397,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -457,18 +410,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -478,7 +427,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -491,18 +439,42 @@ "value": 80 } ] - }, - "unit": "s" + } }, - "overrides": [] + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] }, "gridPos": { - "h": 11, - "w": 6, + "h": 8, + "w": 12, "x": 0, - "y": 12 + "y": 16 }, - "id": 20, + "id": 8, "options": { "legend": { "calcs": [], @@ -515,31 +487,30 @@ "sort": "none" } }, + "pluginVersion": "9.5.12", "targets": [ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", "instant": false, + "interval": "", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P50", + "title": "Local Blocks Proposed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -547,7 +518,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -561,18 +531,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -582,31 +548,25 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] - }, - "unit": "s" + } }, "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 6, - "y": 12 + "h": 8, + "w": 12, + "x": 12, + "y": 16 }, - "id": 29, + "id": 5, "options": { "legend": { "calcs": [], @@ -623,27 +583,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P90", + "title": "Inclusion Commitments Accepted", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -651,7 +606,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -665,18 +619,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -686,7 +636,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -699,18 +648,17 @@ "value": 80 } ] - }, - "unit": "s" + } }, "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 12, - "y": 12 + "h": 8, + "w": 12, + "x": 0, + "y": 24 }, - "id": 30, + "id": 6, "options": { "legend": { "calcs": [], @@ -727,77 +675,162 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P99", + "title": "Total HTTP Requests", "type": "timeseries" - } - ], - "refresh": "5m", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "All", - "value": "$__all" - }, - "description": "BuilderAPI endpoint", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "endpoint", - "options": [ - { - "selected": true, - "text": "All", - "value": "$__all" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" }, - { - "selected": false, - "text": "get_header", - "value": "get_header" + "custom": { + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 }, - { - "selected": false, - "text": "submit_blinded_block", - "value": "submit_blinded_block" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" }, - { - "selected": false, - "text": "register_validator", - "value": "register_validator" + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_duration_seconds", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "HTTP Requests Durations in ms", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] } - ], - "query": "register_validator, get_header, submit_blinded_block", - "queryValue": "", - "skipUrlSync": false, - "type": "custom" - } - ] + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Head Slot", + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] }, "time": { - "from": "now-2d", + "from": "now-15m", "to": "now" }, "timepicker": {}, - "timezone": "browser", - "title": "PBS Metrics", - "uid": "cb_prometheus", - "version": 1, + "timezone": "", + "title": "bolt-prometheus-holesky", + "uid": "bolt-prometheus-holesky", + "version": 3, "weekStart": "" -} \ No newline at end of file +} diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json deleted file mode 100644 index 93b649d80..000000000 --- a/testnets/holesky/grafana/dashboards/system_metrics.json +++ /dev/null @@ -1,853 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "Prometheus as the datasource is obligatory", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "7.4.5" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "table", - "name": "Table", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": 14282, - "graphTooltip": 0, - "id": null, - "iteration": 1617715580880, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 8, - "panels": [], - "title": "CPU", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 15, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "percent", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 11, - "panels": [], - "title": "Memory", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "hiddenSeries": false, - "id": 9, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "hiddenSeries": false, - "id": 14, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Cached", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 17 - }, - "id": 2, - "panels": [], - "title": "Network", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 18 - }, - "hiddenSeries": false, - "id": 4, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "sideWidth": null, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Received Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:674", - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:675", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 18 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Sent Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:832", - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:833", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 26 - }, - "id": 19, - "panels": [], - "title": "Misc", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "id" - }, - "properties": [ - { - "id": "custom.width", - "value": 260 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Running" - }, - "properties": [ - { - "id": "unit", - "value": "d" - }, - { - "id": "decimals", - "value": 1 - }, - { - "id": "custom.displayMode", - "value": "color-text" - }, - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 24, - "x": 0, - "y": 27 - }, - "id": 17, - "options": { - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "7.4.5", - "targets": [ - { - "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Containers Info", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "container_label_com_docker_compose_project", - "container_label_com_docker_compose_project_working_dir", - "image", - "instance", - "name", - "Value", - "container_label_com_docker_compose_service" - ] - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "Value": "Running", - "container_label_com_docker_compose_project": "Label", - "container_label_com_docker_compose_project_working_dir": "Working dir", - "container_label_com_docker_compose_service": "Service", - "image": "Registry Image", - "instance": "Instance", - "name": "Name" - } - } - } - ], - "type": "table" - } - ], - "schemaVersion": 27, - "style": "dark", - "tags": [ - "cadvisor", - "docker" - ], - "templating": { - "list": [ - { - "allValue": ".*", - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "definition": "label_values({__name__=~\"container.*\"},instance)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Host", - "multi": false, - "name": "host", - "options": [], - "query": { - "query": "label_values({__name__=~\"container.*\"},instance)", - "refId": "Prometheus-host-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 5, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": ".*", - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Container", - "multi": false, - "name": "container", - "options": [], - "query": { - "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", - "refId": "Prometheus-container-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Commit-Boost System Metrics", - "uid": "pMEd7m0Mz", - "version": 1, - "description": "Simple exporter for cadvisor only" -} \ No newline at end of file diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml index 91e10e2b4..10df84a1f 100644 --- a/testnets/holesky/grafana/datasources/datasources.yml +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -6,6 +6,6 @@ datasources: uid: bolt-prometheus-holesky access: proxy orgId: 1 - url: http://bolt-prometheus-holesky:49090 + url: http://bolt-prometheus-holesky:9090 isDefault: true editable: true diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json index 6cd87cbc6..1b14e8d5e 100644 --- a/testnets/holesky/targets.json +++ b/testnets/holesky/targets.json @@ -1,14 +1,8 @@ [ { - "targets": ["bolt-sidecar-holesky:8017"], + "targets": ["bolt-sidecar-holesky:9091"], "labels": { - "job": "bolt-sidecar-rpc" - } - }, - { - "targets": ["bolt-sidecar-holesky:18550"], - "labels": { - "job": "bolt-sidecar-builder-proxy" + "job": "bolt-sidecar-holesky" } } ] From db4d39411b5e7887981c6a643362dbf556f65e8a Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 17:14:31 +0200 Subject: [PATCH 058/272] chore(holesky): update rc image for Helix --- testnets/holesky/docker-compose.pbs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index 362dc1c2e..27034a7a5 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -64,7 +64,7 @@ services: ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: - image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc2 restart: unless-stopped depends_on: - db From 72e5f7f4d76c151f4d1c6fe44651fafd7136f42b Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 17:25:00 +0200 Subject: [PATCH 059/272] fix(holesky): remove toml volume from dockerfile --- testnets/holesky/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index e0c54cbc7..756708a95 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -9,7 +9,6 @@ services: entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar env_file: ./bolt-sidecar.env volumes: - - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" bolt-mev-boost-holesky: From 19e2988c1de506b5413453c8318a43ffd2896f08 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 18:16:48 +0200 Subject: [PATCH 060/272] feat(holesky): cadvisor dashboard stub --- testnets/holesky/docker-compose.yml | 11 + .../grafana/dashboards/system_metrics.json | 829 ++++++++++++++++++ testnets/holesky/targets.json | 6 + 3 files changed, 846 insertions(+) create mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 756708a95..74bdfc514 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -28,6 +28,17 @@ services: - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus + bolt-cadvisor-holesky: + image: gcr.io/cadvisor/cadvisor:latest + container_name: bolt-cadvisor-holesky + restart: unless-stopped + ports: + - "38017:8080" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + bolt-grafana-holesky: image: grafana/grafana:latest container_name: bolt-grafana-holesky diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..ebbad9c48 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/system_metrics.json @@ -0,0 +1,829 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Simple exporter for prometheus only", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 7, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "CPU", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 15, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(rate(container_cpu_usage_seconds_total{name=~\"bolt-.*-holesky\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Memory", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 9, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(container_memory_rss{name=~\"bolt-.*-holesky\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 14, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(container_memory_cache{name=~\"bolt-.*-holesky\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Cached", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Network", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 4, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Received Network Traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 6, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Sent Network Traffic", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "custom": { + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "(time() - container_start_time_seconds{name=~\"bolt-.*-holesky\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": ["prometheus", "docker"], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "System Metrics", + "uid": "pMEd7m0Mz", + "version": 9, + "weekStart": "" +} + diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json index 1b14e8d5e..52546f538 100644 --- a/testnets/holesky/targets.json +++ b/testnets/holesky/targets.json @@ -4,5 +4,11 @@ "labels": { "job": "bolt-sidecar-holesky" } + }, + { + "targets": ["bolt-cadvisor-holesky:8080"], + "labels": { + "job": "bolt-cadvisor-holesky" + } } ] From d44236684353421ba6f327f48b7d3eca17b67e08 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 12:17:38 +0200 Subject: [PATCH 061/272] fix(sidecar): test/CI --- bolt-sidecar/src/test_util.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index 6eace3748..c0bac0ddd 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -90,6 +90,7 @@ pub(crate) async fn get_test_config() -> Option { "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY", EcdsaSecretKeyWrapper::random().to_string(), ); + env::set_var("BOLT_SIDECAR_VALIDATOR_INDEXES", "0..64"); let _ = dotenvy::dotenv(); From 3c9f3f8d6bee5e7dcbe8e80d73fa1519b656496d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 12:31:11 +0200 Subject: [PATCH 062/272] fix(holesky): restore commit-boost observability previosly removed by mistake --- testnets/holesky/README.md | 13 + .../grafana/dashboards/bolt_dashboard.json | 236 +++++ .../grafana/dashboards/dashboard.json | 984 ++++++++++++++++++ .../grafana/dashboards/dashboards.yml | 13 + .../grafana/dashboards/system_metrics.json | 853 +++++++++++++++ .../grafana/datasources/datasources.yml | 11 + .../holesky/commit-boost/update-grafana.sh | 4 +- 7 files changed, 2112 insertions(+), 2 deletions(-) create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/dashboard.json create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json create mode 100644 testnets/holesky/commit-boost/grafana/datasources/datasources.yml diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b78a75acb..f9924532d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -224,6 +224,19 @@ This will run all modules in Docker containers. > with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be > configured to point the `builder-api` to this port for Bolt to work. +**Observability** + +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `commit-boost/grafana` directory. + +To update these dashboards, run the following command from the `commit-boost` +directory: + +`bash ./update-grafana.sh ` +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. + ## Native Mode (advanced) For running the Bolt Sidecar as a standalone binary you need to have the diff --git a/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json new file mode 100644 index 000000000..ea72e6d2f --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json @@ -0,0 +1,236 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics related to the bolt-sidecar and bolt-boost.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Bolt sidecar", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Latest head", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 4, + "panels": [], + "title": "Bolt boost", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "cb_pbs_constraints_cache_size", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Constraints cache size", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Bolt Metrics", + "uid": "edxnwlpgaw934c", + "version": 3, + "weekStart": "" +} diff --git a/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json b/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json new file mode 100644 index 000000000..94dd06265 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json @@ -0,0 +1,984 @@ +{ + "__inputs": [], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.1.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": true, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "green", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 61, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "cb_pbs_relay_last_slot", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{relay_id}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Last delivered slot", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "thresholds" + }, + "decimals": 6, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 78, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "cb_pbs_relay_header_value / 1e9", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{relay_id}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Last delivered header value", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 12, + "panels": [], + "repeat": "endpoint", + "repeatDirection": "h", + "title": "$endpoint calls", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 12 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 12 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 23 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P50", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 23 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P90", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 23 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P99", + "type": "timeseries" + } + ], + "refresh": "5m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "description": "BuilderAPI endpoint", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "endpoint", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "register_validator", + "value": "register_validator" + }, + { + "selected": false, + "text": "get_header", + "value": "get_header" + }, + { + "selected": false, + "text": "submit_blinded_block", + "value": "submit_blinded_block" + } + ], + "query": "register_validator, get_header, submit_blinded_block", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PBS Metrics", + "uid": "cb_pbs_metrics", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml b/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml new file mode 100644 index 000000000..db50699f9 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: "default" + orgId: 1 + folder: "" + folderUid: "" + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json b/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..93b649d80 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus as the datasource is obligatory", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.4.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 14282, + "graphTooltip": 0, + "id": null, + "iteration": 1617715580880, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Cached", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Received Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:674", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:675", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Sent Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:832", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:833", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": [ + "cadvisor", + "docker" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Commit-Boost System Metrics", + "uid": "pMEd7m0Mz", + "version": 1, + "description": "Simple exporter for cadvisor only" +} \ No newline at end of file diff --git a/testnets/holesky/commit-boost/grafana/datasources/datasources.yml b/testnets/holesky/commit-boost/grafana/datasources/datasources.yml new file mode 100644 index 000000000..6d29d617b --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/datasources/datasources.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: cb-prometheus + type: prometheus + uid: cb_prometheus + access: proxy + orgId: 1 + url: http://cb_prometheus:9090 + isDefault: true + editable: true diff --git a/testnets/holesky/commit-boost/update-grafana.sh b/testnets/holesky/commit-boost/update-grafana.sh index 1f832d32b..4e5a16695 100755 --- a/testnets/holesky/commit-boost/update-grafana.sh +++ b/testnets/holesky/commit-boost/update-grafana.sh @@ -1,5 +1,5 @@ #!/bin/bash # Fetches the latest dashboards from commit-boost main -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ../grafana/dashboards/dashboard.json -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ../grafana/dashboards/system_metrics.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json From 5a64219eb3dc64999a1f85235791ee93c9facb06 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 13:48:41 +0200 Subject: [PATCH 063/272] fix(holesky/docs): broken links --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index f9924532d..7f11ad361 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -6,9 +6,9 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) * [Off-Chain Setup](#off-chain-setup) - * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Docker Mode (recommended)](#docker-mode-recommended) * [Commit-Boost Mode](#commit-boost-mode) - * [Native Mode (advanced)](#native-mode-(advanced)) + * [Native Mode (advanced)](#native-mode-advanced) * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) From 5fdb3147dc335ee4f070b94e00168d00c0fe7a85 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 13:56:04 +0200 Subject: [PATCH 064/272] fix(holesky): cAdvisor only scans for the services in the current docker compose setup --- testnets/holesky/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 74bdfc514..ecd3b2b57 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -38,6 +38,9 @@ services: - /var/run/docker.sock:/var/run/docker.sock:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro + command: + - --housekeeping_interval=10s + - --docker_only bolt-grafana-holesky: image: grafana/grafana:latest From 1a5bed09ebe05849e012b4334efdb3475e713bcc Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 15 Oct 2024 12:13:09 +0200 Subject: [PATCH 065/272] chore(testnet): holesky launch folder stub --- testnets/holesky/README.md | 106 +++ testnets/holesky/cb-bolt-config.toml | 158 ++++ .../grafana/dashboards/bolt_dashboard.json | 238 +++++ .../holesky/grafana/dashboards/dashboard.json | 803 +++++++++++++++++ .../holesky/grafana/dashboards/dashboards.yml | 13 + .../grafana/dashboards/system_metrics.json | 853 ++++++++++++++++++ .../grafana/datasources/datasources.yml | 11 + testnets/holesky/keys.json | 4 + testnets/holesky/prometheus.yml | 8 + testnets/holesky/targets.json | 34 + testnets/holesky/update-grafana.sh | 5 + 11 files changed, 2233 insertions(+) create mode 100644 testnets/holesky/README.md create mode 100644 testnets/holesky/cb-bolt-config.toml create mode 100644 testnets/holesky/grafana/dashboards/bolt_dashboard.json create mode 100644 testnets/holesky/grafana/dashboards/dashboard.json create mode 100644 testnets/holesky/grafana/dashboards/dashboards.yml create mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json create mode 100644 testnets/holesky/grafana/datasources/datasources.yml create mode 100644 testnets/holesky/keys.json create mode 100644 testnets/holesky/prometheus.yml create mode 100644 testnets/holesky/targets.json create mode 100755 testnets/holesky/update-grafana.sh diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md new file mode 100644 index 000000000..c031285f3 --- /dev/null +++ b/testnets/holesky/README.md @@ -0,0 +1,106 @@ +# Holesky Launch Instructions + +## Components + +The components that need to run to test Bolt on Holesky are: + +- A synced execution client +- A synced beacon node +- Active validators +- Commit-Boost with Bolt configuration + +## Setup + +### Commit-Boost + +#### Installation + +To install the `commit-boost` CLI with `cargo`: + +```bash +# Use specific commit hash to ensure compatibility +cargo install --locked --git https://github.com/Commit-Boost/commit-boost-client --rev aed00e8 commit-boost + +# Test installation +commit-boost --version +``` + +#### Configuration + +A commit-boost configuration file with Bolt support is provided at +[`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the +custom PBS module ([bolt-boost](../../bolt-boost)) that implements the +[constraints-API](https://chainbound.github.io/bolt-docs/api/builder), as well +as the [bolt-sidecar](../../bolt-sidecar) module. This file can be used as a +template for your own configuration. + +The important fields to configure are under the `[modules.env]` section of the +`BOLT` module, which contain the environment variables to configure the bolt +sidecar: + +```toml +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" + +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module (static) +BOLT_SIDECAR_BEACON_API = "" +BOLT_SIDECAR_EXECUTION_API = "" +BOLT_SIDECAR_ENGINE_API = "" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "" # The engine JWT used to authenticate with the engine API +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "" # The active validator indexes (can be defined as a comma-separated list, or a range) + # e.g. "0,1,2,3,4" or "0..4", or a combination of both +``` + +To initialize commit-boost, run the following command: + +```bash +commit-boost init --config cb-bolt-config.toml +``` + +This will create 3 files: + +- `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services +- `.cb.env`: with local env variables, including JWTs for modules +- `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus + +#### Running + +The final step is to run the Commit-Boost services. This can be done with the following command: + +```bash +commit-boost start --docker cb.docker-compose.yml --env .cb.env +``` + +This will run all modules in Docker containers. + +> [!IMPORTANT] +> bolt-boost will be exposed at `pbs.port` (18551 by default, set with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be configured +> to point the `builder-api` to this port for Bolt to work. + +### Bolt Sidecar + +WIP + +### Observability + +commit-boost comes with various observability tools, such as Prometheus, cadvisor, and Grafana. It also comes with some pre-built dashboards, +which can be found in the `grafana` directory. + +To update these dashboards, run the following command: + +```bash +./update-grafana.sh +``` + +In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. + +### Validators + +Validators must be configured to always prefer builder proposals over their own. Refer to client documentation for the specific configuration flags. +**If this is not set, it could lead to commitment faults**. + +#### Registration + +WIP diff --git a/testnets/holesky/cb-bolt-config.toml b/testnets/holesky/cb-bolt-config.toml new file mode 100644 index 000000000..be4e35002 --- /dev/null +++ b/testnets/holesky/cb-bolt-config.toml @@ -0,0 +1,158 @@ +# The main configuration file for the Commit-Boost sidecar. +# Some fields are optional and can be omitted, in which case the default value, if present, will be used. + +# Chain spec id. Supported values: Mainnet, Holesky, Helder +chain = "Holesky" + +# Configuration for the PBS module +[pbs] +# Docker image to use for the PBS module. +# BOLT: We use the bolt-boost PBS module here. +docker_image = "ghcr.io/chainbound/bolt-boost:v0.3.0-alpha-rc.1" +# Whether to enable the PBS module to request signatures from the Signer module (not used in the default PBS image) +# OPTIONAL, DEFAULT: false +with_signer = false +# Port to receive BuilderAPI calls from beacon node +port = 18550 +# Whether to forward `status` calls to relays or skip and return 200 +# OPTIONAL, DEFAULT: true +relay_check = true +# Timeout in milliseconds for the `get_header` call to relays. Note that the CL has also a timeout (e.g. 1 second) so +# this should be lower than that, leaving some margin for overhead +# OPTIONAL, DEFAULT: 950 +timeout_get_header_ms = 950 +# Timeout in milliseconds for the `submit_blinded_block` call to relays. +# OPTIONAL, DEFAULT: 4000 +timeout_get_payload_ms = 4000 +# Timeout in milliseconds for the `register_validator` call to relays. +# OPTIONAL, DEFAULT: 3000 +timeout_register_validator_ms = 3000 +# Whether to skip signature verification of headers against the relay pubkey +# OPTIONAL, DEFAULT: false +skip_sigverify = false +# Minimum bid in ETH that will be accepted from `get_header` +# OPTIONAL, DEFAULT: 0.0 +min_bid_eth = 0.0 +# List of URLs of relay monitors to send registrations to +# OPTIONAL +relay_monitors = [] +# How late in milliseconds in the slot is "late". This impacts the `get_header` requests, by shortening timeouts for `get_header` calls to +# relays and make sure a header is returned within this deadline. If the request from the CL comes later in the slot, then fetching headers is skipped +# to force local building and miniminzing the risk of missed slots. See also the timing games section below +# OPTIONAL, DEFAULT: 2000 +late_in_slot_time_ms = 1000 + +# The PBS module needs one or more [[relays]] as defined below. +[[relays]] +# Relay ID to use in telemetry +# OPTIONAL, DEFAULT: URL hostname +id = "example-relay" +# Relay URL in the format scheme://pubkey@host +url = "http://0xa1cec75a3f0661e99299274182938151e8433c61a19222347ea1313d839229cb4ce4e3e5aa2bdeb71c8fcf1b084963c2@abc.xyz" +# Headers to send with each request for this relay +# OPTIONAL +# headers = { X-MyCustomHeader = "MyCustomValue" } +# Whether to enable timing games, as tuned by `target_first_request_ms` and `frequency_get_header_ms`. +# These values should be carefully chosen for each relay, as each relay has different latency and timing games setups. +# They should only be used by advanced users, and if mis-configured can result in unforeseen effects, e.g. fetching a lower header value, +# or getting a temporary IP ban. +# +# EXAMPLES +# Assuming: timeout_get_header_ms = 950, frequency_get_header_ms = 300, target_first_request_ms = 200, late_in_slot_time_ms = 2000 +# +# 1) CL request comes at 100ms in the slot (max timeout 1050ms in the slot), then: +# - sleep for 100ms +# - send request at 200ms with 850ms timeout +# - send request at 500ms with 550ms timeout +# - send request at 800ms with 250ms timeout +# 2) CL request comes at 1500ms in the slot (max timeout 2000ms in the slot), then: +# - send request at 1500ms with 500ms timeout +# - send request at 1800ms with 200ms timeout +# 3) CL request comes 2500ms in the slot then: +# - return 204 and force local build +# +# OPTIONAL, DEFAULT: false +enable_timing_games = false +# Target time in slot when to send the first header request +# OPTIONAL +target_first_request_ms = 200 +# Frequency in ms to send get_header requests +# OPTIONAL +frequency_get_header_ms = 300 + +# Configuration for the Signer Module, only required if any `commit` module is present, or if `pbs.with_signer = true` +# OPTIONAL +[signer] +# Docker image to use for the Signer module. +# OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest +docker_image = "commitboost_signer" +# Configuration for how the Signer module should load validator keys. Currently two types of loaders are supported: +# - File: load keys from a plain text file (unsafe, use only for testing purposes) +# - ValidatorsDir: load keys from a `keys` and `secrets` folder (ERC-2335 style keystores as used in Lighthouse) +[signer.loader] +# File: path to the keys file +key_path = "./keys.json" +# ValidatorsDir: path to the keys directory +# keys_path = "" +# ValidatorsDir: path to the secrets directory +# secrets_path = "" + +# Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. +# Currently, two types of modules are supported: +# - "commit": modules which request commitment signatures from the validator keys +# - "events": modules which callback to BuilderAPI events as triggered from the PBS modules, used e.g. for monitoring +# If any "commit" module is present, then the [signer] section should also be configured +# OPTIONAL +[[modules]] +# Unique ID of the module +id = "BOLT" +# Type of the module. Supported values: commit, events +type = "commit" +# Docker image of the module +docker_image = "ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1" + +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" + +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module +BOLT_SIDECAR_BEACON_API = "http://100.85.200.41:4400" +BOLT_SIDECAR_EXECUTION_API = "http://100.85.200.41:4485" +BOLT_SIDECAR_ENGINE_API = "http://100.85.200.41:4451" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "89732cef77d7e9a20021ee8f419dbbb51bdf7f60586932c272ddef02e70cb755" # The engine JWT +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "0x0000000000000000000000000000000000000000" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "1..2" # The active validator indexes + +BOLT_SIDECAR_METRICS_PORT = "10000" + +# Configuration for how metrics should be collected and scraped +# OPTIONAL, skip metrics collection if missing +[metrics] +# Path to a `prometheus.yml` file to use in Prometheus. If using a custom config file, be sure to add a +# file discovery section as follows: +# ```yml +# file_sd_configs: +# - files: +# - /etc/prometheus/targets.json +# ``` +# and use the `targets.json` file generated by `commit-boost init` +prometheus_config = "./prometheus.yml" +# Whether to start Grafana with built-in dashboards +# OPTIONAL, DEFAULT: true +use_grafana = true +# Whether to start cadvisor for system monitoring +# OPTIONAL, DEFAULT: true +use_cadvisor = true + +# Configuration for how logs should be collected and stored +# OPTIONAL, info to stdout if missing +[logs] +# Path to the log directory +# OPTIONAL, DEFAULT: /var/logs/commit-boost +log_dir_path = "./logs" +# Log level. Supported values: trace, debug, info, warn, error +# OPTIONAL, DEFAULT: debug to file, info to stdout +log_level = "debug" +# Maximum number of log files to keep +# OPTIONAL +max_log_files = 30 diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json new file mode 100644 index 000000000..886ddfe02 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/bolt_dashboard.json @@ -0,0 +1,238 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics related to the bolt-sidecar and bolt-boost.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Bolt sidecar", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Latest head", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 4, + "panels": [], + "title": "Bolt boost", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "cb_pbs_constraints_cache_size", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Constraints cache size", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Bolt Metrics", + "uid": "edxnwlpgaw934c", + "version": 3, + "weekStart": "" +} diff --git a/testnets/holesky/grafana/dashboards/dashboard.json b/testnets/holesky/grafana/dashboards/dashboard.json new file mode 100644 index 000000000..f903affb2 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/dashboard.json @@ -0,0 +1,803 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": true, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 12, + "panels": [], + "repeat": "endpoint", + "repeatDirection": "h", + "title": "$endpoint calls", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P50", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P90", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 12 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P99", + "type": "timeseries" + } + ], + "refresh": "5m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "description": "BuilderAPI endpoint", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "endpoint", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "get_header", + "value": "get_header" + }, + { + "selected": false, + "text": "submit_blinded_block", + "value": "submit_blinded_block" + }, + { + "selected": false, + "text": "register_validator", + "value": "register_validator" + } + ], + "query": "register_validator, get_header, submit_blinded_block", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PBS Metrics", + "uid": "cb_prometheus", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/testnets/holesky/grafana/dashboards/dashboards.yml b/testnets/holesky/grafana/dashboards/dashboards.yml new file mode 100644 index 000000000..db50699f9 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/dashboards.yml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: "default" + orgId: 1 + folder: "" + folderUid: "" + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..93b649d80 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/system_metrics.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus as the datasource is obligatory", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.4.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 14282, + "graphTooltip": 0, + "id": null, + "iteration": 1617715580880, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Cached", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Received Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:674", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:675", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Sent Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:832", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:833", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": [ + "cadvisor", + "docker" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Commit-Boost System Metrics", + "uid": "pMEd7m0Mz", + "version": 1, + "description": "Simple exporter for cadvisor only" +} \ No newline at end of file diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml new file mode 100644 index 000000000..6d29d617b --- /dev/null +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: cb-prometheus + type: prometheus + uid: cb_prometheus + access: proxy + orgId: 1 + url: http://cb_prometheus:9090 + isDefault: true + editable: true diff --git a/testnets/holesky/keys.json b/testnets/holesky/keys.json new file mode 100644 index 000000000..4689c5d75 --- /dev/null +++ b/testnets/holesky/keys.json @@ -0,0 +1,4 @@ +[ + "0088e364a5396a81b50febbdc8784663fb9089b5e67cbdc173991a00c587673f", + "0x16f3bec1b7f4f1b87c5e1930f944a6dda76ad211a89bc98e8c8e88b5069f8a04" +] diff --git a/testnets/holesky/prometheus.yml b/testnets/holesky/prometheus.yml new file mode 100644 index 000000000..91b5d5905 --- /dev/null +++ b/testnets/holesky/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "commit-boost" + file_sd_configs: + - files: + - /etc/prometheus/targets.json diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json new file mode 100644 index 000000000..6eb23eea1 --- /dev/null +++ b/testnets/holesky/targets.json @@ -0,0 +1,34 @@ +[ + { + "targets": [ + "cb_bolt:10000" + ], + "labels": { + "job": "cb_bolt" + } + }, + { + "targets": [ + "cb_pbs:10000" + ], + "labels": { + "job": "pbs" + } + }, + { + "targets": [ + "cb_signer:10000" + ], + "labels": { + "job": "signer" + } + }, + { + "targets": [ + "cb_cadvisor:8080" + ], + "labels": { + "job": "cadvisor" + } + } +] \ No newline at end of file diff --git a/testnets/holesky/update-grafana.sh b/testnets/holesky/update-grafana.sh new file mode 100755 index 000000000..4e5a16695 --- /dev/null +++ b/testnets/holesky/update-grafana.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# Fetches the latest dashboards from commit-boost main +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json From 9b1808523313b834c5347da4457854ae853ece63 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:25:01 +0200 Subject: [PATCH 066/272] git(sidecar): add bolt-sidecar binaries to .gitignore --- bolt-sidecar/.gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 55509a805..2e73cb821 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -1,4 +1,4 @@ target/ -.env -.env.* +.env* !.env.example +bolt-sidecar* From 8aa2381f4bd08ef5bff9fcdadcd7d2f74240fa87 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:25:35 +0200 Subject: [PATCH 067/272] docker(sidecar): remove redundant dependencies during build --- bolt-sidecar/Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bolt-sidecar/Dockerfile b/bolt-sidecar/Dockerfile index 99c0f839c..fd3e55d37 100644 --- a/bolt-sidecar/Dockerfile +++ b/bolt-sidecar/Dockerfile @@ -23,10 +23,7 @@ FROM base AS builder RUN apt-get update && apt-get install -y \ pkg-config \ libssl-dev \ - build-essential \ - perl \ - gcc \ - make + build-essential # Copy the generated recipe from the planner stage COPY --from=planner /app/recipe.json recipe.json From b0038f18ed6113d6f8c5dfec040e5e092412c5e9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:12:48 +0200 Subject: [PATCH 068/272] chore!(sidecar): rename env for telemetry options --- bolt-sidecar/src/config/telemetry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index 33a2f3ac8..b0ef87da4 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -4,9 +4,9 @@ use serde::Deserialize; #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics - #[clap(short, long, env = "METRICS_PORT", default_value_t = 3300)] + #[clap(short, long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] metrics_port: u16, - #[clap(short, long, env = "DISABLE_METRICS", default_value_t = false)] + #[clap(short, long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] disable_metrics: bool, } From a38ffb616d8bcc8d438e2a9323efc1af43fd40f4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:17:20 +0200 Subject: [PATCH 069/272] chore(sidecar): update .env.example --- bolt-sidecar/.env.example | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index cb234a599..98bf60852 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,33 +1,34 @@ -# Ethereum node connections +# Ethereum Node Connections + PBS URLs +BOLT_SIDECAR_PORT=8000 BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485 BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400 BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 -BOLT_SIDECAR_ENGINE_JWT_HEX= - -# Constraint URL: should point to the constraint API sidecar. -# Usually this corresponds to `mev-boost` or `bolt-boost` BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 - -# Commit-boost specific options (optional) -BOLT_SIDECAR_CB_SIGNER_URL=http://localhost:19551 -BOLT_SIDECAR_CB_JWT_HEX= - -# server ports -BOLT_SIDECAR_PORT=8000 BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 +BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 +BOLT_SIDECAR_JWT_HEX= +BOLT_SIDECAR_FEE_RECIPIENT= +BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -# commitment limits +# Commitments configs BOLT_SIDECAR_MAX_COMMITMENTS=128 -BOLT_SIDECAR_MAX_COMMITTED_GAS=10000000 - -# chain configs -BOLT_SIDECAR_CHAIN=holesky +BOLT_SIDECAR_MAX_COMMITTED_GAS= +BOLT_SIDECAR_MIN_PRIORITY_FEE= BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 + +# Chain configs +BOLT_SIDECAR_CHAIN=Holesky BOLT_SIDECAR_SLOT_TIME=12 -# sidecar security configs -BOLT_SIDECAR_VALIDATOR_INDEXES= -BOLT_SIDECAR_FEE_RECIPIENT= -BOLT_SIDECAR_BUILDER_PRIVATE_KEY= +# Signing options BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= + +# Metrics +BOLT_SIDECAR_METRICS_PORT= +BOLT_SIDECAR_DISABLE_METRICS= From 512956eb738fce42050dfb2863291e9030d1a322 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:46:16 +0200 Subject: [PATCH 070/272] chore(sidecar): update Config.example.toml --- bolt-sidecar/Config.example.toml | 51 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index b73e925da..e6caad16e 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -1,34 +1,37 @@ -# ports +# Ethereum Node Connections + PBS URLs port = 8000 -metrics_port = 3300 - -# node urls -execution_api_url = "http://localhost:8545" -beacon_api_url = "http://localhost:5052" -engine_api_url = "http://localhost:8551" -engine_jwt_hex = "0x573300d0cd9a8e253429998a3ceecf358aa4868d96772d1344a80144d3b7b593" - -# constraints options -constraints_api_url = "http://localhost:3030" +execution_api_url = "http://localhost:4485" +beacon_api_url = "http://localhost:4400" +engine_api_url = "http://localhost:4451" +constraints_url = "http://localhost:19550" constraints_proxy_port = 18551 - validator_indexes = "0..64" -fee_recipient = "0x0155ef0C0fE550C297c1216585e0DE1478EA30e4" +jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +fee_recipient = "0x0000000000000000000000000000000000000000" +builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -builder_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" -commitment_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +# Commitments configs +max_commitments = 128 +max_committed_gas = 10000000 +min_priority_fee = 5000000 -# chain options +# Chain configs [chain] -chain = "Kurtosis" -slot_time = 2 +chain = "Holesky" +slot_time = 12 commitment_deadline = 8000 -[telemetry] -metrics_port = 3300 -disable_metrics = false - -# signing options +# Signing options [constraint_signing] -constraint_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +cb_signer_url = "http://localhost:18550" +cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +keystore_password = "password" +keystore_path = "./keys" delegations_path = "./delegations.json" + +# Metrics +[telemetry] +metrics_port = 8001 +disable_metrics = false From 0ed6dbc098b25bb400babd716737bf2f20b1877a Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 14:03:17 +0200 Subject: [PATCH 071/272] chore!(sidecar): remove short options from telemetry config --- bolt-sidecar/src/config/telemetry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index b0ef87da4..01bc179d6 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -4,9 +4,9 @@ use serde::Deserialize; #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics - #[clap(short, long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] + #[clap(long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] metrics_port: u16, - #[clap(short, long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] + #[clap(long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] disable_metrics: bool, } From 3a2f0142a059768e5cf0e7eb6c00f117225f6449 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 10:38:24 +0200 Subject: [PATCH 072/272] chore(holesky): add .gitignore --- testnets/holesky/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testnets/holesky/.gitignore diff --git a/testnets/holesky/.gitignore b/testnets/holesky/.gitignore new file mode 100644 index 000000000..ef456d924 --- /dev/null +++ b/testnets/holesky/.gitignore @@ -0,0 +1,2 @@ +*.env* +!*.env.example From f09560cfb578f0e27bb94db7eab6ae2bc568e7e6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:00:17 +0200 Subject: [PATCH 073/272] chore(mev-boost): add .env.example --- mev-boost/.env.example | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 mev-boost/.env.example diff --git a/mev-boost/.env.example b/mev-boost/.env.example new file mode 100644 index 000000000..447b2fab8 --- /dev/null +++ b/mev-boost/.env.example @@ -0,0 +1,32 @@ +# Logging settings +LOG_JSON=false # Set to true to log in JSON format +LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic +DEBUG=false # Set to true to enable debug mode +LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=false # Set to true to disable logging the version + +# Server settings +BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup + +# Relay settings +RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) +RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) +MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) + +# Relay timeout settings (in ms) +RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay +RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay +RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests + +# Genesis settings +GENESIS_FORK_VERSION= # Custom genesis fork version +GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) + +# Network settings +SEPOLIA=false # Set to true to use Sepolia network +GOERLI=false # Set to true to use Goerli network +HOLESKY=false # Set to true to use Holesky network + +# Retry settings +REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From 1bd285b7762b18e959293513dd4f91382dfce2fa Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 13:57:44 +0200 Subject: [PATCH 074/272] feat(holesky): update networking & default values --- testnets/holesky/bolt-sidecar.env.example | 8 ++++---- testnets/holesky/docker-compose.yml | 9 +++++++++ testnets/holesky/mev-boost.env.example | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example index a094ec52d..8792c94a2 100644 --- a/testnets/holesky/bolt-sidecar.env.example +++ b/testnets/holesky/bolt-sidecar.env.example @@ -4,17 +4,17 @@ # port should be open on your firewall in order to receive external requests! BOLT_SIDECAR_PORT=8017 # Execution client API URL -BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +BOLT_SIDECAR_EXECUTION_API_URL="http://172.56.0.1:8545" # URL for the beacon client -BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +BOLT_SIDECAR_BEACON_API_URL="http://172.56.0.1:5052" # Execution client Engine API URL. This is needed for fallback block building # and must be a synced Geth node -BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +BOLT_SIDECAR_ENGINE_API_URL="http://172.56.0.1:8551" # The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 # URL to forward the constraints produced by the Bolt sidecar to a server # supporting the Constraints API, such as an MEV-Boost fork -BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://bolt-mev-boost-holesky:18551" # Validator indexes of connected validators that the sidecar should accept # commitments on behalf of. # Accepted values: diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 74bdfc514..fb009da12 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -56,3 +56,12 @@ volumes: driver: local grafana-data: driver: local + +networks: + bolt-default: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.56.0.0/16 + gateway: 172.56.0.1 diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example index 8815677dc..942149d0b 100644 --- a/testnets/holesky/mev-boost.env.example +++ b/testnets/holesky/mev-boost.env.example @@ -6,7 +6,7 @@ LOG_SERVICE_TAG= # Optional: Add a custom service tag to all DISABLE_LOG_VERSION=false # Set to true to disable logging the version # Server settings -BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +BOOST_LISTEN_ADDR=0.0.0.0:18551 # Address for mev-boost server to listen on RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup # Relay settings From d7e2b8f63971f3a45f51d68df93495dbcb54b81c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:23:12 +0200 Subject: [PATCH 075/272] chore(mev-boost): update .gitignore --- mev-boost/.gitignore | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 mev-boost/.gitignore diff --git a/mev-boost/.gitignore b/mev-boost/.gitignore new file mode 100644 index 000000000..c919b8e0d --- /dev/null +++ b/mev-boost/.gitignore @@ -0,0 +1,33 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Ignore VI/Vim swapfiles +.*.sw? + +# IntelliJ +.idea +.ijwb +/mev-boost +/test-cli +/tmp +/dist +.vscode/ +/README.internal.md +/validator_data.json +/build/ + +*.env +!.env.example From 4ee7816f7b4da31914bff9d62ffa7740016f474f Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:57:00 +0200 Subject: [PATCH 076/272] chore(holesky): update docker compose setup --- testnets/holesky/docker-compose.yml | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 testnets/holesky/docker-compose.yml diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml new file mode 100644 index 000000000..52ef4da0c --- /dev/null +++ b/testnets/holesky/docker-compose.yml @@ -0,0 +1,58 @@ +services: + bolt-sidecar: + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1 + container_name: bolt-sidecar + restart: unless-stopped + ports: + - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) + - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" + env_file: ./bolt-sidecar.env + entrypoint: /bin/sh -c /bolt-sidecar + + mev-boost: + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha-rc.1 + container_name: mev-boost + restart: unless-stopped + env_file: ./mev-boost.env + entrypoint: /bin/sh -c '/app/mev-boost' + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - 9090:9090 + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - ./targets.json:/etc/prometheus/targets.json + - prometheus-data:/prometheus + networks: + - monitoring_network + + grafana: + image: grafana/grafana:latest + container_name: cb_grafana + ports: + - 3000:3000 + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - ./grafana/dashboards:/etc/grafana/provisioning/dashboards + - ./grafana/datasources:/etc/grafana/provisioning/datasources + - grafana-data:/var/lib/grafana + networks: + - monitoring_network + depends_on: + - prometheus + logging: + driver: none + +volumes: + prometheus-data: + driver: local + grafana-data: + driver: local +networks: + monitoring_network: + driver: bridge + signer_network: + driver: bridge From 7201f6f1f4fb66dd50d469975c2b9882fe79c107 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:57:36 +0200 Subject: [PATCH 077/272] chore(holesky): move commit-boost config in separate directory --- testnets/holesky/{ => commit-boost}/cb-bolt-config.toml | 2 +- testnets/holesky/commit-boost/cb-prometheus.yml | 8 ++++++++ testnets/holesky/{ => commit-boost}/keys.json | 0 testnets/holesky/{ => commit-boost}/targets.json | 0 testnets/holesky/{ => commit-boost}/update-grafana.sh | 4 ++-- testnets/holesky/prometheus.yml | 4 ++-- 6 files changed, 13 insertions(+), 5 deletions(-) rename testnets/holesky/{ => commit-boost}/cb-bolt-config.toml (99%) create mode 100644 testnets/holesky/commit-boost/cb-prometheus.yml rename testnets/holesky/{ => commit-boost}/keys.json (100%) rename testnets/holesky/{ => commit-boost}/targets.json (100%) rename testnets/holesky/{ => commit-boost}/update-grafana.sh (51%) diff --git a/testnets/holesky/cb-bolt-config.toml b/testnets/holesky/commit-boost/cb-bolt-config.toml similarity index 99% rename from testnets/holesky/cb-bolt-config.toml rename to testnets/holesky/commit-boost/cb-bolt-config.toml index be4e35002..f78b0fc8d 100644 --- a/testnets/holesky/cb-bolt-config.toml +++ b/testnets/holesky/commit-boost/cb-bolt-config.toml @@ -136,7 +136,7 @@ BOLT_SIDECAR_METRICS_PORT = "10000" # - /etc/prometheus/targets.json # ``` # and use the `targets.json` file generated by `commit-boost init` -prometheus_config = "./prometheus.yml" +prometheus_config = "./cb-prometheus.yml" # Whether to start Grafana with built-in dashboards # OPTIONAL, DEFAULT: true use_grafana = true diff --git a/testnets/holesky/commit-boost/cb-prometheus.yml b/testnets/holesky/commit-boost/cb-prometheus.yml new file mode 100644 index 000000000..91b5d5905 --- /dev/null +++ b/testnets/holesky/commit-boost/cb-prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "commit-boost" + file_sd_configs: + - files: + - /etc/prometheus/targets.json diff --git a/testnets/holesky/keys.json b/testnets/holesky/commit-boost/keys.json similarity index 100% rename from testnets/holesky/keys.json rename to testnets/holesky/commit-boost/keys.json diff --git a/testnets/holesky/targets.json b/testnets/holesky/commit-boost/targets.json similarity index 100% rename from testnets/holesky/targets.json rename to testnets/holesky/commit-boost/targets.json diff --git a/testnets/holesky/update-grafana.sh b/testnets/holesky/commit-boost/update-grafana.sh similarity index 51% rename from testnets/holesky/update-grafana.sh rename to testnets/holesky/commit-boost/update-grafana.sh index 4e5a16695..1f832d32b 100755 --- a/testnets/holesky/update-grafana.sh +++ b/testnets/holesky/commit-boost/update-grafana.sh @@ -1,5 +1,5 @@ #!/bin/bash # Fetches the latest dashboards from commit-boost main -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ../grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ../grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/prometheus.yml b/testnets/holesky/prometheus.yml index 91b5d5905..bce41074a 100644 --- a/testnets/holesky/prometheus.yml +++ b/testnets/holesky/prometheus.yml @@ -1,8 +1,8 @@ global: - scrape_interval: 15s + scrape_interval: 5s scrape_configs: - - job_name: "commit-boost" + - job_name: "bolt-sidecar" file_sd_configs: - files: - /etc/prometheus/targets.json From 32aeb6cb85c5070060adaf94a1acc1833ac37f0a Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 12:34:06 +0200 Subject: [PATCH 078/272] fix(delegations-cli): non-descriptive error message --- bolt-cli/src/utils/keystore.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bolt-cli/src/utils/keystore.rs b/bolt-cli/src/utils/keystore.rs index 26810e11b..464a1e5cd 100644 --- a/bolt-cli/src/utils/keystore.rs +++ b/bolt-cli/src/utils/keystore.rs @@ -51,7 +51,9 @@ impl KeystoreSecret { /// Load the keystore passwords from a directory containing individual password files. pub fn from_directory(root_dir: &str) -> Result { let mut secrets = HashMap::new(); - for entry in fs::read_dir(root_dir)? { + for entry in fs::read_dir(&root_dir) + .wrap_err(format!("failed to read secrets directory. path: {}", &root_dir))? + { let entry = entry.wrap_err("Failed to read secrets directory entry")?; let path = entry.path(); @@ -108,12 +110,14 @@ impl Drop for KeystoreSecret { /// -- ... /// Reference: https://github.com/chainbound/bolt/blob/4634ff905561009e4e74f9921dfdabf43717010f/bolt-sidecar/src/signer/keystore.rs#L109 pub fn keystore_paths(keys_path: &str) -> Result> { - let keys_path = Path::new(keys_path).to_path_buf(); + let keys_path_buf = Path::new(keys_path).to_path_buf(); let json_extension = OsString::from("json"); let mut keystores_paths = vec![]; // Iter over the `keys` directory - for entry in read_dir(keys_path)? { + for entry in read_dir(keys_path_buf) + .wrap_err(format!("failed to read keys directory. path: {keys_path}"))? + { let path = read_path(entry)?; if path.is_dir() { for entry in read_dir(path)? { From 67aaf93772d0a90fd509c6a7d9e9c8574bd84232 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:45:07 +0200 Subject: [PATCH 079/272] wip: pbs configs --- testnets/holesky/docker-compose.pbs.yml | 69 +++++++++++++++++++++++++ testnets/holesky/docker-compose.yml | 4 +- testnets/holesky/helix-config.yml | 17 ++++++ testnets/holesky/scripts/run-bn.sh | 28 ++++++++++ testnets/holesky/scripts/run-builder.sh | 32 ++++++++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 testnets/holesky/docker-compose.pbs.yml create mode 100644 testnets/holesky/helix-config.yml create mode 100644 testnets/holesky/scripts/run-bn.sh create mode 100644 testnets/holesky/scripts/run-builder.sh diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml new file mode 100644 index 000000000..be3a3a003 --- /dev/null +++ b/testnets/holesky/docker-compose.pbs.yml @@ -0,0 +1,69 @@ +volumes: + psql_data: + driver: local + chaindata: + driver: local + +services: + redis: + image: redis + restart: unless-stopped + + db: + image: timescale/timescaledb-ha:pg16 + restart: unless-stopped + volumes: + - "psql_data:/var/lib/postgresql/data" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: helixdb + + adminer: + image: adminer + restart: unless-stopped + depends_on: + - db + ports: + - "48093:8080" + environment: + ADMINER_PLUGINS: tables-filter tinymce + + builder: + image: ghcr.io/chainbound/bolt-builder:v0.3.0-alpha.rc1 + restart: unless-stopped + volumes: + - "chaindata:/var/lib/chaindata" + - "./shared:/var/lib/shared" + - "./scripts/run-builder.sh:/scripts/run-builder.sh" + environment: + BUILDER_TX_SIGNING_KEY: "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710" + ports: + - "30367:30303/tcp" + - "30367:30303/udp" + # entrypoint is builder + entrypoint: /scripts/run-builder.sh + + beacon: + image: sigp/lighthouse:latest + restart: unless-stopped + volumes: + - "chaindata:/var/lib/chaindata" + - "./shared:/var/lib/shared" + - "./scripts/run-bn.sh:/scripts/run-bn.sh" + ports: + - "41050:50050/tcp" + - "41050:50050/udp" + entrypoint: /scripts/run-bn.sh + + helix-relay: + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 + restart: unless-stopped + volumes: + - "./helix-config.yml:/helix-config.yml" + ports: + - "44040:4040" + environment: + - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 + - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + command: --config /helix-config.yml diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 52ef4da0c..2751212df 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,6 +1,6 @@ services: bolt-sidecar: - image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1 + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 container_name: bolt-sidecar restart: unless-stopped ports: @@ -10,7 +10,7 @@ services: entrypoint: /bin/sh -c /bolt-sidecar mev-boost: - image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha-rc.1 + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 container_name: mev-boost restart: unless-stopped env_file: ./mev-boost.env diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml new file mode 100644 index 000000000..edeff326e --- /dev/null +++ b/testnets/holesky/helix-config.yml @@ -0,0 +1,17 @@ +postgres: + hostname: db + port: 5432 + db_name: helixdb + user: postgres + password: postgres + +redis: + url: redis://redis:6379 + +simulator: + url: http://builder:8545 + +beacon_clients: + - url: http://beacon:50050 + +network_config: !Holesky diff --git a/testnets/holesky/scripts/run-bn.sh b/testnets/holesky/scripts/run-bn.sh new file mode 100644 index 000000000..5fe326129 --- /dev/null +++ b/testnets/holesky/scripts/run-bn.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +lighthouse beacon_node \ + --network=holesky \ + --debug-level=info \ + --datadir=/var/lib/chaindata \ + --disable-enr-auto-update \ + --enr-udp-port=50050 \ + --enr-tcp-port=50050 \ + --listen-address=0.0.0.0 \ + --port=50050 \ + --http \ + --http-address=0.0.0.0 \ + --http-port=4000 \ + --http-allow-sync-stalled \ + --always-prepare-payload \ + --prepare-payload-lookahead=12000 \ + --slots-per-restore-point=32 \ + --disable-packet-filter \ + --checkpoint-sync-url=https://holesky.beaconstate.info \ + --execution-endpoints=http://builder:8551 \ + --subscribe-all-subnets \ + --metrics \ + --metrics-address=0.0.0.0 \ + --metrics-allow-origin=* \ + --metrics-port=5054 \ + --enable-private-discovery \ + --jwt-secrets=/var/lib/shared/jwtsecret diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh new file mode 100644 index 000000000..d6894ac4e --- /dev/null +++ b/testnets/holesky/scripts/run-builder.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +geth --datadir=/var/lib/chaindata/geth \ + --network=holesky \ + --syncmode=full \ + --gcmode=archive \ + --state.scheme=hash \ + --verbosity=3 \ + --http \ + --http.port=8545 \ + --http.addr=0.0.0.0 \ + --http.vhosts=* \ + --http.corsdomain=* \ + --http.api=admin,engine,net,eth,web3,debug,flashbots,txpool \ + --ws \ + --ws.addr=0.0.0.0 \ + --ws.port=8546 \ + --ws.api=admin,engine,net,eth,web3,debug,flashbots,txpool \ + --ws.origins=* \ + --authrpc.port=8551 \ + --authrpc.addr=0.0.0.0 \ + --authrpc.vhosts=* \ + --authrpc.jwtsecret=/var/lib/shared/jwtsecret \ + --metrics \ + --metrics.addr=0.0.0.0 \ + --metrics.port=6060 \ + --port=30303 \ + --builder \ + --builder.remote_relay_endpoint=http://relay-api:9062 \ + --builder.beacon_endpoints=http://beacon:4000 \ + --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ + --miner.extradata="Bolt Builder" From 9d2b5a741f0516c05e3efb3047c678d51e7aedb7 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:31:30 +0200 Subject: [PATCH 080/272] feat: working pbs docker compose --- testnets/holesky/docker-compose.pbs.yml | 23 ++++++++++++++++++----- testnets/holesky/helix-config.yml | 2 +- testnets/holesky/scripts/run-bn.sh | 0 testnets/holesky/scripts/run-builder.sh | 4 ++-- 4 files changed, 21 insertions(+), 8 deletions(-) mode change 100644 => 100755 testnets/holesky/scripts/run-bn.sh mode change 100644 => 100755 testnets/holesky/scripts/run-builder.sh diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index be3a3a003..362dc1c2e 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -3,6 +3,8 @@ volumes: driver: local chaindata: driver: local + shared: + driver: local services: redis: @@ -34,31 +36,41 @@ services: restart: unless-stopped volumes: - "chaindata:/var/lib/chaindata" - - "./shared:/var/lib/shared" + - "shared:/var/lib/shared" - "./scripts/run-builder.sh:/scripts/run-builder.sh" environment: BUILDER_TX_SIGNING_KEY: "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710" ports: - "30367:30303/tcp" - "30367:30303/udp" - # entrypoint is builder - entrypoint: /scripts/run-builder.sh + entrypoint: + [ + "/bin/sh", + "-c", + "chmod +x /scripts/run-builder.sh && /scripts/run-builder.sh", + ] beacon: image: sigp/lighthouse:latest restart: unless-stopped volumes: - "chaindata:/var/lib/chaindata" - - "./shared:/var/lib/shared" + - "shared:/var/lib/shared" - "./scripts/run-bn.sh:/scripts/run-bn.sh" ports: - "41050:50050/tcp" - "41050:50050/udp" - entrypoint: /scripts/run-bn.sh + entrypoint: + ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 restart: unless-stopped + depends_on: + - db + - redis + - builder + - beacon volumes: - "./helix-config.yml:/helix-config.yml" ports: @@ -66,4 +78,5 @@ services: environment: - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + - RUST_BACKTRACE=1 command: --config /helix-config.yml diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml index edeff326e..3305a335b 100644 --- a/testnets/holesky/helix-config.yml +++ b/testnets/holesky/helix-config.yml @@ -12,6 +12,6 @@ simulator: url: http://builder:8545 beacon_clients: - - url: http://beacon:50050 + - url: http://beacon:4000 network_config: !Holesky diff --git a/testnets/holesky/scripts/run-bn.sh b/testnets/holesky/scripts/run-bn.sh old mode 100644 new mode 100755 diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh old mode 100644 new mode 100755 index d6894ac4e..c1bc12035 --- a/testnets/holesky/scripts/run-builder.sh +++ b/testnets/holesky/scripts/run-builder.sh @@ -1,7 +1,7 @@ #!/bin/sh geth --datadir=/var/lib/chaindata/geth \ - --network=holesky \ + --holesky \ --syncmode=full \ --gcmode=archive \ --state.scheme=hash \ @@ -26,7 +26,7 @@ geth --datadir=/var/lib/chaindata/geth \ --metrics.port=6060 \ --port=30303 \ --builder \ - --builder.remote_relay_endpoint=http://relay-api:9062 \ + --builder.remote_relay_endpoint=http://helix-relay:4040 \ --builder.beacon_endpoints=http://beacon:4000 \ --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ --miner.extradata="Bolt Builder" From ebfc692421e70889fab9ce9945cf603cb76ea06a Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 11:14:27 +0200 Subject: [PATCH 081/272] chore(sidecar): more precise docs for config --- bolt-sidecar/src/config/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index af3256000..fcade3aee 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -33,7 +33,8 @@ pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18551; #[derive(Debug, Parser, Deserialize)] #[clap(trailing_var_arg = true)] pub struct Opts { - /// Port to listen on for incoming JSON-RPC requests + /// Port to listen on for incoming JSON-RPC requests of the Commitments API. + /// This port should be open on your firewall in order to receive external requests! #[clap(long, env = "BOLT_SIDECAR_PORT", default_value_t = DEFAULT_RPC_PORT)] pub port: u16, /// Execution client API URL @@ -42,7 +43,8 @@ pub struct Opts { /// URL for the beacon client #[clap(long, env = "BOLT_SIDECAR_BEACON_API_URL", default_value = "http://localhost:5052")] pub beacon_api_url: Url, - /// Execution client Engine API URL + /// Execution client Engine API URL. This is needed for fallback block building and must be a + /// synced Geth node. #[clap(long, env = "BOLT_SIDECAR_ENGINE_API_URL", default_value = "http://localhost:8551")] pub engine_api_url: Url, /// URL to forward the constraints produced by the Bolt sidecar to a server supporting the @@ -77,10 +79,11 @@ pub struct Opts { /// The fee recipient address for fallback blocks #[clap(long, env = "BOLT_SIDECAR_FEE_RECIPIENT")] pub fee_recipient: Address, - /// Secret BLS key to sign fallback payloads with (If not provided, a random key will be used) + /// Secret BLS key to sign fallback payloads with #[clap(long, env = "BOLT_SIDECAR_BUILDER_PRIVATE_KEY")] pub builder_private_key: BlsSecretKeyWrapper, - /// Secret ECDSA key to sign commitment messages with + /// Secret ECDSA key to sign commitment messages with. The public key associated to it must be + /// then used when registering the operator in the `BoltManager` contract. #[clap(long, env = "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY")] pub commitment_private_key: EcdsaSecretKeyWrapper, /// Operating limits for the sidecar From 12d83b1fb91907d9829fe77483779e49c417cce9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:26:23 +0200 Subject: [PATCH 082/272] docs(holesky): README launch wip update --- testnets/holesky/README.md | 828 +++++++++++++++++++++++++++++++++++-- 1 file changed, 797 insertions(+), 31 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index c031285f3..526089c56 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -1,31 +1,159 @@ -# Holesky Launch Instructions +This document provides instructions for running the Bolt sidecar on the Holesky testnet. + +# Table of Contents + + + +* [Prerequisites](#prerequisites) +* [Setup](#setup) + * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Commit-Boost Mode](#commit-boost-mode) + * [Native Mode (advanced)](#native-mode-(advanced)) + * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) + * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) + * [Configuration file](#configuration-file) + * [Observability](#observability) +* [Register your validators on-chain on the Bolt Registry](#register-your-validators-on-chain-on-the-bolt-registry) + * [Validator Registration](#validator-registration) + * [Bolt Network Entrypoint](#bolt-network-entrypoint) + * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) + * [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) + * [EigenLayer Integration Guide for Node Operators and Solo Stakers](#eigenlayer-integration-guide-for-node-operators-and-solo-stakers) +* [Reference](#reference) + * [Command-line options](#command-line-options) + * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) + * [`bolt-delegations-cli`](#`bolt-delegations-cli`) + * [Installation and usage](#installation-and-usage) + * [Delegations CLI Example](#delegations-cli-example) + * [Using a private key directly](#using-a-private-key-directly) + * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) + * [Avoid restarting the beacon node](#avoid-restarting-the-beacon-node) + + + +# Prerequisites + +In order to run Bolt you need some components already installed and running in +your system. + +**A synced Geth client:** + +Bolt is fully trustless since it is able to produce a fallback block with the +commitments issued in case builders do not return a valid bid. In order to do so +it relies on a synced execution client, configured via the `--execution-api-url` +flag. At the moment only Geth is supported; with more +clients to be supported in the future. + +Using the sidecar with a different execution client could lead to commitment +faults because fallback block building is not supported yet. You can download +Geth from [the official website](https://geth.ethereum.org/downloads). + +**A synced beacon node:** + +Bolt is compatible with every beacon client. Please refer to the various beacon +client implementations to download and run them. -## Components +> [!IMPORTANT] +> In order to correctly run the Bolt sidecar and avoid commitment faults the +> beacon node must be configured so that: +> +> 1. the node's `builder-api` (or equivalent flag) must point to the Bolt +> Sidecar API. +> 2. the node will always prefer the builder payload. For instance, in +> Lighthouse this can be achieved by providing the following flags: +> +> ```text +> --always-prefer-builder-payload +> --builder-fallback-disable-checks +> ``` +> +> It might be necessary to restart your beacon node depending on your existing +> setup. See the [Avoid Restarting the Beacon +> Node](#avoid-restarting-the-beacon-node) for more details. + +**Active validators:** + +The Bolt sidecar requires signing keys from active Ethereum validators, or +authorized delegates acting on their behalf, to issue and sign preconfirmations. + +**LST collateral:** + +For Holesky in order to provide credible proposer commitments it is necessary to +restake 1 ether worth of ETH derivatives per validator in either the Symbiotic +or the EigenLayer protocol. + +# Setup + +There are various way to run the Bolt Sidecar depending on what infrastructure +you want to use and your preferred signing methods: -The components that need to run to test Bolt on Holesky are: +- Docker mode (recommended); +- [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode + (requires Docker). +- Native mode (advanced, requires building everything from source); -- A synced execution client -- A synced beacon node -- Active validators -- Commit-Boost with Bolt configuration +Running the Bolt sidecar as a standalone binary requires building it from +source. Both the standalone binary and the Docker container requires reading +signing keys from [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores, +while the Commit-Boost module relies on an internal signer and a custom PBS +module instead of regular [MEV-Boost](https://boost.flashbots.net/). -## Setup +In this section we're going to explore each of these options and its +requirements. -### Commit-Boost +## Docker Mode (recommended) -#### Installation +First, make sure to have both [Docker](https://docs.docker.com/engine/install/), +[Docker Compose](https://docs.docker.com/compose/install/) and +[git](https://git-scm.com/downloads) installed in your machine. -To install the `commit-boost` CLI with `cargo`: +Then clone the Bolt repository by running: ```bash -# Use specific commit hash to ensure compatibility -cargo install --locked --git https://github.com/Commit-Boost/commit-boost-client --rev aed00e8 commit-boost +git clone --branch v0.3.0 htts://github.com/chainbound/bolt.git && cd bolt +``` + +The Docker Compose setup will spin up the Bolt sidecar along with the Bolt +MEV-Boost fork which includes supports the [Constraints +API](https://docs.boltprotocol.xyz/api/builder). + +Before starting the services, you'll need to provide configuration files +containing the necessary environment variables: + +1. **Bolt Sidecar Configuration:** + + Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you + need a reference, you can use the `.env.example` file in the `bolt-sidecar` + directory as a starting point. For proper configuration of the signing + options, please refer to the [Delegations and + Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) + section of this guide. + +2. **MEV-Boost Configuration:** + + Similarly, create a `mev-boost.env` file in the + `testnets/holesky` folder to configure the MEV-Boost service. If you need a + reference, you can use the `.env.example` file in the `mev-boost` directory as a + starting point. -# Test installation -commit-boost --version +If you prefer not to restart your beacon node, follow the instructions in the +[Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. + +Once the configuration files are in place, you can start the Docker containers +by running: + +```bash +cd testnets/holesky && docker compose up -d ``` -#### Configuration +The docker compose setup comes with various observability tools, such as +Prometheus and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. + +## Commit-Boost Mode + +First download the `commit-boost-cli` binary from the Commit-Boost [official +releases page](https://github.com/Commit-Boost/commit-boost-client/releases) A commit-boost configuration file with Bolt support is provided at [`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the @@ -59,13 +187,13 @@ To initialize commit-boost, run the following command: commit-boost init --config cb-bolt-config.toml ``` -This will create 3 files: +This will create three files: - `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services - `.cb.env`: with local env variables, including JWTs for modules - `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus -#### Running +**Running** The final step is to run the Commit-Boost services. This can be done with the following command: @@ -76,31 +204,669 @@ commit-boost start --docker cb.docker-compose.yml --env .cb.env This will run all modules in Docker containers. > [!IMPORTANT] -> bolt-boost will be exposed at `pbs.port` (18551 by default, set with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be configured -> to point the `builder-api` to this port for Bolt to work. +> The `bolt-boost` service will be exposed at `pbs.port` (18551 by default, set +> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be +> configured to point the `builder-api` to this port for Bolt to work. + +## Native Mode (advanced) + +For running the Bolt Sidecar as a standalone binary you need to have the +following dependencies installed: + +- [git](https://git-scm.com/downloads); +- [Rust](https://www.rust-lang.org/tools/install). +- [Golang](https://golang.org/doc/install). + +Depending on your platform you may need to install additional dependencies. + +
+Linux + +Debian-based distributions: + +```bash +sudo apt update && sudo apt install -y git build-essential libssl-dev build-essential ca-certificates +``` + +Fedora/Red Hat/CentOS distributions: + +```bash +sudo dnf groupinstall "Development Tools" && sudo dnf install -y git openssl-devel ca-certificates pkgconfig +``` + +Arch/Manjaro-based distributions: + +```bash +sudo pacman -Syu --needed base-devel git openssl ca-certificates pkgconf +``` + +Alpine Linux + +```bash +sudo apk add git build-base openssl-dev ca-certificates pkgconf +``` + +
+ +
+ +
+ MacOS + +On MacOS after installing XCode Command Line tools (equivalent to `build-essential` on Linux) you can install the other dependencies with [Homebew](https://brew.sh/): + +```zsh +xcode-select --install +brew install pkg-config openssl +``` + +
+ +--- + +After having installed the dependencies you can clone the Bolt repository by +running: + +```bash +git clone --branch v0.3.0 https://github.com/chainbound/bolt.git && cd bolt +``` + +### Building and running the MEV-Boost fork binary -### Bolt Sidecar +The Bolt protocol relies on a modified version of +[MEV-Boost](https://boost.flashbots.net/) that supports the [Constraints +API](https://docs.boltprotocol.xyz/api/builder). This modified version is +available in the `mev-boost` directory of the project and can be built by +running -WIP +```bash +make build +``` + +in the `mev-boost` directory. The output of the command is a `mev-boost` binary. +To run the `mev-boost` binary please read the official [documentation](https://boost.flashbots.net/). + +If you're already running MEV-Boost along with your beacon client it is +recommended to choose another port this service in order to [avoid restarting +your beacon client](#avoid-restarting-the-beacon-node). Check out the linked +section for more details. + +### Building and running the Bolt sidecar binary + +Then you can build the Bolt sidecar by running: + +```bash +cargo build --release && mv target/release/bolt-sidecar . +``` + +In order to run correctly the sidecar you need to provide either a list command +line options or a configuration file (recommended). All the options available +can be found by running `./bolt-sidecar --help`, or you can find them in the +[reference](#command-line-options) section of this guide. + +#### Configuration file + +A configuration file can be either a `.env` file or a `.toml` file. If you use +`.env` file you can find a `.env.example` file in the repository that you can +use as a template. + +For a `.toml` file you can use the template in the `Config.example.toml`. Lastly +you need to specify the path of the configuration file by setting the +`BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. + +Please read the section on [delegations and signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +to configure such sidecar options properly. + +After you've set up the configuration file you can run the Bolt sidecar with + +```bash +./bolt-sidecar-cli +``` ### Observability -commit-boost comes with various observability tools, such as Prometheus, cadvisor, and Grafana. It also comes with some pre-built dashboards, -which can be found in the `grafana` directory. +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. To update these dashboards, run the following command: +`bash ./update-grafana.sh ` + +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. + +# Register your validators on-chain on the Bolt Registry + +Once you are successfully running the Bolt sidecar you need to register on-chain +on the Bolt Registry to successfully receive preconfirmation requests from users +and RPCs. This step is needed to provide economic security to your +commitments. + +In order to do that you need some collateral in the form of whitelisted Liquid +Staking Token (LST) that needs to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with ETH derivatives on Holesky. Here +are references to the supported tokens on both restaking protocols: + +- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) + - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) +- [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) + - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wETH`](https://holesky.etherscan.io/address/0x94373a4919B3240D86eA41593D5eBa789FEF3848) + - [`cbETH`](https://holesky.etherscan.io/address/0x8720095Fa5739Ab051799211B146a2EEE4Dd8B37) + - [`mETH`](https://holesky.etherscan.io/address/0xe3C063B1BEe9de02eb28352b55D49D85514C67FF) + +Then you need to interact with two contracts on Holesky: +`BoltValidators` and `BoltManager`. The former is used to register your +active validators into the protocol, while the latter is used to manage to +register as an operator into the system and integrate with the restaking +protocols. + +> [!IMPORTANT] +> When registering your operator in the `BoltManager` contract you must use the +> public key associated to the private key used to sign commitments with the +> Bolt Sidecar (the `--commitment-private-key` flag). + +## Validator Registration + +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for +validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. + +The registration process includes the following steps: + +1. Validator signs a message with their BLS private key. This is required to prove that the + validator private key is under their control and that they are indeed its owner. +2. Validator calls the `registerValidator` function providing: + 1. Their BLS public key + 2. The BLS signature of the registration message + 3. The address of the authorized collateral provider + 4. The address of the authorized operator + +Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function +that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and +will allow us to test the registration flow in a controlled environment. + +## Bolt Network Entrypoint + +The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +coordination of validators, operators, and vaults within the Bolt network. + +Key features include: + +1. Retrieval of operator stake and proposer status from their pubkey +2. Integration with Symbiotic +3. Integration with Eigenlayer + +Specific functionalities about the restaking protocols are handled inside +the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. + +### Symbiotic Integration guide for Staking Pools + +As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. +If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) +on how to spin up a Vault and start receiving stake from your node operators. + +Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: + +1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. +2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. +3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. +4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. +5. You can now start approving operators that opt in to your vault directly in Symbiotic. +6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. + +### Symbiotic Integration guide for Operators + +As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide +commitments on their behalf. + +The opt-in process requires the following steps: + +1. register in Symbiotic with `OperatorRegistry.registerOperator()`. +2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. +5. get approved by the vault. +6. start providing commitments with the stake provided by the vault. + +### EigenLayer Integration Guide for Node Operators and Solo Stakers + +> [!NOTE] +> Without loss of generality, we will assume the reader of this guide is a Node +> Operator (NO), since the same steps apply to solo stakers. +> As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) +> in the Bolt AVS built on top of EigenLayer. This requires +> running an Ethereum validator and the Bolt sidecar in order issue +> preconfirmations. + +The Operator will be represented by an Ethereum address that needs +to follow the standard procedure outlined in the +[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: + +1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + +2. As an Ethereum validator offering precofirmations a NO needs some collateral in + order to be economically credible. In order to do that, some entities known as a "stakers" + need to deposit whitelisted Liquid Staking Tokens (LSTs) + into an appropriate "Strategy" associated to the LST via the + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), + so that the Operator has a `min_amount` (for Holesky 1 ether) of collateral associated to it. + Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract + in the `getWhitelistedCollaterals` function. + Note that NOs and stakers can be two different entities + _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. + +3. After the stakers have deposited their collateral into a strategy they need + to choose you as their operator. To do that, they need to call the function + [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). + +4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. + This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. + The payload is a signature whose digest consists of: + + 1. your operator address + 2. the `BoltEigenLayerMiddleware` contract address + 3. a salt + 4. an expiry 2. + + The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) + with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, + the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. + +Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` +contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a +registered operator and so that they can forward you preconfirmation requests. + +The steps required are the following: + +1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. + If you own more than one validator public key, + you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. + The `authorizedOperator` argument must be the same Ethereum address used for + opting into EigenLayer as an Operator. + +2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling + the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network + and allows you to manage operations effectively, such as pausing or resuming + your service. + +3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. + This ensures that your restaked assets are correctly integrated with Bolt’s system. + +# Reference + +## Command-line options + +For completeness, here are all the command-line options available for the Bolt +sidecar. You can see them in your terminal by running the Bolt sidecar binary +with the `--help` flag: + +``` +Command-line options for the Bolt sidecar + +Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > + +Options: + --port + Port to listen on for incoming JSON-RPC requests of the Commitments API + This port should be open on your firewall in order to receive external requests! + + [env: BOLT_SIDECAR_PORT=] + [default: 8000] + + --execution-api-url + Execution client API URL + + [env: BOLT_SIDECAR_EXECUTION_API_URL=] + [default: http://localhost:8545] + + --beacon-api-url + URL for the beacon client + + [env: BOLT_SIDECAR_BEACON_API_URL=] + [default: http://localhost:5052] + + --engine-api-url + Execution client Engine API URL. This is needed for fallback block + building and must be a synced Geth node + + [env: BOLT_SIDECAR_ENGINE_API_URL=] + [default: http://localhost:8551] + + --constraints-api-url + URL to forward the constraints produced by the Bolt sidecar to a + server supporting the Constraints API, such as an MEV-Boost fork + + [env: BOLT_SIDECAR_CONSTRAINTS_API_URL=] + [default: http://localhost:3030] + + --constraints-proxy-port + The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client + + [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] + [default: 18551] + + --validator-indexes + Validator indexes of connected validators that the sidecar should + accept commitments on behalf of. Accepted values: + - a comma-separated list of indexes (e.g. "1,2,3,4") + - a contiguous range of indexes (e.g. "1..4") + - a mix of the above (e.g. "1,2..4,6..8") + + [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] + [default: ] + + --engine-jwt-hex + The JWT secret token to authenticate calls to the engine API. + + It can either be a hex-encoded string or a file path to a file containing the hex-encoded secret. + + [env: BOLT_SIDECAR_ENGINE_JWT_HEX=] + + --fee-recipient + The fee recipient address for fallback blocks + + [env: BOLT_SIDECAR_FEE_RECIPIENT=] + + --builder-private-key + Secret BLS key to sign fallback payloads with + + [env: BOLT_SIDECAR_BUILDER_PRIVATE_KEY=] + + --commitment-private-key + Secret ECDSA key to sign commitment messages with. The public key + associated to it must be then used when registering the operator in the + `BoltManager` contract + + [env: BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY=] + + --max-commitments-per-slot + Max number of commitments to accept per block + + [env: BOLT_SIDECAR_MAX_COMMITMENTS=] + [default: 128] + + --max-committed-gas-per-slot + Max committed gas per slot + + [env: BOLT_SIDECAR_MAX_COMMITTED_GAS=] + [default: 10000000] + + --min-priority-fee + Min priority fee to accept for a commitment + + [env: BOLT_SIDECAR_MIN_PRIORITY_FEE=] + [default: 1000000000] + + --chain + Chain on which the sidecar is running + + [env: BOLT_SIDECAR_CHAIN=] + [default: mainnet] + [possible values: mainnet, holesky, helder, kurtosis] + + --commitment-deadline + The deadline in the slot at which the sidecar will stop accepting new commitments for the next block (parsed as milliseconds) + + [env: BOLT_SIDECAR_COMMITMENT_DEADLINE=] + [default: 8000] + + --slot-time + The slot time duration in seconds. If provided, it overrides the default for the selected [Chain] + + [env: BOLT_SIDECAR_SLOT_TIME=] + [default: 12] + + --constraint-private-key + Private key to use for signing constraint messages + + [env: BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY=] + + --commit-boost-signer-url + URL for the commit-boost sidecar + + [env: BOLT_SIDECAR_CB_SIGNER_URL=] + + --commit-boost-jwt-hex + JWT in hexadecimal format for authenticating with the commit-boost service + + [env: BOLT_SIDECAR_CB_JWT_HEX=] + + --keystore-password + The password for the ERC-2335 keystore. Reference: https://eips.ethereum.org/EIPS/eip-2335 + + [env: BOLT_SIDECAR_KEYSTORE_PASSWORD=] + + --keystore-secrets-path + The path to the ERC-2335 keystore secret passwords Reference: https://eips.ethereum.org/EIPS/eip-2335 + + [env: BOLT_SIDECAR_KEYSTORE_SECRETS_PATH=] + + --keystore-path + Path to the keystores folder. If not provided, the default path is used + + [env: BOLT_SIDECAR_KEYSTORE_PATH=] + + --delegations-path + Path to the delegations file. If not provided, the default path is used + + [env: BOLT_SIDECAR_DELEGATIONS_PATH=] + + --metrics-port + The port on which to expose Prometheus metrics + + [env: BOLT_SIDECAR_METRICS_PORT=] + [default: 3300] + + --disable-metrics + [env: BOLT_SIDECAR_DISABLE_METRICS=] + + -h, --help + Print help (see a summary with '-h') +``` + +## Delegations and signing options for Native and Docker Compose Mode + +As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar +can sign commitments with a delegated set of private keys on behalf of active +Ethereum validators. + +> [!IMPORTANT] +> This is the recommended way to run the Bolt sidecar as it +> doesn't expose the active validator signing keys to any additional risk. + +In order to create these delegation you can use the `bolt-delegations-cli` binary. +If you don't want to use it you can skip the following section. + +### `bolt-delegations-cli` + +`bolt-delegations-cli` is an offline command-line tool for safely generating +delegation and revocation messages signed with a BLS12-381 key for the +[Constraints API](https://docs.boltprotocol.xyz/api/builder) in +[Bolt](https://docs.boltprotocol.xyz/). + +The tool supports two key sources: + +- Local: A BLS private key provided directly from a file. +- Keystore: A keystore file that contains an encrypted BLS private key. + +and outputs a JSON file with the delegation/revocation messages to the provided +`` for the given chain + +Features: + +- Offline usage: Safely generate delegation messages in an offline environment. +- Flexible key source: Support for both direct local BLS private keys and + Ethereum keystore files (ERC-2335 format). +- BLS delegation signing: Sign delegation messages using a BLS secret key and + output the signed delegation in JSON format. + +#### Installation and usage + +Go to the root of the Bolt project you've previously cloned using Git. Enter in +the `bolt-delegations-cli` directory by running `cd bolt-delegations-cli`. + +If you're using the Docker container setup make sure you have +[Rust](https://www.rust-lang.org/tools/install) installed in your system as +well. Then you can build the `bolt-delegations-cli` binary by running: + ```bash -./update-grafana.sh +cargo build --release && mv target/release/bolt-delegations-cli . +``` + +Now you can run the binary by running: + +```bash +./bolt-delegations-cli +``` + +The binary exposes a single `generate` command, which accepts the following +options and subcommands (use `./bolt-delegations-cli generate --help` to see +them): + +```text +Usage: bolt-delegations-cli generate [OPTIONS] --delegatee-pubkey + +Commands: + local Use local private keys to generate the signed messages + keystore Use an EIP-2335 keystore folder to generate the signed messages + help Print this message or the help of the given subcommand(s) + +Options: + --delegatee-pubkey The BLS public key to which the delegation message should be signed [env: DELEGATEE_PUBKEY=] + --out The output file for the delegations [env: OUTPUT_FILE_PATH=] [default: delegations.json] + --chain The chain for which the delegation message is intended [env: CHAIN=] [default: mainnet] [possible values: mainnet, holesky, helder, kurtosis] + --action The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) [env: ACTION=] [default: delegate] [possible values: delegate, revoke] + -h, --help Print help (see more with '--help') +``` + +The environment variables can be also set in a `.env` file. For a reference +example you can check out the `.env.local.example` and the +`.env.keystore.example` + +In the section below you can see a usage example of the binary. + +#### Delegations CLI Example + +1. Using a local BLS private key: + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + local \ + --secret-keys 0xabc123...,0xdef456.. + ``` + +2. Using a Ethereum keystores files and raw password: + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + keystore \ + --path /keys \ + --password myS3cr3tP@ssw0rd + ``` + +3. Using an Ethereum keystores files and secrets folder + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + keystore \ + --path /keys \ + --password-path /secrets + ``` + +When using the `keystore` key source, the `--path` flag should point to the +directory containing the encrypted keypair directories. + +The keystore folder must adhere to the following structure: + +```text +${KEYSTORE_PATH} +|-- 0x81b676591b823270a3284ace7d81cbce2d6cdce55bb0e053874d7e3a08f729453009d3e662ec3130379f43c0f3210b6d +| `-- voting-keystore.json +|-- 0x81ea9f74ef7d935b807474e38954ae3934856219a23e074954b2e860c5a3c400f9aedb42cd27cb4ceb697ca36d1e58cb +| `-- voting-keystore.json +|-- ... + `-- ... +``` + +where the folder names are the public keys and inside every +folder there is a single JSON file containing the keystore file. + +In case of validator-specific passwords (e.g. Lighthouse format) the +`--password-path` flag must be used instead of `--password`, pointing to the +directory containing the password files. + +The passwords folder must adhere to a certain structure as well, as shown below. + ``` +${KEYSTORE_PATH} +|-- 0x81b676591b823270a3284ace7d81cbce2d6cdce55bb0e053874d7e3a08f729453009d3e662ec3130379f43c0f3210b6d +|-- 0x81ea9f74ef7d935b807474e38954ae3934856219a23e074954b2e860c5a3c400f9aedb42cd27cb4ceb697ca36d1e58cb +|-- ... + `-- ... +``` + +That is, the password files should be named after the public key and each file +should just contain one line with the password in plain text. The files +themselves don't need a particular file extension. + +--- + +Now that you have generated the delegation messages you can provide them to the +sidecar using the `--delegations-path` flag (see the +[options](#command-line-options) section). When doing so the sidecar will check if +they're indeed valid messages and will keep in memory the association between +the delegator and the delegatee. + +However in order to sign the commitments you still need to provide the signing +key of the delegatee. There are two ways to do so, as explored in the sections +below. + +### Using a private key directly + +As you can see in the [command line options](#command-line-options) section you +can pass directly the private key as a hex-encoded string to the Bolt sidecar +using the `--private-key` flag. This is the simplest setup and can be used in +case if all the delegations messages point to the same delegatee or if you're +running the sidecar with a single active validator. + +### Using a ERC-2335 Keystore + +The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. +In order to use them you need to provide the `--keystore-path` pointing to the +folder containing the keystore files and the `--keystore-password` or +`keystore-secrets-path` flag pointing to the folder containing the password +file. -In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. +Both the `keys` and `passwords` folders must adhere to the structure outlined +in the [Delegations CLI example](#delegations-cli-example) section. -### Validators +## Avoid restarting the beacon node -Validators must be configured to always prefer builder proposals over their own. Refer to client documentation for the specific configuration flags. -**If this is not set, it could lead to commitment faults**. +As mentioned in the [prerequisites](#prerequisites) section, in order to run the +sidecar correctly it might be necessary to restart your beacon client. That is +because you need to configure the `--builder` flag (or equivalent) to point to +the Bolt sidecar endpoint. -#### Registration +However if you're already running a PBS sidecar like +[MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid +the restart by following this steps when starting the Bolt sidecar: -WIP +1. Set the `--constraints-proxy-port` flag or the + `BOLT_SIDECAR_BUILDER_PROXY_PORT` environment variable to the port previously occupied by + MEV-Boost. +2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it + using another port +3. Set the `--constraints-url` flag or the `BOLT_SIDECAR_CONSTRAINTS_URL` to point to the Bolt MEV-Boost instance. From b854541f7d86284f8fc137b51a1b735026b547b3 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 16:49:18 +0200 Subject: [PATCH 083/272] fix(holesky): add bolt prefix to all services and containers to avoid conflicts --- testnets/holesky/docker-compose.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 2751212df..f88549f40 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,7 +1,7 @@ services: - bolt-sidecar: + bolt-sidecar-holesky: image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 - container_name: bolt-sidecar + container_name: bolt-sidecar-holesky restart: unless-stopped ports: - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) @@ -9,18 +9,18 @@ services: env_file: ./bolt-sidecar.env entrypoint: /bin/sh -c /bolt-sidecar - mev-boost: + bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 - container_name: mev-boost + container_name: bolt-mev-boost-holesky restart: unless-stopped env_file: ./mev-boost.env entrypoint: /bin/sh -c '/app/mev-boost' - prometheus: + bolt-prometheus-holesky: image: prom/prometheus:latest - container_name: prometheus + container_name: bolt-prometheus-holesky ports: - - 9090:9090 + - 49090:49090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json @@ -28,11 +28,11 @@ services: networks: - monitoring_network - grafana: + bolt-grafana-holesky: image: grafana/grafana:latest - container_name: cb_grafana + container_name: bolt-grafana-holesky ports: - - 3000:3000 + - 33000:33000 environment: - GF_SECURITY_ADMIN_PASSWORD=admin volumes: @@ -42,7 +42,7 @@ services: networks: - monitoring_network depends_on: - - prometheus + - bolt-prometheus-holesky logging: driver: none From 8504a90f43749ce8769418f50193c21c33d8880e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 17:34:40 +0200 Subject: [PATCH 084/272] fix(holesky): docker-compose sidecar entrypoint --- testnets/holesky/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index f88549f40..7687aa77e 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -7,7 +7,7 @@ services: - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" env_file: ./bolt-sidecar.env - entrypoint: /bin/sh -c /bolt-sidecar + entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 8887d17dba88d3a0b68abb309340b279121f4191 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 17:35:01 +0200 Subject: [PATCH 085/272] fix(sidecar): .env.example --- bolt-sidecar/.env.example | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index 98bf60852..7621489a6 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -6,7 +6,7 @@ BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 -BOLT_SIDECAR_JWT_HEX= +BOLT_SIDECAR_ENGINE_JWT_HEX= BOLT_SIDECAR_FEE_RECIPIENT= BOLT_SIDECAR_BUILDER_PRIVATE_KEY= @@ -17,17 +17,17 @@ BOLT_SIDECAR_MIN_PRIORITY_FEE= BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 # Chain configs -BOLT_SIDECAR_CHAIN=Holesky +BOLT_SIDECAR_CHAIN=holesky BOLT_SIDECAR_SLOT_TIME=12 -# Signing options -BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= -BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= -BOLT_SIDECAR_CB_SIGNER_URL= -BOLT_SIDECAR_CB_JWT_HEX= -BOLT_SIDECAR_KEYSTORE_PASSWORD= -BOLT_SIDECAR_KEYSTORE_PATH= -BOLT_SIDECAR_DELEGATIONS_PATH= +# Signing options. Uncomment only what you'll use +#BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +#BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +#BOLT_SIDECAR_CB_SIGNER_URL= +#BOLT_SIDECAR_CB_JWT_HEX= +#BOLT_SIDECAR_KEYSTORE_PASSWORD= +#BOLT_SIDECAR_KEYSTORE_PATH= +#BOLT_SIDECAR_DELEGATIONS_PATH= # Metrics BOLT_SIDECAR_METRICS_PORT= From 70fc7d969ac2593d79ef7b1783a92cd04b43843c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 18:20:14 +0200 Subject: [PATCH 086/272] chore(holesky): add simple bash script to create .env stub --- testnets/holesky/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 526089c56..4ea5dc1f0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -124,7 +124,13 @@ containing the necessary environment variables: Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you need a reference, you can use the `.env.example` file in the `bolt-sidecar` - directory as a starting point. For proper configuration of the signing + directory as a starting point. + + ```bash + cat ./bolt-sidecar/.env.example > ./testnets/holesky/bolt-sidecar.env + ``` + + For proper configuration of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) section of this guide. @@ -136,6 +142,10 @@ containing the necessary environment variables: reference, you can use the `.env.example` file in the `mev-boost` directory as a starting point. + ```bash + cat ./mev-boost/.env.example > ./testnets/holesky/mev-boost.env + ``` + If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. From 0197f1b4133a37afa13e5ceacf9c92f9baa64398 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:40:16 +0200 Subject: [PATCH 087/272] chore(sidecar): update Config.example.toml --- bolt-sidecar/Config.example.toml | 87 +++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index e6caad16e..b76ead584 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -1,37 +1,78 @@ # Ethereum Node Connections + PBS URLs -port = 8000 -execution_api_url = "http://localhost:4485" -beacon_api_url = "http://localhost:4400" -engine_api_url = "http://localhost:4451" -constraints_url = "http://localhost:19550" -constraints_proxy_port = 18551 + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +port = 8017 +# Execution client API URL +execution_api_url = "http://localhost:8545" +# URL for the beacon client +beacon_api_url = "http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +engine_api_url = "http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +constraints_proxy_port = 18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +constraints_api_url = "http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") validator_indexes = "0..64" -jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. +engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# The fee recipient address for fallback blocks fee_recipient = "0x0000000000000000000000000000000000000000" +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +# Secret BLS key to sign fallback payloads with builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# Commitments configs -max_commitments = 128 -max_committed_gas = 10000000 -min_priority_fee = 5000000 +# Commitments limits +[limits] +# Max number of commitments to accept per block +max_commitments_per_slot = 128 +# Max committed gas per slot +max_committed_gas_per_slot = 10_000_000 +# Min priority fee to accept for a commitment +min_priority_fee = 4_000_000_000 # 4 Gwei = 4 * 10^9 wei -# Chain configs +# Chain configuration [chain] -chain = "Holesky" +# Chain on which the sidecar is running +chain = "holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] slot_time = 12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) commitment_deadline = 8000 -# Signing options +# Signing options. Uncomment only the signing setup you'll use: +# - single private key -> `constraints_private_key` +# - commit-boost -> `cb_signer_url`, `cb_jwt_hex` +# - keystores -> `keystore_path`, `keystore_password` or `keystore_secrets_path` +# (depending on whether all keystores have the same passwords or not) +# +# If you plan to use delegations, uncomment the option `delegations_path` as +# well. [constraint_signing] -constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -cb_signer_url = "http://localhost:18550" -cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -keystore_password = "password" -keystore_path = "./keys" -delegations_path = "./delegations.json" +# Private key to use for signing constraint messages +# constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +# cb_signer_url = "http://localhost:18551" +# cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# keystore_password = "password" +# keystore_path = "./keys" +# delegations_path = "./delegations.json" -# Metrics +# Telemetry and Metrics [telemetry] -metrics_port = 8001 +metrics_port = 3300 disable_metrics = false From 5942872948ab9e6ffc92b211b5d577be6e9dbfed Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:40:51 +0200 Subject: [PATCH 088/272] chore(sidecar): review CLI options and their defaults --- bolt-sidecar/src/config/chain.rs | 30 ++++++++++++++++++++++-------- bolt-sidecar/src/config/mod.rs | 14 ++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 4ce4f88a6..98edb7248 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -1,4 +1,5 @@ -use std::time::Duration; +use core::fmt; +use std::{fmt::Display, time::Duration}; use clap::{Args, ValueEnum}; use ethereum_consensus::deneb::{compute_fork_data_root, Root}; @@ -25,7 +26,7 @@ pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67]; #[derive(Debug, Clone, Copy, Args, Deserialize)] pub struct ChainConfig { /// Chain on which the sidecar is running - #[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value = "mainnet")] + #[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value_t = Chain::Mainnet)] chain: Chain, /// The deadline in the slot at which the sidecar will stop accepting /// new commitments for the next block (parsed as milliseconds). @@ -65,6 +66,24 @@ pub enum Chain { Kurtosis, } +impl Chain { + /// Get the chain name for the given chain. + pub fn name(&self) -> &'static str { + match self { + Chain::Mainnet => "mainnet", + Chain::Holesky => "holesky", + Chain::Helder => "helder", + Chain::Kurtosis => "kurtosis", + } + } +} + +impl Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name()) + } +} + impl ChainConfig { /// Get the chain ID for the given chain. pub fn chain_id(&self) -> u64 { @@ -78,12 +97,7 @@ impl ChainConfig { /// Get the chain name for the given chain. pub fn name(&self) -> &'static str { - match self.chain { - Chain::Mainnet => "mainnet", - Chain::Holesky => "holesky", - Chain::Helder => "helder", - Chain::Kurtosis => "kurtosis", - } + self.chain.name() } /// Get the slot time for the given chain in seconds. diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index fcade3aee..1b9e64b76 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -23,11 +23,13 @@ use limits::LimitsOpts; use crate::common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}; -/// Default port for the JSON-RPC server exposed by the sidecar. -pub const DEFAULT_RPC_PORT: u16 = 8000; +/// Default port for the JSON-RPC server exposed by the sidecar supporting the Commitments API. +/// +/// 8017 -> BOLT :) +pub const DEFAULT_RPC_PORT: u16 = 8017; -/// Default port for the Constraints proxy server. -pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18551; +/// Default port for the Constraints proxy server, binded to the default port used by MEV-Boost. +pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18550; /// Command-line options for the Bolt sidecar #[derive(Debug, Parser, Deserialize)] @@ -52,7 +54,7 @@ pub struct Opts { #[clap( long, env = "BOLT_SIDECAR_CONSTRAINTS_API_URL", - default_value = "http://localhost:3030" + default_value = "http://localhost:18551" )] pub constraints_api_url: Url, /// The port from which the Bolt sidecar will receive Builder-API requests from the @@ -68,7 +70,7 @@ pub struct Opts { /// - a comma-separated list of indexes (e.g. "1,2,3,4") /// - a contiguous range of indexes (e.g. "1..4") /// - a mix of the above (e.g. "1,2..4,6..8") - #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES", default_value_t)] + #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES")] pub validator_indexes: ValidatorIndexes, /// The JWT secret token to authenticate calls to the engine API. /// From 185c2a690de06bb84f0dddf6f2cdcea2566fe6c9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:51:34 +0200 Subject: [PATCH 089/272] chore(sidecar): add Config.toml to .gitignore --- bolt-sidecar/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 2e73cb821..8fd11d943 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -2,3 +2,4 @@ target/ .env* !.env.example bolt-sidecar* +Config.toml From af4dc936b74c5898a5274ea29d872b509535c996 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:51:52 +0200 Subject: [PATCH 090/272] feat(sidecar): allow reading TOML config from default path --- bolt-sidecar/bin/sidecar.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index c7fbf60cc..6903fffbd 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,13 +1,19 @@ +use std::fs; + use clap::Parser; use eyre::{bail, Result}; use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; +pub const TOML_CONFIG_DEFAULT_PATH: &str = "./Config.toml"; + #[tokio::main] async fn main() -> Result<()> { let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") { Opts::parse_from_toml(config_path.as_str())? + } else if fs::exists(TOML_CONFIG_DEFAULT_PATH).is_ok_and(|exists| exists) { + Opts::parse_from_toml(TOML_CONFIG_DEFAULT_PATH)? } else { Opts::parse() }; From 68ab07a9b3688ab26868ad70f80de2bb4a5a55d9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:00:43 +0200 Subject: [PATCH 091/272] chore(holesky): add *.toml to .gitignore --- testnets/holesky/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/.gitignore b/testnets/holesky/.gitignore index ef456d924..71c6e0c11 100644 --- a/testnets/holesky/.gitignore +++ b/testnets/holesky/.gitignore @@ -1,2 +1,3 @@ *.env* !*.env.example +*.toml From 33f341d54185dee304f65094442796b5530842fc Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:03:14 +0200 Subject: [PATCH 092/272] fix(holesky): broken links in README, use TOML config file instead of .env for the sidecar --- testnets/holesky/README.md | 49 +++++++++++++++----------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4ea5dc1f0..47c849756 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -122,17 +122,17 @@ containing the necessary environment variables: 1. **Bolt Sidecar Configuration:** - Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you - need a reference, you can use the `.env.example` file in the `bolt-sidecar` + Create a `bolt-sidecar.toml` file in the `testnets/holesky` directory. If you + need a reference, you can use the `Config.example.toml` file in the `bolt-sidecar` directory as a starting point. ```bash - cat ./bolt-sidecar/.env.example > ./testnets/holesky/bolt-sidecar.env + cat ./bolt-sidecar/Config.example.toml > ./testnets/holesky/bolt-sidecar.toml ``` For proper configuration of the signing options, please refer to the [Delegations and - Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) + Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. 2. **MEV-Boost Configuration:** @@ -316,15 +316,13 @@ can be found by running `./bolt-sidecar --help`, or you can find them in the #### Configuration file -A configuration file can be either a `.env` file or a `.toml` file. If you use -`.env` file you can find a `.env.example` file in the repository that you can -use as a template. - -For a `.toml` file you can use the template in the `Config.example.toml`. Lastly -you need to specify the path of the configuration file by setting the +You can use a `Config.toml` file to configure the sidecar, for which you can +find a template in the `Config.example.toml` file. +If you wish to place the configuration file in another folder you need to +specify the path of the configuration file by setting the `BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. -Please read the section on [delegations and signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) to configure such sidecar options properly. After you've set up the configuration file you can run the Bolt sidecar with @@ -517,15 +515,14 @@ with the `--help` flag: ``` Command-line options for the Bolt sidecar -Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > +Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: --port - Port to listen on for incoming JSON-RPC requests of the Commitments API - This port should be open on your firewall in order to receive external requests! + Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] - [default: 8000] + [default: 8017] --execution-api-url Execution client API URL @@ -540,34 +537,28 @@ Options: [default: http://localhost:5052] --engine-api-url - Execution client Engine API URL. This is needed for fallback block - building and must be a synced Geth node + Execution client Engine API URL. This is needed for fallback block building and must be a synced Geth node [env: BOLT_SIDECAR_ENGINE_API_URL=] [default: http://localhost:8551] --constraints-api-url - URL to forward the constraints produced by the Bolt sidecar to a - server supporting the Constraints API, such as an MEV-Boost fork + URL to forward the constraints produced by the Bolt sidecar to a server supporting the Constraints API, such as an MEV-Boost fork [env: BOLT_SIDECAR_CONSTRAINTS_API_URL=] - [default: http://localhost:3030] + [default: http://localhost:18551] --constraints-proxy-port The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] - [default: 18551] + [default: 18550] --validator-indexes - Validator indexes of connected validators that the sidecar should - accept commitments on behalf of. Accepted values: - - a comma-separated list of indexes (e.g. "1,2,3,4") - - a contiguous range of indexes (e.g. "1..4") - - a mix of the above (e.g. "1,2..4,6..8") + Validator indexes of connected validators that the sidecar should accept commitments on behalf of. Accepted values: - a comma-separated list of indexes (e.g. "1,2,3,4") - a contiguous range of indexes (e.g. "1..4") - a mix of the + above (e.g. "1,2..4,6..8") [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] - [default: ] --engine-jwt-hex The JWT secret token to authenticate calls to the engine API. @@ -587,9 +578,7 @@ Options: [env: BOLT_SIDECAR_BUILDER_PRIVATE_KEY=] --commitment-private-key - Secret ECDSA key to sign commitment messages with. The public key - associated to it must be then used when registering the operator in the - `BoltManager` contract + Secret ECDSA key to sign commitment messages with. The public key associated to it must be then used when registering the operator in the `BoltManager` contract [env: BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY=] From 743f032b2a7fd9a5f52e6e5175783eed98585cb4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:13:11 +0200 Subject: [PATCH 093/272] chore(holesky): update docker-compose.yml --- testnets/holesky/docker-compose.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 7687aa77e..830402af8 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -4,10 +4,11 @@ services: container_name: bolt-sidecar-holesky restart: unless-stopped ports: - - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) - - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" - env_file: ./bolt-sidecar.env - entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar + - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) + - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" + entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" + volumes: + - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 628b443a5b915198f9c38eac50ef7b6743e7db5e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:09:17 +0200 Subject: [PATCH 094/272] fix(sidecar): min_priority_fee is now u128 and not NonZero This caused issue when parsing from TOML string. Also if that value is zero is not a problem actually --- bolt-sidecar/src/config/limits.rs | 4 ++-- bolt-sidecar/src/state/execution.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bolt-sidecar/src/config/limits.rs b/bolt-sidecar/src/config/limits.rs index b33561072..7e50d24b8 100644 --- a/bolt-sidecar/src/config/limits.rs +++ b/bolt-sidecar/src/config/limits.rs @@ -31,7 +31,7 @@ pub struct LimitsOpts { env = "BOLT_SIDECAR_MIN_PRIORITY_FEE", default_value_t = LimitsOpts::default().min_priority_fee )] - pub min_priority_fee: NonZero, + pub min_priority_fee: u128, } impl Default for LimitsOpts { @@ -41,7 +41,7 @@ impl Default for LimitsOpts { .expect("Valid non-zero"), max_committed_gas_per_slot: NonZero::new(DEFAULT_MAX_COMMITTED_GAS) .expect("Valid non-zero"), - min_priority_fee: NonZero::new(DEFAULT_MIN_PRIORITY_FEE).expect("Valid non-zero"), + min_priority_fee: DEFAULT_MIN_PRIORITY_FEE, } } } diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index b82418317..f6e4262d9 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -299,7 +299,7 @@ impl ExecutionState { } // Ensure max_priority_fee_per_gas is greater than or equal to min_priority_fee - if !req.validate_min_priority_fee(max_basefee, self.limits.min_priority_fee.get()) { + if !req.validate_min_priority_fee(max_basefee, self.limits.min_priority_fee) { return Err(ValidationError::MaxPriorityFeePerGasTooLow); } @@ -764,7 +764,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(200000000).unwrap(), // 0.2 gwei + min_priority_fee: 200000000, // 0.2 gwei }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -803,7 +803,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2000000000).unwrap(), + min_priority_fee: 2000000000, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -834,7 +834,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -876,7 +876,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -923,7 +923,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -1061,7 +1061,7 @@ mod tests { let limits: LimitsOpts = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(1000000000).unwrap(), + min_priority_fee: 1000000000, }; let mut state = ExecutionState::new(client.clone(), limits).await?; From 9d557243c4075eed90de52ab67436b6514b304a9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:08 +0200 Subject: [PATCH 095/272] fix(holesky): cat -> cp in README --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 47c849756..1e75c3cb9 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -127,7 +127,7 @@ containing the necessary environment variables: directory as a starting point. ```bash - cat ./bolt-sidecar/Config.example.toml > ./testnets/holesky/bolt-sidecar.toml + cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml ``` For proper configuration of the signing @@ -143,7 +143,7 @@ containing the necessary environment variables: starting point. ```bash - cat ./mev-boost/.env.example > ./testnets/holesky/mev-boost.env + cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env ``` If you prefer not to restart your beacon node, follow the instructions in the From 02865247a29c66257ea0c1b1b3526e055b489afc Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:34 +0200 Subject: [PATCH 096/272] chore(holesky): delegations flag in README --- testnets/holesky/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 1e75c3cb9..9d91de16e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -742,6 +742,11 @@ Options: -h, --help Print help (see more with '--help') ``` +> [!TIP] +> If you're using the Docker Compose Mode please don't set the `--out` flag and +> provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` +> file. + The environment variables can be also set in a `.env` file. For a reference example you can check out the `.env.local.example` and the `.env.keystore.example` From b402cdb1bdfdc13dea562017f2c8786089389ba8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:52 +0200 Subject: [PATCH 097/272] fix(holesky): provide delegations cli volume --- testnets/holesky/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 830402af8..86d2c3dce 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -9,6 +9,7 @@ services: entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" volumes: - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" + - "../../bolt-delegations-cli/delegations.json:/etc/delegations.json" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 55f58cc7e5e8e8beeed675f5ef28aa46219e6823 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 17:17:35 +0200 Subject: [PATCH 098/272] docs(holesky): update instructions --- testnets/holesky/README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 9d91de16e..4b80c8241 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -5,7 +5,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) -* [Setup](#setup) +* [Off-Chain Setup](#off-chain-setup) * [Docker Mode (recommended)](#docker-mode-(recommended)) * [Commit-Boost Mode](#commit-boost-mode) * [Native Mode (advanced)](#native-mode-(advanced)) @@ -13,7 +13,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) * [Observability](#observability) -* [Register your validators on-chain on the Bolt Registry](#register-your-validators-on-chain-on-the-bolt-registry) +* [On-Chain Registration](#on-chain-registration) * [Validator Registration](#validator-registration) * [Bolt Network Entrypoint](#bolt-network-entrypoint) * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) @@ -33,7 +33,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky # Prerequisites -In order to run Bolt you need some components already installed and running in +In order to run Bolt you need some components already installed and running on your system. **A synced Geth client:** @@ -76,13 +76,7 @@ client implementations to download and run them. The Bolt sidecar requires signing keys from active Ethereum validators, or authorized delegates acting on their behalf, to issue and sign preconfirmations. -**LST collateral:** - -For Holesky in order to provide credible proposer commitments it is necessary to -restake 1 ether worth of ETH derivatives per validator in either the Symbiotic -or the EigenLayer protocol. - -# Setup +# Off-Chain Setup There are various way to run the Bolt Sidecar depending on what infrastructure you want to use and your preferred signing methods: @@ -157,8 +151,7 @@ cd testnets/holesky && docker compose up -d ``` The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards, which can -be found in the `grafana` directory. +Prometheus and Grafana. It also comes with some pre-built dashboards which you can find at `http://localhost:3000`. ## Commit-Boost Mode @@ -344,7 +337,7 @@ To update these dashboards, run the following command: In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. -# Register your validators on-chain on the Bolt Registry +# On-Chain Registration Once you are successfully running the Bolt sidecar you need to register on-chain on the Bolt Registry to successfully receive preconfirmation requests from users From afc0dc5af1e34bfe36751efe79733f92042a5e11 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 17:46:17 +0200 Subject: [PATCH 099/272] docs(holesky): smol chagnes --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4b80c8241..b4fea395d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -104,7 +104,7 @@ First, make sure to have both [Docker](https://docs.docker.com/engine/install/), Then clone the Bolt repository by running: ```bash -git clone --branch v0.3.0 htts://github.com/chainbound/bolt.git && cd bolt +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git && cd bolt ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt From 619d9f0f7c93c3f5542281fb160712b61a7495ed Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 18:26:05 +0200 Subject: [PATCH 100/272] docs(holesky): smol changes --- testnets/holesky/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b4fea395d..af896fa11 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -67,6 +67,9 @@ client implementations to download and run them. > --builder-fallback-disable-checks > ``` > +> In other clients like Vouch, the same can be achieved by setting the `builder-boost-factor` to a large value +> like 18446744073709551615. +> > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon > Node](#avoid-restarting-the-beacon-node) for more details. @@ -124,17 +127,20 @@ containing the necessary environment variables: cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml ``` - For proper configuration of the signing - options, please refer to the [Delegations and + Next up, fill out all the values that are required. For proper configuration + of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. 2. **MEV-Boost Configuration:** - Similarly, create a `mev-boost.env` file in the - `testnets/holesky` folder to configure the MEV-Boost service. If you need a - reference, you can use the `.env.example` file in the `mev-boost` directory as a - starting point. + Copy over the example environment file: + + ```bash + cp ../../mev-boost/.env.example mev-boost.env + ``` + + Then configure the file accordingly. ```bash cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env From a44016b5b583be352a1246f84d71504c2cff3fb3 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 18:45:07 +0200 Subject: [PATCH 101/272] docs(holesky): validator registration script --- testnets/holesky/README.md | 55 +++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index af896fa11..34e41aaff 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -350,10 +350,10 @@ on the Bolt Registry to successfully receive preconfirmation requests from users and RPCs. This step is needed to provide economic security to your commitments. -In order to do that you need some collateral in the form of whitelisted Liquid -Staking Token (LST) that needs to be restaked in either the Symbiotic or -EigenLayer protocol. Bolt is compatible with ETH derivatives on Holesky. Here -are references to the supported tokens on both restaking protocols: +In order to do that you need some collateral in the form of whitelisted ETH derivative +tokens that need to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens +on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) @@ -376,6 +376,24 @@ protocols. > public key associated to the private key used to sign commitments with the > Bolt Sidecar (the `--commitment-private-key` flag). +## Prerequisites + +- Install the Foundry tools: + +```bash +curl -L https://foundry.paradigm.xyz | bash +source $HOME/.bashrc +foundryup +``` + +- Clone the Bolt repo and navigate to the [contracts](https://github.com/chainbound/bolt/tree/unstable/bolt-contracts) directory: + +```bash +git clone https://github.com/chainbound/bolt +cd bolt-contracts +forge install +``` + ## Validator Registration The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for @@ -395,6 +413,35 @@ Until the Pectra hard-fork will be activated, the contract will also expose a `r that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and will allow us to test the registration flow in a controlled environment. +Note that the account initiating the registration will be the `controller` account for those validators. Only the `controller` can then +deregister validator or change any preferences. + +### Registration Steps + +> [!NOTE] +> All of these scripts can be simulated on a Holesky fork using Anvil with the following command: +> ```bash +> anvil --fork-url https://holesky.drpc.org +> ``` +> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in all of the `forge` commands below. + +To register your validators, we provide the following Foundry script: [`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` directory. + +- First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that +both `maxCommittedGasLimit` and `authorizedOperator` must reflect the values specified in previous steps, during the configuration of the sidecar. +`pubkeys` should be configured with all of the validator public keys that you wish to register. + +- Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. +This controller key will be used to run the script and will mark the corresponding account as the controller account for these validators. + +- Finally, run the script: +```bash +forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast +``` + +If the script executed succesfully, your validators were registered. + ## Bolt Network Entrypoint The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that From d0300363d1ca8498fd4d89680469f94e994b5ab4 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Tue, 22 Oct 2024 10:25:18 +0200 Subject: [PATCH 102/272] docs(holesky): operator registration --- testnets/holesky/README.md | 115 +++++++++++++------------------------ 1 file changed, 41 insertions(+), 74 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 34e41aaff..72988c64d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -444,7 +444,7 @@ If the script executed succesfully, your validators were registered. ## Bolt Network Entrypoint -The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. @@ -457,44 +457,46 @@ Key features include: Specific functionalities about the restaking protocols are handled inside the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. -### Symbiotic Integration guide for Staking Pools +## Operator Registration +In this section we outline how to register as an operator, i.e. an entity uniquely identified by an Ethereum address and responsible for +duties like signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in +the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically +connect validators to an on-chain address associated with some stake, which is what the operator is. -As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. -If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) -on how to spin up a Vault and start receiving stake from your node operators. +**In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will +be read by the Forge scripts for registering operators and needs to be set correctly. You also have to invoke the scripts from the +[`bolt-contracts`](../../bolt-contracts) directory. -Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: - -1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. -2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. -3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. -4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. -5. You can now start approving operators that opt in to your vault directly in Symbiotic. -6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. - -### Symbiotic Integration guide for Operators +### Symbiotic Registration Steps As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide -commitments on their behalf. +commitments on their behalf. The opt-in process requires the following steps: +#### External Steps + +> [!NOTE] The network and supported vault addresses can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). + 1. register in Symbiotic with `OperatorRegistry.registerOperator()`. 2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. 3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. -4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. -5. get approved by the vault. -6. start providing commitments with the stake provided by the vault. -### EigenLayer Integration Guide for Node Operators and Solo Stakers +#### Internal Steps -> [!NOTE] -> Without loss of generality, we will assume the reader of this guide is a Node -> Operator (NO), since the same steps apply to solo stakers. -> As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) -> in the Bolt AVS built on top of EigenLayer. This requires -> running an Ethereum validator and the Bolt sidecar in order issue -> preconfirmations. +Run the provided Forge script to register a Symbiotic operator: + +```bash +forge script script/holesky/validators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +``` + +If all goes well, your Symbiotic operator was registered into Bolt. + +### EigenLayer Registration Steps + +#### External Steps + +> [!NOTE] The supported strategies can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). The Operator will be represented by an Ethereum address that needs to follow the standard procedure outlined in the @@ -502,53 +504,18 @@ to follow the standard procedure outlined in the 1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). -2. As an Ethereum validator offering precofirmations a NO needs some collateral in - order to be economically credible. In order to do that, some entities known as a "stakers" - need to deposit whitelisted Liquid Staking Tokens (LSTs) - into an appropriate "Strategy" associated to the LST via the - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), - so that the Operator has a `min_amount` (for Holesky 1 ether) of collateral associated to it. - Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract - in the `getWhitelistedCollaterals` function. - Note that NOs and stakers can be two different entities - _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. - -3. After the stakers have deposited their collateral into a strategy they need - to choose you as their operator. To do that, they need to call the function - [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). - -4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. - This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. - The payload is a signature whose digest consists of: - - 1. your operator address - 2. the `BoltEigenLayerMiddleware` contract address - 3. a salt - 4. an expiry 2. - - The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) - with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, - the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. - -Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` -contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a -registered operator and so that they can forward you preconfirmation requests. - -The steps required are the following: - -1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. - If you own more than one validator public key, - you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. - The `authorizedOperator` argument must be the same Ethereum address used for - opting into EigenLayer as an Operator. - -2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling - the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network - and allows you to manage operations effectively, such as pausing or resuming - your service. - -3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. - This ensures that your restaked assets are correctly integrated with Bolt’s system. +2. You can then use the same account to deposit into a supported EigenLayer strategy using [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). This will add the deposit into the collateral of the operator +so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. + +#### Internal Steps + +Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: + +```bash +forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +``` + +If all goes well, your EigenLayer operator was registered into Bolt. # Reference From b285e338bb1a34accd7baa0879b6f3411e4f20c7 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 13:24:53 +0200 Subject: [PATCH 103/272] chore(holesky): README fmt --- testnets/holesky/README.md | 160 ++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 64 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 72988c64d..f3fe42e53 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -15,10 +15,11 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Observability](#observability) * [On-Chain Registration](#on-chain-registration) * [Validator Registration](#validator-registration) + * [Registration Steps](#registration-steps) * [Bolt Network Entrypoint](#bolt-network-entrypoint) - * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) - * [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) - * [EigenLayer Integration Guide for Node Operators and Solo Stakers](#eigenlayer-integration-guide-for-node-operators-and-solo-stakers) + * [Operator Registration](#operator-registration) + * [Symbiotic Registration Steps](#symbiotic-registration-steps) + * [EigenLayer Registration Steps](#eigenlayer-registration-steps) * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) @@ -67,8 +68,9 @@ client implementations to download and run them. > --builder-fallback-disable-checks > ``` > -> In other clients like Vouch, the same can be achieved by setting the `builder-boost-factor` to a large value -> like 18446744073709551615. +> In other clients like Vouch, the same can be achieved by setting the +> `builder-boost-factor` to a large value like `18446744073709551615` (`2**64 - +1`). > > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon @@ -134,18 +136,14 @@ containing the necessary environment variables: 2. **MEV-Boost Configuration:** - Copy over the example environment file: - - ```bash - cp ../../mev-boost/.env.example mev-boost.env - ``` - - Then configure the file accordingly. + Copy over the example configuration file: ```bash cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env ``` + Then configure it accordingly. + If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. @@ -376,7 +374,7 @@ protocols. > public key associated to the private key used to sign commitments with the > Bolt Sidecar (the `--commitment-private-key` flag). -## Prerequisites +**Prerequisites** - Install the Foundry tools: @@ -396,46 +394,60 @@ forge install ## Validator Registration -The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for -validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only +point of entry for validators to signal their intent to participate in Bolt +Protocol and authenticate with their BLS private key. The registration process includes the following steps: -1. Validator signs a message with their BLS private key. This is required to prove that the - validator private key is under their control and that they are indeed its owner. +1. Validator signs a message with their BLS private key. This is required to + prove that the validator private key is under their control and that they are + indeed its owner. 2. Validator calls the `registerValidator` function providing: 1. Their BLS public key 2. The BLS signature of the registration message 3. The address of the authorized collateral provider 4. The address of the authorized operator -Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function -that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and -will allow us to test the registration flow in a controlled environment. +Until the Pectra hard-fork will be activated, the contract will also expose a +`registerValidatorUnsafe` function that will not check the BLS signature. This +is gated by a feature flag that will be turned off post-Pectra and will allow us +to test the registration flow in a controlled environment. -Note that the account initiating the registration will be the `controller` account for those validators. Only the `controller` can then -deregister validator or change any preferences. +Note that the account initiating the registration will be the `controller` +account for those validators. Only the `controller` can then deregister +validator or change any preferences. ### Registration Steps > [!NOTE] -> All of these scripts can be simulated on a Holesky fork using Anvil with the following command: -> ```bash -> anvil --fork-url https://holesky.drpc.org -> ``` -> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in all of the `forge` commands below. - -To register your validators, we provide the following Foundry script: [`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). -Note that in order to run these scripts, you must be in the `bolt-contracts` directory. - -- First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that -both `maxCommittedGasLimit` and `authorizedOperator` must reflect the values specified in previous steps, during the configuration of the sidecar. -`pubkeys` should be configured with all of the validator public keys that you wish to register. - -- Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. -This controller key will be used to run the script and will mark the corresponding account as the controller account for these validators. +> All of these scripts can be simulated on a Holesky fork using Anvil with the +> following command: +> +> `bash anvil --fork-url https://holesky.drpc.org ` +> +> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in +> all of the `forge` commands below. + +To register your validators, we provide the following Foundry script: +[`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` +directory. + +- First, configure + [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) + to your requirements. Note that both `maxCommittedGasLimit` and + `authorizedOperator` must reflect the values specified in previous steps, during + the configuration of the sidecar. `pubkeys` should be configured with all of the + validator public keys that you wish to register. + +- Next up, decide on a controller account and save the key in an environment + variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run + the script and will mark the corresponding account as the controller account for + these validators. - Finally, run the script: + ```bash forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast ``` @@ -444,8 +456,9 @@ If the script executed succesfully, your validators were registered. ## Bolt Network Entrypoint -The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that -integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) +contract is a crucial component of Bolt that integrates with restaking +ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. Key features include: @@ -454,35 +467,45 @@ Key features include: 2. Integration with Symbiotic 3. Integration with Eigenlayer -Specific functionalities about the restaking protocols are handled inside -the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. +Specific functionalities about the restaking protocols are handled inside the +`IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and +`BoltEigenlayerMiddleware`. ## Operator Registration -In this section we outline how to register as an operator, i.e. an entity uniquely identified by an Ethereum address and responsible for -duties like signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in -the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically -connect validators to an on-chain address associated with some stake, which is what the operator is. -**In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will -be read by the Forge scripts for registering operators and needs to be set correctly. You also have to invoke the scripts from the -[`bolt-contracts`](../../bolt-contracts) directory. +In this section we outline how to register as an operator, i.e. an entity +uniquely identified by an Ethereum address and responsible for duties like +signing commitments. Note that in Bolt, there is no real separation between +validators and an operator. An operator is only real in the sense that its +private key will be used to sign commitments on the corresponding validators' +sidecars. However, we need a way to logically connect validators to an on-chain +address associated with some stake, which is what the operator is. + +**In the next sections we assume you have saved the private key corresponding to +the operator address in `$OPERATOR_SK`.** This private key will be read by the +Forge scripts for registering operators and needs to be set correctly. You also +have to invoke the scripts from the [`bolt-contracts`](../../bolt-contracts) +directory. ### Symbiotic Registration Steps -As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide -commitments on their behalf. +As an operator, you will need to opt-in to the Bolt Network and any Vault that +trusts you to provide commitments on their behalf. The opt-in process requires the following steps: -#### External Steps +**External Steps** -> [!NOTE] The network and supported vault addresses can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +> [!NOTE] +> The network and supported vault addresses can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). 1. register in Symbiotic with `OperatorRegistry.registerOperator()`. -2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +2. opt-in to the Bolt network with + `OperatorNetworkOptInService.optIn(networkAddress)`. 3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. -#### Internal Steps +**Internal Steps** Run the provided Forge script to register a Symbiotic operator: @@ -494,22 +517,31 @@ If all goes well, your Symbiotic operator was registered into Bolt. ### EigenLayer Registration Steps -#### External Steps +**External Steps** -> [!NOTE] The supported strategies can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +> [!NOTE] +> The supported strategies can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -The Operator will be represented by an Ethereum address that needs -to follow the standard procedure outlined in the -[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: +The Operator will be represented by an Ethereum address that needs to follow the +standard procedure outlined in the [EigenLayer +documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go +through the steps: -1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). +1. As an Operator, you register into EigenLayer using + [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). -2. You can then use the same account to deposit into a supported EigenLayer strategy using [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). This will add the deposit into the collateral of the operator -so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. +2. You can then use the same account to deposit into a supported EigenLayer + strategy using + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). + This will add the deposit into the collateral of the operator so that Bolt can + read it. Note that you need to deposit a minimum of `1 ether` of the strategies + underlying token in order to opt in. -#### Internal Steps +**Internal Steps** -Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: +Set the operator private key to an `OPERATOR_SK` environment variable, and then +run the following Forge script from the `bolt-contracts` directory: ```bash forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast From c0dbf4caf6e0340a3cf458f35227bb0c2be4c496 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 13:51:03 +0200 Subject: [PATCH 104/272] chore(holesky): validator controller reference on README --- testnets/holesky/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index f3fe42e53..531f3d982 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -443,8 +443,9 @@ directory. - Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run - the script and will mark the corresponding account as the controller account for - these validators. + the script and will mark the corresponding account as the [controller + account](https://github.com/chainbound/bolt/blob/06bdd8e75d759d91f6178ad73f962b1f4ad43fd8/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol#L18-L19) + for these validators. - Finally, run the script: From 4648dafb924770a094fad7a010e374d72779f7ac Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 14:04:01 +0200 Subject: [PATCH 105/272] fix(holesky): stale flags in README --- testnets/holesky/README.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 531f3d982..fb6594a4e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -888,17 +888,21 @@ below. As you can see in the [command line options](#command-line-options) section you can pass directly the private key as a hex-encoded string to the Bolt sidecar -using the `--private-key` flag. This is the simplest setup and can be used in +using the `--constraint-private-key` flag (or `constraint_private_key` in the +TOML file). + +This is the simplest setup and can be used in case if all the delegations messages point to the same delegatee or if you're running the sidecar with a single active validator. ### Using a ERC-2335 Keystore -The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. -In order to use them you need to provide the `--keystore-path` pointing to the -folder containing the keystore files and the `--keystore-password` or -`keystore-secrets-path` flag pointing to the folder containing the password -file. +The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) +keystores for loading signing keypairs. In order to use them you need to provide +the `--keystore-path` pointing to the folder containing the keystore files and +the `--keystore-password` or `keystore-secrets-path` flag pointing to the folder +containing the password file (in the TOML configuration file these are the +`keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). Both the `keys` and `passwords` folders must adhere to the structure outlined in the [Delegations CLI example](#delegations-cli-example) section. @@ -914,9 +918,9 @@ However if you're already running a PBS sidecar like [MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid the restart by following this steps when starting the Bolt sidecar: -1. Set the `--constraints-proxy-port` flag or the - `BOLT_SIDECAR_BUILDER_PROXY_PORT` environment variable to the port previously occupied by - MEV-Boost. +1. Set the `--constraints-proxy-port` flag (the `constraints_proxy_port` option + in the TOML file) to the port previously occupied by MEV-Boost. 2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it using another port -3. Set the `--constraints-url` flag or the `BOLT_SIDECAR_CONSTRAINTS_URL` to point to the Bolt MEV-Boost instance. +3. Set the `--constraints-api-url` flag (or the `constraints_api_url` in the + TOML file) to point to the Bolt MEV-Boost instance. From 23beddf741f846a167db4ba3cec2a5350d7969e4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 14:04:19 +0200 Subject: [PATCH 106/272] chore(sidecar): mark required fields in the Config.example.toml --- bolt-sidecar/Config.example.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index b76ead584..2bb86003b 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -21,18 +21,23 @@ constraints_api_url = "http://localhost:18551" # - a comma-separated list of indexes (e.g. "1,2,3,4") # - a contiguous range of indexes (e.g. "1..4") # - a mix of the above (e.g. "1,2..4,6..8") +# REQUIRED validator_indexes = "0..64" # The JWT secret token to authenticate calls to the engine API. It can be # either be a hex-encoded string or a file path to a file containing the # hex-encoded secret. +# REQUIRED engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" # The fee recipient address for fallback blocks +# REQUIRED fee_recipient = "0x0000000000000000000000000000000000000000" # Secret ECDSA key to sign commitment messages with. The public key associated # to it must be then used when registering the operator in the `BoltManager` # contract +# REQUIRED commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" # Secret BLS key to sign fallback payloads with +# REQUIRED builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" # Commitments limits @@ -69,6 +74,7 @@ commitment_deadline = 8000 # cb_signer_url = "http://localhost:18551" # cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" # keystore_password = "password" +# keystore_secrets_path = "./secrets" # keystore_path = "./keys" # delegations_path = "./delegations.json" From 6b92e59a0fb23566d61b4231677d41cde084e1df Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 10:31:36 +0200 Subject: [PATCH 107/272] fix(holesky): wrong code links and snippets in README --- testnets/holesky/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index fb6594a4e..fa1a31cfd 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -424,9 +424,9 @@ validator or change any preferences. > All of these scripts can be simulated on a Holesky fork using Anvil with the > following command: > -> `bash anvil --fork-url https://holesky.drpc.org ` +> `anvil --fork-url https://holesky.drpc.org --port 8545` > -> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in +> In order to use this local fork, replace `$HOLESKY_RPC` with `localhost:8545` in > all of the `forge` commands below. To register your validators, we provide the following Foundry script: @@ -530,11 +530,11 @@ documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: 1. As an Operator, you register into EigenLayer using - [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/DelegationManager.sol#L107-L119). 2. You can then use the same account to deposit into a supported EigenLayer strategy using - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). This will add the deposit into the collateral of the operator so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. From bbf670a31ebbce22dccae85c0d47cbbb735418ac Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 17:41:11 +0200 Subject: [PATCH 108/272] feat(holesky): test commit grafana dashboard and prometheus setup --- .../grafana/dashboards/bolt_dashboard.json | 740 ++++++++++++++++-- .../grafana/datasources/datasources.yml | 6 +- testnets/holesky/targets.json | 14 + 3 files changed, 686 insertions(+), 74 deletions(-) create mode 100644 testnets/holesky/targets.json diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json index 886ddfe02..dce85acb0 100644 --- a/testnets/holesky/grafana/dashboards/bolt_dashboard.json +++ b/testnets/holesky/grafana/dashboards/bolt_dashboard.json @@ -15,36 +15,144 @@ } ] }, - "description": "Metrics related to the bolt-sidecar and bolt-boost.", "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 3, + "id": 2, "links": [], + "liveNow": false, "panels": [ { - "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 1, - "w": 24, + "h": 8, + "w": 12, "x": 0, "y": 0 }, - "id": 2, - "panels": [], - "title": "Bolt sidecar", - "type": "row" + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transactions Preconfirmed", + "type": "timeseries" }, { "datasource": { - "default": true, "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { - "mode": "thresholds" + "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", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } }, "mappings": [], "thresholds": { @@ -53,6 +161,10 @@ { "color": "green", "value": null + }, + { + "color": "red", + "value": 80 } ] } @@ -62,67 +174,130 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, - "y": 1 + "x": 12, + "y": 0 }, - "id": 1, + "id": 10, "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + "tooltip": { + "mode": "single", + "sort": "none" + } }, - "pluginVersion": "11.2.0", "targets": [ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, - "disableTextWrap": false, "editorMode": "builder", - "expr": "bolt_sidecar_latest_head", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "expr": "bolt_sidecar_validation_errors", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "Latest head", - "type": "stat" + "title": "Invalid Transactions Reasons", + "type": "timeseries" }, { - "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 1, - "w": 24, + "h": 8, + "w": 12, "x": 0, - "y": 9 + "y": 8 }, - "id": 4, - "panels": [], - "title": "Bolt boost", - "type": "row" + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Remote Blocks Proposed", + "type": "timeseries" }, { "datasource": { - "default": true, "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { @@ -130,13 +305,11 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, - "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -145,8 +318,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -182,10 +354,10 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, - "y": 10 + "x": 12, + "y": 8 }, - "id": 3, + "id": 4, "options": { "legend": { "calcs": [], @@ -202,25 +374,451 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, - "disableTextWrap": false, "editorMode": "builder", - "expr": "cb_pbs_constraints_cache_size", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "{{__name__}}", + "expr": "bolt_sidecar_inclusion_commitments_received", + "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "Constraints cache size", + "title": "Inclusion Commitments Received", "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Local Blocks Proposed", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Inclusion Commitments Accepted", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Total HTTP Requests", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_duration_seconds", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "HTTP Requests Durations in ms", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Head Slot", + "type": "stat" } ], - "schemaVersion": 39, + "refresh": "", + "schemaVersion": 38, + "style": "dark", "tags": [], "templating": { "list": [] @@ -230,9 +828,9 @@ "to": "now" }, "timepicker": {}, - "timezone": "browser", - "title": "Bolt Metrics", - "uid": "edxnwlpgaw934c", + "timezone": "", + "title": "Bolt Sidecar", + "uid": "e5960f6d-a1ed-4538-9c7c-3ecba4d4b4b1", "version": 3, "weekStart": "" } diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml index 6d29d617b..91e10e2b4 100644 --- a/testnets/holesky/grafana/datasources/datasources.yml +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -1,11 +1,11 @@ apiVersion: 1 datasources: - - name: cb-prometheus + - name: bolt-prometheus-holesky type: prometheus - uid: cb_prometheus + uid: bolt-prometheus-holesky access: proxy orgId: 1 - url: http://cb_prometheus:9090 + url: http://bolt-prometheus-holesky:49090 isDefault: true editable: true diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json new file mode 100644 index 000000000..6cd87cbc6 --- /dev/null +++ b/testnets/holesky/targets.json @@ -0,0 +1,14 @@ +[ + { + "targets": ["bolt-sidecar-holesky:8017"], + "labels": { + "job": "bolt-sidecar-rpc" + } + }, + { + "targets": ["bolt-sidecar-holesky:18550"], + "labels": { + "job": "bolt-sidecar-builder-proxy" + } + } +] From 4918c135f024d88e465df4fdb577ca5b3db11c73 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 10:53:16 +0200 Subject: [PATCH 109/272] fix(contracts): move operators scripts into new operators folder --- .../RegisterEigenLayerOperator.s.sol | 0 .../{validators => operators}/RegisterSymbioticOperator.s.sol | 0 testnets/holesky/README.md | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename bolt-contracts/script/holesky/{validators => operators}/RegisterEigenLayerOperator.s.sol (100%) rename bolt-contracts/script/holesky/{validators => operators}/RegisterSymbioticOperator.s.sol (100%) diff --git a/bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol similarity index 100% rename from bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol rename to bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol diff --git a/bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol similarity index 100% rename from bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol rename to bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index fa1a31cfd..4a6f6df60 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -511,7 +511,7 @@ The opt-in process requires the following steps: Run the provided Forge script to register a Symbiotic operator: ```bash -forge script script/holesky/validators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast ``` If all goes well, your Symbiotic operator was registered into Bolt. @@ -545,7 +545,7 @@ Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: ```bash -forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast ``` If all goes well, your EigenLayer operator was registered into Bolt. From a3ba7cdd5720aad66c553d48842054e510f19793 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 11:46:03 +0200 Subject: [PATCH 110/272] feat(holesky): EL script for StrategyManager.depositIntoStrategy --- .../config/holesky/deployments.json | 74 +++++++++--------- bolt-contracts/config/holesky/operator.json | 9 ++- .../eigenlayer/depositIntoStrategy.json | 5 ++ .../RegisterEigenLayerOperator.s.sol | 59 +++++++++++++- testnets/holesky/README.md | 77 ++++++++++++++----- 5 files changed, 159 insertions(+), 65 deletions(-) create mode 100644 bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json diff --git a/bolt-contracts/config/holesky/deployments.json b/bolt-contracts/config/holesky/deployments.json index a5a41415d..2866f9e27 100644 --- a/bolt-contracts/config/holesky/deployments.json +++ b/bolt-contracts/config/holesky/deployments.json @@ -1,38 +1,38 @@ { - "bolt": { - "validators": "0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8", - "parameters": "0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12", - "manager": "0x440202829b493F9FF43E730EB5e8379EEa3678CF" - }, - "symbiotic": { - "network": "0xb017002D8024d8c8870A5CECeFCc63887650D2a4", - "operatorRegistry": "0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548", - "networkOptInService": "0x58973d16FFA900D11fC22e5e2B6840d9f7e13401", - "vaultFactory": "0x407A039D94948484D356eFB765b3c74382A050B4", - "vaultConfigurator": "0xD2191FE92987171691d552C219b8caEf186eb9cA", - "networkRegistry": "0x7d03b7343BF8d5cEC7C0C27ecE084a20113D15C9", - "networkMiddlewareService": "0x62a1ddfD86b4c1636759d9286D3A0EC722D086e3", - "middleware": "0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8", - "supportedVaults": [ - "0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78", - "0xe5708788c90e971f73D928b7c5A8FD09137010e0", - "0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd", - "0xC56Ba584929c6f381744fA2d7a028fA927817f2b", - "0xcDdeFfcD2bA579B8801af1d603812fF64c301462", - "0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f" - ] - }, - "eigenLayer": { - "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", - "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", - "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", - "middleware": "0xa632a3e652110Bb2901D5cE390685E6a9838Ca04", - "supportedStrategies": [ - "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", - "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", - "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", - "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", - "0xaccc5A86732BE85b5012e8614AF237801636F8e5" - ] - } -} \ No newline at end of file + "bolt": { + "validators": "0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8", + "parameters": "0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12", + "manager": "0x440202829b493F9FF43E730EB5e8379EEa3678CF" + }, + "symbiotic": { + "network": "0xb017002D8024d8c8870A5CECeFCc63887650D2a4", + "operatorRegistry": "0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548", + "networkOptInService": "0x58973d16FFA900D11fC22e5e2B6840d9f7e13401", + "vaultFactory": "0x407A039D94948484D356eFB765b3c74382A050B4", + "vaultConfigurator": "0xD2191FE92987171691d552C219b8caEf186eb9cA", + "networkRegistry": "0x7d03b7343BF8d5cEC7C0C27ecE084a20113D15C9", + "networkMiddlewareService": "0x62a1ddfD86b4c1636759d9286D3A0EC722D086e3", + "middleware": "0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8", + "supportedVaults": [ + "0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78", + "0xe5708788c90e971f73D928b7c5A8FD09137010e0", + "0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd", + "0xC56Ba584929c6f381744fA2d7a028fA927817f2b", + "0xcDdeFfcD2bA579B8801af1d603812fF64c301462", + "0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f" + ] + }, + "eigenLayer": { + "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", + "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", + "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", + "middleware": "0xa632a3e652110Bb2901D5cE390685E6a9838Ca04", + "supportedStrategies": [ + "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", + "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", + "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", + "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", + "0xaccc5A86732BE85b5012e8614AF237801636F8e5" + ] + } +} diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operator.json index 0b6373c92..7e1d43c10 100644 --- a/bolt-contracts/config/holesky/operator.json +++ b/bolt-contracts/config/holesky/operator.json @@ -1,5 +1,6 @@ { - "rpc": "localhost:50051", - "salt": "0x000000000000000abc0000000000000000000000000000000000000000000000", - "expiry": null -} \ No newline at end of file + "rpc": ":", + "salt": "0x0000000000000000000_salt_value_0000000000000000000000000000000000", + "expiry": "0x00000000000000000_expiry_value_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} + diff --git a/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json new file mode 100644 index 000000000..f500598aa --- /dev/null +++ b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json @@ -0,0 +1,5 @@ +{ + "strategy": "", + "token": "", + "amount": "" +} diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 3acbe2d81..109f70c73 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -4,13 +4,14 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; +import {IDelegationManager} from "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy, IERC20} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; import {IBoltMiddlewareV1} from "../../../src/interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; contract RegisterEigenLayerOperator is Script { struct OperatorConfig { @@ -19,9 +20,27 @@ contract RegisterEigenLayerOperator is Script { uint256 expiry; } - function run() public { + function S01_depositIntoStrategy() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); + IStrategyManager strategyManager = _readStrategyManager(); + + string memory json = vm.readFile("config/holesky/operators/eigenlayer/depositIntoStrategy.json"); + + IStrategy strategy = IStrategy(vm.parseJsonAddress(json, ".strategy")); + IERC20 token = IERC20(vm.parseJsonAddress(json, ".token")); + uint256 amount = vm.parseJsonUint(json, ".amount"); + + vm.startBroadcast(operatorSk); + // Allowance must be set before depositing + token.approve(address(strategyManager), amount); + strategyManager.depositIntoStrategy(strategy, token, amount); + console.log("Successfully run StrategyManager.depositIntoStrategy"); + vm.stopBroadcast(); + } + + function S02_registerIntoBoltAVS() public { + uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); @@ -53,6 +72,16 @@ contract RegisterEigenLayerOperator is Script { vm.stopBroadcast(); } + function S03_checkOperatorRegistration() public view { + address operatorPublicKey = vm.envAddress("OPERATOR_PK"); + console.log("Checking operator registration for address", operatorPublicKey); + + IBoltManagerV1 boltManager = _readBoltManager(); + bool isRegistered = boltManager.isOperator(operatorPublicKey); + console.log("Operator is registered:", isRegistered); + require(isRegistered, "Operator is not registered"); + } + function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -69,6 +98,28 @@ contract RegisterEigenLayerOperator is Script { return IAVSDirectory(vm.parseJsonAddress(json, ".eigenLayer.avsDirectory")); } + function _readDelegationManager() public view returns (IDelegationManager) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return IDelegationManager(vm.parseJsonAddress(json, ".eigenLayer.delegationManager")); + } + + function _readStrategyManager() public view returns (IStrategyManager) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IStrategyManager(vm.parseJsonAddress(json, ".eigenLayer.strategyManager")); + } + + function _readBoltManager() public view returns (IBoltManagerV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + } + function _readConfig( string memory path ) public view returns (OperatorConfig memory) { diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4a6f6df60..cfdf062da 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -524,31 +524,66 @@ If all goes well, your Symbiotic operator was registered into Bolt. > The supported strategies can be found in > [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -The Operator will be represented by an Ethereum address that needs to follow the -standard procedure outlined in the [EigenLayer -documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go -through the steps: +If you're not registered as an operator in EigenLayer yet, you need to do so by +following [the official +guide](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-introduction). +This requires installing the EigenLayer CLI and opt into the protocol by +registering via the +[`DelegationManager.registerAsOperator`](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-installation) +function. + +After that you need to deposit into a supported EigenLayer +strategy using +[`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). +This will add the deposit into the collateral of the operator so that Bolt can +read it. Note that you need to deposit a minimum of `1 ether` of the strategies +underlying token in order to opt in. + +We've provided a script to facilitate the procedure. If you want to use it, +please set the operator private key to an `OPERATOR_SK` environment variable. + +First, you need to first configure the deposit details in this JSON +file: -1. As an Operator, you register into EigenLayer using - [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/DelegationManager.sol#L107-L119). +```bash +$EDITOR ./config/holesky/operators/eigenlayer/depositIntoStrategy.json +``` + +Then you can run the following Forge script: -2. You can then use the same account to deposit into a supported EigenLayer - strategy using - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). - This will add the deposit into the collateral of the operator so that Bolt can - read it. Note that you need to deposit a minimum of `1 ether` of the strategies - underlying token in order to opt in. +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S01_depositIntoStrategy()" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast +``` **Internal Steps** -Set the operator private key to an `OPERATOR_SK` environment variable, and then -run the following Forge script from the `bolt-contracts` directory: +After having deposited collateral into a strategy you need to register into the +Bolt AVS. We've provided a script to facilitate the procedure. If you want to +use it, please set the operator private key to an `OPERATOR_SK` environment +variable, and then run the following Forge script from the `bolt-contracts` +directory: ```bash -forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S02_registerIntoBoltAVS" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -If all goes well, your EigenLayer operator was registered into Bolt. +To check if your operator is correctly registered, set the operator public key +in the `OPERATOR_PK` environment variable and run the following script: + +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S03_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` # Reference @@ -559,13 +594,14 @@ sidecar. You can see them in your terminal by running the Bolt sidecar binary with the `--help` flag: ``` + Command-line options for the Bolt sidecar Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: - --port - Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! +--port +Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] [default: 8017] @@ -709,8 +745,9 @@ Options: --disable-metrics [env: BOLT_SIDECAR_DISABLE_METRICS=] - -h, --help - Print help (see a summary with '-h') +-h, --help +Print help (see a summary with '-h') + ``` ## Delegations and signing options for Native and Docker Compose Mode From 6b71506cccb3f345ec132daf61eccdceb676c151 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 10:22:06 +0200 Subject: [PATCH 111/272] fix(holesky): formatting in README --- testnets/holesky/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index cfdf062da..6089342c1 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -600,8 +600,8 @@ Command-line options for the Bolt sidecar Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: ---port -Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! + --port + Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] [default: 8017] @@ -637,8 +637,11 @@ Port to listen on for incoming JSON-RPC requests of the Commitments API. This po [default: 18550] --validator-indexes - Validator indexes of connected validators that the sidecar should accept commitments on behalf of. Accepted values: - a comma-separated list of indexes (e.g. "1,2,3,4") - a contiguous range of indexes (e.g. "1..4") - a mix of the - above (e.g. "1,2..4,6..8") + Validator indexes of connected validators that the sidecar should accept commitments on behalf of. + Accepted values: + - a comma-separated list of indexes (e.g. "1,2,3,4") + - a contiguous range of indexes (e.g. "1..4") + - a mix of the above (e.g. "1,2..4,6..8") [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] @@ -745,8 +748,8 @@ Port to listen on for incoming JSON-RPC requests of the Commitments API. This po --disable-metrics [env: BOLT_SIDECAR_DISABLE_METRICS=] --h, --help -Print help (see a summary with '-h') + -h, --help + Print help (see a summary with '-h') ``` From 8c191b068e9f1cd9c19c3d0ac1bfd960d05641f8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 11:04:20 +0200 Subject: [PATCH 112/272] chore(holesky): better explanation of operator config steps for EL --- .../eigenlayer/registerIntoBoltAVS.json} | 5 ++- .../RegisterEigenLayerOperator.s.sol | 2 +- testnets/holesky/README.md | 33 +++++++++++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) rename bolt-contracts/config/holesky/{operator.json => operators/eigenlayer/registerIntoBoltAVS.json} (68%) diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json similarity index 68% rename from bolt-contracts/config/holesky/operator.json rename to bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json index 7e1d43c10..dd38cee99 100644 --- a/bolt-contracts/config/holesky/operator.json +++ b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json @@ -1,6 +1,5 @@ { "rpc": ":", - "salt": "0x0000000000000000000_salt_value_0000000000000000000000000000000000", - "expiry": "0x00000000000000000_expiry_value_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "salt": "0x0000000000000000000_salt_value_000000000000000000000000000000000", + "expiry": "0x00000000000000000_expiry_value_000000000000000000000000000000000" } - diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 109f70c73..c67b7ef06 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -45,7 +45,7 @@ contract RegisterEigenLayerOperator is Script { BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); IAVSDirectory avsDirectory = _readAvsDirectory(); - OperatorConfig memory config = _readConfig("config/holesky/operator.json"); + OperatorConfig memory config = _readConfig("config/holesky/operators/eigenlayer/registerIntoBoltAVS.json"); console.log("Registering EigenLayer operator"); console.log("Operator address:", operator); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 6089342c1..524d64335 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -563,9 +563,36 @@ forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ After having deposited collateral into a strategy you need to register into the Bolt AVS. We've provided a script to facilitate the procedure. If you want to -use it, please set the operator private key to an `OPERATOR_SK` environment -variable, and then run the following Forge script from the `bolt-contracts` -directory: +use it, please set follow these steps: + +1. configure the operator details in this JSON file + + ```bash + $EDITOR ./config/holesky/operators/eigenlayer/registerIntoBoltAVS.json + ``` + + In there you'll need to set the the following fields: + + - `rpc` -- the RPC URL of your operator which supports the Commitments API + - `salt` -- an unique 32 bytes value to avoid replay attacks. To generate it on + both Linux and MacOS you can run: + + ```bash + echo -n "0x"; head -c 32 /dev/urandom | hexdump -e '32/1 "%02x" "\n"' + ``` + + - `expiry` -- the timestamp of the signature expiry in seconds. To generate it + on both Linux and MacOS run the following command, replacing + `` with the desired timestamp: + + ```bash + echo -n "0x"; printf "%064x\n" + ``` + +2. set the operator private key to an `OPERATOR_SK` environment + variable; +3. run the following Forge script from the `bolt-contracts` + directory: ```bash forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ From 61cc8a1855a5cc3b30eabefc24363ee3d7afc476 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 16:08:38 +0200 Subject: [PATCH 113/272] feat(holesky): update scripts + README for Symbiotic integration --- .../operators/RegisterSymbioticOperator.s.sol | 37 +++++++++++----- testnets/holesky/README.md | 43 +++++++++++++++---- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index 0089432c0..f2728c85b 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -4,7 +4,10 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; + import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; +import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { @@ -14,7 +17,7 @@ contract RegisterSymbioticOperator is Script { address symbioticNetwork; } - function run() public { + function S01_registerIntoBolt() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); @@ -22,15 +25,12 @@ contract RegisterSymbioticOperator is Script { Config memory config = _readConfig(); // First, make sure the operator is opted into the network - if (!config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork)) { - console.log("Operator is not opted into the network yet. Opting in..."); - vm.startBroadcast(operatorSk); - config.symbioticNetworkOptInService.optIn(config.symbioticNetwork); - vm.stopBroadcast(); - console.log("Operator successfully opted into the network"); - } - - console.log("Registering Symbiotic operator"); + require( + config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork), + "Operator must be opted in into Bolt Network" + ); + + console.log("Registering Symbiotic operator into Bolt"); console.log("Operator address:", operator); console.log("Operator RPC:", config.rpc); @@ -41,6 +41,16 @@ contract RegisterSymbioticOperator is Script { vm.stopBroadcast(); } + function S02_checkOperatorRegistration() public view { + address operatorPublicKey = vm.envAddress("OPERATOR_PK"); + console.log("Checking operator registration for address", operatorPublicKey); + + IBoltManagerV1 boltManager = _readBoltManager(); + bool isRegistered = boltManager.isOperator(operatorPublicKey); + console.log("Operator is registered:", isRegistered); + require(isRegistered, "Operator is not registered"); + } + function _readConfig() public view returns (Config memory) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -56,4 +66,11 @@ contract RegisterSymbioticOperator is Script { symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) }); } + + function _readBoltManager() public view returns (IBoltManagerV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + } } diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 524d64335..b15e25fc0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -493,28 +493,53 @@ directory. As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide commitments on their behalf. -The opt-in process requires the following steps: - **External Steps** > [!NOTE] > The network and supported vault addresses can be found in > [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -1. register in Symbiotic with `OperatorRegistry.registerOperator()`. -2. opt-in to the Bolt network with - `OperatorNetworkOptInService.optIn(networkAddress)`. -3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +Make sure you have installed the [Symbiotic +CLI](https://docs.symbiotic.fi/guides/cli/). + +The opt-in process requires the following steps: + +1. if you haven't done it already, register as a Symbiotic Operator with the + [`register-operator`](https://docs.symbiotic.fi/guides/cli/#register-operator) + command; +2. opt-in to the Bolt network with the + [`opt-in-network`](https://docs.symbiotic.fi/guides/cli/#opt-in-network) + command; +3. opt-in to any vault using the + [`opt-in-vault`](https://docs.symbiotic.fi/guides/cli/#opt-in-vault) command; +4. deposit collateral into the vault using the + [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. **Internal Steps** -Run the provided Forge script to register a Symbiotic operator: +After having deposited collateral into a vault you need to register into +Bolt as a Symbiotic operator. We've provided a script to facilitate the +procedure. If you want to use it, please set the operator private key to an +`OPERATOR_SK` environment variable, and then run the following Forge script from +the `bolt-contracts` directory: ```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -If all goes well, your Symbiotic operator was registered into Bolt. +To check if your operator is correctly registered, set the operator public key +in the `OPERATOR_PK` environment variable and run the following script: + +```bash +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S02_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` ### EigenLayer Registration Steps From 3b222d314add039a14002d1d85f7ae4ffd316aa9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 11:13:17 +0200 Subject: [PATCH 114/272] fix(holesky): config options for Symbiotic guide Don't use operators.json file but provide the RPC URL using an enviroment value --- .../operators/RegisterSymbioticOperator.s.sol | 5 ++-- testnets/holesky/README.md | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index f2728c85b..b5a6d46d8 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -19,6 +19,7 @@ contract RegisterSymbioticOperator is Script { function S01_registerIntoBolt() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); + string memory rpc = vm.envString("OPERATOR_RPC"); address operator = vm.addr(operatorSk); @@ -32,10 +33,10 @@ contract RegisterSymbioticOperator is Script { console.log("Registering Symbiotic operator into Bolt"); console.log("Operator address:", operator); - console.log("Operator RPC:", config.rpc); + console.log("Operator RPC:", rpc); vm.startBroadcast(operatorSk); - config.symbioticMiddleware.registerOperator(config.rpc); + config.symbioticMiddleware.registerOperator(rpc); console.log("Successfully registered Symbiotic operator"); vm.stopBroadcast(); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b15e25fc0..154328429 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -519,17 +519,20 @@ The opt-in process requires the following steps: After having deposited collateral into a vault you need to register into Bolt as a Symbiotic operator. We've provided a script to facilitate the -procedure. If you want to use it, please set the operator private key to an -`OPERATOR_SK` environment variable, and then run the following Forge script from -the `bolt-contracts` directory: +procedure. If you want to use it, please follow these steps: -```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ - --sig "S01_registerIntoBolt" \ - --rpc-url $HOLESKY_RPC \ - -vvvv \ - --broadcast -``` +1. set the operator private key to the `OPERATOR_SK` environment variable; +2. set the operator RPC URL which supports the Commitments API to the + `OPERATOR_RPC` environment variable; +3. run the following Forge script from the `bolt-contracts` directory: + + ```bash + forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast + ``` To check if your operator is correctly registered, set the operator public key in the `OPERATOR_PK` environment variable and run the following script: From cb4d90e98ff49be471be2dee09f8e98d12d56aa2 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 13:41:41 +0200 Subject: [PATCH 115/272] chore(holesky): upgrade vault addresses for Symbiotic --- testnets/holesky/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 154328429..6574cb06d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -354,8 +354,12 @@ EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) - - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) + - [`rETH`](https://holesky.etherscan.io/address/0xe5708788c90e971f73D928b7c5A8FD09137010e0) + - [`stETH`](https://holesky.etherscan.io/address/0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd) + - [`WETH`](https://holesky.etherscan.io/address/0xC56Ba584929c6f381744fA2d7a028fA927817f2b) + - [`cbETH`](https://holesky.etherscan.io/address/0xcDdeFfcD2bA579B8801af1d603812fF64c301462) + - [`mETH`](https://holesky.etherscan.io/address/0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f) - [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) From dc162a76556974b5fa4d3be55f4b6c7bb37cdcd1 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Thu, 24 Oct 2024 14:45:07 +0200 Subject: [PATCH 116/272] fix(contracts/scripts): register Symbiotic operator script --- .../operators/RegisterSymbioticOperator.s.sol | 25 +++++++++++-------- testnets/holesky/README.md | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index b5a6d46d8..d57902e9b 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -11,7 +11,6 @@ import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { - string rpc; BoltSymbioticMiddlewareV1 symbioticMiddleware; IOptInService symbioticNetworkOptInService; address symbioticNetwork; @@ -25,14 +24,15 @@ contract RegisterSymbioticOperator is Script { Config memory config = _readConfig(); + console.log("Registering Symbiotic operator into Bolt"); + console.log("Operator address:", operator); + // First, make sure the operator is opted into the network require( config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork), "Operator must be opted in into Bolt Network" ); - console.log("Registering Symbiotic operator into Bolt"); - console.log("Operator address:", operator); console.log("Operator RPC:", rpc); vm.startBroadcast(operatorSk); @@ -40,14 +40,23 @@ contract RegisterSymbioticOperator is Script { console.log("Successfully registered Symbiotic operator"); vm.stopBroadcast(); + + (address[] memory tokens, uint256[] memory amounts) = + config.symbioticMiddleware.getOperatorCollaterals(operator); + + console.log("Operator collateral:"); + for (uint256 i; i < tokens.length; ++i) { + console.log("Collateral:", tokens[i], "Amount:", amounts[i]); + } } function S02_checkOperatorRegistration() public view { - address operatorPublicKey = vm.envAddress("OPERATOR_PK"); - console.log("Checking operator registration for address", operatorPublicKey); + address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); + console.log("Checking operator registration for address", operatorAddress); IBoltManagerV1 boltManager = _readBoltManager(); - bool isRegistered = boltManager.isOperator(operatorPublicKey); + bool isRegistered = boltManager.isOperator(operatorAddress); + console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); } @@ -57,11 +66,7 @@ contract RegisterSymbioticOperator is Script { string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - string memory operatorPath = string.concat(root, "/config/holesky/operator.json"); - string memory operatorJson = vm.readFile(operatorPath); - return Config({ - rpc: vm.parseJsonString(operatorJson, ".rpc"), symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), symbioticMiddleware: BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")), symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 6574cb06d..5ddfb7a3f 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -538,8 +538,8 @@ procedure. If you want to use it, please follow these steps: --broadcast ``` -To check if your operator is correctly registered, set the operator public key -in the `OPERATOR_PK` environment variable and run the following script: +To check if your operator is correctly registered, set the operator address +in the `OPERATOR_ADDRESS` environment variable and run the following script: ```bash forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ From e1154f9b45d4133d2c4002f5746647d696dc3e85 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:32:12 +0200 Subject: [PATCH 117/272] chore(holesky): updated bolt-cli usage guide --- testnets/holesky/README.md | 235 ++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 95 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 5ddfb7a3f..5ad5016ba 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -23,9 +23,8 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt-delegations-cli`](#`bolt-delegations-cli`) + * [`bolt-cli`](#bolt-cli) * [Installation and usage](#installation-and-usage) - * [Delegations CLI Example](#delegations-cli-example) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) * [Avoid restarting the beacon node](#avoid-restarting-the-beacon-node) @@ -42,8 +41,8 @@ your system. Bolt is fully trustless since it is able to produce a fallback block with the commitments issued in case builders do not return a valid bid. In order to do so it relies on a synced execution client, configured via the `--execution-api-url` -flag. At the moment only Geth is supported; with more -clients to be supported in the future. +flag. **At the moment only Geth is supported; with more +clients to be supported in the future.** Using the sidecar with a different execution client could lead to commitment faults because fallback block building is not supported yet. You can download @@ -74,22 +73,25 @@ client implementations to download and run them. > > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon -> Node](#avoid-restarting-the-beacon-node) for more details. +> Node](#avoid-restarting-the-beacon-node) section for more details. **Active validators:** -The Bolt sidecar requires signing keys from active Ethereum validators, or -authorized delegates acting on their behalf, to issue and sign preconfirmations. +The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, +or **authorized delegates** acting on their behalf, to issue and sign preconfirmations. + +To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +section. # Off-Chain Setup There are various way to run the Bolt Sidecar depending on what infrastructure you want to use and your preferred signing methods: -- Docker mode (recommended); +- Docker mode (recommended) - [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode - (requires Docker). -- Native mode (advanced, requires building everything from source); + (requires Docker) +- Native mode (advanced, requires building everything from source) Running the Bolt sidecar as a standalone binary requires building it from source. Both the standalone binary and the Docker container requires reading @@ -102,19 +104,19 @@ requirements. ## Docker Mode (recommended) -First, make sure to have both [Docker](https://docs.docker.com/engine/install/), +First, make sure to have [Docker](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/) and [git](https://git-scm.com/downloads) installed in your machine. Then clone the Bolt repository by running: ```bash -git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git && cd bolt +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git +cd bolt/testnets/holesky ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt -MEV-Boost fork which includes supports the [Constraints -API](https://docs.boltprotocol.xyz/api/builder). +MEV-Boost fork which includes supports the [Constraints API](https://docs.boltprotocol.xyz/api/builder). Before starting the services, you'll need to provide configuration files containing the necessary environment variables: @@ -652,7 +654,10 @@ For completeness, here are all the command-line options available for the Bolt sidecar. You can see them in your terminal by running the Bolt sidecar binary with the `--help` flag: -``` +
+CLI help Reference + +```text Command-line options for the Bolt sidecar @@ -812,6 +817,9 @@ Options: ``` +
+ + ## Delegations and signing options for Native and Docker Compose Mode As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar @@ -825,115 +833,150 @@ Ethereum validators. In order to create these delegation you can use the `bolt-delegations-cli` binary. If you don't want to use it you can skip the following section. -### `bolt-delegations-cli` +### `bolt` CLI -`bolt-delegations-cli` is an offline command-line tool for safely generating -delegation and revocation messages signed with a BLS12-381 key for the -[Constraints API](https://docs.boltprotocol.xyz/api/builder) in -[Bolt](https://docs.boltprotocol.xyz/). +`bolt` CLI is an offline tool for safely generating delegation and revocation messages +signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) +in [Bolt](https://docs.boltprotocol.xyz/). -The tool supports two key sources: +The tool supports three key sources: -- Local: A BLS private key provided directly from a file. -- Keystore: A keystore file that contains an encrypted BLS private key. +- **Secret Keys**: A list of BLS private keys provided directly as hex-strings. +- **Local Keystore**: A EIP-2335 keystore that contains an encrypted BLS private keys. +- **Dirk**: A remote Dirk server that provides the BLS signatures for the delegation messages. and outputs a JSON file with the delegation/revocation messages to the provided -`` for the given chain +`` for the given chain. -Features: +#### Installation and usage -- Offline usage: Safely generate delegation messages in an offline environment. -- Flexible key source: Support for both direct local BLS private keys and - Ethereum keystore files (ERC-2335 format). -- BLS delegation signing: Sign delegation messages using a BLS secret key and - output the signed delegation in JSON format. +Prerequisites: -#### Installation and usage +- [Rust toolchain][rust] +- [Protoc][protoc] -Go to the root of the Bolt project you've previously cloned using Git. Enter in -the `bolt-delegations-cli` directory by running `cd bolt-delegations-cli`. +Once you have the necessary prerequisites, you can build the binary +in the following way: -If you're using the Docker container setup make sure you have -[Rust](https://www.rust-lang.org/tools/install) installed in your system as -well. Then you can build the `bolt-delegations-cli` binary by running: +```shell +# clone the Bolt repository if you haven't already +git clone git@github.com:chainbound/bolt.git -```bash -cargo build --release && mv target/release/bolt-delegations-cli . -``` +# navigate to the Bolt CLI package directory +cd bolt-cli -Now you can run the binary by running: +# build and install the binary on your machine +cargo install --path . --force -```bash -./bolt-delegations-cli +# test the installation +bolt --version ``` -The binary exposes a single `generate` command, which accepts the following -options and subcommands (use `./bolt-delegations-cli generate --help` to see -them): +The binary can be used with the following command: -```text -Usage: bolt-delegations-cli generate [OPTIONS] --delegatee-pubkey +```shell +bolt delegate --delegate-pubkey + --out + --chain + + +``` -Commands: - local Use local private keys to generate the signed messages - keystore Use an EIP-2335 keystore folder to generate the signed messages - help Print this message or the help of the given subcommand(s) +where: -Options: - --delegatee-pubkey The BLS public key to which the delegation message should be signed [env: DELEGATEE_PUBKEY=] - --out The output file for the delegations [env: OUTPUT_FILE_PATH=] [default: delegations.json] - --chain The chain for which the delegation message is intended [env: CHAIN=] [default: mainnet] [possible values: mainnet, holesky, helder, kurtosis] - --action The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) [env: ACTION=] [default: delegate] [possible values: delegate, revoke] - -h, --help Print help (see more with '--help') -``` +- `` is the public key of the delegatee. +- `` is the path to the file where the delegation JSON messages will be written. +- `` is the chain for which the delegations are being generated (e.g. Holesky). +- `` is the key source to use for generating the delegations. It can be one of: + - `secret-keys`: A list of BLS private keys provided directly as hex-strings. + - `local-keystore`: A EIP-2335 keystore that contains an encrypted BLS private keys. + - `dirk`: A remote Dirk server that provides the BLS signatures for the delegation messages. + +You can also find more information about the available key source +options by running `bolt delegate --help`. > [!TIP] > If you're using the Docker Compose Mode please don't set the `--out` flag and > provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` > file. -The environment variables can be also set in a `.env` file. For a reference -example you can check out the `.env.local.example` and the -`.env.keystore.example` +Here you can see usage examples for each key source: + +
+Usage + +```text +❯ bolt-cli delegate --help +Generate BLS delegation or revocation messages +Usage: bolt-cli delegate [OPTIONS] --delegatee-pubkey +Commands: +secret-keys Use local secret keys to generate the signed messages +local-keystore Use an EIP-2335 filesystem keystore directory to generate the signed messages +dirk Use a remote DIRK keystore to generate the signed messages +help Print this message or the help of the given subcommand(s) +Options: + --delegatee-pubkey + The BLS public key to which the delegation message should be signed + [env: DELEGATEE_PUBKEY=] + --out + The output file for the delegations + [env: OUTPUT_FILE_PATH=] + [default: delegations.json] + --chain + The chain for which the delegation message is intended + [env: CHAIN=] + [default: mainnet] + [possible values: mainnet, holesky, helder, kurtosis] + --action + The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) + [env: ACTION=] + [default: delegate] + Possible values: + - delegate: Create a delegation message + - revoke: Create a revocation message +-h, --help + Print help (see a summary with '-h') +``` -In the section below you can see a usage example of the binary. +
-#### Delegations CLI Example +
+Examples -1. Using a local BLS private key: +1. Generating a delegation using a local BLS secret key - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - local \ - --secret-keys 0xabc123...,0xdef456.. - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x8d0edf4fe9c80cd640220ca7a68a48efcbc56a13536d6b274bf3719befaffa13688ebee9f37414b3dddc8c7e77233ce8 \ + --chain holesky \ + secret-keys --secret-keys 642e0d33fde8968a48b5f560c1b20143eb82036c1aa6c7f4adc4beed919a22e3 +``` -2. Using a Ethereum keystores files and raw password: +2. Generating a delegation using an ERC-2335 keystore directory - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - keystore \ - --path /keys \ - --password myS3cr3tP@ssw0rd - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x8d0edf4fe9c80cd640220ca7a68a48efcbc56a13536d6b274bf3719befaffa13688ebee9f37414b3dddc8c7e77233ce8 \ + --chain holesky \ + local-keystore --path test_data/lighthouse/validators --password-path test_data/lighthouse/secrets +``` -3. Using an Ethereum keystores files and secrets folder +3. Generating a delegation using a remote DIRK keystore - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - keystore \ - --path /keys \ - --password-path /secrets - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x83eeddfac5e60f8fe607ee8713efb8877c295ad9f8ca075f4d8f6f2ae241a30dd57f78f6f3863a9fe0d5b5db9d550b93 \ + dirk --url https://localhost:9091 \ + --client-cert-path ./test_data/dirk/client1.crt \ + --client-key-path ./test_data/dirk/client1.key \ + --ca-cert-path ./test_data/dirk/security/ca.crt \ + --wallet-path wallet1 --passphrases secret +``` + +
+ +
+Keystore-specific instructions When using the `keystore` key source, the `--path` flag should point to the directory containing the encrypted keypair directories. @@ -971,6 +1014,8 @@ That is, the password files should be named after the public key and each file should just contain one line with the password in plain text. The files themselves don't need a particular file extension. +
+ --- Now that you have generated the delegation messages you can provide them to the @@ -990,9 +1035,9 @@ can pass directly the private key as a hex-encoded string to the Bolt sidecar using the `--constraint-private-key` flag (or `constraint_private_key` in the TOML file). -This is the simplest setup and can be used in -case if all the delegations messages point to the same delegatee or if you're -running the sidecar with a single active validator. +This is the simplest setup and can be used in case if all the delegations messages +point to the same delegatee or if you're running the sidecar with a single active +validator. ### Using a ERC-2335 Keystore From 809185780e3dedcff37c9e6e70b176d811211c13 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:36:54 +0200 Subject: [PATCH 118/272] fix: broken links --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 5ad5016ba..45a2fd339 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -852,8 +852,8 @@ and outputs a JSON file with the delegation/revocation messages to the provided Prerequisites: -- [Rust toolchain][rust] -- [Protoc][protoc] +- [Rust toolchain](https://www.rust-lang.org/tools/install) +- [Protoc](https://grpc.io/docs/protoc-installation/) Once you have the necessary prerequisites, you can build the binary in the following way: From 2de1617dab55c2d98b661623410bd049e97a98a8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:09:56 +0200 Subject: [PATCH 119/272] chore!(sidecar,holesky): drop support for TOML, use .env everywhere --- bolt-sidecar/.env.example | 83 +++++++++++++++------- bolt-sidecar/Config.example.toml | 84 ----------------------- bolt-sidecar/bin/sidecar.rs | 12 +--- bolt-sidecar/src/config/mod.rs | 23 ------- bolt-sidecar/src/primitives/delegation.rs | 2 +- testnets/holesky/bolt-sidecar.env.example | 67 ++++++++++++++++++ testnets/holesky/mev-boost.env.example | 32 +++++++++ 7 files changed, 159 insertions(+), 144 deletions(-) delete mode 100644 bolt-sidecar/Config.example.toml create mode 100644 testnets/holesky/bolt-sidecar.env.example create mode 100644 testnets/holesky/mev-boost.env.example diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index 7621489a6..cf7785bb4 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,34 +1,67 @@ # Ethereum Node Connections + PBS URLs -BOLT_SIDECAR_PORT=8000 -BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485 -BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400 -BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 -BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 -BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 -BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +BOLT_SIDECAR_PORT=8017 +# Execution client API URL +BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +# URL for the beacon client +BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") +BOLT_SIDECAR_VALIDATOR_INDEXES= +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. BOLT_SIDECAR_ENGINE_JWT_HEX= +# The fee recipient address for fallback blocks BOLT_SIDECAR_FEE_RECIPIENT= +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +# Secret BLS key to sign fallback payloads with BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -# Commitments configs -BOLT_SIDECAR_MAX_COMMITMENTS=128 -BOLT_SIDECAR_MAX_COMMITTED_GAS= -BOLT_SIDECAR_MIN_PRIORITY_FEE= -BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 +# Commitments limits +# Max number of commitments to accept per block +BOLT_SIDECAR_MAX_COMMITMENTS_PER_SLOT=128 +# Max committed gas per slot +BOLT_SIDECAR_MAX_COMMITTED_GAS_PER_SLOT=10_000_000 +# Min priority fee to accept for a commitment +BOLT_SIDECAR_MIN_PRIORITY_FEE=4_000_000_000 # 4 Gwei = 4 * 10^9 wei -# Chain configs -BOLT_SIDECAR_CHAIN=holesky +# Chain configuration +# Chain on which the sidecar is running +BOLT_SIDECAR_CHAIN="holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] BOLT_SIDECAR_SLOT_TIME=12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) +BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 -# Signing options. Uncomment only what you'll use -#BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= -#BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= -#BOLT_SIDECAR_CB_SIGNER_URL= -#BOLT_SIDECAR_CB_JWT_HEX= -#BOLT_SIDECAR_KEYSTORE_PASSWORD= -#BOLT_SIDECAR_KEYSTORE_PATH= -#BOLT_SIDECAR_DELEGATIONS_PATH= +# Signing options. +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_SECRETS_PATH= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= -# Metrics -BOLT_SIDECAR_METRICS_PORT= -BOLT_SIDECAR_DISABLE_METRICS= +# Telemetry and Metrics +BOLT_SIDECAR_METRICS_PORT=9091 +BOLT_SIDECAR_DISABLE_METRICS=false diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml deleted file mode 100644 index 2bb86003b..000000000 --- a/bolt-sidecar/Config.example.toml +++ /dev/null @@ -1,84 +0,0 @@ -# Ethereum Node Connections + PBS URLs - -# Port to listen on for incoming JSON-RPC requests of the Commitments API. This -# port should be open on your firewall in order to receive external requests! -port = 8017 -# Execution client API URL -execution_api_url = "http://localhost:8545" -# URL for the beacon client -beacon_api_url = "http://localhost:5052" -# Execution client Engine API URL. This is needed for fallback block building -# and must be a synced Geth node -engine_api_url = "http://localhost:8551" -# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client -constraints_proxy_port = 18550 -# URL to forward the constraints produced by the Bolt sidecar to a server -# supporting the Constraints API, such as an MEV-Boost fork -constraints_api_url = "http://localhost:18551" -# Validator indexes of connected validators that the sidecar should accept -# commitments on behalf of. -# Accepted values: -# - a comma-separated list of indexes (e.g. "1,2,3,4") -# - a contiguous range of indexes (e.g. "1..4") -# - a mix of the above (e.g. "1,2..4,6..8") -# REQUIRED -validator_indexes = "0..64" -# The JWT secret token to authenticate calls to the engine API. It can be -# either be a hex-encoded string or a file path to a file containing the -# hex-encoded secret. -# REQUIRED -engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -# The fee recipient address for fallback blocks -# REQUIRED -fee_recipient = "0x0000000000000000000000000000000000000000" -# Secret ECDSA key to sign commitment messages with. The public key associated -# to it must be then used when registering the operator in the `BoltManager` -# contract -# REQUIRED -commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# Secret BLS key to sign fallback payloads with -# REQUIRED -builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" - -# Commitments limits -[limits] -# Max number of commitments to accept per block -max_commitments_per_slot = 128 -# Max committed gas per slot -max_committed_gas_per_slot = 10_000_000 -# Min priority fee to accept for a commitment -min_priority_fee = 4_000_000_000 # 4 Gwei = 4 * 10^9 wei - -# Chain configuration -[chain] -# Chain on which the sidecar is running -chain = "holesky" -# The slot time duration in seconds. If provided, it overrides the default for -# the selected [chain] -slot_time = 12 -# The deadline in the slot at which the sidecar will stop accepting new -# commitments for the next block (parsed as milliseconds) -commitment_deadline = 8000 - -# Signing options. Uncomment only the signing setup you'll use: -# - single private key -> `constraints_private_key` -# - commit-boost -> `cb_signer_url`, `cb_jwt_hex` -# - keystores -> `keystore_path`, `keystore_password` or `keystore_secrets_path` -# (depending on whether all keystores have the same passwords or not) -# -# If you plan to use delegations, uncomment the option `delegations_path` as -# well. -[constraint_signing] -# Private key to use for signing constraint messages -# constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# cb_signer_url = "http://localhost:18551" -# cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -# keystore_password = "password" -# keystore_secrets_path = "./secrets" -# keystore_path = "./keys" -# delegations_path = "./delegations.json" - -# Telemetry and Metrics -[telemetry] -metrics_port = 3300 -disable_metrics = false diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 6903fffbd..fbf0ce31c 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,22 +1,12 @@ -use std::fs; - use clap::Parser; use eyre::{bail, Result}; use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; -pub const TOML_CONFIG_DEFAULT_PATH: &str = "./Config.toml"; - #[tokio::main] async fn main() -> Result<()> { - let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") { - Opts::parse_from_toml(config_path.as_str())? - } else if fs::exists(TOML_CONFIG_DEFAULT_PATH).is_ok_and(|exists| exists) { - Opts::parse_from_toml(TOML_CONFIG_DEFAULT_PATH)? - } else { - Opts::parse() - }; + let opts = Opts::parse(); if let Err(err) = init_telemetry_stack(opts.telemetry.metrics_port()) { bail!("Failed to initialize telemetry stack: {:?}", err) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 1b9e64b76..7d05e1171 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -1,8 +1,5 @@ -use std::fs; - use alloy::primitives::Address; use clap::Parser; -use eyre::Context; use reqwest::Url; use serde::Deserialize; @@ -110,14 +107,6 @@ pub struct Opts { pub extra_args: Vec, } -impl Opts { - /// Parse the configuration from a TOML file. - pub fn parse_from_toml(file_path: &str) -> eyre::Result { - let contents = fs::read_to_string(file_path).wrap_err("Unable to read file")?; - toml::from_str(&contents).wrap_err("Error parsing the TOML file") - } -} - #[cfg(test)] mod tests { use super::*; @@ -136,16 +125,4 @@ mod tests { let localhost_socket = "0.0.0.0:3030".parse().unwrap(); assert_eq!(socket_addr, localhost_socket); } - - #[test] - fn test_parse_config_from_toml() { - let path = env!("CARGO_MANIFEST_DIR").to_string() + "/Config.example.toml"; - - let config = Opts::parse_from_toml(&path).expect("Failed to parse config from TOML"); - assert_eq!(config.execution_api_url, Url::parse("http://localhost:8545").unwrap()); - assert_eq!(config.beacon_api_url, Url::parse("http://localhost:5052").unwrap()); - assert_eq!(config.engine_api_url, Url::parse("http://localhost:8551").unwrap()); - assert_eq!(config.constraints_api_url, Url::parse("http://localhost:3030").unwrap()); - assert_eq!(config.constraints_proxy_port, 18551); - } } diff --git a/bolt-sidecar/src/primitives/delegation.rs b/bolt-sidecar/src/primitives/delegation.rs index 20b689bbd..3d41f275f 100644 --- a/bolt-sidecar/src/primitives/delegation.rs +++ b/bolt-sidecar/src/primitives/delegation.rs @@ -47,7 +47,7 @@ impl SignableBLS for DelegationMessage { } } -/// read the delegaitons from disk if they exist and add them to the constraints client +/// read the delegations from disk if they exist and add them to the constraints client pub fn read_signed_delegations_from_file( file_path: &PathBuf, ) -> eyre::Result> { diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example new file mode 100644 index 000000000..a094ec52d --- /dev/null +++ b/testnets/holesky/bolt-sidecar.env.example @@ -0,0 +1,67 @@ +# Ethereum Node Connections + PBS URLs + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +BOLT_SIDECAR_PORT=8017 +# Execution client API URL +BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +# URL for the beacon client +BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") +BOLT_SIDECAR_VALIDATOR_INDEXES= +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. +BOLT_SIDECAR_ENGINE_JWT_HEX= +# The fee recipient address for fallback blocks +BOLT_SIDECAR_FEE_RECIPIENT= +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +# Secret BLS key to sign fallback payloads with +BOLT_SIDECAR_BUILDER_PRIVATE_KEY= + +# Commitments limits +# Max number of commitments to accept per block +BOLT_SIDECAR_MAX_COMMITMENTS_PER_SLOT=128 +# Max committed gas per slot +BOLT_SIDECAR_MAX_COMMITTED_GAS_PER_SLOT=10_000_000 +# Min priority fee to accept for a commitment +BOLT_SIDECAR_MIN_PRIORITY_FEE=4_000_000_000 # 4 Gwei = 4 * 10^9 wei + +# Chain configuration +# Chain on which the sidecar is running +BOLT_SIDECAR_CHAIN="holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] +BOLT_SIDECAR_SLOT_TIME=12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) +BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 + +# Signing options. +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_SECRETS_PATH= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= + +# Telemetry and Metrics +BOLT_SIDECAR_METRICS_PORT=9091 # Changing this requires also changing the `target.json` file +BOLT_SIDECAR_DISABLE_METRICS=false diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example new file mode 100644 index 000000000..8815677dc --- /dev/null +++ b/testnets/holesky/mev-boost.env.example @@ -0,0 +1,32 @@ +# Logging settings +LOG_JSON=false # Set to true to log in JSON format +LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic +DEBUG=false # Set to true to enable debug mode +LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=false # Set to true to disable logging the version + +# Server settings +BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup + +# Relay settings +RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) +RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) +MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) + +# Relay timeout settings (in ms) +RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay +RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay +RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests + +# Genesis settings -- Not needed if using one of the predefined networks +# GENESIS_FORK_VERSION= # Custom genesis fork version +# GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) + +# Network settings +SEPOLIA=false # Set to true to use Sepolia network +GOERLI=false # Set to true to use Goerli network +HOLESKY=true # Set to true to use Holesky network + +# Retry settings +REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From 8e995901b1634b5e16d10b2d5a439ec9b0f2987e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:35:32 +0200 Subject: [PATCH 120/272] chore(holesky): update README after dropping TOML support --- testnets/holesky/README.md | 91 +++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 45a2fd339..7da86a4d7 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -23,7 +23,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt-cli`](#bolt-cli) + * [`bolt` CLI](#`bolt`-cli) * [Installation and usage](#installation-and-usage) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) @@ -77,10 +77,10 @@ client implementations to download and run them. **Active validators:** -The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, +The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, or **authorized delegates** acting on their behalf, to issue and sign preconfirmations. -To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section. # Off-Chain Setup @@ -123,41 +123,48 @@ containing the necessary environment variables: 1. **Bolt Sidecar Configuration:** - Create a `bolt-sidecar.toml` file in the `testnets/holesky` directory. If you - need a reference, you can use the `Config.example.toml` file in the `bolt-sidecar` - directory as a starting point. + Change directory to the `testnets/holesky` folder and create a + `bolt-sidecar.env` file starting from the reference template: ```bash - cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml + cd testnets/holesky + cp bolt-sidecar.env.example bolt-sidecar.env ``` - Next up, fill out all the values that are required. For proper configuration - of the signing options, please refer to the [Delegations and + Next up, fill out the values that are left blank. Please also review the + default values and see that they work for your setup. For proper + configuration of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. + If you've generated a `delegation.json` file using the Bolt CLI please + place it in the `testnets/holesky` directory by replacing the existing empty + one. + 2. **MEV-Boost Configuration:** - Copy over the example configuration file: + Change directory to the `testnets/holesky` folder if you haven't already and + copy over the example configuration file: ```bash - cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env + cp ./mev-boost.env.example ./mev-boost.env ``` - Then configure it accordingly. +Then configure it accordingly and review the default values chosen. If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. -Once the configuration files are in place, you can start the Docker containers -by running: +Once the configuration files are in place, make sure you are in the +`testnets/holesky` directory and then run: ```bash -cd testnets/holesky && docker compose up -d +docker compose up -d --env-file bolt-sidecar.env ``` The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards which you can find at `http://localhost:3000`. +Prometheus and Grafana. It also comes with some pre-built dashboards which you +can find at `http://localhost:28017`. ## Commit-Boost Mode @@ -315,11 +322,8 @@ can be found by running `./bolt-sidecar --help`, or you can find them in the #### Configuration file -You can use a `Config.toml` file to configure the sidecar, for which you can -find a template in the `Config.example.toml` file. -If you wish to place the configuration file in another folder you need to -specify the path of the configuration file by setting the -`BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. +You can use a `.env` file to configure the sidecar, for which you can +find a template in the `.env.example` file. Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) to configure such sidecar options properly. @@ -332,16 +336,22 @@ After you've set up the configuration file you can run the Bolt sidecar with ### Observability -Commit-Boost comes with various observability tools, such as Prometheus, -cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +The bolt sidecar comes with various observability tools, such as Prometheus +and Grafana. It also comes with some pre-built dashboards, which can be found in the `grafana` directory. -To update these dashboards, run the following command: +To run these dashboards change directory to the `bolt-sidecar/infra` folder and +run: + +```bash +docker compose -f telemetry.compose.yml up -d +``` -`bash ./update-grafana.sh ` +To stop the services run: -In this directory, you can also find a Bolt dashboard, which will be launched -alongside the other dashboards. +```bash +docker compose -f telemetry.compose.yml down +``` # On-Chain Registration @@ -819,7 +829,6 @@ Options: - ## Delegations and signing options for Native and Docker Compose Mode As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar @@ -830,13 +839,13 @@ Ethereum validators. > This is the recommended way to run the Bolt sidecar as it > doesn't expose the active validator signing keys to any additional risk. -In order to create these delegation you can use the `bolt-delegations-cli` binary. +In order to create these delegation you can use the `bolt` CLI binary. If you don't want to use it you can skip the following section. ### `bolt` CLI -`bolt` CLI is an offline tool for safely generating delegation and revocation messages -signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) +`bolt` CLI is an offline tool for safely generating delegation and revocation messages +signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) in [Bolt](https://docs.boltprotocol.xyz/). The tool supports three key sources: @@ -855,7 +864,7 @@ Prerequisites: - [Rust toolchain](https://www.rust-lang.org/tools/install) - [Protoc](https://grpc.io/docs/protoc-installation/) -Once you have the necessary prerequisites, you can build the binary +Once you have the necessary prerequisites, you can build the binary in the following way: ```shell @@ -875,10 +884,10 @@ bolt --version The binary can be used with the following command: ```shell -bolt delegate --delegate-pubkey - --out - --chain - +bolt delegate --delegate-pubkey + --out + --chain + ``` @@ -891,8 +900,8 @@ where: - `secret-keys`: A list of BLS private keys provided directly as hex-strings. - `local-keystore`: A EIP-2335 keystore that contains an encrypted BLS private keys. - `dirk`: A remote Dirk server that provides the BLS signatures for the delegation messages. - -You can also find more information about the available key source + +You can also find more information about the available key source options by running `bolt delegate --help`. > [!TIP] @@ -1035,8 +1044,8 @@ can pass directly the private key as a hex-encoded string to the Bolt sidecar using the `--constraint-private-key` flag (or `constraint_private_key` in the TOML file). -This is the simplest setup and can be used in case if all the delegations messages -point to the same delegatee or if you're running the sidecar with a single active +This is the simplest setup and can be used in case if all the delegations messages +point to the same delegatee or if you're running the sidecar with a single active validator. ### Using a ERC-2335 Keystore @@ -1049,7 +1058,7 @@ containing the password file (in the TOML configuration file these are the `keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). Both the `keys` and `passwords` folders must adhere to the structure outlined -in the [Delegations CLI example](#delegations-cli-example) section. +in the [Installation and Usage](#installation-and-usage) section. ## Avoid restarting the beacon node From 74044aa4709a7e62b9726974f51179e8f66b49b7 Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Thu, 24 Oct 2024 16:55:06 +0200 Subject: [PATCH 121/272] Update testnets/holesky/README.md Co-authored-by: nicolas <48695862+merklefruit@users.noreply.github.com> --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 7da86a4d7..b78a75acb 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -884,7 +884,7 @@ bolt --version The binary can be used with the following command: ```shell -bolt delegate --delegate-pubkey +bolt delegate --delegatee-pubkey --out --chain From 9d6243a98cc64ca0fc70837b99cb8c0b341bf34e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:11:47 +0200 Subject: [PATCH 122/272] fix(holesky): docker compose setup -- grafana, prometheus --- testnets/holesky/delegations.json | 1 + testnets/holesky/docker-compose.yml | 22 +- .../grafana/dashboards/bolt_dashboard.json | 836 ----------------- .../holesky/grafana/dashboards/dashboard.json | 473 +++++----- .../grafana/dashboards/system_metrics.json | 853 ------------------ .../grafana/datasources/datasources.yml | 2 +- testnets/holesky/targets.json | 10 +- 7 files changed, 262 insertions(+), 1935 deletions(-) create mode 100644 testnets/holesky/delegations.json delete mode 100644 testnets/holesky/grafana/dashboards/bolt_dashboard.json delete mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/delegations.json b/testnets/holesky/delegations.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/testnets/holesky/delegations.json @@ -0,0 +1 @@ +[] diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 86d2c3dce..e0c54cbc7 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -6,10 +6,11 @@ services: ports: - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" - entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" + entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar + env_file: ./bolt-sidecar.env volumes: - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" - - "../../bolt-delegations-cli/delegations.json:/etc/delegations.json" + - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 @@ -22,39 +23,26 @@ services: image: prom/prometheus:latest container_name: bolt-prometheus-holesky ports: - - 49090:49090 + - 18017:9090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus - networks: - - monitoring_network bolt-grafana-holesky: image: grafana/grafana:latest container_name: bolt-grafana-holesky ports: - - 33000:33000 - environment: - - GF_SECURITY_ADMIN_PASSWORD=admin + - 28017:3000 volumes: - ./grafana/dashboards:/etc/grafana/provisioning/dashboards - ./grafana/datasources:/etc/grafana/provisioning/datasources - grafana-data:/var/lib/grafana - networks: - - monitoring_network depends_on: - bolt-prometheus-holesky - logging: - driver: none volumes: prometheus-data: driver: local grafana-data: driver: local -networks: - monitoring_network: - driver: bridge - signer_network: - driver: bridge diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json deleted file mode 100644 index dce85acb0..000000000 --- a/testnets/holesky/grafana/dashboards/bolt_dashboard.json +++ /dev/null @@ -1,836 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 2, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_transactions_preconfirmed", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Transactions Preconfirmed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_validation_errors", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Invalid Transactions Reasons", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_remote_blocks_proposed", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Remote Blocks Proposed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_inclusion_commitments_received", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Inclusion Commitments Received", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 16 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.5.12", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "bolt_sidecar_local_blocks_proposed", - "instant": false, - "interval": "", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Local Blocks Proposed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 16 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_inclusion_commitments_accepted", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Inclusion Commitments Accepted", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_http_requests_total", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Total HTTP Requests", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 7, - "options": { - "bucketOffset": 0, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_http_requests_duration_seconds", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "HTTP Requests Durations in ms", - "type": "histogram" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 0, - "y": 32 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.12", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_latest_head", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Latest Head Slot", - "type": "stat" - } - ], - "refresh": "", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Bolt Sidecar", - "uid": "e5960f6d-a1ed-4538-9c7c-3ecba4d4b4b1", - "version": 3, - "weekStart": "" -} diff --git a/testnets/holesky/grafana/dashboards/dashboard.json b/testnets/holesky/grafana/dashboards/dashboard.json index f903affb2..8823158a8 100644 --- a/testnets/holesky/grafana/dashboards/dashboard.json +++ b/testnets/holesky/grafana/dashboards/dashboard.json @@ -18,28 +18,14 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, + "id": 2, "links": [], - "liveNow": true, + "liveNow": false, "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 12, - "panels": [], - "repeat": "endpoint", - "repeatDirection": "h", - "title": "$endpoint calls", - "type": "row" - }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -47,7 +33,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -61,11 +46,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -82,7 +63,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -100,12 +80,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, + "h": 8, + "w": 12, "x": 0, - "y": 1 + "y": 0 }, - "id": 11, + "id": 9, "options": { "legend": { "calcs": [], @@ -122,23 +102,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Relay Success QPH", + "title": "Transactions Preconfirmed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -146,7 +125,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -160,11 +138,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -181,7 +155,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -199,12 +172,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 6, - "y": 1 + "h": 8, + "w": 12, + "x": 12, + "y": 0 }, - "id": 13, + "id": 10, "options": { "legend": { "calcs": [], @@ -221,23 +194,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_validation_errors", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Relay Error QPH", + "title": "Invalid Transactions Reasons", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -245,7 +217,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -259,11 +230,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -280,17 +247,12 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -298,12 +260,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 12, - "y": 1 + "h": 8, + "w": 12, + "x": 0, + "y": 8 }, - "id": 43, + "id": 2, "options": { "legend": { "calcs": [], @@ -320,23 +282,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Beacon Node Success QPH", + "title": "Remote Blocks Proposed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -344,7 +305,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -358,11 +318,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -379,7 +335,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -397,12 +352,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 18, - "y": 1 + "h": 8, + "w": 12, + "x": 12, + "y": 8 }, - "id": 44, + "id": 4, "options": { "legend": { "calcs": [], @@ -419,23 +374,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_received", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Beacon Node Error QPH", + "title": "Inclusion Commitments Received", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -443,7 +397,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -457,18 +410,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -478,7 +427,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -491,18 +439,42 @@ "value": 80 } ] - }, - "unit": "s" + } }, - "overrides": [] + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] }, "gridPos": { - "h": 11, - "w": 6, + "h": 8, + "w": 12, "x": 0, - "y": 12 + "y": 16 }, - "id": 20, + "id": 8, "options": { "legend": { "calcs": [], @@ -515,31 +487,30 @@ "sort": "none" } }, + "pluginVersion": "9.5.12", "targets": [ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", "instant": false, + "interval": "", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P50", + "title": "Local Blocks Proposed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -547,7 +518,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -561,18 +531,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -582,31 +548,25 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] - }, - "unit": "s" + } }, "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 6, - "y": 12 + "h": 8, + "w": 12, + "x": 12, + "y": 16 }, - "id": 29, + "id": 5, "options": { "legend": { "calcs": [], @@ -623,27 +583,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P90", + "title": "Inclusion Commitments Accepted", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -651,7 +606,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -665,18 +619,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -686,7 +636,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -699,18 +648,17 @@ "value": 80 } ] - }, - "unit": "s" + } }, "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 12, - "y": 12 + "h": 8, + "w": 12, + "x": 0, + "y": 24 }, - "id": 30, + "id": 6, "options": { "legend": { "calcs": [], @@ -727,77 +675,162 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P99", + "title": "Total HTTP Requests", "type": "timeseries" - } - ], - "refresh": "5m", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "All", - "value": "$__all" - }, - "description": "BuilderAPI endpoint", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "endpoint", - "options": [ - { - "selected": true, - "text": "All", - "value": "$__all" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" }, - { - "selected": false, - "text": "get_header", - "value": "get_header" + "custom": { + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 }, - { - "selected": false, - "text": "submit_blinded_block", - "value": "submit_blinded_block" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" }, - { - "selected": false, - "text": "register_validator", - "value": "register_validator" + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_duration_seconds", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "HTTP Requests Durations in ms", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] } - ], - "query": "register_validator, get_header, submit_blinded_block", - "queryValue": "", - "skipUrlSync": false, - "type": "custom" - } - ] + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Head Slot", + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] }, "time": { - "from": "now-2d", + "from": "now-15m", "to": "now" }, "timepicker": {}, - "timezone": "browser", - "title": "PBS Metrics", - "uid": "cb_prometheus", - "version": 1, + "timezone": "", + "title": "bolt-prometheus-holesky", + "uid": "bolt-prometheus-holesky", + "version": 3, "weekStart": "" -} \ No newline at end of file +} diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json deleted file mode 100644 index 93b649d80..000000000 --- a/testnets/holesky/grafana/dashboards/system_metrics.json +++ /dev/null @@ -1,853 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "Prometheus as the datasource is obligatory", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "7.4.5" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "table", - "name": "Table", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": 14282, - "graphTooltip": 0, - "id": null, - "iteration": 1617715580880, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 8, - "panels": [], - "title": "CPU", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 15, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "percent", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 11, - "panels": [], - "title": "Memory", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "hiddenSeries": false, - "id": 9, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "hiddenSeries": false, - "id": 14, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Cached", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 17 - }, - "id": 2, - "panels": [], - "title": "Network", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 18 - }, - "hiddenSeries": false, - "id": 4, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "sideWidth": null, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Received Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:674", - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:675", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 18 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Sent Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:832", - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:833", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 26 - }, - "id": 19, - "panels": [], - "title": "Misc", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "id" - }, - "properties": [ - { - "id": "custom.width", - "value": 260 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Running" - }, - "properties": [ - { - "id": "unit", - "value": "d" - }, - { - "id": "decimals", - "value": 1 - }, - { - "id": "custom.displayMode", - "value": "color-text" - }, - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 24, - "x": 0, - "y": 27 - }, - "id": 17, - "options": { - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "7.4.5", - "targets": [ - { - "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Containers Info", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "container_label_com_docker_compose_project", - "container_label_com_docker_compose_project_working_dir", - "image", - "instance", - "name", - "Value", - "container_label_com_docker_compose_service" - ] - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "Value": "Running", - "container_label_com_docker_compose_project": "Label", - "container_label_com_docker_compose_project_working_dir": "Working dir", - "container_label_com_docker_compose_service": "Service", - "image": "Registry Image", - "instance": "Instance", - "name": "Name" - } - } - } - ], - "type": "table" - } - ], - "schemaVersion": 27, - "style": "dark", - "tags": [ - "cadvisor", - "docker" - ], - "templating": { - "list": [ - { - "allValue": ".*", - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "definition": "label_values({__name__=~\"container.*\"},instance)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Host", - "multi": false, - "name": "host", - "options": [], - "query": { - "query": "label_values({__name__=~\"container.*\"},instance)", - "refId": "Prometheus-host-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 5, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": ".*", - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Container", - "multi": false, - "name": "container", - "options": [], - "query": { - "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", - "refId": "Prometheus-container-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Commit-Boost System Metrics", - "uid": "pMEd7m0Mz", - "version": 1, - "description": "Simple exporter for cadvisor only" -} \ No newline at end of file diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml index 91e10e2b4..10df84a1f 100644 --- a/testnets/holesky/grafana/datasources/datasources.yml +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -6,6 +6,6 @@ datasources: uid: bolt-prometheus-holesky access: proxy orgId: 1 - url: http://bolt-prometheus-holesky:49090 + url: http://bolt-prometheus-holesky:9090 isDefault: true editable: true diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json index 6cd87cbc6..1b14e8d5e 100644 --- a/testnets/holesky/targets.json +++ b/testnets/holesky/targets.json @@ -1,14 +1,8 @@ [ { - "targets": ["bolt-sidecar-holesky:8017"], + "targets": ["bolt-sidecar-holesky:9091"], "labels": { - "job": "bolt-sidecar-rpc" - } - }, - { - "targets": ["bolt-sidecar-holesky:18550"], - "labels": { - "job": "bolt-sidecar-builder-proxy" + "job": "bolt-sidecar-holesky" } } ] From 0ee6fc64c4076749342de72347af817ad034e0d4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 17:14:31 +0200 Subject: [PATCH 123/272] chore(holesky): update rc image for Helix --- testnets/holesky/docker-compose.pbs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index 362dc1c2e..27034a7a5 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -64,7 +64,7 @@ services: ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: - image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc2 restart: unless-stopped depends_on: - db From dac00cc2762d257a360b02050e56f892e9d81438 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 17:25:00 +0200 Subject: [PATCH 124/272] fix(holesky): remove toml volume from dockerfile --- testnets/holesky/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index e0c54cbc7..756708a95 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -9,7 +9,6 @@ services: entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar env_file: ./bolt-sidecar.env volumes: - - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" bolt-mev-boost-holesky: From cd84836e134e752dbd8d6468d9a55f1f0e5cccfd Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 18:16:48 +0200 Subject: [PATCH 125/272] feat(holesky): cadvisor dashboard stub --- testnets/holesky/docker-compose.yml | 11 + .../grafana/dashboards/system_metrics.json | 829 ++++++++++++++++++ testnets/holesky/targets.json | 6 + 3 files changed, 846 insertions(+) create mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 756708a95..74bdfc514 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -28,6 +28,17 @@ services: - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus + bolt-cadvisor-holesky: + image: gcr.io/cadvisor/cadvisor:latest + container_name: bolt-cadvisor-holesky + restart: unless-stopped + ports: + - "38017:8080" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + bolt-grafana-holesky: image: grafana/grafana:latest container_name: bolt-grafana-holesky diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..ebbad9c48 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/system_metrics.json @@ -0,0 +1,829 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Simple exporter for prometheus only", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 7, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "CPU", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 15, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(rate(container_cpu_usage_seconds_total{name=~\"bolt-.*-holesky\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Memory", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 9, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(container_memory_rss{name=~\"bolt-.*-holesky\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 14, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(container_memory_cache{name=~\"bolt-.*-holesky\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Cached", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Network", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 4, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Received Network Traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 6, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Sent Network Traffic", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "custom": { + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "(time() - container_start_time_seconds{name=~\"bolt-.*-holesky\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": ["prometheus", "docker"], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "System Metrics", + "uid": "pMEd7m0Mz", + "version": 9, + "weekStart": "" +} + diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json index 1b14e8d5e..52546f538 100644 --- a/testnets/holesky/targets.json +++ b/testnets/holesky/targets.json @@ -4,5 +4,11 @@ "labels": { "job": "bolt-sidecar-holesky" } + }, + { + "targets": ["bolt-cadvisor-holesky:8080"], + "labels": { + "job": "bolt-cadvisor-holesky" + } } ] From 59c61f43620667f22a8e3113f8b5e52d3118ca36 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 12:17:38 +0200 Subject: [PATCH 126/272] fix(sidecar): test/CI --- bolt-sidecar/src/test_util.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index 6eace3748..c0bac0ddd 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -90,6 +90,7 @@ pub(crate) async fn get_test_config() -> Option { "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY", EcdsaSecretKeyWrapper::random().to_string(), ); + env::set_var("BOLT_SIDECAR_VALIDATOR_INDEXES", "0..64"); let _ = dotenvy::dotenv(); From 6df15597ebd777a8364addd8068e6e8e886b99c8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 12:31:11 +0200 Subject: [PATCH 127/272] fix(holesky): restore commit-boost observability previosly removed by mistake --- testnets/holesky/README.md | 13 + .../grafana/dashboards/bolt_dashboard.json | 236 +++++ .../grafana/dashboards/dashboard.json | 984 ++++++++++++++++++ .../grafana/dashboards/dashboards.yml | 13 + .../grafana/dashboards/system_metrics.json | 853 +++++++++++++++ .../grafana/datasources/datasources.yml | 11 + .../holesky/commit-boost/update-grafana.sh | 4 +- 7 files changed, 2112 insertions(+), 2 deletions(-) create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/dashboard.json create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json create mode 100644 testnets/holesky/commit-boost/grafana/datasources/datasources.yml diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b78a75acb..f9924532d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -224,6 +224,19 @@ This will run all modules in Docker containers. > with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be > configured to point the `builder-api` to this port for Bolt to work. +**Observability** + +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `commit-boost/grafana` directory. + +To update these dashboards, run the following command from the `commit-boost` +directory: + +`bash ./update-grafana.sh ` +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. + ## Native Mode (advanced) For running the Bolt Sidecar as a standalone binary you need to have the diff --git a/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json new file mode 100644 index 000000000..ea72e6d2f --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json @@ -0,0 +1,236 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics related to the bolt-sidecar and bolt-boost.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Bolt sidecar", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Latest head", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 4, + "panels": [], + "title": "Bolt boost", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "cb_pbs_constraints_cache_size", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Constraints cache size", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Bolt Metrics", + "uid": "edxnwlpgaw934c", + "version": 3, + "weekStart": "" +} diff --git a/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json b/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json new file mode 100644 index 000000000..94dd06265 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json @@ -0,0 +1,984 @@ +{ + "__inputs": [], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.1.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": true, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "green", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 61, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "cb_pbs_relay_last_slot", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{relay_id}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Last delivered slot", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "thresholds" + }, + "decimals": 6, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 78, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "cb_pbs_relay_header_value / 1e9", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{relay_id}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Last delivered header value", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 12, + "panels": [], + "repeat": "endpoint", + "repeatDirection": "h", + "title": "$endpoint calls", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 12 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 12 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 23 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P50", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 23 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P90", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 23 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P99", + "type": "timeseries" + } + ], + "refresh": "5m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "description": "BuilderAPI endpoint", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "endpoint", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "register_validator", + "value": "register_validator" + }, + { + "selected": false, + "text": "get_header", + "value": "get_header" + }, + { + "selected": false, + "text": "submit_blinded_block", + "value": "submit_blinded_block" + } + ], + "query": "register_validator, get_header, submit_blinded_block", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PBS Metrics", + "uid": "cb_pbs_metrics", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml b/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml new file mode 100644 index 000000000..db50699f9 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: "default" + orgId: 1 + folder: "" + folderUid: "" + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json b/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..93b649d80 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus as the datasource is obligatory", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.4.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 14282, + "graphTooltip": 0, + "id": null, + "iteration": 1617715580880, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Cached", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Received Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:674", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:675", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Sent Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:832", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:833", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": [ + "cadvisor", + "docker" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Commit-Boost System Metrics", + "uid": "pMEd7m0Mz", + "version": 1, + "description": "Simple exporter for cadvisor only" +} \ No newline at end of file diff --git a/testnets/holesky/commit-boost/grafana/datasources/datasources.yml b/testnets/holesky/commit-boost/grafana/datasources/datasources.yml new file mode 100644 index 000000000..6d29d617b --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/datasources/datasources.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: cb-prometheus + type: prometheus + uid: cb_prometheus + access: proxy + orgId: 1 + url: http://cb_prometheus:9090 + isDefault: true + editable: true diff --git a/testnets/holesky/commit-boost/update-grafana.sh b/testnets/holesky/commit-boost/update-grafana.sh index 1f832d32b..4e5a16695 100755 --- a/testnets/holesky/commit-boost/update-grafana.sh +++ b/testnets/holesky/commit-boost/update-grafana.sh @@ -1,5 +1,5 @@ #!/bin/bash # Fetches the latest dashboards from commit-boost main -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ../grafana/dashboards/dashboard.json -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ../grafana/dashboards/system_metrics.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json From c1e26b5b27fee316d6dd17ae5b5eebe648cb6ce0 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 13:48:41 +0200 Subject: [PATCH 128/272] fix(holesky/docs): broken links --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index f9924532d..7f11ad361 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -6,9 +6,9 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) * [Off-Chain Setup](#off-chain-setup) - * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Docker Mode (recommended)](#docker-mode-recommended) * [Commit-Boost Mode](#commit-boost-mode) - * [Native Mode (advanced)](#native-mode-(advanced)) + * [Native Mode (advanced)](#native-mode-advanced) * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) From 4c5fb29cf8de77f5b1aa6ea469a2e08962ff0fad Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 13:56:04 +0200 Subject: [PATCH 129/272] fix(holesky): cAdvisor only scans for the services in the current docker compose setup --- testnets/holesky/docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 74bdfc514..ecd3b2b57 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -38,6 +38,9 @@ services: - /var/run/docker.sock:/var/run/docker.sock:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro + command: + - --housekeeping_interval=10s + - --docker_only bolt-grafana-holesky: image: grafana/grafana:latest From 3a5edcc02085e15b72aafbf32538c663b37f564a Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 14:05:57 +0200 Subject: [PATCH 130/272] fix(holesky): fix conflict --- testnets/holesky/mev-boost.env.example | 4 ---- 1 file changed, 4 deletions(-) diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example index d08dafe03..942149d0b 100644 --- a/testnets/holesky/mev-boost.env.example +++ b/testnets/holesky/mev-boost.env.example @@ -6,11 +6,7 @@ LOG_SERVICE_TAG= # Optional: Add a custom service tag to all DISABLE_LOG_VERSION=false # Set to true to disable logging the version # Server settings -<<<<<<< HEAD BOOST_LISTEN_ADDR=0.0.0.0:18551 # Address for mev-boost server to listen on -======= -BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on ->>>>>>> 4c5fb29cf8de77f5b1aa6ea469a2e08962ff0fad RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup # Relay settings From 12f2c69758f557a53b4502161ee2c8a569cd7811 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 14:06:31 +0200 Subject: [PATCH 131/272] fix(holesky): add custom network to compose --- testnets/holesky/docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index c15cd1d67..66ab25379 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -38,6 +38,8 @@ services: - /var/run/docker.sock:/var/run/docker.sock:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro + networks: + - bolt-default command: - --housekeeping_interval=10s - --docker_only @@ -51,6 +53,8 @@ services: - ./grafana/dashboards:/etc/grafana/provisioning/dashboards - ./grafana/datasources:/etc/grafana/provisioning/datasources - grafana-data:/var/lib/grafana + networks: + - bolt-default depends_on: - bolt-prometheus-holesky From eaeed3f996313cf1375d4b455256db519bf98f2e Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 14:20:14 +0200 Subject: [PATCH 132/272] fix(holesky): typo --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 7f11ad361..c2847c66f 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -344,7 +344,7 @@ to configure such sidecar options properly. After you've set up the configuration file you can run the Bolt sidecar with ```bash -./bolt-sidecar-cli +./bolt-sidecar ``` ### Observability From de9e97e8843bf97f536aa0929abee9339367039e Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 16:00:17 +0200 Subject: [PATCH 133/272] fix(holesky): genesis fork version on builder --- testnets/holesky/scripts/run-builder.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh index c1bc12035..4af4bbf93 100755 --- a/testnets/holesky/scripts/run-builder.sh +++ b/testnets/holesky/scripts/run-builder.sh @@ -28,5 +28,6 @@ geth --datadir=/var/lib/chaindata/geth \ --builder \ --builder.remote_relay_endpoint=http://helix-relay:4040 \ --builder.beacon_endpoints=http://beacon:4000 \ + --builder.genesis_fork_version=0x01017000 \ --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ --miner.extradata="Bolt Builder" From f0c3eea8357df47dae8fee90c3a069d6e100f91c Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Sun, 27 Oct 2024 10:11:41 +0100 Subject: [PATCH 134/272] fix(holesky): cb port description --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index c2847c66f..0b8e83ca0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -220,8 +220,8 @@ commit-boost start --docker cb.docker-compose.yml --env .cb.env This will run all modules in Docker containers. > [!IMPORTANT] -> The `bolt-boost` service will be exposed at `pbs.port` (18551 by default, set -> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be +> The `bolt-sidecar` service will be exposed at 18551 by default, set +> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`, and your beacon node MUST be > configured to point the `builder-api` to this port for Bolt to work. **Observability** From d5450f0f29848ad63f6c8655987f634d01e56e2d Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 10:22:28 +0100 Subject: [PATCH 135/272] docs(holesky): document cb status --- testnets/holesky/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 0b8e83ca0..c4bc81e6a 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -168,6 +168,10 @@ can find at `http://localhost:28017`. ## Commit-Boost Mode +> [!IMPORTANT] +> Running with commit-boost is still in the process of being tested. Keep track of the issue +> here: https://github.com/chainbound/bolt/issues/328. + First download the `commit-boost-cli` binary from the Commit-Boost [official releases page](https://github.com/Commit-Boost/commit-boost-client/releases) From b5f67ba8d8d8b0548118698b45ebd3c6f853cf27 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 15 Oct 2024 12:13:09 +0200 Subject: [PATCH 136/272] chore(testnet): holesky launch folder stub --- testnets/holesky/README.md | 106 +++ testnets/holesky/cb-bolt-config.toml | 158 ++++ .../grafana/dashboards/bolt_dashboard.json | 238 +++++ .../holesky/grafana/dashboards/dashboard.json | 803 +++++++++++++++++ .../holesky/grafana/dashboards/dashboards.yml | 13 + .../grafana/dashboards/system_metrics.json | 853 ++++++++++++++++++ .../grafana/datasources/datasources.yml | 11 + testnets/holesky/keys.json | 4 + testnets/holesky/prometheus.yml | 8 + testnets/holesky/targets.json | 34 + testnets/holesky/update-grafana.sh | 5 + 11 files changed, 2233 insertions(+) create mode 100644 testnets/holesky/README.md create mode 100644 testnets/holesky/cb-bolt-config.toml create mode 100644 testnets/holesky/grafana/dashboards/bolt_dashboard.json create mode 100644 testnets/holesky/grafana/dashboards/dashboard.json create mode 100644 testnets/holesky/grafana/dashboards/dashboards.yml create mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json create mode 100644 testnets/holesky/grafana/datasources/datasources.yml create mode 100644 testnets/holesky/keys.json create mode 100644 testnets/holesky/prometheus.yml create mode 100644 testnets/holesky/targets.json create mode 100755 testnets/holesky/update-grafana.sh diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md new file mode 100644 index 000000000..c031285f3 --- /dev/null +++ b/testnets/holesky/README.md @@ -0,0 +1,106 @@ +# Holesky Launch Instructions + +## Components + +The components that need to run to test Bolt on Holesky are: + +- A synced execution client +- A synced beacon node +- Active validators +- Commit-Boost with Bolt configuration + +## Setup + +### Commit-Boost + +#### Installation + +To install the `commit-boost` CLI with `cargo`: + +```bash +# Use specific commit hash to ensure compatibility +cargo install --locked --git https://github.com/Commit-Boost/commit-boost-client --rev aed00e8 commit-boost + +# Test installation +commit-boost --version +``` + +#### Configuration + +A commit-boost configuration file with Bolt support is provided at +[`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the +custom PBS module ([bolt-boost](../../bolt-boost)) that implements the +[constraints-API](https://chainbound.github.io/bolt-docs/api/builder), as well +as the [bolt-sidecar](../../bolt-sidecar) module. This file can be used as a +template for your own configuration. + +The important fields to configure are under the `[modules.env]` section of the +`BOLT` module, which contain the environment variables to configure the bolt +sidecar: + +```toml +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" + +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module (static) +BOLT_SIDECAR_BEACON_API = "" +BOLT_SIDECAR_EXECUTION_API = "" +BOLT_SIDECAR_ENGINE_API = "" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "" # The engine JWT used to authenticate with the engine API +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "" # The active validator indexes (can be defined as a comma-separated list, or a range) + # e.g. "0,1,2,3,4" or "0..4", or a combination of both +``` + +To initialize commit-boost, run the following command: + +```bash +commit-boost init --config cb-bolt-config.toml +``` + +This will create 3 files: + +- `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services +- `.cb.env`: with local env variables, including JWTs for modules +- `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus + +#### Running + +The final step is to run the Commit-Boost services. This can be done with the following command: + +```bash +commit-boost start --docker cb.docker-compose.yml --env .cb.env +``` + +This will run all modules in Docker containers. + +> [!IMPORTANT] +> bolt-boost will be exposed at `pbs.port` (18551 by default, set with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be configured +> to point the `builder-api` to this port for Bolt to work. + +### Bolt Sidecar + +WIP + +### Observability + +commit-boost comes with various observability tools, such as Prometheus, cadvisor, and Grafana. It also comes with some pre-built dashboards, +which can be found in the `grafana` directory. + +To update these dashboards, run the following command: + +```bash +./update-grafana.sh +``` + +In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. + +### Validators + +Validators must be configured to always prefer builder proposals over their own. Refer to client documentation for the specific configuration flags. +**If this is not set, it could lead to commitment faults**. + +#### Registration + +WIP diff --git a/testnets/holesky/cb-bolt-config.toml b/testnets/holesky/cb-bolt-config.toml new file mode 100644 index 000000000..be4e35002 --- /dev/null +++ b/testnets/holesky/cb-bolt-config.toml @@ -0,0 +1,158 @@ +# The main configuration file for the Commit-Boost sidecar. +# Some fields are optional and can be omitted, in which case the default value, if present, will be used. + +# Chain spec id. Supported values: Mainnet, Holesky, Helder +chain = "Holesky" + +# Configuration for the PBS module +[pbs] +# Docker image to use for the PBS module. +# BOLT: We use the bolt-boost PBS module here. +docker_image = "ghcr.io/chainbound/bolt-boost:v0.3.0-alpha-rc.1" +# Whether to enable the PBS module to request signatures from the Signer module (not used in the default PBS image) +# OPTIONAL, DEFAULT: false +with_signer = false +# Port to receive BuilderAPI calls from beacon node +port = 18550 +# Whether to forward `status` calls to relays or skip and return 200 +# OPTIONAL, DEFAULT: true +relay_check = true +# Timeout in milliseconds for the `get_header` call to relays. Note that the CL has also a timeout (e.g. 1 second) so +# this should be lower than that, leaving some margin for overhead +# OPTIONAL, DEFAULT: 950 +timeout_get_header_ms = 950 +# Timeout in milliseconds for the `submit_blinded_block` call to relays. +# OPTIONAL, DEFAULT: 4000 +timeout_get_payload_ms = 4000 +# Timeout in milliseconds for the `register_validator` call to relays. +# OPTIONAL, DEFAULT: 3000 +timeout_register_validator_ms = 3000 +# Whether to skip signature verification of headers against the relay pubkey +# OPTIONAL, DEFAULT: false +skip_sigverify = false +# Minimum bid in ETH that will be accepted from `get_header` +# OPTIONAL, DEFAULT: 0.0 +min_bid_eth = 0.0 +# List of URLs of relay monitors to send registrations to +# OPTIONAL +relay_monitors = [] +# How late in milliseconds in the slot is "late". This impacts the `get_header` requests, by shortening timeouts for `get_header` calls to +# relays and make sure a header is returned within this deadline. If the request from the CL comes later in the slot, then fetching headers is skipped +# to force local building and miniminzing the risk of missed slots. See also the timing games section below +# OPTIONAL, DEFAULT: 2000 +late_in_slot_time_ms = 1000 + +# The PBS module needs one or more [[relays]] as defined below. +[[relays]] +# Relay ID to use in telemetry +# OPTIONAL, DEFAULT: URL hostname +id = "example-relay" +# Relay URL in the format scheme://pubkey@host +url = "http://0xa1cec75a3f0661e99299274182938151e8433c61a19222347ea1313d839229cb4ce4e3e5aa2bdeb71c8fcf1b084963c2@abc.xyz" +# Headers to send with each request for this relay +# OPTIONAL +# headers = { X-MyCustomHeader = "MyCustomValue" } +# Whether to enable timing games, as tuned by `target_first_request_ms` and `frequency_get_header_ms`. +# These values should be carefully chosen for each relay, as each relay has different latency and timing games setups. +# They should only be used by advanced users, and if mis-configured can result in unforeseen effects, e.g. fetching a lower header value, +# or getting a temporary IP ban. +# +# EXAMPLES +# Assuming: timeout_get_header_ms = 950, frequency_get_header_ms = 300, target_first_request_ms = 200, late_in_slot_time_ms = 2000 +# +# 1) CL request comes at 100ms in the slot (max timeout 1050ms in the slot), then: +# - sleep for 100ms +# - send request at 200ms with 850ms timeout +# - send request at 500ms with 550ms timeout +# - send request at 800ms with 250ms timeout +# 2) CL request comes at 1500ms in the slot (max timeout 2000ms in the slot), then: +# - send request at 1500ms with 500ms timeout +# - send request at 1800ms with 200ms timeout +# 3) CL request comes 2500ms in the slot then: +# - return 204 and force local build +# +# OPTIONAL, DEFAULT: false +enable_timing_games = false +# Target time in slot when to send the first header request +# OPTIONAL +target_first_request_ms = 200 +# Frequency in ms to send get_header requests +# OPTIONAL +frequency_get_header_ms = 300 + +# Configuration for the Signer Module, only required if any `commit` module is present, or if `pbs.with_signer = true` +# OPTIONAL +[signer] +# Docker image to use for the Signer module. +# OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest +docker_image = "commitboost_signer" +# Configuration for how the Signer module should load validator keys. Currently two types of loaders are supported: +# - File: load keys from a plain text file (unsafe, use only for testing purposes) +# - ValidatorsDir: load keys from a `keys` and `secrets` folder (ERC-2335 style keystores as used in Lighthouse) +[signer.loader] +# File: path to the keys file +key_path = "./keys.json" +# ValidatorsDir: path to the keys directory +# keys_path = "" +# ValidatorsDir: path to the secrets directory +# secrets_path = "" + +# Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. +# Currently, two types of modules are supported: +# - "commit": modules which request commitment signatures from the validator keys +# - "events": modules which callback to BuilderAPI events as triggered from the PBS modules, used e.g. for monitoring +# If any "commit" module is present, then the [signer] section should also be configured +# OPTIONAL +[[modules]] +# Unique ID of the module +id = "BOLT" +# Type of the module. Supported values: commit, events +type = "commit" +# Docker image of the module +docker_image = "ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1" + +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" + +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module +BOLT_SIDECAR_BEACON_API = "http://100.85.200.41:4400" +BOLT_SIDECAR_EXECUTION_API = "http://100.85.200.41:4485" +BOLT_SIDECAR_ENGINE_API = "http://100.85.200.41:4451" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "89732cef77d7e9a20021ee8f419dbbb51bdf7f60586932c272ddef02e70cb755" # The engine JWT +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "0x0000000000000000000000000000000000000000" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "1..2" # The active validator indexes + +BOLT_SIDECAR_METRICS_PORT = "10000" + +# Configuration for how metrics should be collected and scraped +# OPTIONAL, skip metrics collection if missing +[metrics] +# Path to a `prometheus.yml` file to use in Prometheus. If using a custom config file, be sure to add a +# file discovery section as follows: +# ```yml +# file_sd_configs: +# - files: +# - /etc/prometheus/targets.json +# ``` +# and use the `targets.json` file generated by `commit-boost init` +prometheus_config = "./prometheus.yml" +# Whether to start Grafana with built-in dashboards +# OPTIONAL, DEFAULT: true +use_grafana = true +# Whether to start cadvisor for system monitoring +# OPTIONAL, DEFAULT: true +use_cadvisor = true + +# Configuration for how logs should be collected and stored +# OPTIONAL, info to stdout if missing +[logs] +# Path to the log directory +# OPTIONAL, DEFAULT: /var/logs/commit-boost +log_dir_path = "./logs" +# Log level. Supported values: trace, debug, info, warn, error +# OPTIONAL, DEFAULT: debug to file, info to stdout +log_level = "debug" +# Maximum number of log files to keep +# OPTIONAL +max_log_files = 30 diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json new file mode 100644 index 000000000..886ddfe02 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/bolt_dashboard.json @@ -0,0 +1,238 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics related to the bolt-sidecar and bolt-boost.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Bolt sidecar", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Latest head", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 4, + "panels": [], + "title": "Bolt boost", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "cb_pbs_constraints_cache_size", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Constraints cache size", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Bolt Metrics", + "uid": "edxnwlpgaw934c", + "version": 3, + "weekStart": "" +} diff --git a/testnets/holesky/grafana/dashboards/dashboard.json b/testnets/holesky/grafana/dashboards/dashboard.json new file mode 100644 index 000000000..f903affb2 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/dashboard.json @@ -0,0 +1,803 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": true, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 12, + "panels": [], + "repeat": "endpoint", + "repeatDirection": "h", + "title": "$endpoint calls", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 1 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 1 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P50", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P90", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 12 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P99", + "type": "timeseries" + } + ], + "refresh": "5m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": true, + "text": "All", + "value": "$__all" + }, + "description": "BuilderAPI endpoint", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "endpoint", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "get_header", + "value": "get_header" + }, + { + "selected": false, + "text": "submit_blinded_block", + "value": "submit_blinded_block" + }, + { + "selected": false, + "text": "register_validator", + "value": "register_validator" + } + ], + "query": "register_validator, get_header, submit_blinded_block", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PBS Metrics", + "uid": "cb_prometheus", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/testnets/holesky/grafana/dashboards/dashboards.yml b/testnets/holesky/grafana/dashboards/dashboards.yml new file mode 100644 index 000000000..db50699f9 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/dashboards.yml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: "default" + orgId: 1 + folder: "" + folderUid: "" + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..93b649d80 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/system_metrics.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus as the datasource is obligatory", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.4.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 14282, + "graphTooltip": 0, + "id": null, + "iteration": 1617715580880, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Cached", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Received Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:674", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:675", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Sent Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:832", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:833", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": [ + "cadvisor", + "docker" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Commit-Boost System Metrics", + "uid": "pMEd7m0Mz", + "version": 1, + "description": "Simple exporter for cadvisor only" +} \ No newline at end of file diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml new file mode 100644 index 000000000..6d29d617b --- /dev/null +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: cb-prometheus + type: prometheus + uid: cb_prometheus + access: proxy + orgId: 1 + url: http://cb_prometheus:9090 + isDefault: true + editable: true diff --git a/testnets/holesky/keys.json b/testnets/holesky/keys.json new file mode 100644 index 000000000..4689c5d75 --- /dev/null +++ b/testnets/holesky/keys.json @@ -0,0 +1,4 @@ +[ + "0088e364a5396a81b50febbdc8784663fb9089b5e67cbdc173991a00c587673f", + "0x16f3bec1b7f4f1b87c5e1930f944a6dda76ad211a89bc98e8c8e88b5069f8a04" +] diff --git a/testnets/holesky/prometheus.yml b/testnets/holesky/prometheus.yml new file mode 100644 index 000000000..91b5d5905 --- /dev/null +++ b/testnets/holesky/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "commit-boost" + file_sd_configs: + - files: + - /etc/prometheus/targets.json diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json new file mode 100644 index 000000000..6eb23eea1 --- /dev/null +++ b/testnets/holesky/targets.json @@ -0,0 +1,34 @@ +[ + { + "targets": [ + "cb_bolt:10000" + ], + "labels": { + "job": "cb_bolt" + } + }, + { + "targets": [ + "cb_pbs:10000" + ], + "labels": { + "job": "pbs" + } + }, + { + "targets": [ + "cb_signer:10000" + ], + "labels": { + "job": "signer" + } + }, + { + "targets": [ + "cb_cadvisor:8080" + ], + "labels": { + "job": "cadvisor" + } + } +] \ No newline at end of file diff --git a/testnets/holesky/update-grafana.sh b/testnets/holesky/update-grafana.sh new file mode 100755 index 000000000..4e5a16695 --- /dev/null +++ b/testnets/holesky/update-grafana.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# Fetches the latest dashboards from commit-boost main +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json From 4afe62060b0479537cfc2aa8a58f42e696afd5f7 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:25:01 +0200 Subject: [PATCH 137/272] git(sidecar): add bolt-sidecar binaries to .gitignore --- bolt-sidecar/.gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 55509a805..2e73cb821 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -1,4 +1,4 @@ target/ -.env -.env.* +.env* !.env.example +bolt-sidecar* From 54160326dc438c7bba187dcd254cfecbfc73a5f4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:25:35 +0200 Subject: [PATCH 138/272] docker(sidecar): remove redundant dependencies during build --- bolt-sidecar/Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bolt-sidecar/Dockerfile b/bolt-sidecar/Dockerfile index 99c0f839c..fd3e55d37 100644 --- a/bolt-sidecar/Dockerfile +++ b/bolt-sidecar/Dockerfile @@ -23,10 +23,7 @@ FROM base AS builder RUN apt-get update && apt-get install -y \ pkg-config \ libssl-dev \ - build-essential \ - perl \ - gcc \ - make + build-essential # Copy the generated recipe from the planner stage COPY --from=planner /app/recipe.json recipe.json From d3f9b0cb1f9e1fced48fb621ecc18a65b604ea32 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:12:48 +0200 Subject: [PATCH 139/272] chore!(sidecar): rename env for telemetry options --- bolt-sidecar/src/config/telemetry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index 33a2f3ac8..b0ef87da4 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -4,9 +4,9 @@ use serde::Deserialize; #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics - #[clap(short, long, env = "METRICS_PORT", default_value_t = 3300)] + #[clap(short, long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] metrics_port: u16, - #[clap(short, long, env = "DISABLE_METRICS", default_value_t = false)] + #[clap(short, long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] disable_metrics: bool, } From 82aac7f8d15fd924d60a0be3f3b5e17dac00d7d6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:17:20 +0200 Subject: [PATCH 140/272] chore(sidecar): update .env.example --- bolt-sidecar/.env.example | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index cb234a599..98bf60852 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,33 +1,34 @@ -# Ethereum node connections +# Ethereum Node Connections + PBS URLs +BOLT_SIDECAR_PORT=8000 BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485 BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400 BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 -BOLT_SIDECAR_ENGINE_JWT_HEX= - -# Constraint URL: should point to the constraint API sidecar. -# Usually this corresponds to `mev-boost` or `bolt-boost` BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 - -# Commit-boost specific options (optional) -BOLT_SIDECAR_CB_SIGNER_URL=http://localhost:19551 -BOLT_SIDECAR_CB_JWT_HEX= - -# server ports -BOLT_SIDECAR_PORT=8000 BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 +BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 +BOLT_SIDECAR_JWT_HEX= +BOLT_SIDECAR_FEE_RECIPIENT= +BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -# commitment limits +# Commitments configs BOLT_SIDECAR_MAX_COMMITMENTS=128 -BOLT_SIDECAR_MAX_COMMITTED_GAS=10000000 - -# chain configs -BOLT_SIDECAR_CHAIN=holesky +BOLT_SIDECAR_MAX_COMMITTED_GAS= +BOLT_SIDECAR_MIN_PRIORITY_FEE= BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 + +# Chain configs +BOLT_SIDECAR_CHAIN=Holesky BOLT_SIDECAR_SLOT_TIME=12 -# sidecar security configs -BOLT_SIDECAR_VALIDATOR_INDEXES= -BOLT_SIDECAR_FEE_RECIPIENT= -BOLT_SIDECAR_BUILDER_PRIVATE_KEY= +# Signing options BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= + +# Metrics +BOLT_SIDECAR_METRICS_PORT= +BOLT_SIDECAR_DISABLE_METRICS= From cc83818463ccea448b43f73f5262ef81db3f8be7 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 11:46:16 +0200 Subject: [PATCH 141/272] chore(sidecar): update Config.example.toml --- bolt-sidecar/Config.example.toml | 51 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index b73e925da..e6caad16e 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -1,34 +1,37 @@ -# ports +# Ethereum Node Connections + PBS URLs port = 8000 -metrics_port = 3300 - -# node urls -execution_api_url = "http://localhost:8545" -beacon_api_url = "http://localhost:5052" -engine_api_url = "http://localhost:8551" -engine_jwt_hex = "0x573300d0cd9a8e253429998a3ceecf358aa4868d96772d1344a80144d3b7b593" - -# constraints options -constraints_api_url = "http://localhost:3030" +execution_api_url = "http://localhost:4485" +beacon_api_url = "http://localhost:4400" +engine_api_url = "http://localhost:4451" +constraints_url = "http://localhost:19550" constraints_proxy_port = 18551 - validator_indexes = "0..64" -fee_recipient = "0x0155ef0C0fE550C297c1216585e0DE1478EA30e4" +jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +fee_recipient = "0x0000000000000000000000000000000000000000" +builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -builder_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" -commitment_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +# Commitments configs +max_commitments = 128 +max_committed_gas = 10000000 +min_priority_fee = 5000000 -# chain options +# Chain configs [chain] -chain = "Kurtosis" -slot_time = 2 +chain = "Holesky" +slot_time = 12 commitment_deadline = 8000 -[telemetry] -metrics_port = 3300 -disable_metrics = false - -# signing options +# Signing options [constraint_signing] -constraint_private_key = "0x359c156600e1f5715a58c9e09f17c8d04e7ee3a9f88b39f6e489ffca0148fabe" +constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +cb_signer_url = "http://localhost:18550" +cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +keystore_password = "password" +keystore_path = "./keys" delegations_path = "./delegations.json" + +# Metrics +[telemetry] +metrics_port = 8001 +disable_metrics = false From 91b44ae0a0ae0dc1a91e5051f0229d7992d4d151 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 14:03:17 +0200 Subject: [PATCH 142/272] chore!(sidecar): remove short options from telemetry config --- bolt-sidecar/src/config/telemetry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index b0ef87da4..01bc179d6 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -4,9 +4,9 @@ use serde::Deserialize; #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics - #[clap(short, long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] + #[clap(long, env = "BOLT_SIDECAR_METRICS_PORT", default_value_t = 3300)] metrics_port: u16, - #[clap(short, long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] + #[clap(long, env = "BOLT_SIDECAR_DISABLE_METRICS", default_value_t = false)] disable_metrics: bool, } From 3701587871566c9456bcf2b896e5a39bcb17bd4d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 10:38:24 +0200 Subject: [PATCH 143/272] chore(holesky): add .gitignore --- testnets/holesky/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testnets/holesky/.gitignore diff --git a/testnets/holesky/.gitignore b/testnets/holesky/.gitignore new file mode 100644 index 000000000..ef456d924 --- /dev/null +++ b/testnets/holesky/.gitignore @@ -0,0 +1,2 @@ +*.env* +!*.env.example From e22156d55950ff59003d30e5f545ae14859953fe Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:00:17 +0200 Subject: [PATCH 144/272] chore(mev-boost): add .env.example --- mev-boost/.env.example | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 mev-boost/.env.example diff --git a/mev-boost/.env.example b/mev-boost/.env.example new file mode 100644 index 000000000..447b2fab8 --- /dev/null +++ b/mev-boost/.env.example @@ -0,0 +1,32 @@ +# Logging settings +LOG_JSON=false # Set to true to log in JSON format +LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic +DEBUG=false # Set to true to enable debug mode +LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=false # Set to true to disable logging the version + +# Server settings +BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup + +# Relay settings +RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) +RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) +MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) + +# Relay timeout settings (in ms) +RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay +RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay +RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests + +# Genesis settings +GENESIS_FORK_VERSION= # Custom genesis fork version +GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) + +# Network settings +SEPOLIA=false # Set to true to use Sepolia network +GOERLI=false # Set to true to use Goerli network +HOLESKY=false # Set to true to use Holesky network + +# Retry settings +REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From 31ac432b8dab1c9643e8179f6776053cd346801b Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:23:12 +0200 Subject: [PATCH 145/272] chore(mev-boost): update .gitignore --- mev-boost/.env.example | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 mev-boost/.env.example diff --git a/mev-boost/.env.example b/mev-boost/.env.example deleted file mode 100644 index 447b2fab8..000000000 --- a/mev-boost/.env.example +++ /dev/null @@ -1,32 +0,0 @@ -# Logging settings -LOG_JSON=false # Set to true to log in JSON format -LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic -DEBUG=false # Set to true to enable debug mode -LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries -DISABLE_LOG_VERSION=false # Set to true to disable logging the version - -# Server settings -BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on -RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup - -# Relay settings -RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) -RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) -MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) - -# Relay timeout settings (in ms) -RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay -RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay -RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests - -# Genesis settings -GENESIS_FORK_VERSION= # Custom genesis fork version -GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) - -# Network settings -SEPOLIA=false # Set to true to use Sepolia network -GOERLI=false # Set to true to use Goerli network -HOLESKY=false # Set to true to use Holesky network - -# Retry settings -REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From ce27645b9ba57dbf3fbad1c00db875db2a4f6bca Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:57:00 +0200 Subject: [PATCH 146/272] chore(holesky): update docker compose setup --- testnets/holesky/docker-compose.yml | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 testnets/holesky/docker-compose.yml diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml new file mode 100644 index 000000000..52ef4da0c --- /dev/null +++ b/testnets/holesky/docker-compose.yml @@ -0,0 +1,58 @@ +services: + bolt-sidecar: + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1 + container_name: bolt-sidecar + restart: unless-stopped + ports: + - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) + - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" + env_file: ./bolt-sidecar.env + entrypoint: /bin/sh -c /bolt-sidecar + + mev-boost: + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha-rc.1 + container_name: mev-boost + restart: unless-stopped + env_file: ./mev-boost.env + entrypoint: /bin/sh -c '/app/mev-boost' + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - 9090:9090 + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - ./targets.json:/etc/prometheus/targets.json + - prometheus-data:/prometheus + networks: + - monitoring_network + + grafana: + image: grafana/grafana:latest + container_name: cb_grafana + ports: + - 3000:3000 + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - ./grafana/dashboards:/etc/grafana/provisioning/dashboards + - ./grafana/datasources:/etc/grafana/provisioning/datasources + - grafana-data:/var/lib/grafana + networks: + - monitoring_network + depends_on: + - prometheus + logging: + driver: none + +volumes: + prometheus-data: + driver: local + grafana-data: + driver: local +networks: + monitoring_network: + driver: bridge + signer_network: + driver: bridge From a02f72554d3710a86cd04cdd3e82a02aeb15d356 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 17 Oct 2024 11:57:36 +0200 Subject: [PATCH 147/272] chore(holesky): move commit-boost config in separate directory --- testnets/holesky/{ => commit-boost}/cb-bolt-config.toml | 2 +- testnets/holesky/commit-boost/cb-prometheus.yml | 8 ++++++++ testnets/holesky/{ => commit-boost}/keys.json | 0 testnets/holesky/{ => commit-boost}/targets.json | 0 testnets/holesky/{ => commit-boost}/update-grafana.sh | 4 ++-- testnets/holesky/prometheus.yml | 4 ++-- 6 files changed, 13 insertions(+), 5 deletions(-) rename testnets/holesky/{ => commit-boost}/cb-bolt-config.toml (99%) create mode 100644 testnets/holesky/commit-boost/cb-prometheus.yml rename testnets/holesky/{ => commit-boost}/keys.json (100%) rename testnets/holesky/{ => commit-boost}/targets.json (100%) rename testnets/holesky/{ => commit-boost}/update-grafana.sh (51%) diff --git a/testnets/holesky/cb-bolt-config.toml b/testnets/holesky/commit-boost/cb-bolt-config.toml similarity index 99% rename from testnets/holesky/cb-bolt-config.toml rename to testnets/holesky/commit-boost/cb-bolt-config.toml index be4e35002..f78b0fc8d 100644 --- a/testnets/holesky/cb-bolt-config.toml +++ b/testnets/holesky/commit-boost/cb-bolt-config.toml @@ -136,7 +136,7 @@ BOLT_SIDECAR_METRICS_PORT = "10000" # - /etc/prometheus/targets.json # ``` # and use the `targets.json` file generated by `commit-boost init` -prometheus_config = "./prometheus.yml" +prometheus_config = "./cb-prometheus.yml" # Whether to start Grafana with built-in dashboards # OPTIONAL, DEFAULT: true use_grafana = true diff --git a/testnets/holesky/commit-boost/cb-prometheus.yml b/testnets/holesky/commit-boost/cb-prometheus.yml new file mode 100644 index 000000000..91b5d5905 --- /dev/null +++ b/testnets/holesky/commit-boost/cb-prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "commit-boost" + file_sd_configs: + - files: + - /etc/prometheus/targets.json diff --git a/testnets/holesky/keys.json b/testnets/holesky/commit-boost/keys.json similarity index 100% rename from testnets/holesky/keys.json rename to testnets/holesky/commit-boost/keys.json diff --git a/testnets/holesky/targets.json b/testnets/holesky/commit-boost/targets.json similarity index 100% rename from testnets/holesky/targets.json rename to testnets/holesky/commit-boost/targets.json diff --git a/testnets/holesky/update-grafana.sh b/testnets/holesky/commit-boost/update-grafana.sh similarity index 51% rename from testnets/holesky/update-grafana.sh rename to testnets/holesky/commit-boost/update-grafana.sh index 4e5a16695..1f832d32b 100755 --- a/testnets/holesky/update-grafana.sh +++ b/testnets/holesky/commit-boost/update-grafana.sh @@ -1,5 +1,5 @@ #!/bin/bash # Fetches the latest dashboards from commit-boost main -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ../grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ../grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/prometheus.yml b/testnets/holesky/prometheus.yml index 91b5d5905..bce41074a 100644 --- a/testnets/holesky/prometheus.yml +++ b/testnets/holesky/prometheus.yml @@ -1,8 +1,8 @@ global: - scrape_interval: 15s + scrape_interval: 5s scrape_configs: - - job_name: "commit-boost" + - job_name: "bolt-sidecar" file_sd_configs: - files: - /etc/prometheus/targets.json From 022ef5a090827f0a96536cebb132818f0ab31b38 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 12:34:06 +0200 Subject: [PATCH 148/272] fix(delegations-cli): non-descriptive error message --- bolt-cli/src/common/keystore.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bolt-cli/src/common/keystore.rs b/bolt-cli/src/common/keystore.rs index 26810e11b..464a1e5cd 100644 --- a/bolt-cli/src/common/keystore.rs +++ b/bolt-cli/src/common/keystore.rs @@ -51,7 +51,9 @@ impl KeystoreSecret { /// Load the keystore passwords from a directory containing individual password files. pub fn from_directory(root_dir: &str) -> Result { let mut secrets = HashMap::new(); - for entry in fs::read_dir(root_dir)? { + for entry in fs::read_dir(&root_dir) + .wrap_err(format!("failed to read secrets directory. path: {}", &root_dir))? + { let entry = entry.wrap_err("Failed to read secrets directory entry")?; let path = entry.path(); @@ -108,12 +110,14 @@ impl Drop for KeystoreSecret { /// -- ... /// Reference: https://github.com/chainbound/bolt/blob/4634ff905561009e4e74f9921dfdabf43717010f/bolt-sidecar/src/signer/keystore.rs#L109 pub fn keystore_paths(keys_path: &str) -> Result> { - let keys_path = Path::new(keys_path).to_path_buf(); + let keys_path_buf = Path::new(keys_path).to_path_buf(); let json_extension = OsString::from("json"); let mut keystores_paths = vec![]; // Iter over the `keys` directory - for entry in read_dir(keys_path)? { + for entry in read_dir(keys_path_buf) + .wrap_err(format!("failed to read keys directory. path: {keys_path}"))? + { let path = read_path(entry)?; if path.is_dir() { for entry in read_dir(path)? { From e0208ad957d08bd24c4d941ea6b90d761c6f74f8 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:45:07 +0200 Subject: [PATCH 149/272] wip: pbs configs --- testnets/holesky/docker-compose.pbs.yml | 69 +++++++++++++++++++++++++ testnets/holesky/docker-compose.yml | 4 +- testnets/holesky/helix-config.yml | 17 ++++++ testnets/holesky/scripts/run-bn.sh | 28 ++++++++++ testnets/holesky/scripts/run-builder.sh | 32 ++++++++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 testnets/holesky/docker-compose.pbs.yml create mode 100644 testnets/holesky/helix-config.yml create mode 100644 testnets/holesky/scripts/run-bn.sh create mode 100644 testnets/holesky/scripts/run-builder.sh diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml new file mode 100644 index 000000000..be3a3a003 --- /dev/null +++ b/testnets/holesky/docker-compose.pbs.yml @@ -0,0 +1,69 @@ +volumes: + psql_data: + driver: local + chaindata: + driver: local + +services: + redis: + image: redis + restart: unless-stopped + + db: + image: timescale/timescaledb-ha:pg16 + restart: unless-stopped + volumes: + - "psql_data:/var/lib/postgresql/data" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: helixdb + + adminer: + image: adminer + restart: unless-stopped + depends_on: + - db + ports: + - "48093:8080" + environment: + ADMINER_PLUGINS: tables-filter tinymce + + builder: + image: ghcr.io/chainbound/bolt-builder:v0.3.0-alpha.rc1 + restart: unless-stopped + volumes: + - "chaindata:/var/lib/chaindata" + - "./shared:/var/lib/shared" + - "./scripts/run-builder.sh:/scripts/run-builder.sh" + environment: + BUILDER_TX_SIGNING_KEY: "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710" + ports: + - "30367:30303/tcp" + - "30367:30303/udp" + # entrypoint is builder + entrypoint: /scripts/run-builder.sh + + beacon: + image: sigp/lighthouse:latest + restart: unless-stopped + volumes: + - "chaindata:/var/lib/chaindata" + - "./shared:/var/lib/shared" + - "./scripts/run-bn.sh:/scripts/run-bn.sh" + ports: + - "41050:50050/tcp" + - "41050:50050/udp" + entrypoint: /scripts/run-bn.sh + + helix-relay: + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 + restart: unless-stopped + volumes: + - "./helix-config.yml:/helix-config.yml" + ports: + - "44040:4040" + environment: + - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 + - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + command: --config /helix-config.yml diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 52ef4da0c..2751212df 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,6 +1,6 @@ services: bolt-sidecar: - image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha-rc.1 + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 container_name: bolt-sidecar restart: unless-stopped ports: @@ -10,7 +10,7 @@ services: entrypoint: /bin/sh -c /bolt-sidecar mev-boost: - image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha-rc.1 + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 container_name: mev-boost restart: unless-stopped env_file: ./mev-boost.env diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml new file mode 100644 index 000000000..edeff326e --- /dev/null +++ b/testnets/holesky/helix-config.yml @@ -0,0 +1,17 @@ +postgres: + hostname: db + port: 5432 + db_name: helixdb + user: postgres + password: postgres + +redis: + url: redis://redis:6379 + +simulator: + url: http://builder:8545 + +beacon_clients: + - url: http://beacon:50050 + +network_config: !Holesky diff --git a/testnets/holesky/scripts/run-bn.sh b/testnets/holesky/scripts/run-bn.sh new file mode 100644 index 000000000..5fe326129 --- /dev/null +++ b/testnets/holesky/scripts/run-bn.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +lighthouse beacon_node \ + --network=holesky \ + --debug-level=info \ + --datadir=/var/lib/chaindata \ + --disable-enr-auto-update \ + --enr-udp-port=50050 \ + --enr-tcp-port=50050 \ + --listen-address=0.0.0.0 \ + --port=50050 \ + --http \ + --http-address=0.0.0.0 \ + --http-port=4000 \ + --http-allow-sync-stalled \ + --always-prepare-payload \ + --prepare-payload-lookahead=12000 \ + --slots-per-restore-point=32 \ + --disable-packet-filter \ + --checkpoint-sync-url=https://holesky.beaconstate.info \ + --execution-endpoints=http://builder:8551 \ + --subscribe-all-subnets \ + --metrics \ + --metrics-address=0.0.0.0 \ + --metrics-allow-origin=* \ + --metrics-port=5054 \ + --enable-private-discovery \ + --jwt-secrets=/var/lib/shared/jwtsecret diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh new file mode 100644 index 000000000..d6894ac4e --- /dev/null +++ b/testnets/holesky/scripts/run-builder.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +geth --datadir=/var/lib/chaindata/geth \ + --network=holesky \ + --syncmode=full \ + --gcmode=archive \ + --state.scheme=hash \ + --verbosity=3 \ + --http \ + --http.port=8545 \ + --http.addr=0.0.0.0 \ + --http.vhosts=* \ + --http.corsdomain=* \ + --http.api=admin,engine,net,eth,web3,debug,flashbots,txpool \ + --ws \ + --ws.addr=0.0.0.0 \ + --ws.port=8546 \ + --ws.api=admin,engine,net,eth,web3,debug,flashbots,txpool \ + --ws.origins=* \ + --authrpc.port=8551 \ + --authrpc.addr=0.0.0.0 \ + --authrpc.vhosts=* \ + --authrpc.jwtsecret=/var/lib/shared/jwtsecret \ + --metrics \ + --metrics.addr=0.0.0.0 \ + --metrics.port=6060 \ + --port=30303 \ + --builder \ + --builder.remote_relay_endpoint=http://relay-api:9062 \ + --builder.beacon_endpoints=http://beacon:4000 \ + --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ + --miner.extradata="Bolt Builder" From cc458583c6f48fecc9fc042f282b51df7a47aaa8 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:31:30 +0200 Subject: [PATCH 150/272] feat: working pbs docker compose --- testnets/holesky/docker-compose.pbs.yml | 23 ++++++++++++++++++----- testnets/holesky/helix-config.yml | 2 +- testnets/holesky/scripts/run-bn.sh | 0 testnets/holesky/scripts/run-builder.sh | 4 ++-- 4 files changed, 21 insertions(+), 8 deletions(-) mode change 100644 => 100755 testnets/holesky/scripts/run-bn.sh mode change 100644 => 100755 testnets/holesky/scripts/run-builder.sh diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index be3a3a003..362dc1c2e 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -3,6 +3,8 @@ volumes: driver: local chaindata: driver: local + shared: + driver: local services: redis: @@ -34,31 +36,41 @@ services: restart: unless-stopped volumes: - "chaindata:/var/lib/chaindata" - - "./shared:/var/lib/shared" + - "shared:/var/lib/shared" - "./scripts/run-builder.sh:/scripts/run-builder.sh" environment: BUILDER_TX_SIGNING_KEY: "0x53321db7c1e331d93a11a41d16f004d7ff63972ec8ec7c25db329728ceeb1710" ports: - "30367:30303/tcp" - "30367:30303/udp" - # entrypoint is builder - entrypoint: /scripts/run-builder.sh + entrypoint: + [ + "/bin/sh", + "-c", + "chmod +x /scripts/run-builder.sh && /scripts/run-builder.sh", + ] beacon: image: sigp/lighthouse:latest restart: unless-stopped volumes: - "chaindata:/var/lib/chaindata" - - "./shared:/var/lib/shared" + - "shared:/var/lib/shared" - "./scripts/run-bn.sh:/scripts/run-bn.sh" ports: - "41050:50050/tcp" - "41050:50050/udp" - entrypoint: /scripts/run-bn.sh + entrypoint: + ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 restart: unless-stopped + depends_on: + - db + - redis + - builder + - beacon volumes: - "./helix-config.yml:/helix-config.yml" ports: @@ -66,4 +78,5 @@ services: environment: - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + - RUST_BACKTRACE=1 command: --config /helix-config.yml diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml index edeff326e..3305a335b 100644 --- a/testnets/holesky/helix-config.yml +++ b/testnets/holesky/helix-config.yml @@ -12,6 +12,6 @@ simulator: url: http://builder:8545 beacon_clients: - - url: http://beacon:50050 + - url: http://beacon:4000 network_config: !Holesky diff --git a/testnets/holesky/scripts/run-bn.sh b/testnets/holesky/scripts/run-bn.sh old mode 100644 new mode 100755 diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh old mode 100644 new mode 100755 index d6894ac4e..c1bc12035 --- a/testnets/holesky/scripts/run-builder.sh +++ b/testnets/holesky/scripts/run-builder.sh @@ -1,7 +1,7 @@ #!/bin/sh geth --datadir=/var/lib/chaindata/geth \ - --network=holesky \ + --holesky \ --syncmode=full \ --gcmode=archive \ --state.scheme=hash \ @@ -26,7 +26,7 @@ geth --datadir=/var/lib/chaindata/geth \ --metrics.port=6060 \ --port=30303 \ --builder \ - --builder.remote_relay_endpoint=http://relay-api:9062 \ + --builder.remote_relay_endpoint=http://helix-relay:4040 \ --builder.beacon_endpoints=http://beacon:4000 \ --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ --miner.extradata="Bolt Builder" From e8da054c04a11aaf075d74f01c3444ced0d9cb11 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 11:14:27 +0200 Subject: [PATCH 151/272] chore(sidecar): more precise docs for config --- bolt-sidecar/src/config/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index af3256000..fcade3aee 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -33,7 +33,8 @@ pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18551; #[derive(Debug, Parser, Deserialize)] #[clap(trailing_var_arg = true)] pub struct Opts { - /// Port to listen on for incoming JSON-RPC requests + /// Port to listen on for incoming JSON-RPC requests of the Commitments API. + /// This port should be open on your firewall in order to receive external requests! #[clap(long, env = "BOLT_SIDECAR_PORT", default_value_t = DEFAULT_RPC_PORT)] pub port: u16, /// Execution client API URL @@ -42,7 +43,8 @@ pub struct Opts { /// URL for the beacon client #[clap(long, env = "BOLT_SIDECAR_BEACON_API_URL", default_value = "http://localhost:5052")] pub beacon_api_url: Url, - /// Execution client Engine API URL + /// Execution client Engine API URL. This is needed for fallback block building and must be a + /// synced Geth node. #[clap(long, env = "BOLT_SIDECAR_ENGINE_API_URL", default_value = "http://localhost:8551")] pub engine_api_url: Url, /// URL to forward the constraints produced by the Bolt sidecar to a server supporting the @@ -77,10 +79,11 @@ pub struct Opts { /// The fee recipient address for fallback blocks #[clap(long, env = "BOLT_SIDECAR_FEE_RECIPIENT")] pub fee_recipient: Address, - /// Secret BLS key to sign fallback payloads with (If not provided, a random key will be used) + /// Secret BLS key to sign fallback payloads with #[clap(long, env = "BOLT_SIDECAR_BUILDER_PRIVATE_KEY")] pub builder_private_key: BlsSecretKeyWrapper, - /// Secret ECDSA key to sign commitment messages with + /// Secret ECDSA key to sign commitment messages with. The public key associated to it must be + /// then used when registering the operator in the `BoltManager` contract. #[clap(long, env = "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY")] pub commitment_private_key: EcdsaSecretKeyWrapper, /// Operating limits for the sidecar From 574c273f18e238a6a462397047f8c0824b09b3ff Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 16 Oct 2024 10:26:23 +0200 Subject: [PATCH 152/272] docs(holesky): README launch wip update --- testnets/holesky/README.md | 828 +++++++++++++++++++++++++++++++++++-- 1 file changed, 797 insertions(+), 31 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index c031285f3..526089c56 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -1,31 +1,159 @@ -# Holesky Launch Instructions +This document provides instructions for running the Bolt sidecar on the Holesky testnet. + +# Table of Contents + + + +* [Prerequisites](#prerequisites) +* [Setup](#setup) + * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Commit-Boost Mode](#commit-boost-mode) + * [Native Mode (advanced)](#native-mode-(advanced)) + * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) + * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) + * [Configuration file](#configuration-file) + * [Observability](#observability) +* [Register your validators on-chain on the Bolt Registry](#register-your-validators-on-chain-on-the-bolt-registry) + * [Validator Registration](#validator-registration) + * [Bolt Network Entrypoint](#bolt-network-entrypoint) + * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) + * [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) + * [EigenLayer Integration Guide for Node Operators and Solo Stakers](#eigenlayer-integration-guide-for-node-operators-and-solo-stakers) +* [Reference](#reference) + * [Command-line options](#command-line-options) + * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) + * [`bolt-delegations-cli`](#`bolt-delegations-cli`) + * [Installation and usage](#installation-and-usage) + * [Delegations CLI Example](#delegations-cli-example) + * [Using a private key directly](#using-a-private-key-directly) + * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) + * [Avoid restarting the beacon node](#avoid-restarting-the-beacon-node) + + + +# Prerequisites + +In order to run Bolt you need some components already installed and running in +your system. + +**A synced Geth client:** + +Bolt is fully trustless since it is able to produce a fallback block with the +commitments issued in case builders do not return a valid bid. In order to do so +it relies on a synced execution client, configured via the `--execution-api-url` +flag. At the moment only Geth is supported; with more +clients to be supported in the future. + +Using the sidecar with a different execution client could lead to commitment +faults because fallback block building is not supported yet. You can download +Geth from [the official website](https://geth.ethereum.org/downloads). + +**A synced beacon node:** + +Bolt is compatible with every beacon client. Please refer to the various beacon +client implementations to download and run them. -## Components +> [!IMPORTANT] +> In order to correctly run the Bolt sidecar and avoid commitment faults the +> beacon node must be configured so that: +> +> 1. the node's `builder-api` (or equivalent flag) must point to the Bolt +> Sidecar API. +> 2. the node will always prefer the builder payload. For instance, in +> Lighthouse this can be achieved by providing the following flags: +> +> ```text +> --always-prefer-builder-payload +> --builder-fallback-disable-checks +> ``` +> +> It might be necessary to restart your beacon node depending on your existing +> setup. See the [Avoid Restarting the Beacon +> Node](#avoid-restarting-the-beacon-node) for more details. + +**Active validators:** + +The Bolt sidecar requires signing keys from active Ethereum validators, or +authorized delegates acting on their behalf, to issue and sign preconfirmations. + +**LST collateral:** + +For Holesky in order to provide credible proposer commitments it is necessary to +restake 1 ether worth of ETH derivatives per validator in either the Symbiotic +or the EigenLayer protocol. + +# Setup + +There are various way to run the Bolt Sidecar depending on what infrastructure +you want to use and your preferred signing methods: -The components that need to run to test Bolt on Holesky are: +- Docker mode (recommended); +- [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode + (requires Docker). +- Native mode (advanced, requires building everything from source); -- A synced execution client -- A synced beacon node -- Active validators -- Commit-Boost with Bolt configuration +Running the Bolt sidecar as a standalone binary requires building it from +source. Both the standalone binary and the Docker container requires reading +signing keys from [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores, +while the Commit-Boost module relies on an internal signer and a custom PBS +module instead of regular [MEV-Boost](https://boost.flashbots.net/). -## Setup +In this section we're going to explore each of these options and its +requirements. -### Commit-Boost +## Docker Mode (recommended) -#### Installation +First, make sure to have both [Docker](https://docs.docker.com/engine/install/), +[Docker Compose](https://docs.docker.com/compose/install/) and +[git](https://git-scm.com/downloads) installed in your machine. -To install the `commit-boost` CLI with `cargo`: +Then clone the Bolt repository by running: ```bash -# Use specific commit hash to ensure compatibility -cargo install --locked --git https://github.com/Commit-Boost/commit-boost-client --rev aed00e8 commit-boost +git clone --branch v0.3.0 htts://github.com/chainbound/bolt.git && cd bolt +``` + +The Docker Compose setup will spin up the Bolt sidecar along with the Bolt +MEV-Boost fork which includes supports the [Constraints +API](https://docs.boltprotocol.xyz/api/builder). + +Before starting the services, you'll need to provide configuration files +containing the necessary environment variables: + +1. **Bolt Sidecar Configuration:** + + Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you + need a reference, you can use the `.env.example` file in the `bolt-sidecar` + directory as a starting point. For proper configuration of the signing + options, please refer to the [Delegations and + Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) + section of this guide. + +2. **MEV-Boost Configuration:** + + Similarly, create a `mev-boost.env` file in the + `testnets/holesky` folder to configure the MEV-Boost service. If you need a + reference, you can use the `.env.example` file in the `mev-boost` directory as a + starting point. -# Test installation -commit-boost --version +If you prefer not to restart your beacon node, follow the instructions in the +[Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. + +Once the configuration files are in place, you can start the Docker containers +by running: + +```bash +cd testnets/holesky && docker compose up -d ``` -#### Configuration +The docker compose setup comes with various observability tools, such as +Prometheus and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. + +## Commit-Boost Mode + +First download the `commit-boost-cli` binary from the Commit-Boost [official +releases page](https://github.com/Commit-Boost/commit-boost-client/releases) A commit-boost configuration file with Bolt support is provided at [`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the @@ -59,13 +187,13 @@ To initialize commit-boost, run the following command: commit-boost init --config cb-bolt-config.toml ``` -This will create 3 files: +This will create three files: - `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services - `.cb.env`: with local env variables, including JWTs for modules - `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus -#### Running +**Running** The final step is to run the Commit-Boost services. This can be done with the following command: @@ -76,31 +204,669 @@ commit-boost start --docker cb.docker-compose.yml --env .cb.env This will run all modules in Docker containers. > [!IMPORTANT] -> bolt-boost will be exposed at `pbs.port` (18551 by default, set with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be configured -> to point the `builder-api` to this port for Bolt to work. +> The `bolt-boost` service will be exposed at `pbs.port` (18551 by default, set +> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be +> configured to point the `builder-api` to this port for Bolt to work. + +## Native Mode (advanced) + +For running the Bolt Sidecar as a standalone binary you need to have the +following dependencies installed: + +- [git](https://git-scm.com/downloads); +- [Rust](https://www.rust-lang.org/tools/install). +- [Golang](https://golang.org/doc/install). + +Depending on your platform you may need to install additional dependencies. + +
+Linux + +Debian-based distributions: + +```bash +sudo apt update && sudo apt install -y git build-essential libssl-dev build-essential ca-certificates +``` + +Fedora/Red Hat/CentOS distributions: + +```bash +sudo dnf groupinstall "Development Tools" && sudo dnf install -y git openssl-devel ca-certificates pkgconfig +``` + +Arch/Manjaro-based distributions: + +```bash +sudo pacman -Syu --needed base-devel git openssl ca-certificates pkgconf +``` + +Alpine Linux + +```bash +sudo apk add git build-base openssl-dev ca-certificates pkgconf +``` + +
+ +
+ +
+ MacOS + +On MacOS after installing XCode Command Line tools (equivalent to `build-essential` on Linux) you can install the other dependencies with [Homebew](https://brew.sh/): + +```zsh +xcode-select --install +brew install pkg-config openssl +``` + +
+ +--- + +After having installed the dependencies you can clone the Bolt repository by +running: + +```bash +git clone --branch v0.3.0 https://github.com/chainbound/bolt.git && cd bolt +``` + +### Building and running the MEV-Boost fork binary -### Bolt Sidecar +The Bolt protocol relies on a modified version of +[MEV-Boost](https://boost.flashbots.net/) that supports the [Constraints +API](https://docs.boltprotocol.xyz/api/builder). This modified version is +available in the `mev-boost` directory of the project and can be built by +running -WIP +```bash +make build +``` + +in the `mev-boost` directory. The output of the command is a `mev-boost` binary. +To run the `mev-boost` binary please read the official [documentation](https://boost.flashbots.net/). + +If you're already running MEV-Boost along with your beacon client it is +recommended to choose another port this service in order to [avoid restarting +your beacon client](#avoid-restarting-the-beacon-node). Check out the linked +section for more details. + +### Building and running the Bolt sidecar binary + +Then you can build the Bolt sidecar by running: + +```bash +cargo build --release && mv target/release/bolt-sidecar . +``` + +In order to run correctly the sidecar you need to provide either a list command +line options or a configuration file (recommended). All the options available +can be found by running `./bolt-sidecar --help`, or you can find them in the +[reference](#command-line-options) section of this guide. + +#### Configuration file + +A configuration file can be either a `.env` file or a `.toml` file. If you use +`.env` file you can find a `.env.example` file in the repository that you can +use as a template. + +For a `.toml` file you can use the template in the `Config.example.toml`. Lastly +you need to specify the path of the configuration file by setting the +`BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. + +Please read the section on [delegations and signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +to configure such sidecar options properly. + +After you've set up the configuration file you can run the Bolt sidecar with + +```bash +./bolt-sidecar-cli +``` ### Observability -commit-boost comes with various observability tools, such as Prometheus, cadvisor, and Grafana. It also comes with some pre-built dashboards, -which can be found in the `grafana` directory. +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. To update these dashboards, run the following command: +`bash ./update-grafana.sh ` + +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. + +# Register your validators on-chain on the Bolt Registry + +Once you are successfully running the Bolt sidecar you need to register on-chain +on the Bolt Registry to successfully receive preconfirmation requests from users +and RPCs. This step is needed to provide economic security to your +commitments. + +In order to do that you need some collateral in the form of whitelisted Liquid +Staking Token (LST) that needs to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with ETH derivatives on Holesky. Here +are references to the supported tokens on both restaking protocols: + +- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) + - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) +- [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) + - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wETH`](https://holesky.etherscan.io/address/0x94373a4919B3240D86eA41593D5eBa789FEF3848) + - [`cbETH`](https://holesky.etherscan.io/address/0x8720095Fa5739Ab051799211B146a2EEE4Dd8B37) + - [`mETH`](https://holesky.etherscan.io/address/0xe3C063B1BEe9de02eb28352b55D49D85514C67FF) + +Then you need to interact with two contracts on Holesky: +`BoltValidators` and `BoltManager`. The former is used to register your +active validators into the protocol, while the latter is used to manage to +register as an operator into the system and integrate with the restaking +protocols. + +> [!IMPORTANT] +> When registering your operator in the `BoltManager` contract you must use the +> public key associated to the private key used to sign commitments with the +> Bolt Sidecar (the `--commitment-private-key` flag). + +## Validator Registration + +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for +validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. + +The registration process includes the following steps: + +1. Validator signs a message with their BLS private key. This is required to prove that the + validator private key is under their control and that they are indeed its owner. +2. Validator calls the `registerValidator` function providing: + 1. Their BLS public key + 2. The BLS signature of the registration message + 3. The address of the authorized collateral provider + 4. The address of the authorized operator + +Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function +that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and +will allow us to test the registration flow in a controlled environment. + +## Bolt Network Entrypoint + +The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +coordination of validators, operators, and vaults within the Bolt network. + +Key features include: + +1. Retrieval of operator stake and proposer status from their pubkey +2. Integration with Symbiotic +3. Integration with Eigenlayer + +Specific functionalities about the restaking protocols are handled inside +the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. + +### Symbiotic Integration guide for Staking Pools + +As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. +If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) +on how to spin up a Vault and start receiving stake from your node operators. + +Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: + +1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. +2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. +3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. +4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. +5. You can now start approving operators that opt in to your vault directly in Symbiotic. +6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. + +### Symbiotic Integration guide for Operators + +As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide +commitments on their behalf. + +The opt-in process requires the following steps: + +1. register in Symbiotic with `OperatorRegistry.registerOperator()`. +2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. +5. get approved by the vault. +6. start providing commitments with the stake provided by the vault. + +### EigenLayer Integration Guide for Node Operators and Solo Stakers + +> [!NOTE] +> Without loss of generality, we will assume the reader of this guide is a Node +> Operator (NO), since the same steps apply to solo stakers. +> As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) +> in the Bolt AVS built on top of EigenLayer. This requires +> running an Ethereum validator and the Bolt sidecar in order issue +> preconfirmations. + +The Operator will be represented by an Ethereum address that needs +to follow the standard procedure outlined in the +[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: + +1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + +2. As an Ethereum validator offering precofirmations a NO needs some collateral in + order to be economically credible. In order to do that, some entities known as a "stakers" + need to deposit whitelisted Liquid Staking Tokens (LSTs) + into an appropriate "Strategy" associated to the LST via the + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), + so that the Operator has a `min_amount` (for Holesky 1 ether) of collateral associated to it. + Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract + in the `getWhitelistedCollaterals` function. + Note that NOs and stakers can be two different entities + _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. + +3. After the stakers have deposited their collateral into a strategy they need + to choose you as their operator. To do that, they need to call the function + [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). + +4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. + This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. + The payload is a signature whose digest consists of: + + 1. your operator address + 2. the `BoltEigenLayerMiddleware` contract address + 3. a salt + 4. an expiry 2. + + The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) + with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, + the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. + +Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` +contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a +registered operator and so that they can forward you preconfirmation requests. + +The steps required are the following: + +1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. + If you own more than one validator public key, + you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. + The `authorizedOperator` argument must be the same Ethereum address used for + opting into EigenLayer as an Operator. + +2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling + the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network + and allows you to manage operations effectively, such as pausing or resuming + your service. + +3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. + This ensures that your restaked assets are correctly integrated with Bolt’s system. + +# Reference + +## Command-line options + +For completeness, here are all the command-line options available for the Bolt +sidecar. You can see them in your terminal by running the Bolt sidecar binary +with the `--help` flag: + +``` +Command-line options for the Bolt sidecar + +Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > + +Options: + --port + Port to listen on for incoming JSON-RPC requests of the Commitments API + This port should be open on your firewall in order to receive external requests! + + [env: BOLT_SIDECAR_PORT=] + [default: 8000] + + --execution-api-url + Execution client API URL + + [env: BOLT_SIDECAR_EXECUTION_API_URL=] + [default: http://localhost:8545] + + --beacon-api-url + URL for the beacon client + + [env: BOLT_SIDECAR_BEACON_API_URL=] + [default: http://localhost:5052] + + --engine-api-url + Execution client Engine API URL. This is needed for fallback block + building and must be a synced Geth node + + [env: BOLT_SIDECAR_ENGINE_API_URL=] + [default: http://localhost:8551] + + --constraints-api-url + URL to forward the constraints produced by the Bolt sidecar to a + server supporting the Constraints API, such as an MEV-Boost fork + + [env: BOLT_SIDECAR_CONSTRAINTS_API_URL=] + [default: http://localhost:3030] + + --constraints-proxy-port + The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client + + [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] + [default: 18551] + + --validator-indexes + Validator indexes of connected validators that the sidecar should + accept commitments on behalf of. Accepted values: + - a comma-separated list of indexes (e.g. "1,2,3,4") + - a contiguous range of indexes (e.g. "1..4") + - a mix of the above (e.g. "1,2..4,6..8") + + [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] + [default: ] + + --engine-jwt-hex + The JWT secret token to authenticate calls to the engine API. + + It can either be a hex-encoded string or a file path to a file containing the hex-encoded secret. + + [env: BOLT_SIDECAR_ENGINE_JWT_HEX=] + + --fee-recipient + The fee recipient address for fallback blocks + + [env: BOLT_SIDECAR_FEE_RECIPIENT=] + + --builder-private-key + Secret BLS key to sign fallback payloads with + + [env: BOLT_SIDECAR_BUILDER_PRIVATE_KEY=] + + --commitment-private-key + Secret ECDSA key to sign commitment messages with. The public key + associated to it must be then used when registering the operator in the + `BoltManager` contract + + [env: BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY=] + + --max-commitments-per-slot + Max number of commitments to accept per block + + [env: BOLT_SIDECAR_MAX_COMMITMENTS=] + [default: 128] + + --max-committed-gas-per-slot + Max committed gas per slot + + [env: BOLT_SIDECAR_MAX_COMMITTED_GAS=] + [default: 10000000] + + --min-priority-fee + Min priority fee to accept for a commitment + + [env: BOLT_SIDECAR_MIN_PRIORITY_FEE=] + [default: 1000000000] + + --chain + Chain on which the sidecar is running + + [env: BOLT_SIDECAR_CHAIN=] + [default: mainnet] + [possible values: mainnet, holesky, helder, kurtosis] + + --commitment-deadline + The deadline in the slot at which the sidecar will stop accepting new commitments for the next block (parsed as milliseconds) + + [env: BOLT_SIDECAR_COMMITMENT_DEADLINE=] + [default: 8000] + + --slot-time + The slot time duration in seconds. If provided, it overrides the default for the selected [Chain] + + [env: BOLT_SIDECAR_SLOT_TIME=] + [default: 12] + + --constraint-private-key + Private key to use for signing constraint messages + + [env: BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY=] + + --commit-boost-signer-url + URL for the commit-boost sidecar + + [env: BOLT_SIDECAR_CB_SIGNER_URL=] + + --commit-boost-jwt-hex + JWT in hexadecimal format for authenticating with the commit-boost service + + [env: BOLT_SIDECAR_CB_JWT_HEX=] + + --keystore-password + The password for the ERC-2335 keystore. Reference: https://eips.ethereum.org/EIPS/eip-2335 + + [env: BOLT_SIDECAR_KEYSTORE_PASSWORD=] + + --keystore-secrets-path + The path to the ERC-2335 keystore secret passwords Reference: https://eips.ethereum.org/EIPS/eip-2335 + + [env: BOLT_SIDECAR_KEYSTORE_SECRETS_PATH=] + + --keystore-path + Path to the keystores folder. If not provided, the default path is used + + [env: BOLT_SIDECAR_KEYSTORE_PATH=] + + --delegations-path + Path to the delegations file. If not provided, the default path is used + + [env: BOLT_SIDECAR_DELEGATIONS_PATH=] + + --metrics-port + The port on which to expose Prometheus metrics + + [env: BOLT_SIDECAR_METRICS_PORT=] + [default: 3300] + + --disable-metrics + [env: BOLT_SIDECAR_DISABLE_METRICS=] + + -h, --help + Print help (see a summary with '-h') +``` + +## Delegations and signing options for Native and Docker Compose Mode + +As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar +can sign commitments with a delegated set of private keys on behalf of active +Ethereum validators. + +> [!IMPORTANT] +> This is the recommended way to run the Bolt sidecar as it +> doesn't expose the active validator signing keys to any additional risk. + +In order to create these delegation you can use the `bolt-delegations-cli` binary. +If you don't want to use it you can skip the following section. + +### `bolt-delegations-cli` + +`bolt-delegations-cli` is an offline command-line tool for safely generating +delegation and revocation messages signed with a BLS12-381 key for the +[Constraints API](https://docs.boltprotocol.xyz/api/builder) in +[Bolt](https://docs.boltprotocol.xyz/). + +The tool supports two key sources: + +- Local: A BLS private key provided directly from a file. +- Keystore: A keystore file that contains an encrypted BLS private key. + +and outputs a JSON file with the delegation/revocation messages to the provided +`` for the given chain + +Features: + +- Offline usage: Safely generate delegation messages in an offline environment. +- Flexible key source: Support for both direct local BLS private keys and + Ethereum keystore files (ERC-2335 format). +- BLS delegation signing: Sign delegation messages using a BLS secret key and + output the signed delegation in JSON format. + +#### Installation and usage + +Go to the root of the Bolt project you've previously cloned using Git. Enter in +the `bolt-delegations-cli` directory by running `cd bolt-delegations-cli`. + +If you're using the Docker container setup make sure you have +[Rust](https://www.rust-lang.org/tools/install) installed in your system as +well. Then you can build the `bolt-delegations-cli` binary by running: + ```bash -./update-grafana.sh +cargo build --release && mv target/release/bolt-delegations-cli . +``` + +Now you can run the binary by running: + +```bash +./bolt-delegations-cli +``` + +The binary exposes a single `generate` command, which accepts the following +options and subcommands (use `./bolt-delegations-cli generate --help` to see +them): + +```text +Usage: bolt-delegations-cli generate [OPTIONS] --delegatee-pubkey + +Commands: + local Use local private keys to generate the signed messages + keystore Use an EIP-2335 keystore folder to generate the signed messages + help Print this message or the help of the given subcommand(s) + +Options: + --delegatee-pubkey The BLS public key to which the delegation message should be signed [env: DELEGATEE_PUBKEY=] + --out The output file for the delegations [env: OUTPUT_FILE_PATH=] [default: delegations.json] + --chain The chain for which the delegation message is intended [env: CHAIN=] [default: mainnet] [possible values: mainnet, holesky, helder, kurtosis] + --action The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) [env: ACTION=] [default: delegate] [possible values: delegate, revoke] + -h, --help Print help (see more with '--help') +``` + +The environment variables can be also set in a `.env` file. For a reference +example you can check out the `.env.local.example` and the +`.env.keystore.example` + +In the section below you can see a usage example of the binary. + +#### Delegations CLI Example + +1. Using a local BLS private key: + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + local \ + --secret-keys 0xabc123...,0xdef456.. + ``` + +2. Using a Ethereum keystores files and raw password: + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + keystore \ + --path /keys \ + --password myS3cr3tP@ssw0rd + ``` + +3. Using an Ethereum keystores files and secrets folder + + ```text + bolt-delegations-cli generate \ + --delegatee-pubkey 0x7890ab... \ + --out my_delegations.json \ + --chain holesky \ + keystore \ + --path /keys \ + --password-path /secrets + ``` + +When using the `keystore` key source, the `--path` flag should point to the +directory containing the encrypted keypair directories. + +The keystore folder must adhere to the following structure: + +```text +${KEYSTORE_PATH} +|-- 0x81b676591b823270a3284ace7d81cbce2d6cdce55bb0e053874d7e3a08f729453009d3e662ec3130379f43c0f3210b6d +| `-- voting-keystore.json +|-- 0x81ea9f74ef7d935b807474e38954ae3934856219a23e074954b2e860c5a3c400f9aedb42cd27cb4ceb697ca36d1e58cb +| `-- voting-keystore.json +|-- ... + `-- ... +``` + +where the folder names are the public keys and inside every +folder there is a single JSON file containing the keystore file. + +In case of validator-specific passwords (e.g. Lighthouse format) the +`--password-path` flag must be used instead of `--password`, pointing to the +directory containing the password files. + +The passwords folder must adhere to a certain structure as well, as shown below. + ``` +${KEYSTORE_PATH} +|-- 0x81b676591b823270a3284ace7d81cbce2d6cdce55bb0e053874d7e3a08f729453009d3e662ec3130379f43c0f3210b6d +|-- 0x81ea9f74ef7d935b807474e38954ae3934856219a23e074954b2e860c5a3c400f9aedb42cd27cb4ceb697ca36d1e58cb +|-- ... + `-- ... +``` + +That is, the password files should be named after the public key and each file +should just contain one line with the password in plain text. The files +themselves don't need a particular file extension. + +--- + +Now that you have generated the delegation messages you can provide them to the +sidecar using the `--delegations-path` flag (see the +[options](#command-line-options) section). When doing so the sidecar will check if +they're indeed valid messages and will keep in memory the association between +the delegator and the delegatee. + +However in order to sign the commitments you still need to provide the signing +key of the delegatee. There are two ways to do so, as explored in the sections +below. + +### Using a private key directly + +As you can see in the [command line options](#command-line-options) section you +can pass directly the private key as a hex-encoded string to the Bolt sidecar +using the `--private-key` flag. This is the simplest setup and can be used in +case if all the delegations messages point to the same delegatee or if you're +running the sidecar with a single active validator. + +### Using a ERC-2335 Keystore + +The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. +In order to use them you need to provide the `--keystore-path` pointing to the +folder containing the keystore files and the `--keystore-password` or +`keystore-secrets-path` flag pointing to the folder containing the password +file. -In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. +Both the `keys` and `passwords` folders must adhere to the structure outlined +in the [Delegations CLI example](#delegations-cli-example) section. -### Validators +## Avoid restarting the beacon node -Validators must be configured to always prefer builder proposals over their own. Refer to client documentation for the specific configuration flags. -**If this is not set, it could lead to commitment faults**. +As mentioned in the [prerequisites](#prerequisites) section, in order to run the +sidecar correctly it might be necessary to restart your beacon client. That is +because you need to configure the `--builder` flag (or equivalent) to point to +the Bolt sidecar endpoint. -#### Registration +However if you're already running a PBS sidecar like +[MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid +the restart by following this steps when starting the Bolt sidecar: -WIP +1. Set the `--constraints-proxy-port` flag or the + `BOLT_SIDECAR_BUILDER_PROXY_PORT` environment variable to the port previously occupied by + MEV-Boost. +2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it + using another port +3. Set the `--constraints-url` flag or the `BOLT_SIDECAR_CONSTRAINTS_URL` to point to the Bolt MEV-Boost instance. From 9852e8859cd2579662d6af712e96a6268cdf3327 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 16:49:18 +0200 Subject: [PATCH 153/272] fix(holesky): add bolt prefix to all services and containers to avoid conflicts --- testnets/holesky/docker-compose.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 2751212df..f88549f40 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,7 +1,7 @@ services: - bolt-sidecar: + bolt-sidecar-holesky: image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 - container_name: bolt-sidecar + container_name: bolt-sidecar-holesky restart: unless-stopped ports: - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) @@ -9,18 +9,18 @@ services: env_file: ./bolt-sidecar.env entrypoint: /bin/sh -c /bolt-sidecar - mev-boost: + bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 - container_name: mev-boost + container_name: bolt-mev-boost-holesky restart: unless-stopped env_file: ./mev-boost.env entrypoint: /bin/sh -c '/app/mev-boost' - prometheus: + bolt-prometheus-holesky: image: prom/prometheus:latest - container_name: prometheus + container_name: bolt-prometheus-holesky ports: - - 9090:9090 + - 49090:49090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json @@ -28,11 +28,11 @@ services: networks: - monitoring_network - grafana: + bolt-grafana-holesky: image: grafana/grafana:latest - container_name: cb_grafana + container_name: bolt-grafana-holesky ports: - - 3000:3000 + - 33000:33000 environment: - GF_SECURITY_ADMIN_PASSWORD=admin volumes: @@ -42,7 +42,7 @@ services: networks: - monitoring_network depends_on: - - prometheus + - bolt-prometheus-holesky logging: driver: none From dbfc2f416d875dda03579381c80ad85646c32600 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 17:34:40 +0200 Subject: [PATCH 154/272] fix(holesky): docker-compose sidecar entrypoint --- testnets/holesky/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index f88549f40..7687aa77e 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -7,7 +7,7 @@ services: - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" env_file: ./bolt-sidecar.env - entrypoint: /bin/sh -c /bolt-sidecar + entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 6cfdd372fe8a9704700dd08080387f5eecafa7af Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 17:35:01 +0200 Subject: [PATCH 155/272] fix(sidecar): .env.example --- bolt-sidecar/.env.example | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index 98bf60852..7621489a6 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -6,7 +6,7 @@ BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 -BOLT_SIDECAR_JWT_HEX= +BOLT_SIDECAR_ENGINE_JWT_HEX= BOLT_SIDECAR_FEE_RECIPIENT= BOLT_SIDECAR_BUILDER_PRIVATE_KEY= @@ -17,17 +17,17 @@ BOLT_SIDECAR_MIN_PRIORITY_FEE= BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 # Chain configs -BOLT_SIDECAR_CHAIN=Holesky +BOLT_SIDECAR_CHAIN=holesky BOLT_SIDECAR_SLOT_TIME=12 -# Signing options -BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= -BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= -BOLT_SIDECAR_CB_SIGNER_URL= -BOLT_SIDECAR_CB_JWT_HEX= -BOLT_SIDECAR_KEYSTORE_PASSWORD= -BOLT_SIDECAR_KEYSTORE_PATH= -BOLT_SIDECAR_DELEGATIONS_PATH= +# Signing options. Uncomment only what you'll use +#BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +#BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +#BOLT_SIDECAR_CB_SIGNER_URL= +#BOLT_SIDECAR_CB_JWT_HEX= +#BOLT_SIDECAR_KEYSTORE_PASSWORD= +#BOLT_SIDECAR_KEYSTORE_PATH= +#BOLT_SIDECAR_DELEGATIONS_PATH= # Metrics BOLT_SIDECAR_METRICS_PORT= From 5a2ae133179bc47711b143102d7311ba59620d01 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 21 Oct 2024 18:20:14 +0200 Subject: [PATCH 156/272] chore(holesky): add simple bash script to create .env stub --- testnets/holesky/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 526089c56..4ea5dc1f0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -124,7 +124,13 @@ containing the necessary environment variables: Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you need a reference, you can use the `.env.example` file in the `bolt-sidecar` - directory as a starting point. For proper configuration of the signing + directory as a starting point. + + ```bash + cat ./bolt-sidecar/.env.example > ./testnets/holesky/bolt-sidecar.env + ``` + + For proper configuration of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) section of this guide. @@ -136,6 +142,10 @@ containing the necessary environment variables: reference, you can use the `.env.example` file in the `mev-boost` directory as a starting point. + ```bash + cat ./mev-boost/.env.example > ./testnets/holesky/mev-boost.env + ``` + If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. From ff47200529d3045ea87d815990a11553a05696d5 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:40:16 +0200 Subject: [PATCH 157/272] chore(sidecar): update Config.example.toml --- bolt-sidecar/Config.example.toml | 87 +++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index e6caad16e..b76ead584 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -1,37 +1,78 @@ # Ethereum Node Connections + PBS URLs -port = 8000 -execution_api_url = "http://localhost:4485" -beacon_api_url = "http://localhost:4400" -engine_api_url = "http://localhost:4451" -constraints_url = "http://localhost:19550" -constraints_proxy_port = 18551 + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +port = 8017 +# Execution client API URL +execution_api_url = "http://localhost:8545" +# URL for the beacon client +beacon_api_url = "http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +engine_api_url = "http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +constraints_proxy_port = 18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +constraints_api_url = "http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") validator_indexes = "0..64" -jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. +engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# The fee recipient address for fallback blocks fee_recipient = "0x0000000000000000000000000000000000000000" +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +# Secret BLS key to sign fallback payloads with builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# Commitments configs -max_commitments = 128 -max_committed_gas = 10000000 -min_priority_fee = 5000000 +# Commitments limits +[limits] +# Max number of commitments to accept per block +max_commitments_per_slot = 128 +# Max committed gas per slot +max_committed_gas_per_slot = 10_000_000 +# Min priority fee to accept for a commitment +min_priority_fee = 4_000_000_000 # 4 Gwei = 4 * 10^9 wei -# Chain configs +# Chain configuration [chain] -chain = "Holesky" +# Chain on which the sidecar is running +chain = "holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] slot_time = 12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) commitment_deadline = 8000 -# Signing options +# Signing options. Uncomment only the signing setup you'll use: +# - single private key -> `constraints_private_key` +# - commit-boost -> `cb_signer_url`, `cb_jwt_hex` +# - keystores -> `keystore_path`, `keystore_password` or `keystore_secrets_path` +# (depending on whether all keystores have the same passwords or not) +# +# If you plan to use delegations, uncomment the option `delegations_path` as +# well. [constraint_signing] -constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -cb_signer_url = "http://localhost:18550" -cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -keystore_password = "password" -keystore_path = "./keys" -delegations_path = "./delegations.json" +# Private key to use for signing constraint messages +# constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" +# cb_signer_url = "http://localhost:18551" +# cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" +# keystore_password = "password" +# keystore_path = "./keys" +# delegations_path = "./delegations.json" -# Metrics +# Telemetry and Metrics [telemetry] -metrics_port = 8001 +metrics_port = 3300 disable_metrics = false From 55abca913bc8be275fb96a15da6d8879ca1ef6d7 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:40:51 +0200 Subject: [PATCH 158/272] chore(sidecar): review CLI options and their defaults --- bolt-sidecar/src/config/chain.rs | 30 ++++++++++++++++++++++-------- bolt-sidecar/src/config/mod.rs | 14 ++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 4ce4f88a6..98edb7248 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -1,4 +1,5 @@ -use std::time::Duration; +use core::fmt; +use std::{fmt::Display, time::Duration}; use clap::{Args, ValueEnum}; use ethereum_consensus::deneb::{compute_fork_data_root, Root}; @@ -25,7 +26,7 @@ pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67]; #[derive(Debug, Clone, Copy, Args, Deserialize)] pub struct ChainConfig { /// Chain on which the sidecar is running - #[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value = "mainnet")] + #[clap(long, env = "BOLT_SIDECAR_CHAIN", default_value_t = Chain::Mainnet)] chain: Chain, /// The deadline in the slot at which the sidecar will stop accepting /// new commitments for the next block (parsed as milliseconds). @@ -65,6 +66,24 @@ pub enum Chain { Kurtosis, } +impl Chain { + /// Get the chain name for the given chain. + pub fn name(&self) -> &'static str { + match self { + Chain::Mainnet => "mainnet", + Chain::Holesky => "holesky", + Chain::Helder => "helder", + Chain::Kurtosis => "kurtosis", + } + } +} + +impl Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name()) + } +} + impl ChainConfig { /// Get the chain ID for the given chain. pub fn chain_id(&self) -> u64 { @@ -78,12 +97,7 @@ impl ChainConfig { /// Get the chain name for the given chain. pub fn name(&self) -> &'static str { - match self.chain { - Chain::Mainnet => "mainnet", - Chain::Holesky => "holesky", - Chain::Helder => "helder", - Chain::Kurtosis => "kurtosis", - } + self.chain.name() } /// Get the slot time for the given chain in seconds. diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index fcade3aee..1b9e64b76 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -23,11 +23,13 @@ use limits::LimitsOpts; use crate::common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}; -/// Default port for the JSON-RPC server exposed by the sidecar. -pub const DEFAULT_RPC_PORT: u16 = 8000; +/// Default port for the JSON-RPC server exposed by the sidecar supporting the Commitments API. +/// +/// 8017 -> BOLT :) +pub const DEFAULT_RPC_PORT: u16 = 8017; -/// Default port for the Constraints proxy server. -pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18551; +/// Default port for the Constraints proxy server, binded to the default port used by MEV-Boost. +pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18550; /// Command-line options for the Bolt sidecar #[derive(Debug, Parser, Deserialize)] @@ -52,7 +54,7 @@ pub struct Opts { #[clap( long, env = "BOLT_SIDECAR_CONSTRAINTS_API_URL", - default_value = "http://localhost:3030" + default_value = "http://localhost:18551" )] pub constraints_api_url: Url, /// The port from which the Bolt sidecar will receive Builder-API requests from the @@ -68,7 +70,7 @@ pub struct Opts { /// - a comma-separated list of indexes (e.g. "1,2,3,4") /// - a contiguous range of indexes (e.g. "1..4") /// - a mix of the above (e.g. "1,2..4,6..8") - #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES", default_value_t)] + #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES")] pub validator_indexes: ValidatorIndexes, /// The JWT secret token to authenticate calls to the engine API. /// From dc93a4ec4ec62030dc451f4974140001089392b4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:51:34 +0200 Subject: [PATCH 159/272] chore(sidecar): add Config.toml to .gitignore --- bolt-sidecar/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-sidecar/.gitignore b/bolt-sidecar/.gitignore index 2e73cb821..8fd11d943 100644 --- a/bolt-sidecar/.gitignore +++ b/bolt-sidecar/.gitignore @@ -2,3 +2,4 @@ target/ .env* !.env.example bolt-sidecar* +Config.toml From 90d3142a5aae77d5b27b813fa2d59591c4d4c0c9 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 10:51:52 +0200 Subject: [PATCH 160/272] feat(sidecar): allow reading TOML config from default path --- bolt-sidecar/bin/sidecar.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index c7fbf60cc..6903fffbd 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,13 +1,19 @@ +use std::fs; + use clap::Parser; use eyre::{bail, Result}; use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; +pub const TOML_CONFIG_DEFAULT_PATH: &str = "./Config.toml"; + #[tokio::main] async fn main() -> Result<()> { let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") { Opts::parse_from_toml(config_path.as_str())? + } else if fs::exists(TOML_CONFIG_DEFAULT_PATH).is_ok_and(|exists| exists) { + Opts::parse_from_toml(TOML_CONFIG_DEFAULT_PATH)? } else { Opts::parse() }; From d3b34e7a46f6584e479547b1190ccc70c25906d8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:00:43 +0200 Subject: [PATCH 161/272] chore(holesky): add *.toml to .gitignore --- testnets/holesky/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/.gitignore b/testnets/holesky/.gitignore index ef456d924..71c6e0c11 100644 --- a/testnets/holesky/.gitignore +++ b/testnets/holesky/.gitignore @@ -1,2 +1,3 @@ *.env* !*.env.example +*.toml From dc46e2696f0ded383338c9130adc86ce44882342 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:03:14 +0200 Subject: [PATCH 162/272] fix(holesky): broken links in README, use TOML config file instead of .env for the sidecar --- testnets/holesky/README.md | 49 +++++++++++++++----------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4ea5dc1f0..47c849756 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -122,17 +122,17 @@ containing the necessary environment variables: 1. **Bolt Sidecar Configuration:** - Create a `bolt-sidecar.env` file in the `testnets/holesky` directory. If you - need a reference, you can use the `.env.example` file in the `bolt-sidecar` + Create a `bolt-sidecar.toml` file in the `testnets/holesky` directory. If you + need a reference, you can use the `Config.example.toml` file in the `bolt-sidecar` directory as a starting point. ```bash - cat ./bolt-sidecar/.env.example > ./testnets/holesky/bolt-sidecar.env + cat ./bolt-sidecar/Config.example.toml > ./testnets/holesky/bolt-sidecar.toml ``` For proper configuration of the signing options, please refer to the [Delegations and - Signing](#delegations-and-signing-options-for-standalone-and-docker-container-setup) + Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. 2. **MEV-Boost Configuration:** @@ -316,15 +316,13 @@ can be found by running `./bolt-sidecar --help`, or you can find them in the #### Configuration file -A configuration file can be either a `.env` file or a `.toml` file. If you use -`.env` file you can find a `.env.example` file in the repository that you can -use as a template. - -For a `.toml` file you can use the template in the `Config.example.toml`. Lastly -you need to specify the path of the configuration file by setting the +You can use a `Config.toml` file to configure the sidecar, for which you can +find a template in the `Config.example.toml` file. +If you wish to place the configuration file in another folder you need to +specify the path of the configuration file by setting the `BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. -Please read the section on [delegations and signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) to configure such sidecar options properly. After you've set up the configuration file you can run the Bolt sidecar with @@ -517,15 +515,14 @@ with the `--help` flag: ``` Command-line options for the Bolt sidecar -Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > +Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: --port - Port to listen on for incoming JSON-RPC requests of the Commitments API - This port should be open on your firewall in order to receive external requests! + Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] - [default: 8000] + [default: 8017] --execution-api-url Execution client API URL @@ -540,34 +537,28 @@ Options: [default: http://localhost:5052] --engine-api-url - Execution client Engine API URL. This is needed for fallback block - building and must be a synced Geth node + Execution client Engine API URL. This is needed for fallback block building and must be a synced Geth node [env: BOLT_SIDECAR_ENGINE_API_URL=] [default: http://localhost:8551] --constraints-api-url - URL to forward the constraints produced by the Bolt sidecar to a - server supporting the Constraints API, such as an MEV-Boost fork + URL to forward the constraints produced by the Bolt sidecar to a server supporting the Constraints API, such as an MEV-Boost fork [env: BOLT_SIDECAR_CONSTRAINTS_API_URL=] - [default: http://localhost:3030] + [default: http://localhost:18551] --constraints-proxy-port The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] - [default: 18551] + [default: 18550] --validator-indexes - Validator indexes of connected validators that the sidecar should - accept commitments on behalf of. Accepted values: - - a comma-separated list of indexes (e.g. "1,2,3,4") - - a contiguous range of indexes (e.g. "1..4") - - a mix of the above (e.g. "1,2..4,6..8") + Validator indexes of connected validators that the sidecar should accept commitments on behalf of. Accepted values: - a comma-separated list of indexes (e.g. "1,2,3,4") - a contiguous range of indexes (e.g. "1..4") - a mix of the + above (e.g. "1,2..4,6..8") [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] - [default: ] --engine-jwt-hex The JWT secret token to authenticate calls to the engine API. @@ -587,9 +578,7 @@ Options: [env: BOLT_SIDECAR_BUILDER_PRIVATE_KEY=] --commitment-private-key - Secret ECDSA key to sign commitment messages with. The public key - associated to it must be then used when registering the operator in the - `BoltManager` contract + Secret ECDSA key to sign commitment messages with. The public key associated to it must be then used when registering the operator in the `BoltManager` contract [env: BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY=] From 1e412c53dd99a83b699715ac30075da3553960a2 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 11:13:11 +0200 Subject: [PATCH 163/272] chore(holesky): update docker-compose.yml --- testnets/holesky/docker-compose.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 7687aa77e..830402af8 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -4,10 +4,11 @@ services: container_name: bolt-sidecar-holesky restart: unless-stopped ports: - - "${BOLT_SIDECAR_PORT}:${BOLT_SIDECAR_PORT}" # Bolt RPC port (this should be opened on your firewall!) - - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT}" - env_file: ./bolt-sidecar.env - entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar + - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) + - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" + entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" + volumes: + - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 32d4027af1621af45a90904f192f900691359123 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:09:17 +0200 Subject: [PATCH 164/272] fix(sidecar): min_priority_fee is now u128 and not NonZero This caused issue when parsing from TOML string. Also if that value is zero is not a problem actually --- bolt-sidecar/src/config/limits.rs | 4 ++-- bolt-sidecar/src/state/execution.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bolt-sidecar/src/config/limits.rs b/bolt-sidecar/src/config/limits.rs index b33561072..7e50d24b8 100644 --- a/bolt-sidecar/src/config/limits.rs +++ b/bolt-sidecar/src/config/limits.rs @@ -31,7 +31,7 @@ pub struct LimitsOpts { env = "BOLT_SIDECAR_MIN_PRIORITY_FEE", default_value_t = LimitsOpts::default().min_priority_fee )] - pub min_priority_fee: NonZero, + pub min_priority_fee: u128, } impl Default for LimitsOpts { @@ -41,7 +41,7 @@ impl Default for LimitsOpts { .expect("Valid non-zero"), max_committed_gas_per_slot: NonZero::new(DEFAULT_MAX_COMMITTED_GAS) .expect("Valid non-zero"), - min_priority_fee: NonZero::new(DEFAULT_MIN_PRIORITY_FEE).expect("Valid non-zero"), + min_priority_fee: DEFAULT_MIN_PRIORITY_FEE, } } } diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index b82418317..f6e4262d9 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -299,7 +299,7 @@ impl ExecutionState { } // Ensure max_priority_fee_per_gas is greater than or equal to min_priority_fee - if !req.validate_min_priority_fee(max_basefee, self.limits.min_priority_fee.get()) { + if !req.validate_min_priority_fee(max_basefee, self.limits.min_priority_fee) { return Err(ValidationError::MaxPriorityFeePerGasTooLow); } @@ -764,7 +764,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(200000000).unwrap(), // 0.2 gwei + min_priority_fee: 200000000, // 0.2 gwei }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -803,7 +803,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2000000000).unwrap(), + min_priority_fee: 2000000000, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -834,7 +834,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -876,7 +876,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -923,7 +923,7 @@ mod tests { let limits = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(2 * GWEI_TO_WEI as u128).unwrap(), + min_priority_fee: 2 * GWEI_TO_WEI as u128, }; let mut state = ExecutionState::new(client.clone(), limits).await?; @@ -1061,7 +1061,7 @@ mod tests { let limits: LimitsOpts = LimitsOpts { max_commitments_per_slot: NonZero::new(10).unwrap(), max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), - min_priority_fee: NonZero::new(1000000000).unwrap(), + min_priority_fee: 1000000000, }; let mut state = ExecutionState::new(client.clone(), limits).await?; From a72034de2cdee2527adc09f759d0eb4050b8974e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:08 +0200 Subject: [PATCH 165/272] fix(holesky): cat -> cp in README --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 47c849756..1e75c3cb9 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -127,7 +127,7 @@ containing the necessary environment variables: directory as a starting point. ```bash - cat ./bolt-sidecar/Config.example.toml > ./testnets/holesky/bolt-sidecar.toml + cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml ``` For proper configuration of the signing @@ -143,7 +143,7 @@ containing the necessary environment variables: starting point. ```bash - cat ./mev-boost/.env.example > ./testnets/holesky/mev-boost.env + cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env ``` If you prefer not to restart your beacon node, follow the instructions in the From d603b4dd4606d81a3f35abaaaed76b3f69da9ba0 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:34 +0200 Subject: [PATCH 166/272] chore(holesky): delegations flag in README --- testnets/holesky/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 1e75c3cb9..9d91de16e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -742,6 +742,11 @@ Options: -h, --help Print help (see more with '--help') ``` +> [!TIP] +> If you're using the Docker Compose Mode please don't set the `--out` flag and +> provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` +> file. + The environment variables can be also set in a `.env` file. For a reference example you can check out the `.env.local.example` and the `.env.keystore.example` From 5cc0f3d62ed3bad7571d77f340b4d7a16bccc515 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 12:13:52 +0200 Subject: [PATCH 167/272] fix(holesky): provide delegations cli volume --- testnets/holesky/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 830402af8..86d2c3dce 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -9,6 +9,7 @@ services: entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" volumes: - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" + - "../../bolt-delegations-cli/delegations.json:/etc/delegations.json" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 From 551146741ffcbc0c33501ab0b88c1628411e23ef Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 17:17:35 +0200 Subject: [PATCH 168/272] docs(holesky): update instructions --- testnets/holesky/README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 9d91de16e..4b80c8241 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -5,7 +5,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) -* [Setup](#setup) +* [Off-Chain Setup](#off-chain-setup) * [Docker Mode (recommended)](#docker-mode-(recommended)) * [Commit-Boost Mode](#commit-boost-mode) * [Native Mode (advanced)](#native-mode-(advanced)) @@ -13,7 +13,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) * [Observability](#observability) -* [Register your validators on-chain on the Bolt Registry](#register-your-validators-on-chain-on-the-bolt-registry) +* [On-Chain Registration](#on-chain-registration) * [Validator Registration](#validator-registration) * [Bolt Network Entrypoint](#bolt-network-entrypoint) * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) @@ -33,7 +33,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky # Prerequisites -In order to run Bolt you need some components already installed and running in +In order to run Bolt you need some components already installed and running on your system. **A synced Geth client:** @@ -76,13 +76,7 @@ client implementations to download and run them. The Bolt sidecar requires signing keys from active Ethereum validators, or authorized delegates acting on their behalf, to issue and sign preconfirmations. -**LST collateral:** - -For Holesky in order to provide credible proposer commitments it is necessary to -restake 1 ether worth of ETH derivatives per validator in either the Symbiotic -or the EigenLayer protocol. - -# Setup +# Off-Chain Setup There are various way to run the Bolt Sidecar depending on what infrastructure you want to use and your preferred signing methods: @@ -157,8 +151,7 @@ cd testnets/holesky && docker compose up -d ``` The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards, which can -be found in the `grafana` directory. +Prometheus and Grafana. It also comes with some pre-built dashboards which you can find at `http://localhost:3000`. ## Commit-Boost Mode @@ -344,7 +337,7 @@ To update these dashboards, run the following command: In this directory, you can also find a Bolt dashboard, which will be launched alongside the other dashboards. -# Register your validators on-chain on the Bolt Registry +# On-Chain Registration Once you are successfully running the Bolt sidecar you need to register on-chain on the Bolt Registry to successfully receive preconfirmation requests from users From 8432b433d5599a9c923a5f8f08c10e015e90f8b2 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 17:46:17 +0200 Subject: [PATCH 169/272] docs(holesky): smol chagnes --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4b80c8241..b4fea395d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -104,7 +104,7 @@ First, make sure to have both [Docker](https://docs.docker.com/engine/install/), Then clone the Bolt repository by running: ```bash -git clone --branch v0.3.0 htts://github.com/chainbound/bolt.git && cd bolt +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git && cd bolt ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt From bdeacac0724a3c1618da02f471ec8a1be71b554d Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 18:26:05 +0200 Subject: [PATCH 170/272] docs(holesky): smol changes --- testnets/holesky/README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b4fea395d..af896fa11 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -67,6 +67,9 @@ client implementations to download and run them. > --builder-fallback-disable-checks > ``` > +> In other clients like Vouch, the same can be achieved by setting the `builder-boost-factor` to a large value +> like 18446744073709551615. +> > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon > Node](#avoid-restarting-the-beacon-node) for more details. @@ -124,17 +127,20 @@ containing the necessary environment variables: cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml ``` - For proper configuration of the signing - options, please refer to the [Delegations and + Next up, fill out all the values that are required. For proper configuration + of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. 2. **MEV-Boost Configuration:** - Similarly, create a `mev-boost.env` file in the - `testnets/holesky` folder to configure the MEV-Boost service. If you need a - reference, you can use the `.env.example` file in the `mev-boost` directory as a - starting point. + Copy over the example environment file: + + ```bash + cp ../../mev-boost/.env.example mev-boost.env + ``` + + Then configure the file accordingly. ```bash cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env From 59510dbfb0c283eafca544bbcee543867d50e390 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 21 Oct 2024 18:45:07 +0200 Subject: [PATCH 171/272] docs(holesky): validator registration script --- testnets/holesky/README.md | 55 +++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index af896fa11..34e41aaff 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -350,10 +350,10 @@ on the Bolt Registry to successfully receive preconfirmation requests from users and RPCs. This step is needed to provide economic security to your commitments. -In order to do that you need some collateral in the form of whitelisted Liquid -Staking Token (LST) that needs to be restaked in either the Symbiotic or -EigenLayer protocol. Bolt is compatible with ETH derivatives on Holesky. Here -are references to the supported tokens on both restaking protocols: +In order to do that you need some collateral in the form of whitelisted ETH derivative +tokens that need to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens +on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) @@ -376,6 +376,24 @@ protocols. > public key associated to the private key used to sign commitments with the > Bolt Sidecar (the `--commitment-private-key` flag). +## Prerequisites + +- Install the Foundry tools: + +```bash +curl -L https://foundry.paradigm.xyz | bash +source $HOME/.bashrc +foundryup +``` + +- Clone the Bolt repo and navigate to the [contracts](https://github.com/chainbound/bolt/tree/unstable/bolt-contracts) directory: + +```bash +git clone https://github.com/chainbound/bolt +cd bolt-contracts +forge install +``` + ## Validator Registration The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for @@ -395,6 +413,35 @@ Until the Pectra hard-fork will be activated, the contract will also expose a `r that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and will allow us to test the registration flow in a controlled environment. +Note that the account initiating the registration will be the `controller` account for those validators. Only the `controller` can then +deregister validator or change any preferences. + +### Registration Steps + +> [!NOTE] +> All of these scripts can be simulated on a Holesky fork using Anvil with the following command: +> ```bash +> anvil --fork-url https://holesky.drpc.org +> ``` +> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in all of the `forge` commands below. + +To register your validators, we provide the following Foundry script: [`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` directory. + +- First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that +both `maxCommittedGasLimit` and `authorizedOperator` must reflect the values specified in previous steps, during the configuration of the sidecar. +`pubkeys` should be configured with all of the validator public keys that you wish to register. + +- Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. +This controller key will be used to run the script and will mark the corresponding account as the controller account for these validators. + +- Finally, run the script: +```bash +forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast +``` + +If the script executed succesfully, your validators were registered. + ## Bolt Network Entrypoint The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that From 808d0906b00a3073927f0fa9e4985b7e49f0676b Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Tue, 22 Oct 2024 10:25:18 +0200 Subject: [PATCH 172/272] docs(holesky): operator registration --- testnets/holesky/README.md | 115 +++++++++++++------------------------ 1 file changed, 41 insertions(+), 74 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 34e41aaff..72988c64d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -444,7 +444,7 @@ If the script executed succesfully, your validators were registered. ## Bolt Network Entrypoint -The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. @@ -457,44 +457,46 @@ Key features include: Specific functionalities about the restaking protocols are handled inside the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. -### Symbiotic Integration guide for Staking Pools +## Operator Registration +In this section we outline how to register as an operator, i.e. an entity uniquely identified by an Ethereum address and responsible for +duties like signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in +the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically +connect validators to an on-chain address associated with some stake, which is what the operator is. -As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. -If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) -on how to spin up a Vault and start receiving stake from your node operators. +**In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will +be read by the Forge scripts for registering operators and needs to be set correctly. You also have to invoke the scripts from the +[`bolt-contracts`](../../bolt-contracts) directory. -Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: - -1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. -2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. -3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. -4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. -5. You can now start approving operators that opt in to your vault directly in Symbiotic. -6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. - -### Symbiotic Integration guide for Operators +### Symbiotic Registration Steps As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide -commitments on their behalf. +commitments on their behalf. The opt-in process requires the following steps: +#### External Steps + +> [!NOTE] The network and supported vault addresses can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). + 1. register in Symbiotic with `OperatorRegistry.registerOperator()`. 2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. 3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. -4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. -5. get approved by the vault. -6. start providing commitments with the stake provided by the vault. -### EigenLayer Integration Guide for Node Operators and Solo Stakers +#### Internal Steps -> [!NOTE] -> Without loss of generality, we will assume the reader of this guide is a Node -> Operator (NO), since the same steps apply to solo stakers. -> As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) -> in the Bolt AVS built on top of EigenLayer. This requires -> running an Ethereum validator and the Bolt sidecar in order issue -> preconfirmations. +Run the provided Forge script to register a Symbiotic operator: + +```bash +forge script script/holesky/validators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +``` + +If all goes well, your Symbiotic operator was registered into Bolt. + +### EigenLayer Registration Steps + +#### External Steps + +> [!NOTE] The supported strategies can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). The Operator will be represented by an Ethereum address that needs to follow the standard procedure outlined in the @@ -502,53 +504,18 @@ to follow the standard procedure outlined in the 1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). -2. As an Ethereum validator offering precofirmations a NO needs some collateral in - order to be economically credible. In order to do that, some entities known as a "stakers" - need to deposit whitelisted Liquid Staking Tokens (LSTs) - into an appropriate "Strategy" associated to the LST via the - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), - so that the Operator has a `min_amount` (for Holesky 1 ether) of collateral associated to it. - Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract - in the `getWhitelistedCollaterals` function. - Note that NOs and stakers can be two different entities - _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. - -3. After the stakers have deposited their collateral into a strategy they need - to choose you as their operator. To do that, they need to call the function - [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). - -4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. - This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. - The payload is a signature whose digest consists of: - - 1. your operator address - 2. the `BoltEigenLayerMiddleware` contract address - 3. a salt - 4. an expiry 2. - - The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) - with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, - the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. - -Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` -contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a -registered operator and so that they can forward you preconfirmation requests. - -The steps required are the following: - -1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. - If you own more than one validator public key, - you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. - The `authorizedOperator` argument must be the same Ethereum address used for - opting into EigenLayer as an Operator. - -2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling - the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network - and allows you to manage operations effectively, such as pausing or resuming - your service. - -3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. - This ensures that your restaked assets are correctly integrated with Bolt’s system. +2. You can then use the same account to deposit into a supported EigenLayer strategy using [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). This will add the deposit into the collateral of the operator +so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. + +#### Internal Steps + +Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: + +```bash +forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +``` + +If all goes well, your EigenLayer operator was registered into Bolt. # Reference From 3cbbc7dd1dfa1ac1698938b714d2b5fdf99ff4e3 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 13:24:53 +0200 Subject: [PATCH 173/272] chore(holesky): README fmt --- testnets/holesky/README.md | 160 ++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 64 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 72988c64d..f3fe42e53 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -15,10 +15,11 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Observability](#observability) * [On-Chain Registration](#on-chain-registration) * [Validator Registration](#validator-registration) + * [Registration Steps](#registration-steps) * [Bolt Network Entrypoint](#bolt-network-entrypoint) - * [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) - * [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) - * [EigenLayer Integration Guide for Node Operators and Solo Stakers](#eigenlayer-integration-guide-for-node-operators-and-solo-stakers) + * [Operator Registration](#operator-registration) + * [Symbiotic Registration Steps](#symbiotic-registration-steps) + * [EigenLayer Registration Steps](#eigenlayer-registration-steps) * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) @@ -67,8 +68,9 @@ client implementations to download and run them. > --builder-fallback-disable-checks > ``` > -> In other clients like Vouch, the same can be achieved by setting the `builder-boost-factor` to a large value -> like 18446744073709551615. +> In other clients like Vouch, the same can be achieved by setting the +> `builder-boost-factor` to a large value like `18446744073709551615` (`2**64 - +1`). > > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon @@ -134,18 +136,14 @@ containing the necessary environment variables: 2. **MEV-Boost Configuration:** - Copy over the example environment file: - - ```bash - cp ../../mev-boost/.env.example mev-boost.env - ``` - - Then configure the file accordingly. + Copy over the example configuration file: ```bash cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env ``` + Then configure it accordingly. + If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. @@ -376,7 +374,7 @@ protocols. > public key associated to the private key used to sign commitments with the > Bolt Sidecar (the `--commitment-private-key` flag). -## Prerequisites +**Prerequisites** - Install the Foundry tools: @@ -396,46 +394,60 @@ forge install ## Validator Registration -The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for -validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only +point of entry for validators to signal their intent to participate in Bolt +Protocol and authenticate with their BLS private key. The registration process includes the following steps: -1. Validator signs a message with their BLS private key. This is required to prove that the - validator private key is under their control and that they are indeed its owner. +1. Validator signs a message with their BLS private key. This is required to + prove that the validator private key is under their control and that they are + indeed its owner. 2. Validator calls the `registerValidator` function providing: 1. Their BLS public key 2. The BLS signature of the registration message 3. The address of the authorized collateral provider 4. The address of the authorized operator -Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function -that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and -will allow us to test the registration flow in a controlled environment. +Until the Pectra hard-fork will be activated, the contract will also expose a +`registerValidatorUnsafe` function that will not check the BLS signature. This +is gated by a feature flag that will be turned off post-Pectra and will allow us +to test the registration flow in a controlled environment. -Note that the account initiating the registration will be the `controller` account for those validators. Only the `controller` can then -deregister validator or change any preferences. +Note that the account initiating the registration will be the `controller` +account for those validators. Only the `controller` can then deregister +validator or change any preferences. ### Registration Steps > [!NOTE] -> All of these scripts can be simulated on a Holesky fork using Anvil with the following command: -> ```bash -> anvil --fork-url https://holesky.drpc.org -> ``` -> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in all of the `forge` commands below. - -To register your validators, we provide the following Foundry script: [`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). -Note that in order to run these scripts, you must be in the `bolt-contracts` directory. - -- First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that -both `maxCommittedGasLimit` and `authorizedOperator` must reflect the values specified in previous steps, during the configuration of the sidecar. -`pubkeys` should be configured with all of the validator public keys that you wish to register. - -- Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. -This controller key will be used to run the script and will mark the corresponding account as the controller account for these validators. +> All of these scripts can be simulated on a Holesky fork using Anvil with the +> following command: +> +> `bash anvil --fork-url https://holesky.drpc.org ` +> +> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in +> all of the `forge` commands below. + +To register your validators, we provide the following Foundry script: +[`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` +directory. + +- First, configure + [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) + to your requirements. Note that both `maxCommittedGasLimit` and + `authorizedOperator` must reflect the values specified in previous steps, during + the configuration of the sidecar. `pubkeys` should be configured with all of the + validator public keys that you wish to register. + +- Next up, decide on a controller account and save the key in an environment + variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run + the script and will mark the corresponding account as the controller account for + these validators. - Finally, run the script: + ```bash forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast ``` @@ -444,8 +456,9 @@ If the script executed succesfully, your validators were registered. ## Bolt Network Entrypoint -The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that -integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) +contract is a crucial component of Bolt that integrates with restaking +ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. Key features include: @@ -454,35 +467,45 @@ Key features include: 2. Integration with Symbiotic 3. Integration with Eigenlayer -Specific functionalities about the restaking protocols are handled inside -the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. +Specific functionalities about the restaking protocols are handled inside the +`IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and +`BoltEigenlayerMiddleware`. ## Operator Registration -In this section we outline how to register as an operator, i.e. an entity uniquely identified by an Ethereum address and responsible for -duties like signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in -the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically -connect validators to an on-chain address associated with some stake, which is what the operator is. -**In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will -be read by the Forge scripts for registering operators and needs to be set correctly. You also have to invoke the scripts from the -[`bolt-contracts`](../../bolt-contracts) directory. +In this section we outline how to register as an operator, i.e. an entity +uniquely identified by an Ethereum address and responsible for duties like +signing commitments. Note that in Bolt, there is no real separation between +validators and an operator. An operator is only real in the sense that its +private key will be used to sign commitments on the corresponding validators' +sidecars. However, we need a way to logically connect validators to an on-chain +address associated with some stake, which is what the operator is. + +**In the next sections we assume you have saved the private key corresponding to +the operator address in `$OPERATOR_SK`.** This private key will be read by the +Forge scripts for registering operators and needs to be set correctly. You also +have to invoke the scripts from the [`bolt-contracts`](../../bolt-contracts) +directory. ### Symbiotic Registration Steps -As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide -commitments on their behalf. +As an operator, you will need to opt-in to the Bolt Network and any Vault that +trusts you to provide commitments on their behalf. The opt-in process requires the following steps: -#### External Steps +**External Steps** -> [!NOTE] The network and supported vault addresses can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +> [!NOTE] +> The network and supported vault addresses can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). 1. register in Symbiotic with `OperatorRegistry.registerOperator()`. -2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +2. opt-in to the Bolt network with + `OperatorNetworkOptInService.optIn(networkAddress)`. 3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. -#### Internal Steps +**Internal Steps** Run the provided Forge script to register a Symbiotic operator: @@ -494,22 +517,31 @@ If all goes well, your Symbiotic operator was registered into Bolt. ### EigenLayer Registration Steps -#### External Steps +**External Steps** -> [!NOTE] The supported strategies can be found in [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +> [!NOTE] +> The supported strategies can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -The Operator will be represented by an Ethereum address that needs -to follow the standard procedure outlined in the -[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: +The Operator will be represented by an Ethereum address that needs to follow the +standard procedure outlined in the [EigenLayer +documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go +through the steps: -1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). +1. As an Operator, you register into EigenLayer using + [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). -2. You can then use the same account to deposit into a supported EigenLayer strategy using [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). This will add the deposit into the collateral of the operator -so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. +2. You can then use the same account to deposit into a supported EigenLayer + strategy using + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). + This will add the deposit into the collateral of the operator so that Bolt can + read it. Note that you need to deposit a minimum of `1 ether` of the strategies + underlying token in order to opt in. -#### Internal Steps +**Internal Steps** -Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: +Set the operator private key to an `OPERATOR_SK` environment variable, and then +run the following Forge script from the `bolt-contracts` directory: ```bash forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast From 4131a9294887d247e515d32e93282411342bb2d3 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 13:51:03 +0200 Subject: [PATCH 174/272] chore(holesky): validator controller reference on README --- testnets/holesky/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index f3fe42e53..531f3d982 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -443,8 +443,9 @@ directory. - Next up, decide on a controller account and save the key in an environment variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run - the script and will mark the corresponding account as the controller account for - these validators. + the script and will mark the corresponding account as the [controller + account](https://github.com/chainbound/bolt/blob/06bdd8e75d759d91f6178ad73f962b1f4ad43fd8/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol#L18-L19) + for these validators. - Finally, run the script: From 9fb22a15755d8acb051b7e917d7abca045dd24b8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 14:04:01 +0200 Subject: [PATCH 175/272] fix(holesky): stale flags in README --- testnets/holesky/README.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 531f3d982..fb6594a4e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -888,17 +888,21 @@ below. As you can see in the [command line options](#command-line-options) section you can pass directly the private key as a hex-encoded string to the Bolt sidecar -using the `--private-key` flag. This is the simplest setup and can be used in +using the `--constraint-private-key` flag (or `constraint_private_key` in the +TOML file). + +This is the simplest setup and can be used in case if all the delegations messages point to the same delegatee or if you're running the sidecar with a single active validator. ### Using a ERC-2335 Keystore -The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. -In order to use them you need to provide the `--keystore-path` pointing to the -folder containing the keystore files and the `--keystore-password` or -`keystore-secrets-path` flag pointing to the folder containing the password -file. +The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) +keystores for loading signing keypairs. In order to use them you need to provide +the `--keystore-path` pointing to the folder containing the keystore files and +the `--keystore-password` or `keystore-secrets-path` flag pointing to the folder +containing the password file (in the TOML configuration file these are the +`keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). Both the `keys` and `passwords` folders must adhere to the structure outlined in the [Delegations CLI example](#delegations-cli-example) section. @@ -914,9 +918,9 @@ However if you're already running a PBS sidecar like [MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid the restart by following this steps when starting the Bolt sidecar: -1. Set the `--constraints-proxy-port` flag or the - `BOLT_SIDECAR_BUILDER_PROXY_PORT` environment variable to the port previously occupied by - MEV-Boost. +1. Set the `--constraints-proxy-port` flag (the `constraints_proxy_port` option + in the TOML file) to the port previously occupied by MEV-Boost. 2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it using another port -3. Set the `--constraints-url` flag or the `BOLT_SIDECAR_CONSTRAINTS_URL` to point to the Bolt MEV-Boost instance. +3. Set the `--constraints-api-url` flag (or the `constraints_api_url` in the + TOML file) to point to the Bolt MEV-Boost instance. From 3b8731caadc66fb89427901736a7d2d1789b2b70 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 22 Oct 2024 14:04:19 +0200 Subject: [PATCH 176/272] chore(sidecar): mark required fields in the Config.example.toml --- bolt-sidecar/Config.example.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml index b76ead584..2bb86003b 100644 --- a/bolt-sidecar/Config.example.toml +++ b/bolt-sidecar/Config.example.toml @@ -21,18 +21,23 @@ constraints_api_url = "http://localhost:18551" # - a comma-separated list of indexes (e.g. "1,2,3,4") # - a contiguous range of indexes (e.g. "1..4") # - a mix of the above (e.g. "1,2..4,6..8") +# REQUIRED validator_indexes = "0..64" # The JWT secret token to authenticate calls to the engine API. It can be # either be a hex-encoded string or a file path to a file containing the # hex-encoded secret. +# REQUIRED engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" # The fee recipient address for fallback blocks +# REQUIRED fee_recipient = "0x0000000000000000000000000000000000000000" # Secret ECDSA key to sign commitment messages with. The public key associated # to it must be then used when registering the operator in the `BoltManager` # contract +# REQUIRED commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" # Secret BLS key to sign fallback payloads with +# REQUIRED builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" # Commitments limits @@ -69,6 +74,7 @@ commitment_deadline = 8000 # cb_signer_url = "http://localhost:18551" # cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" # keystore_password = "password" +# keystore_secrets_path = "./secrets" # keystore_path = "./keys" # delegations_path = "./delegations.json" From 6663b1890acaa3081039eeb24949b0b8bcb01a8d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 10:31:36 +0200 Subject: [PATCH 177/272] fix(holesky): wrong code links and snippets in README --- testnets/holesky/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index fb6594a4e..fa1a31cfd 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -424,9 +424,9 @@ validator or change any preferences. > All of these scripts can be simulated on a Holesky fork using Anvil with the > following command: > -> `bash anvil --fork-url https://holesky.drpc.org ` +> `anvil --fork-url https://holesky.drpc.org --port 8545` > -> In order to use this local fork, replace `$HOLESKY_RPC` with localhost:8545 in +> In order to use this local fork, replace `$HOLESKY_RPC` with `localhost:8545` in > all of the `forge` commands below. To register your validators, we provide the following Foundry script: @@ -530,11 +530,11 @@ documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: 1. As an Operator, you register into EigenLayer using - [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/DelegationManager.sol#L107-L119). 2. You can then use the same account to deposit into a supported EigenLayer strategy using - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110). + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). This will add the deposit into the collateral of the operator so that Bolt can read it. Note that you need to deposit a minimum of `1 ether` of the strategies underlying token in order to opt in. From cd48a2aa1ca8d611d9ca626fd3d5bf71ca9f7a14 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 17:41:11 +0200 Subject: [PATCH 178/272] feat(holesky): test commit grafana dashboard and prometheus setup --- .../grafana/dashboards/bolt_dashboard.json | 740 ++++++++++++++++-- .../grafana/datasources/datasources.yml | 6 +- testnets/holesky/targets.json | 14 + 3 files changed, 686 insertions(+), 74 deletions(-) create mode 100644 testnets/holesky/targets.json diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json index 886ddfe02..dce85acb0 100644 --- a/testnets/holesky/grafana/dashboards/bolt_dashboard.json +++ b/testnets/holesky/grafana/dashboards/bolt_dashboard.json @@ -15,36 +15,144 @@ } ] }, - "description": "Metrics related to the bolt-sidecar and bolt-boost.", "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 3, + "id": 2, "links": [], + "liveNow": false, "panels": [ { - "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 1, - "w": 24, + "h": 8, + "w": 12, "x": 0, "y": 0 }, - "id": 2, - "panels": [], - "title": "Bolt sidecar", - "type": "row" + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transactions Preconfirmed", + "type": "timeseries" }, { "datasource": { - "default": true, "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { "color": { - "mode": "thresholds" + "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", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } }, "mappings": [], "thresholds": { @@ -53,6 +161,10 @@ { "color": "green", "value": null + }, + { + "color": "red", + "value": 80 } ] } @@ -62,67 +174,130 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, - "y": 1 + "x": 12, + "y": 0 }, - "id": 1, + "id": 10, "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + "tooltip": { + "mode": "single", + "sort": "none" + } }, - "pluginVersion": "11.2.0", "targets": [ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, - "disableTextWrap": false, "editorMode": "builder", - "expr": "bolt_sidecar_latest_head", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "expr": "bolt_sidecar_validation_errors", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "Latest head", - "type": "stat" + "title": "Invalid Transactions Reasons", + "type": "timeseries" }, { - "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + } + ] + } + }, + "overrides": [] + }, "gridPos": { - "h": 1, - "w": 24, + "h": 8, + "w": 12, "x": 0, - "y": 9 + "y": 8 }, - "id": 4, - "panels": [], - "title": "Bolt boost", - "type": "row" + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Remote Blocks Proposed", + "type": "timeseries" }, { "datasource": { - "default": true, "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, "fieldConfig": { "defaults": { @@ -130,13 +305,11 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, - "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -145,8 +318,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -182,10 +354,10 @@ "gridPos": { "h": 8, "w": 12, - "x": 0, - "y": 10 + "x": 12, + "y": 8 }, - "id": 3, + "id": 4, "options": { "legend": { "calcs": [], @@ -202,25 +374,451 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "PBFA97CFB590B2093" }, - "disableTextWrap": false, "editorMode": "builder", - "expr": "cb_pbs_constraints_cache_size", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "{{__name__}}", + "expr": "bolt_sidecar_inclusion_commitments_received", + "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "Constraints cache size", + "title": "Inclusion Commitments Received", "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Local Blocks Proposed", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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": "stepAfter", + "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 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Inclusion Commitments Accepted", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "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", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Total HTTP Requests", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_duration_seconds", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "HTTP Requests Durations in ms", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Head Slot", + "type": "stat" } ], - "schemaVersion": 39, + "refresh": "", + "schemaVersion": 38, + "style": "dark", "tags": [], "templating": { "list": [] @@ -230,9 +828,9 @@ "to": "now" }, "timepicker": {}, - "timezone": "browser", - "title": "Bolt Metrics", - "uid": "edxnwlpgaw934c", + "timezone": "", + "title": "Bolt Sidecar", + "uid": "e5960f6d-a1ed-4538-9c7c-3ecba4d4b4b1", "version": 3, "weekStart": "" } diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml index 6d29d617b..91e10e2b4 100644 --- a/testnets/holesky/grafana/datasources/datasources.yml +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -1,11 +1,11 @@ apiVersion: 1 datasources: - - name: cb-prometheus + - name: bolt-prometheus-holesky type: prometheus - uid: cb_prometheus + uid: bolt-prometheus-holesky access: proxy orgId: 1 - url: http://cb_prometheus:9090 + url: http://bolt-prometheus-holesky:49090 isDefault: true editable: true diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json new file mode 100644 index 000000000..6cd87cbc6 --- /dev/null +++ b/testnets/holesky/targets.json @@ -0,0 +1,14 @@ +[ + { + "targets": ["bolt-sidecar-holesky:8017"], + "labels": { + "job": "bolt-sidecar-rpc" + } + }, + { + "targets": ["bolt-sidecar-holesky:18550"], + "labels": { + "job": "bolt-sidecar-builder-proxy" + } + } +] From a017f92e81ab5d925e05da83b526eb4cc8b3326c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 10:53:16 +0200 Subject: [PATCH 179/272] fix(contracts): move operators scripts into new operators folder --- .../RegisterEigenLayerOperator.s.sol | 0 .../{validators => operators}/RegisterSymbioticOperator.s.sol | 0 testnets/holesky/README.md | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename bolt-contracts/script/holesky/{validators => operators}/RegisterEigenLayerOperator.s.sol (100%) rename bolt-contracts/script/holesky/{validators => operators}/RegisterSymbioticOperator.s.sol (100%) diff --git a/bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol similarity index 100% rename from bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol rename to bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol diff --git a/bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol similarity index 100% rename from bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol rename to bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index fa1a31cfd..4a6f6df60 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -511,7 +511,7 @@ The opt-in process requires the following steps: Run the provided Forge script to register a Symbiotic operator: ```bash -forge script script/holesky/validators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast ``` If all goes well, your Symbiotic operator was registered into Bolt. @@ -545,7 +545,7 @@ Set the operator private key to an `OPERATOR_SK` environment variable, and then run the following Forge script from the `bolt-contracts` directory: ```bash -forge script script/holesky/validators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast ``` If all goes well, your EigenLayer operator was registered into Bolt. From 32c63919690ec9fce0d011af8116c87e6a80e6be Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 11:46:03 +0200 Subject: [PATCH 180/272] feat(holesky): EL script for StrategyManager.depositIntoStrategy --- .../config/holesky/deployments.json | 74 +++++++++--------- bolt-contracts/config/holesky/operator.json | 9 ++- .../eigenlayer/depositIntoStrategy.json | 5 ++ .../RegisterEigenLayerOperator.s.sol | 59 +++++++++++++- testnets/holesky/README.md | 77 ++++++++++++++----- 5 files changed, 159 insertions(+), 65 deletions(-) create mode 100644 bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json diff --git a/bolt-contracts/config/holesky/deployments.json b/bolt-contracts/config/holesky/deployments.json index a5a41415d..2866f9e27 100644 --- a/bolt-contracts/config/holesky/deployments.json +++ b/bolt-contracts/config/holesky/deployments.json @@ -1,38 +1,38 @@ { - "bolt": { - "validators": "0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8", - "parameters": "0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12", - "manager": "0x440202829b493F9FF43E730EB5e8379EEa3678CF" - }, - "symbiotic": { - "network": "0xb017002D8024d8c8870A5CECeFCc63887650D2a4", - "operatorRegistry": "0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548", - "networkOptInService": "0x58973d16FFA900D11fC22e5e2B6840d9f7e13401", - "vaultFactory": "0x407A039D94948484D356eFB765b3c74382A050B4", - "vaultConfigurator": "0xD2191FE92987171691d552C219b8caEf186eb9cA", - "networkRegistry": "0x7d03b7343BF8d5cEC7C0C27ecE084a20113D15C9", - "networkMiddlewareService": "0x62a1ddfD86b4c1636759d9286D3A0EC722D086e3", - "middleware": "0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8", - "supportedVaults": [ - "0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78", - "0xe5708788c90e971f73D928b7c5A8FD09137010e0", - "0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd", - "0xC56Ba584929c6f381744fA2d7a028fA927817f2b", - "0xcDdeFfcD2bA579B8801af1d603812fF64c301462", - "0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f" - ] - }, - "eigenLayer": { - "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", - "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", - "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", - "middleware": "0xa632a3e652110Bb2901D5cE390685E6a9838Ca04", - "supportedStrategies": [ - "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", - "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", - "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", - "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", - "0xaccc5A86732BE85b5012e8614AF237801636F8e5" - ] - } -} \ No newline at end of file + "bolt": { + "validators": "0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8", + "parameters": "0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12", + "manager": "0x440202829b493F9FF43E730EB5e8379EEa3678CF" + }, + "symbiotic": { + "network": "0xb017002D8024d8c8870A5CECeFCc63887650D2a4", + "operatorRegistry": "0x6F75a4ffF97326A00e52662d82EA4FdE86a2C548", + "networkOptInService": "0x58973d16FFA900D11fC22e5e2B6840d9f7e13401", + "vaultFactory": "0x407A039D94948484D356eFB765b3c74382A050B4", + "vaultConfigurator": "0xD2191FE92987171691d552C219b8caEf186eb9cA", + "networkRegistry": "0x7d03b7343BF8d5cEC7C0C27ecE084a20113D15C9", + "networkMiddlewareService": "0x62a1ddfD86b4c1636759d9286D3A0EC722D086e3", + "middleware": "0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8", + "supportedVaults": [ + "0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78", + "0xe5708788c90e971f73D928b7c5A8FD09137010e0", + "0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd", + "0xC56Ba584929c6f381744fA2d7a028fA927817f2b", + "0xcDdeFfcD2bA579B8801af1d603812fF64c301462", + "0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f" + ] + }, + "eigenLayer": { + "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", + "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", + "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", + "middleware": "0xa632a3e652110Bb2901D5cE390685E6a9838Ca04", + "supportedStrategies": [ + "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", + "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", + "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", + "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", + "0xaccc5A86732BE85b5012e8614AF237801636F8e5" + ] + } +} diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operator.json index 0b6373c92..7e1d43c10 100644 --- a/bolt-contracts/config/holesky/operator.json +++ b/bolt-contracts/config/holesky/operator.json @@ -1,5 +1,6 @@ { - "rpc": "localhost:50051", - "salt": "0x000000000000000abc0000000000000000000000000000000000000000000000", - "expiry": null -} \ No newline at end of file + "rpc": ":", + "salt": "0x0000000000000000000_salt_value_0000000000000000000000000000000000", + "expiry": "0x00000000000000000_expiry_value_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +} + diff --git a/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json new file mode 100644 index 000000000..f500598aa --- /dev/null +++ b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json @@ -0,0 +1,5 @@ +{ + "strategy": "", + "token": "", + "amount": "" +} diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 3acbe2d81..109f70c73 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -4,13 +4,14 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; +import {IDelegationManager} from "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy, IERC20} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; import {IBoltMiddlewareV1} from "../../../src/interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; contract RegisterEigenLayerOperator is Script { struct OperatorConfig { @@ -19,9 +20,27 @@ contract RegisterEigenLayerOperator is Script { uint256 expiry; } - function run() public { + function S01_depositIntoStrategy() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); + IStrategyManager strategyManager = _readStrategyManager(); + + string memory json = vm.readFile("config/holesky/operators/eigenlayer/depositIntoStrategy.json"); + + IStrategy strategy = IStrategy(vm.parseJsonAddress(json, ".strategy")); + IERC20 token = IERC20(vm.parseJsonAddress(json, ".token")); + uint256 amount = vm.parseJsonUint(json, ".amount"); + + vm.startBroadcast(operatorSk); + // Allowance must be set before depositing + token.approve(address(strategyManager), amount); + strategyManager.depositIntoStrategy(strategy, token, amount); + console.log("Successfully run StrategyManager.depositIntoStrategy"); + vm.stopBroadcast(); + } + + function S02_registerIntoBoltAVS() public { + uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); @@ -53,6 +72,16 @@ contract RegisterEigenLayerOperator is Script { vm.stopBroadcast(); } + function S03_checkOperatorRegistration() public view { + address operatorPublicKey = vm.envAddress("OPERATOR_PK"); + console.log("Checking operator registration for address", operatorPublicKey); + + IBoltManagerV1 boltManager = _readBoltManager(); + bool isRegistered = boltManager.isOperator(operatorPublicKey); + console.log("Operator is registered:", isRegistered); + require(isRegistered, "Operator is not registered"); + } + function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -69,6 +98,28 @@ contract RegisterEigenLayerOperator is Script { return IAVSDirectory(vm.parseJsonAddress(json, ".eigenLayer.avsDirectory")); } + function _readDelegationManager() public view returns (IDelegationManager) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return IDelegationManager(vm.parseJsonAddress(json, ".eigenLayer.delegationManager")); + } + + function _readStrategyManager() public view returns (IStrategyManager) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IStrategyManager(vm.parseJsonAddress(json, ".eigenLayer.strategyManager")); + } + + function _readBoltManager() public view returns (IBoltManagerV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + } + function _readConfig( string memory path ) public view returns (OperatorConfig memory) { diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 4a6f6df60..cfdf062da 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -524,31 +524,66 @@ If all goes well, your Symbiotic operator was registered into Bolt. > The supported strategies can be found in > [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -The Operator will be represented by an Ethereum address that needs to follow the -standard procedure outlined in the [EigenLayer -documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go -through the steps: +If you're not registered as an operator in EigenLayer yet, you need to do so by +following [the official +guide](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-introduction). +This requires installing the EigenLayer CLI and opt into the protocol by +registering via the +[`DelegationManager.registerAsOperator`](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-installation) +function. + +After that you need to deposit into a supported EigenLayer +strategy using +[`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). +This will add the deposit into the collateral of the operator so that Bolt can +read it. Note that you need to deposit a minimum of `1 ether` of the strategies +underlying token in order to opt in. + +We've provided a script to facilitate the procedure. If you want to use it, +please set the operator private key to an `OPERATOR_SK` environment variable. + +First, you need to first configure the deposit details in this JSON +file: -1. As an Operator, you register into EigenLayer using - [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/DelegationManager.sol#L107-L119). +```bash +$EDITOR ./config/holesky/operators/eigenlayer/depositIntoStrategy.json +``` + +Then you can run the following Forge script: -2. You can then use the same account to deposit into a supported EigenLayer - strategy using - [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). - This will add the deposit into the collateral of the operator so that Bolt can - read it. Note that you need to deposit a minimum of `1 ether` of the strategies - underlying token in order to opt in. +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S01_depositIntoStrategy()" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast +``` **Internal Steps** -Set the operator private key to an `OPERATOR_SK` environment variable, and then -run the following Forge script from the `bolt-contracts` directory: +After having deposited collateral into a strategy you need to register into the +Bolt AVS. We've provided a script to facilitate the procedure. If you want to +use it, please set the operator private key to an `OPERATOR_SK` environment +variable, and then run the following Forge script from the `bolt-contracts` +directory: ```bash -forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S02_registerIntoBoltAVS" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -If all goes well, your EigenLayer operator was registered into Bolt. +To check if your operator is correctly registered, set the operator public key +in the `OPERATOR_PK` environment variable and run the following script: + +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S03_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` # Reference @@ -559,13 +594,14 @@ sidecar. You can see them in your terminal by running the Bolt sidecar binary with the `--help` flag: ``` + Command-line options for the Bolt sidecar Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: - --port - Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! +--port +Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] [default: 8017] @@ -709,8 +745,9 @@ Options: --disable-metrics [env: BOLT_SIDECAR_DISABLE_METRICS=] - -h, --help - Print help (see a summary with '-h') +-h, --help +Print help (see a summary with '-h') + ``` ## Delegations and signing options for Native and Docker Compose Mode From fab2107f5890009d5b3faefb67c27bda3768ab61 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 10:22:06 +0200 Subject: [PATCH 181/272] fix(holesky): formatting in README --- testnets/holesky/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index cfdf062da..6089342c1 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -600,8 +600,8 @@ Command-line options for the Bolt sidecar Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: ---port -Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! + --port + Port to listen on for incoming JSON-RPC requests of the Commitments API. This port should be open on your firewall in order to receive external requests! [env: BOLT_SIDECAR_PORT=] [default: 8017] @@ -637,8 +637,11 @@ Port to listen on for incoming JSON-RPC requests of the Commitments API. This po [default: 18550] --validator-indexes - Validator indexes of connected validators that the sidecar should accept commitments on behalf of. Accepted values: - a comma-separated list of indexes (e.g. "1,2,3,4") - a contiguous range of indexes (e.g. "1..4") - a mix of the - above (e.g. "1,2..4,6..8") + Validator indexes of connected validators that the sidecar should accept commitments on behalf of. + Accepted values: + - a comma-separated list of indexes (e.g. "1,2,3,4") + - a contiguous range of indexes (e.g. "1..4") + - a mix of the above (e.g. "1,2..4,6..8") [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] @@ -745,8 +748,8 @@ Port to listen on for incoming JSON-RPC requests of the Commitments API. This po --disable-metrics [env: BOLT_SIDECAR_DISABLE_METRICS=] --h, --help -Print help (see a summary with '-h') + -h, --help + Print help (see a summary with '-h') ``` From 310861f7f68be575c0832a136dd0441ed8a3a82c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 11:04:20 +0200 Subject: [PATCH 182/272] chore(holesky): better explanation of operator config steps for EL --- .../eigenlayer/registerIntoBoltAVS.json} | 5 ++- .../RegisterEigenLayerOperator.s.sol | 2 +- testnets/holesky/README.md | 33 +++++++++++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) rename bolt-contracts/config/holesky/{operator.json => operators/eigenlayer/registerIntoBoltAVS.json} (68%) diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json similarity index 68% rename from bolt-contracts/config/holesky/operator.json rename to bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json index 7e1d43c10..dd38cee99 100644 --- a/bolt-contracts/config/holesky/operator.json +++ b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json @@ -1,6 +1,5 @@ { "rpc": ":", - "salt": "0x0000000000000000000_salt_value_0000000000000000000000000000000000", - "expiry": "0x00000000000000000_expiry_value_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "salt": "0x0000000000000000000_salt_value_000000000000000000000000000000000", + "expiry": "0x00000000000000000_expiry_value_000000000000000000000000000000000" } - diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 109f70c73..c67b7ef06 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -45,7 +45,7 @@ contract RegisterEigenLayerOperator is Script { BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); IAVSDirectory avsDirectory = _readAvsDirectory(); - OperatorConfig memory config = _readConfig("config/holesky/operator.json"); + OperatorConfig memory config = _readConfig("config/holesky/operators/eigenlayer/registerIntoBoltAVS.json"); console.log("Registering EigenLayer operator"); console.log("Operator address:", operator); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 6089342c1..524d64335 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -563,9 +563,36 @@ forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ After having deposited collateral into a strategy you need to register into the Bolt AVS. We've provided a script to facilitate the procedure. If you want to -use it, please set the operator private key to an `OPERATOR_SK` environment -variable, and then run the following Forge script from the `bolt-contracts` -directory: +use it, please set follow these steps: + +1. configure the operator details in this JSON file + + ```bash + $EDITOR ./config/holesky/operators/eigenlayer/registerIntoBoltAVS.json + ``` + + In there you'll need to set the the following fields: + + - `rpc` -- the RPC URL of your operator which supports the Commitments API + - `salt` -- an unique 32 bytes value to avoid replay attacks. To generate it on + both Linux and MacOS you can run: + + ```bash + echo -n "0x"; head -c 32 /dev/urandom | hexdump -e '32/1 "%02x" "\n"' + ``` + + - `expiry` -- the timestamp of the signature expiry in seconds. To generate it + on both Linux and MacOS run the following command, replacing + `` with the desired timestamp: + + ```bash + echo -n "0x"; printf "%064x\n" + ``` + +2. set the operator private key to an `OPERATOR_SK` environment + variable; +3. run the following Forge script from the `bolt-contracts` + directory: ```bash forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ From 5c46e4ac9537456647ae83987518a98c00e62c11 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 23 Oct 2024 16:08:38 +0200 Subject: [PATCH 183/272] feat(holesky): update scripts + README for Symbiotic integration --- .../operators/RegisterSymbioticOperator.s.sol | 37 +++++++++++----- testnets/holesky/README.md | 43 +++++++++++++++---- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index 0089432c0..f2728c85b 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -4,7 +4,10 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; + import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; +import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { @@ -14,7 +17,7 @@ contract RegisterSymbioticOperator is Script { address symbioticNetwork; } - function run() public { + function S01_registerIntoBolt() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); @@ -22,15 +25,12 @@ contract RegisterSymbioticOperator is Script { Config memory config = _readConfig(); // First, make sure the operator is opted into the network - if (!config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork)) { - console.log("Operator is not opted into the network yet. Opting in..."); - vm.startBroadcast(operatorSk); - config.symbioticNetworkOptInService.optIn(config.symbioticNetwork); - vm.stopBroadcast(); - console.log("Operator successfully opted into the network"); - } - - console.log("Registering Symbiotic operator"); + require( + config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork), + "Operator must be opted in into Bolt Network" + ); + + console.log("Registering Symbiotic operator into Bolt"); console.log("Operator address:", operator); console.log("Operator RPC:", config.rpc); @@ -41,6 +41,16 @@ contract RegisterSymbioticOperator is Script { vm.stopBroadcast(); } + function S02_checkOperatorRegistration() public view { + address operatorPublicKey = vm.envAddress("OPERATOR_PK"); + console.log("Checking operator registration for address", operatorPublicKey); + + IBoltManagerV1 boltManager = _readBoltManager(); + bool isRegistered = boltManager.isOperator(operatorPublicKey); + console.log("Operator is registered:", isRegistered); + require(isRegistered, "Operator is not registered"); + } + function _readConfig() public view returns (Config memory) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -56,4 +66,11 @@ contract RegisterSymbioticOperator is Script { symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) }); } + + function _readBoltManager() public view returns (IBoltManagerV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + } } diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 524d64335..b15e25fc0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -493,28 +493,53 @@ directory. As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide commitments on their behalf. -The opt-in process requires the following steps: - **External Steps** > [!NOTE] > The network and supported vault addresses can be found in > [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -1. register in Symbiotic with `OperatorRegistry.registerOperator()`. -2. opt-in to the Bolt network with - `OperatorNetworkOptInService.optIn(networkAddress)`. -3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +Make sure you have installed the [Symbiotic +CLI](https://docs.symbiotic.fi/guides/cli/). + +The opt-in process requires the following steps: + +1. if you haven't done it already, register as a Symbiotic Operator with the + [`register-operator`](https://docs.symbiotic.fi/guides/cli/#register-operator) + command; +2. opt-in to the Bolt network with the + [`opt-in-network`](https://docs.symbiotic.fi/guides/cli/#opt-in-network) + command; +3. opt-in to any vault using the + [`opt-in-vault`](https://docs.symbiotic.fi/guides/cli/#opt-in-vault) command; +4. deposit collateral into the vault using the + [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. **Internal Steps** -Run the provided Forge script to register a Symbiotic operator: +After having deposited collateral into a vault you need to register into +Bolt as a Symbiotic operator. We've provided a script to facilitate the +procedure. If you want to use it, please set the operator private key to an +`OPERATOR_SK` environment variable, and then run the following Forge script from +the `bolt-contracts` directory: ```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol --rpc-url $HOLESKY_RPC -vvvv --broadcast +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -If all goes well, your Symbiotic operator was registered into Bolt. +To check if your operator is correctly registered, set the operator public key +in the `OPERATOR_PK` environment variable and run the following script: + +```bash +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S02_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` ### EigenLayer Registration Steps From ee3109cae0b3810352134ff25a7074b7f375a2f0 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 11:13:17 +0200 Subject: [PATCH 184/272] fix(holesky): config options for Symbiotic guide Don't use operators.json file but provide the RPC URL using an enviroment value --- .../operators/RegisterSymbioticOperator.s.sol | 5 ++-- testnets/holesky/README.md | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index f2728c85b..b5a6d46d8 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -19,6 +19,7 @@ contract RegisterSymbioticOperator is Script { function S01_registerIntoBolt() public { uint256 operatorSk = vm.envUint("OPERATOR_SK"); + string memory rpc = vm.envString("OPERATOR_RPC"); address operator = vm.addr(operatorSk); @@ -32,10 +33,10 @@ contract RegisterSymbioticOperator is Script { console.log("Registering Symbiotic operator into Bolt"); console.log("Operator address:", operator); - console.log("Operator RPC:", config.rpc); + console.log("Operator RPC:", rpc); vm.startBroadcast(operatorSk); - config.symbioticMiddleware.registerOperator(config.rpc); + config.symbioticMiddleware.registerOperator(rpc); console.log("Successfully registered Symbiotic operator"); vm.stopBroadcast(); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b15e25fc0..154328429 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -519,17 +519,20 @@ The opt-in process requires the following steps: After having deposited collateral into a vault you need to register into Bolt as a Symbiotic operator. We've provided a script to facilitate the -procedure. If you want to use it, please set the operator private key to an -`OPERATOR_SK` environment variable, and then run the following Forge script from -the `bolt-contracts` directory: +procedure. If you want to use it, please follow these steps: -```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ - --sig "S01_registerIntoBolt" \ - --rpc-url $HOLESKY_RPC \ - -vvvv \ - --broadcast -``` +1. set the operator private key to the `OPERATOR_SK` environment variable; +2. set the operator RPC URL which supports the Commitments API to the + `OPERATOR_RPC` environment variable; +3. run the following Forge script from the `bolt-contracts` directory: + + ```bash + forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast + ``` To check if your operator is correctly registered, set the operator public key in the `OPERATOR_PK` environment variable and run the following script: From 2451c75661bce29fcaf602517f8e747b2ae60ccd Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 13:41:41 +0200 Subject: [PATCH 185/272] chore(holesky): upgrade vault addresses for Symbiotic --- testnets/holesky/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 154328429..6574cb06d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -354,8 +354,12 @@ EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - - [`wstETH`](https://holesky.etherscan.io/address/0x8d09a4502Cc8Cf1547aD300E066060D043f6982D) - - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) + - [`rETH`](https://holesky.etherscan.io/address/0xe5708788c90e971f73D928b7c5A8FD09137010e0) + - [`stETH`](https://holesky.etherscan.io/address/0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd) + - [`WETH`](https://holesky.etherscan.io/address/0xC56Ba584929c6f381744fA2d7a028fA927817f2b) + - [`cbETH`](https://holesky.etherscan.io/address/0xcDdeFfcD2bA579B8801af1d603812fF64c301462) + - [`mETH`](https://holesky.etherscan.io/address/0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f) - [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) From 3062f3ab87db669ae8f9a20794d333966a557379 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Thu, 24 Oct 2024 14:45:07 +0200 Subject: [PATCH 186/272] fix(contracts/scripts): register Symbiotic operator script --- .../operators/RegisterSymbioticOperator.s.sol | 25 +++++++++++-------- testnets/holesky/README.md | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index b5a6d46d8..d57902e9b 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -11,7 +11,6 @@ import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { - string rpc; BoltSymbioticMiddlewareV1 symbioticMiddleware; IOptInService symbioticNetworkOptInService; address symbioticNetwork; @@ -25,14 +24,15 @@ contract RegisterSymbioticOperator is Script { Config memory config = _readConfig(); + console.log("Registering Symbiotic operator into Bolt"); + console.log("Operator address:", operator); + // First, make sure the operator is opted into the network require( config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork), "Operator must be opted in into Bolt Network" ); - console.log("Registering Symbiotic operator into Bolt"); - console.log("Operator address:", operator); console.log("Operator RPC:", rpc); vm.startBroadcast(operatorSk); @@ -40,14 +40,23 @@ contract RegisterSymbioticOperator is Script { console.log("Successfully registered Symbiotic operator"); vm.stopBroadcast(); + + (address[] memory tokens, uint256[] memory amounts) = + config.symbioticMiddleware.getOperatorCollaterals(operator); + + console.log("Operator collateral:"); + for (uint256 i; i < tokens.length; ++i) { + console.log("Collateral:", tokens[i], "Amount:", amounts[i]); + } } function S02_checkOperatorRegistration() public view { - address operatorPublicKey = vm.envAddress("OPERATOR_PK"); - console.log("Checking operator registration for address", operatorPublicKey); + address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); + console.log("Checking operator registration for address", operatorAddress); IBoltManagerV1 boltManager = _readBoltManager(); - bool isRegistered = boltManager.isOperator(operatorPublicKey); + bool isRegistered = boltManager.isOperator(operatorAddress); + console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); } @@ -57,11 +66,7 @@ contract RegisterSymbioticOperator is Script { string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - string memory operatorPath = string.concat(root, "/config/holesky/operator.json"); - string memory operatorJson = vm.readFile(operatorPath); - return Config({ - rpc: vm.parseJsonString(operatorJson, ".rpc"), symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), symbioticMiddleware: BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")), symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 6574cb06d..5ddfb7a3f 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -538,8 +538,8 @@ procedure. If you want to use it, please follow these steps: --broadcast ``` -To check if your operator is correctly registered, set the operator public key -in the `OPERATOR_PK` environment variable and run the following script: +To check if your operator is correctly registered, set the operator address +in the `OPERATOR_ADDRESS` environment variable and run the following script: ```bash forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ From b39ce6ca5916d36b030fb635b0448bff6dbb2b69 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:32:12 +0200 Subject: [PATCH 187/272] chore(holesky): updated bolt-cli usage guide --- testnets/holesky/README.md | 235 ++++++++++++++++++++++--------------- 1 file changed, 140 insertions(+), 95 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 5ddfb7a3f..5ad5016ba 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -23,9 +23,8 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt-delegations-cli`](#`bolt-delegations-cli`) + * [`bolt-cli`](#bolt-cli) * [Installation and usage](#installation-and-usage) - * [Delegations CLI Example](#delegations-cli-example) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) * [Avoid restarting the beacon node](#avoid-restarting-the-beacon-node) @@ -42,8 +41,8 @@ your system. Bolt is fully trustless since it is able to produce a fallback block with the commitments issued in case builders do not return a valid bid. In order to do so it relies on a synced execution client, configured via the `--execution-api-url` -flag. At the moment only Geth is supported; with more -clients to be supported in the future. +flag. **At the moment only Geth is supported; with more +clients to be supported in the future.** Using the sidecar with a different execution client could lead to commitment faults because fallback block building is not supported yet. You can download @@ -74,22 +73,25 @@ client implementations to download and run them. > > It might be necessary to restart your beacon node depending on your existing > setup. See the [Avoid Restarting the Beacon -> Node](#avoid-restarting-the-beacon-node) for more details. +> Node](#avoid-restarting-the-beacon-node) section for more details. **Active validators:** -The Bolt sidecar requires signing keys from active Ethereum validators, or -authorized delegates acting on their behalf, to issue and sign preconfirmations. +The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, +or **authorized delegates** acting on their behalf, to issue and sign preconfirmations. + +To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +section. # Off-Chain Setup There are various way to run the Bolt Sidecar depending on what infrastructure you want to use and your preferred signing methods: -- Docker mode (recommended); +- Docker mode (recommended) - [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode - (requires Docker). -- Native mode (advanced, requires building everything from source); + (requires Docker) +- Native mode (advanced, requires building everything from source) Running the Bolt sidecar as a standalone binary requires building it from source. Both the standalone binary and the Docker container requires reading @@ -102,19 +104,19 @@ requirements. ## Docker Mode (recommended) -First, make sure to have both [Docker](https://docs.docker.com/engine/install/), +First, make sure to have [Docker](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/) and [git](https://git-scm.com/downloads) installed in your machine. Then clone the Bolt repository by running: ```bash -git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git && cd bolt +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git +cd bolt/testnets/holesky ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt -MEV-Boost fork which includes supports the [Constraints -API](https://docs.boltprotocol.xyz/api/builder). +MEV-Boost fork which includes supports the [Constraints API](https://docs.boltprotocol.xyz/api/builder). Before starting the services, you'll need to provide configuration files containing the necessary environment variables: @@ -652,7 +654,10 @@ For completeness, here are all the command-line options available for the Bolt sidecar. You can see them in your terminal by running the Bolt sidecar binary with the `--help` flag: -``` +
+CLI help Reference + +```text Command-line options for the Bolt sidecar @@ -812,6 +817,9 @@ Options: ``` +
+ + ## Delegations and signing options for Native and Docker Compose Mode As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar @@ -825,115 +833,150 @@ Ethereum validators. In order to create these delegation you can use the `bolt-delegations-cli` binary. If you don't want to use it you can skip the following section. -### `bolt-delegations-cli` +### `bolt` CLI -`bolt-delegations-cli` is an offline command-line tool for safely generating -delegation and revocation messages signed with a BLS12-381 key for the -[Constraints API](https://docs.boltprotocol.xyz/api/builder) in -[Bolt](https://docs.boltprotocol.xyz/). +`bolt` CLI is an offline tool for safely generating delegation and revocation messages +signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) +in [Bolt](https://docs.boltprotocol.xyz/). -The tool supports two key sources: +The tool supports three key sources: -- Local: A BLS private key provided directly from a file. -- Keystore: A keystore file that contains an encrypted BLS private key. +- **Secret Keys**: A list of BLS private keys provided directly as hex-strings. +- **Local Keystore**: A EIP-2335 keystore that contains an encrypted BLS private keys. +- **Dirk**: A remote Dirk server that provides the BLS signatures for the delegation messages. and outputs a JSON file with the delegation/revocation messages to the provided -`` for the given chain +`` for the given chain. -Features: +#### Installation and usage -- Offline usage: Safely generate delegation messages in an offline environment. -- Flexible key source: Support for both direct local BLS private keys and - Ethereum keystore files (ERC-2335 format). -- BLS delegation signing: Sign delegation messages using a BLS secret key and - output the signed delegation in JSON format. +Prerequisites: -#### Installation and usage +- [Rust toolchain][rust] +- [Protoc][protoc] -Go to the root of the Bolt project you've previously cloned using Git. Enter in -the `bolt-delegations-cli` directory by running `cd bolt-delegations-cli`. +Once you have the necessary prerequisites, you can build the binary +in the following way: -If you're using the Docker container setup make sure you have -[Rust](https://www.rust-lang.org/tools/install) installed in your system as -well. Then you can build the `bolt-delegations-cli` binary by running: +```shell +# clone the Bolt repository if you haven't already +git clone git@github.com:chainbound/bolt.git -```bash -cargo build --release && mv target/release/bolt-delegations-cli . -``` +# navigate to the Bolt CLI package directory +cd bolt-cli -Now you can run the binary by running: +# build and install the binary on your machine +cargo install --path . --force -```bash -./bolt-delegations-cli +# test the installation +bolt --version ``` -The binary exposes a single `generate` command, which accepts the following -options and subcommands (use `./bolt-delegations-cli generate --help` to see -them): +The binary can be used with the following command: -```text -Usage: bolt-delegations-cli generate [OPTIONS] --delegatee-pubkey +```shell +bolt delegate --delegate-pubkey + --out + --chain + + +``` -Commands: - local Use local private keys to generate the signed messages - keystore Use an EIP-2335 keystore folder to generate the signed messages - help Print this message or the help of the given subcommand(s) +where: -Options: - --delegatee-pubkey The BLS public key to which the delegation message should be signed [env: DELEGATEE_PUBKEY=] - --out The output file for the delegations [env: OUTPUT_FILE_PATH=] [default: delegations.json] - --chain The chain for which the delegation message is intended [env: CHAIN=] [default: mainnet] [possible values: mainnet, holesky, helder, kurtosis] - --action The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) [env: ACTION=] [default: delegate] [possible values: delegate, revoke] - -h, --help Print help (see more with '--help') -``` +- `` is the public key of the delegatee. +- `` is the path to the file where the delegation JSON messages will be written. +- `` is the chain for which the delegations are being generated (e.g. Holesky). +- `` is the key source to use for generating the delegations. It can be one of: + - `secret-keys`: A list of BLS private keys provided directly as hex-strings. + - `local-keystore`: A EIP-2335 keystore that contains an encrypted BLS private keys. + - `dirk`: A remote Dirk server that provides the BLS signatures for the delegation messages. + +You can also find more information about the available key source +options by running `bolt delegate --help`. > [!TIP] > If you're using the Docker Compose Mode please don't set the `--out` flag and > provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` > file. -The environment variables can be also set in a `.env` file. For a reference -example you can check out the `.env.local.example` and the -`.env.keystore.example` +Here you can see usage examples for each key source: + +
+Usage + +```text +❯ bolt-cli delegate --help +Generate BLS delegation or revocation messages +Usage: bolt-cli delegate [OPTIONS] --delegatee-pubkey +Commands: +secret-keys Use local secret keys to generate the signed messages +local-keystore Use an EIP-2335 filesystem keystore directory to generate the signed messages +dirk Use a remote DIRK keystore to generate the signed messages +help Print this message or the help of the given subcommand(s) +Options: + --delegatee-pubkey + The BLS public key to which the delegation message should be signed + [env: DELEGATEE_PUBKEY=] + --out + The output file for the delegations + [env: OUTPUT_FILE_PATH=] + [default: delegations.json] + --chain + The chain for which the delegation message is intended + [env: CHAIN=] + [default: mainnet] + [possible values: mainnet, holesky, helder, kurtosis] + --action + The action to perform. The tool can be used to generate delegation or revocation messages (default: delegate) + [env: ACTION=] + [default: delegate] + Possible values: + - delegate: Create a delegation message + - revoke: Create a revocation message +-h, --help + Print help (see a summary with '-h') +``` -In the section below you can see a usage example of the binary. +
-#### Delegations CLI Example +
+Examples -1. Using a local BLS private key: +1. Generating a delegation using a local BLS secret key - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - local \ - --secret-keys 0xabc123...,0xdef456.. - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x8d0edf4fe9c80cd640220ca7a68a48efcbc56a13536d6b274bf3719befaffa13688ebee9f37414b3dddc8c7e77233ce8 \ + --chain holesky \ + secret-keys --secret-keys 642e0d33fde8968a48b5f560c1b20143eb82036c1aa6c7f4adc4beed919a22e3 +``` -2. Using a Ethereum keystores files and raw password: +2. Generating a delegation using an ERC-2335 keystore directory - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - keystore \ - --path /keys \ - --password myS3cr3tP@ssw0rd - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x8d0edf4fe9c80cd640220ca7a68a48efcbc56a13536d6b274bf3719befaffa13688ebee9f37414b3dddc8c7e77233ce8 \ + --chain holesky \ + local-keystore --path test_data/lighthouse/validators --password-path test_data/lighthouse/secrets +``` -3. Using an Ethereum keystores files and secrets folder +3. Generating a delegation using a remote DIRK keystore - ```text - bolt-delegations-cli generate \ - --delegatee-pubkey 0x7890ab... \ - --out my_delegations.json \ - --chain holesky \ - keystore \ - --path /keys \ - --password-path /secrets - ``` +```text +bolt-cli delegate \ + --delegatee-pubkey 0x83eeddfac5e60f8fe607ee8713efb8877c295ad9f8ca075f4d8f6f2ae241a30dd57f78f6f3863a9fe0d5b5db9d550b93 \ + dirk --url https://localhost:9091 \ + --client-cert-path ./test_data/dirk/client1.crt \ + --client-key-path ./test_data/dirk/client1.key \ + --ca-cert-path ./test_data/dirk/security/ca.crt \ + --wallet-path wallet1 --passphrases secret +``` + +
+ +
+Keystore-specific instructions When using the `keystore` key source, the `--path` flag should point to the directory containing the encrypted keypair directories. @@ -971,6 +1014,8 @@ That is, the password files should be named after the public key and each file should just contain one line with the password in plain text. The files themselves don't need a particular file extension. +
+ --- Now that you have generated the delegation messages you can provide them to the @@ -990,9 +1035,9 @@ can pass directly the private key as a hex-encoded string to the Bolt sidecar using the `--constraint-private-key` flag (or `constraint_private_key` in the TOML file). -This is the simplest setup and can be used in -case if all the delegations messages point to the same delegatee or if you're -running the sidecar with a single active validator. +This is the simplest setup and can be used in case if all the delegations messages +point to the same delegatee or if you're running the sidecar with a single active +validator. ### Using a ERC-2335 Keystore From 062389cafb4ef1f865b45fe9c389a92e88ab46cc Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:36:54 +0200 Subject: [PATCH 188/272] fix: broken links --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 5ad5016ba..45a2fd339 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -852,8 +852,8 @@ and outputs a JSON file with the delegation/revocation messages to the provided Prerequisites: -- [Rust toolchain][rust] -- [Protoc][protoc] +- [Rust toolchain](https://www.rust-lang.org/tools/install) +- [Protoc](https://grpc.io/docs/protoc-installation/) Once you have the necessary prerequisites, you can build the binary in the following way: From 32cb2f0de7380e8af9105a23e696d214e5aa5b95 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:09:56 +0200 Subject: [PATCH 189/272] chore!(sidecar,holesky): drop support for TOML, use .env everywhere --- bolt-sidecar/.env.example | 83 +++++++++++++++------- bolt-sidecar/Config.example.toml | 84 ----------------------- bolt-sidecar/bin/sidecar.rs | 12 +--- bolt-sidecar/src/config/mod.rs | 23 ------- bolt-sidecar/src/primitives/delegation.rs | 2 +- testnets/holesky/bolt-sidecar.env.example | 67 ++++++++++++++++++ testnets/holesky/mev-boost.env.example | 32 +++++++++ 7 files changed, 159 insertions(+), 144 deletions(-) delete mode 100644 bolt-sidecar/Config.example.toml create mode 100644 testnets/holesky/bolt-sidecar.env.example create mode 100644 testnets/holesky/mev-boost.env.example diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index 7621489a6..cf7785bb4 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -1,34 +1,67 @@ # Ethereum Node Connections + PBS URLs -BOLT_SIDECAR_PORT=8000 -BOLT_SIDECAR_EXECUTION_API_URL=http://localhost:4485 -BOLT_SIDECAR_BEACON_API_URL=http://localhost:4400 -BOLT_SIDECAR_ENGINE_API_URL=http://localhost:4451 -BOLT_SIDECAR_CONSTRAINTS_URL=http://localhost:19550 -BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18551 -BOLT_SIDECAR_VALIDATOR_INDEXES=0..64 + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +BOLT_SIDECAR_PORT=8017 +# Execution client API URL +BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +# URL for the beacon client +BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") +BOLT_SIDECAR_VALIDATOR_INDEXES= +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. BOLT_SIDECAR_ENGINE_JWT_HEX= +# The fee recipient address for fallback blocks BOLT_SIDECAR_FEE_RECIPIENT= +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +# Secret BLS key to sign fallback payloads with BOLT_SIDECAR_BUILDER_PRIVATE_KEY= -# Commitments configs -BOLT_SIDECAR_MAX_COMMITMENTS=128 -BOLT_SIDECAR_MAX_COMMITTED_GAS= -BOLT_SIDECAR_MIN_PRIORITY_FEE= -BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 +# Commitments limits +# Max number of commitments to accept per block +BOLT_SIDECAR_MAX_COMMITMENTS_PER_SLOT=128 +# Max committed gas per slot +BOLT_SIDECAR_MAX_COMMITTED_GAS_PER_SLOT=10_000_000 +# Min priority fee to accept for a commitment +BOLT_SIDECAR_MIN_PRIORITY_FEE=4_000_000_000 # 4 Gwei = 4 * 10^9 wei -# Chain configs -BOLT_SIDECAR_CHAIN=holesky +# Chain configuration +# Chain on which the sidecar is running +BOLT_SIDECAR_CHAIN="holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] BOLT_SIDECAR_SLOT_TIME=12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) +BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 -# Signing options. Uncomment only what you'll use -#BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= -#BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= -#BOLT_SIDECAR_CB_SIGNER_URL= -#BOLT_SIDECAR_CB_JWT_HEX= -#BOLT_SIDECAR_KEYSTORE_PASSWORD= -#BOLT_SIDECAR_KEYSTORE_PATH= -#BOLT_SIDECAR_DELEGATIONS_PATH= +# Signing options. +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_SECRETS_PATH= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= -# Metrics -BOLT_SIDECAR_METRICS_PORT= -BOLT_SIDECAR_DISABLE_METRICS= +# Telemetry and Metrics +BOLT_SIDECAR_METRICS_PORT=9091 +BOLT_SIDECAR_DISABLE_METRICS=false diff --git a/bolt-sidecar/Config.example.toml b/bolt-sidecar/Config.example.toml deleted file mode 100644 index 2bb86003b..000000000 --- a/bolt-sidecar/Config.example.toml +++ /dev/null @@ -1,84 +0,0 @@ -# Ethereum Node Connections + PBS URLs - -# Port to listen on for incoming JSON-RPC requests of the Commitments API. This -# port should be open on your firewall in order to receive external requests! -port = 8017 -# Execution client API URL -execution_api_url = "http://localhost:8545" -# URL for the beacon client -beacon_api_url = "http://localhost:5052" -# Execution client Engine API URL. This is needed for fallback block building -# and must be a synced Geth node -engine_api_url = "http://localhost:8551" -# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client -constraints_proxy_port = 18550 -# URL to forward the constraints produced by the Bolt sidecar to a server -# supporting the Constraints API, such as an MEV-Boost fork -constraints_api_url = "http://localhost:18551" -# Validator indexes of connected validators that the sidecar should accept -# commitments on behalf of. -# Accepted values: -# - a comma-separated list of indexes (e.g. "1,2,3,4") -# - a contiguous range of indexes (e.g. "1..4") -# - a mix of the above (e.g. "1,2..4,6..8") -# REQUIRED -validator_indexes = "0..64" -# The JWT secret token to authenticate calls to the engine API. It can be -# either be a hex-encoded string or a file path to a file containing the -# hex-encoded secret. -# REQUIRED -engine_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -# The fee recipient address for fallback blocks -# REQUIRED -fee_recipient = "0x0000000000000000000000000000000000000000" -# Secret ECDSA key to sign commitment messages with. The public key associated -# to it must be then used when registering the operator in the `BoltManager` -# contract -# REQUIRED -commitment_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# Secret BLS key to sign fallback payloads with -# REQUIRED -builder_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" - -# Commitments limits -[limits] -# Max number of commitments to accept per block -max_commitments_per_slot = 128 -# Max committed gas per slot -max_committed_gas_per_slot = 10_000_000 -# Min priority fee to accept for a commitment -min_priority_fee = 4_000_000_000 # 4 Gwei = 4 * 10^9 wei - -# Chain configuration -[chain] -# Chain on which the sidecar is running -chain = "holesky" -# The slot time duration in seconds. If provided, it overrides the default for -# the selected [chain] -slot_time = 12 -# The deadline in the slot at which the sidecar will stop accepting new -# commitments for the next block (parsed as milliseconds) -commitment_deadline = 8000 - -# Signing options. Uncomment only the signing setup you'll use: -# - single private key -> `constraints_private_key` -# - commit-boost -> `cb_signer_url`, `cb_jwt_hex` -# - keystores -> `keystore_path`, `keystore_password` or `keystore_secrets_path` -# (depending on whether all keystores have the same passwords or not) -# -# If you plan to use delegations, uncomment the option `delegations_path` as -# well. -[constraint_signing] -# Private key to use for signing constraint messages -# constraint_private_key = "0x0000000000000000000000000000000000000000000000000000000000000000" -# cb_signer_url = "http://localhost:18551" -# cb_jwt_hex = "0x0000000000000000000000000000000000000000000000000000000000000000" -# keystore_password = "password" -# keystore_secrets_path = "./secrets" -# keystore_path = "./keys" -# delegations_path = "./delegations.json" - -# Telemetry and Metrics -[telemetry] -metrics_port = 3300 -disable_metrics = false diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 6903fffbd..fbf0ce31c 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,22 +1,12 @@ -use std::fs; - use clap::Parser; use eyre::{bail, Result}; use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; -pub const TOML_CONFIG_DEFAULT_PATH: &str = "./Config.toml"; - #[tokio::main] async fn main() -> Result<()> { - let opts = if let Ok(config_path) = std::env::var("BOLT_SIDECAR_CONFIG_PATH") { - Opts::parse_from_toml(config_path.as_str())? - } else if fs::exists(TOML_CONFIG_DEFAULT_PATH).is_ok_and(|exists| exists) { - Opts::parse_from_toml(TOML_CONFIG_DEFAULT_PATH)? - } else { - Opts::parse() - }; + let opts = Opts::parse(); if let Err(err) = init_telemetry_stack(opts.telemetry.metrics_port()) { bail!("Failed to initialize telemetry stack: {:?}", err) diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 1b9e64b76..7d05e1171 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -1,8 +1,5 @@ -use std::fs; - use alloy::primitives::Address; use clap::Parser; -use eyre::Context; use reqwest::Url; use serde::Deserialize; @@ -110,14 +107,6 @@ pub struct Opts { pub extra_args: Vec, } -impl Opts { - /// Parse the configuration from a TOML file. - pub fn parse_from_toml(file_path: &str) -> eyre::Result { - let contents = fs::read_to_string(file_path).wrap_err("Unable to read file")?; - toml::from_str(&contents).wrap_err("Error parsing the TOML file") - } -} - #[cfg(test)] mod tests { use super::*; @@ -136,16 +125,4 @@ mod tests { let localhost_socket = "0.0.0.0:3030".parse().unwrap(); assert_eq!(socket_addr, localhost_socket); } - - #[test] - fn test_parse_config_from_toml() { - let path = env!("CARGO_MANIFEST_DIR").to_string() + "/Config.example.toml"; - - let config = Opts::parse_from_toml(&path).expect("Failed to parse config from TOML"); - assert_eq!(config.execution_api_url, Url::parse("http://localhost:8545").unwrap()); - assert_eq!(config.beacon_api_url, Url::parse("http://localhost:5052").unwrap()); - assert_eq!(config.engine_api_url, Url::parse("http://localhost:8551").unwrap()); - assert_eq!(config.constraints_api_url, Url::parse("http://localhost:3030").unwrap()); - assert_eq!(config.constraints_proxy_port, 18551); - } } diff --git a/bolt-sidecar/src/primitives/delegation.rs b/bolt-sidecar/src/primitives/delegation.rs index 20b689bbd..3d41f275f 100644 --- a/bolt-sidecar/src/primitives/delegation.rs +++ b/bolt-sidecar/src/primitives/delegation.rs @@ -47,7 +47,7 @@ impl SignableBLS for DelegationMessage { } } -/// read the delegaitons from disk if they exist and add them to the constraints client +/// read the delegations from disk if they exist and add them to the constraints client pub fn read_signed_delegations_from_file( file_path: &PathBuf, ) -> eyre::Result> { diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example new file mode 100644 index 000000000..a094ec52d --- /dev/null +++ b/testnets/holesky/bolt-sidecar.env.example @@ -0,0 +1,67 @@ +# Ethereum Node Connections + PBS URLs + +# Port to listen on for incoming JSON-RPC requests of the Commitments API. This +# port should be open on your firewall in order to receive external requests! +BOLT_SIDECAR_PORT=8017 +# Execution client API URL +BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +# URL for the beacon client +BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +# Execution client Engine API URL. This is needed for fallback block building +# and must be a synced Geth node +BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +# The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 +# URL to forward the constraints produced by the Bolt sidecar to a server +# supporting the Constraints API, such as an MEV-Boost fork +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +# Validator indexes of connected validators that the sidecar should accept +# commitments on behalf of. +# Accepted values: +# - a comma-separated list of indexes (e.g. "1,2,3,4") +# - a contiguous range of indexes (e.g. "1..4") +# - a mix of the above (e.g. "1,2..4,6..8") +BOLT_SIDECAR_VALIDATOR_INDEXES= +# The JWT secret token to authenticate calls to the engine API. It can be +# either be a hex-encoded string or a file path to a file containing the +# hex-encoded secret. +BOLT_SIDECAR_ENGINE_JWT_HEX= +# The fee recipient address for fallback blocks +BOLT_SIDECAR_FEE_RECIPIENT= +# Secret ECDSA key to sign commitment messages with. The public key associated +# to it must be then used when registering the operator in the `BoltManager` +# contract +BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY= +# Secret BLS key to sign fallback payloads with +BOLT_SIDECAR_BUILDER_PRIVATE_KEY= + +# Commitments limits +# Max number of commitments to accept per block +BOLT_SIDECAR_MAX_COMMITMENTS_PER_SLOT=128 +# Max committed gas per slot +BOLT_SIDECAR_MAX_COMMITTED_GAS_PER_SLOT=10_000_000 +# Min priority fee to accept for a commitment +BOLT_SIDECAR_MIN_PRIORITY_FEE=4_000_000_000 # 4 Gwei = 4 * 10^9 wei + +# Chain configuration +# Chain on which the sidecar is running +BOLT_SIDECAR_CHAIN="holesky" +# The slot time duration in seconds. If provided, it overrides the default for +# the selected [chain] +BOLT_SIDECAR_SLOT_TIME=12 +# The deadline in the slot at which the sidecar will stop accepting new +# commitments for the next block (parsed as milliseconds) +BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 + +# Signing options. +BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= +BOLT_SIDECAR_CB_SIGNER_URL= +BOLT_SIDECAR_CB_JWT_HEX= +BOLT_SIDECAR_KEYSTORE_PASSWORD= +BOLT_SIDECAR_KEYSTORE_SECRETS_PATH= +BOLT_SIDECAR_KEYSTORE_PATH= +BOLT_SIDECAR_DELEGATIONS_PATH= + +# Telemetry and Metrics +BOLT_SIDECAR_METRICS_PORT=9091 # Changing this requires also changing the `target.json` file +BOLT_SIDECAR_DISABLE_METRICS=false diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example new file mode 100644 index 000000000..8815677dc --- /dev/null +++ b/testnets/holesky/mev-boost.env.example @@ -0,0 +1,32 @@ +# Logging settings +LOG_JSON=false # Set to true to log in JSON format +LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic +DEBUG=false # Set to true to enable debug mode +LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=false # Set to true to disable logging the version + +# Server settings +BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup + +# Relay settings +RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) +RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) +MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) + +# Relay timeout settings (in ms) +RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay +RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay +RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests + +# Genesis settings -- Not needed if using one of the predefined networks +# GENESIS_FORK_VERSION= # Custom genesis fork version +# GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) + +# Network settings +SEPOLIA=false # Set to true to use Sepolia network +GOERLI=false # Set to true to use Goerli network +HOLESKY=true # Set to true to use Holesky network + +# Retry settings +REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request From 9d759c4a42dc535d0ae281fef0d32f0caba8b53b Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:35:32 +0200 Subject: [PATCH 190/272] chore(holesky): update README after dropping TOML support --- testnets/holesky/README.md | 91 +++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 45a2fd339..7da86a4d7 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -23,7 +23,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt-cli`](#bolt-cli) + * [`bolt` CLI](#`bolt`-cli) * [Installation and usage](#installation-and-usage) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) @@ -77,10 +77,10 @@ client implementations to download and run them. **Active validators:** -The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, +The Bolt sidecar requires access to BLS signing keys from active Ethereum validators, or **authorized delegates** acting on their behalf, to issue and sign preconfirmations. -To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section. # Off-Chain Setup @@ -123,41 +123,48 @@ containing the necessary environment variables: 1. **Bolt Sidecar Configuration:** - Create a `bolt-sidecar.toml` file in the `testnets/holesky` directory. If you - need a reference, you can use the `Config.example.toml` file in the `bolt-sidecar` - directory as a starting point. + Change directory to the `testnets/holesky` folder and create a + `bolt-sidecar.env` file starting from the reference template: ```bash - cp ./bolt-sidecar/Config.example.toml ./testnets/holesky/bolt-sidecar.toml + cd testnets/holesky + cp bolt-sidecar.env.example bolt-sidecar.env ``` - Next up, fill out all the values that are required. For proper configuration - of the signing options, please refer to the [Delegations and + Next up, fill out the values that are left blank. Please also review the + default values and see that they work for your setup. For proper + configuration of the signing options, please refer to the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. + If you've generated a `delegation.json` file using the Bolt CLI please + place it in the `testnets/holesky` directory by replacing the existing empty + one. + 2. **MEV-Boost Configuration:** - Copy over the example configuration file: + Change directory to the `testnets/holesky` folder if you haven't already and + copy over the example configuration file: ```bash - cp ./mev-boost/.env.example ./testnets/holesky/mev-boost.env + cp ./mev-boost.env.example ./mev-boost.env ``` - Then configure it accordingly. +Then configure it accordingly and review the default values chosen. If you prefer not to restart your beacon node, follow the instructions in the [Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. -Once the configuration files are in place, you can start the Docker containers -by running: +Once the configuration files are in place, make sure you are in the +`testnets/holesky` directory and then run: ```bash -cd testnets/holesky && docker compose up -d +docker compose up -d --env-file bolt-sidecar.env ``` The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards which you can find at `http://localhost:3000`. +Prometheus and Grafana. It also comes with some pre-built dashboards which you +can find at `http://localhost:28017`. ## Commit-Boost Mode @@ -315,11 +322,8 @@ can be found by running `./bolt-sidecar --help`, or you can find them in the #### Configuration file -You can use a `Config.toml` file to configure the sidecar, for which you can -find a template in the `Config.example.toml` file. -If you wish to place the configuration file in another folder you need to -specify the path of the configuration file by setting the -`BOLT_SIDECAR_CONFIG_PATH` environment variable to the path of the file. +You can use a `.env` file to configure the sidecar, for which you can +find a template in the `.env.example` file. Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) to configure such sidecar options properly. @@ -332,16 +336,22 @@ After you've set up the configuration file you can run the Bolt sidecar with ### Observability -Commit-Boost comes with various observability tools, such as Prometheus, -cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +The bolt sidecar comes with various observability tools, such as Prometheus +and Grafana. It also comes with some pre-built dashboards, which can be found in the `grafana` directory. -To update these dashboards, run the following command: +To run these dashboards change directory to the `bolt-sidecar/infra` folder and +run: + +```bash +docker compose -f telemetry.compose.yml up -d +``` -`bash ./update-grafana.sh ` +To stop the services run: -In this directory, you can also find a Bolt dashboard, which will be launched -alongside the other dashboards. +```bash +docker compose -f telemetry.compose.yml down +``` # On-Chain Registration @@ -819,7 +829,6 @@ Options: - ## Delegations and signing options for Native and Docker Compose Mode As mentioned in the [prerequisites](#prerequisites) section, the Bolt sidecar @@ -830,13 +839,13 @@ Ethereum validators. > This is the recommended way to run the Bolt sidecar as it > doesn't expose the active validator signing keys to any additional risk. -In order to create these delegation you can use the `bolt-delegations-cli` binary. +In order to create these delegation you can use the `bolt` CLI binary. If you don't want to use it you can skip the following section. ### `bolt` CLI -`bolt` CLI is an offline tool for safely generating delegation and revocation messages -signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) +`bolt` CLI is an offline tool for safely generating delegation and revocation messages +signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) in [Bolt](https://docs.boltprotocol.xyz/). The tool supports three key sources: @@ -855,7 +864,7 @@ Prerequisites: - [Rust toolchain](https://www.rust-lang.org/tools/install) - [Protoc](https://grpc.io/docs/protoc-installation/) -Once you have the necessary prerequisites, you can build the binary +Once you have the necessary prerequisites, you can build the binary in the following way: ```shell @@ -875,10 +884,10 @@ bolt --version The binary can be used with the following command: ```shell -bolt delegate --delegate-pubkey - --out - --chain - +bolt delegate --delegate-pubkey + --out + --chain + ``` @@ -891,8 +900,8 @@ where: - `secret-keys`: A list of BLS private keys provided directly as hex-strings. - `local-keystore`: A EIP-2335 keystore that contains an encrypted BLS private keys. - `dirk`: A remote Dirk server that provides the BLS signatures for the delegation messages. - -You can also find more information about the available key source + +You can also find more information about the available key source options by running `bolt delegate --help`. > [!TIP] @@ -1035,8 +1044,8 @@ can pass directly the private key as a hex-encoded string to the Bolt sidecar using the `--constraint-private-key` flag (or `constraint_private_key` in the TOML file). -This is the simplest setup and can be used in case if all the delegations messages -point to the same delegatee or if you're running the sidecar with a single active +This is the simplest setup and can be used in case if all the delegations messages +point to the same delegatee or if you're running the sidecar with a single active validator. ### Using a ERC-2335 Keystore @@ -1049,7 +1058,7 @@ containing the password file (in the TOML configuration file these are the `keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). Both the `keys` and `passwords` folders must adhere to the structure outlined -in the [Delegations CLI example](#delegations-cli-example) section. +in the [Installation and Usage](#installation-and-usage) section. ## Avoid restarting the beacon node From f06c36273fdb9c62a1d19e1494e09ed8b72dbd5f Mon Sep 17 00:00:00 2001 From: Lorenzo Date: Thu, 24 Oct 2024 16:55:06 +0200 Subject: [PATCH 191/272] Update testnets/holesky/README.md Co-authored-by: nicolas <48695862+merklefruit@users.noreply.github.com> --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 7da86a4d7..b78a75acb 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -884,7 +884,7 @@ bolt --version The binary can be used with the following command: ```shell -bolt delegate --delegate-pubkey +bolt delegate --delegatee-pubkey --out --chain From 261fdd18ec3738bfc7e685530d08e71cbdbae5ee Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 16:11:47 +0200 Subject: [PATCH 192/272] fix(holesky): docker compose setup -- grafana, prometheus --- testnets/holesky/delegations.json | 1 + testnets/holesky/docker-compose.yml | 22 +- .../grafana/dashboards/bolt_dashboard.json | 836 ----------------- .../holesky/grafana/dashboards/dashboard.json | 473 +++++----- .../grafana/dashboards/system_metrics.json | 853 ------------------ .../grafana/datasources/datasources.yml | 2 +- testnets/holesky/targets.json | 10 +- 7 files changed, 262 insertions(+), 1935 deletions(-) create mode 100644 testnets/holesky/delegations.json delete mode 100644 testnets/holesky/grafana/dashboards/bolt_dashboard.json delete mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/delegations.json b/testnets/holesky/delegations.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/testnets/holesky/delegations.json @@ -0,0 +1 @@ +[] diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 86d2c3dce..e0c54cbc7 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -6,10 +6,11 @@ services: ports: - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" - entrypoint: /bin/sh -c "BOLT_SIDECAR_CONFIG_PATH=/etc/bolt-sidecar.toml /usr/local/bin/bolt-sidecar" + entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar + env_file: ./bolt-sidecar.env volumes: - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" - - "../../bolt-delegations-cli/delegations.json:/etc/delegations.json" + - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 @@ -22,39 +23,26 @@ services: image: prom/prometheus:latest container_name: bolt-prometheus-holesky ports: - - 49090:49090 + - 18017:9090 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus - networks: - - monitoring_network bolt-grafana-holesky: image: grafana/grafana:latest container_name: bolt-grafana-holesky ports: - - 33000:33000 - environment: - - GF_SECURITY_ADMIN_PASSWORD=admin + - 28017:3000 volumes: - ./grafana/dashboards:/etc/grafana/provisioning/dashboards - ./grafana/datasources:/etc/grafana/provisioning/datasources - grafana-data:/var/lib/grafana - networks: - - monitoring_network depends_on: - bolt-prometheus-holesky - logging: - driver: none volumes: prometheus-data: driver: local grafana-data: driver: local -networks: - monitoring_network: - driver: bridge - signer_network: - driver: bridge diff --git a/testnets/holesky/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/grafana/dashboards/bolt_dashboard.json deleted file mode 100644 index dce85acb0..000000000 --- a/testnets/holesky/grafana/dashboards/bolt_dashboard.json +++ /dev/null @@ -1,836 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 2, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_transactions_preconfirmed", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Transactions Preconfirmed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_validation_errors", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Invalid Transactions Reasons", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_remote_blocks_proposed", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Remote Blocks Proposed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_inclusion_commitments_received", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Inclusion Commitments Received", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 16 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.5.12", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "exemplar": false, - "expr": "bolt_sidecar_local_blocks_proposed", - "instant": false, - "interval": "", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Local Blocks Proposed", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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": "stepAfter", - "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 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 16 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_inclusion_commitments_accepted", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Inclusion Commitments Accepted", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "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", - "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 - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_http_requests_total", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Total HTTP Requests", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 7, - "options": { - "bucketOffset": 0, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_http_requests_duration_seconds", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "HTTP Requests Durations in ms", - "type": "histogram" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 3, - "x": 0, - "y": 32 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.12", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "editorMode": "builder", - "expr": "bolt_sidecar_latest_head", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Latest Head Slot", - "type": "stat" - } - ], - "refresh": "", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Bolt Sidecar", - "uid": "e5960f6d-a1ed-4538-9c7c-3ecba4d4b4b1", - "version": 3, - "weekStart": "" -} diff --git a/testnets/holesky/grafana/dashboards/dashboard.json b/testnets/holesky/grafana/dashboards/dashboard.json index f903affb2..8823158a8 100644 --- a/testnets/holesky/grafana/dashboards/dashboard.json +++ b/testnets/holesky/grafana/dashboards/dashboard.json @@ -18,28 +18,14 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, + "id": 2, "links": [], - "liveNow": true, + "liveNow": false, "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 12, - "panels": [], - "repeat": "endpoint", - "repeatDirection": "h", - "title": "$endpoint calls", - "type": "row" - }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -47,7 +33,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -61,11 +46,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -82,7 +63,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -100,12 +80,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, + "h": 8, + "w": 12, "x": 0, - "y": 1 + "y": 0 }, - "id": 11, + "id": 9, "options": { "legend": { "calcs": [], @@ -122,23 +102,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Relay Success QPH", + "title": "Transactions Preconfirmed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -146,7 +125,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -160,11 +138,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -181,7 +155,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -199,12 +172,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 6, - "y": 1 + "h": 8, + "w": 12, + "x": 12, + "y": 0 }, - "id": 13, + "id": 10, "options": { "legend": { "calcs": [], @@ -221,23 +194,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_validation_errors", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Relay Error QPH", + "title": "Invalid Transactions Reasons", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -245,7 +217,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -259,11 +230,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -280,17 +247,12 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -298,12 +260,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 12, - "y": 1 + "h": 8, + "w": 12, + "x": 0, + "y": 8 }, - "id": 43, + "id": 2, "options": { "legend": { "calcs": [], @@ -320,23 +282,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Beacon Node Success QPH", + "title": "Remote Blocks Proposed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -344,7 +305,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -358,11 +318,7 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { @@ -379,7 +335,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -397,12 +352,12 @@ "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 18, - "y": 1 + "h": 8, + "w": 12, + "x": 12, + "y": 8 }, - "id": 44, + "id": 4, "options": { "legend": { "calcs": [], @@ -419,23 +374,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "editorMode": "code", - "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_received", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "$endpoint Beacon Node Error QPH", + "title": "Inclusion Commitments Received", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -443,7 +397,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -457,18 +410,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -478,7 +427,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -491,18 +439,42 @@ "value": 80 } ] - }, - "unit": "s" + } }, - "overrides": [] + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "{__name__=\"bolt_sidecar_local_blocks_proposed\", instance=\"172.16.0.25:9063\", job=\"bolt-sidecar\"}" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] }, "gridPos": { - "h": 11, - "w": 6, + "h": 8, + "w": 12, "x": 0, - "y": 12 + "y": 16 }, - "id": 20, + "id": 8, "options": { "legend": { "calcs": [], @@ -515,31 +487,30 @@ "sort": "none" } }, + "pluginVersion": "9.5.12", "targets": [ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", "instant": false, + "interval": "", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P50", + "title": "Local Blocks Proposed", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -547,7 +518,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -561,18 +531,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, + "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -582,31 +548,25 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] - }, - "unit": "s" + } }, "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 6, - "y": 12 + "h": 8, + "w": 12, + "x": 12, + "y": 16 }, - "id": 29, + "id": 5, "options": { "legend": { "calcs": [], @@ -623,27 +583,22 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P90", + "title": "Inclusion Commitments Accepted", "type": "timeseries" }, { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, "fieldConfig": { "defaults": { @@ -651,7 +606,6 @@ "mode": "palette-classic" }, "custom": { - "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -665,18 +619,14 @@ "tooltip": false, "viz": false }, - "insertNulls": false, "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -686,7 +636,6 @@ } }, "mappings": [], - "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -699,18 +648,17 @@ "value": 80 } ] - }, - "unit": "s" + } }, "overrides": [] }, "gridPos": { - "h": 11, - "w": 6, - "x": 12, - "y": 12 + "h": 8, + "w": 12, + "x": 0, + "y": 24 }, - "id": 30, + "id": 6, "options": { "legend": { "calcs": [], @@ -727,77 +675,162 @@ { "datasource": { "type": "prometheus", - "uid": "cb_prometheus" + "uid": "bolt-prometheus-holesky" }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", "legendFormat": "__auto", "range": true, - "refId": "A", - "useBackend": false + "refId": "A" } ], - "title": "$endpoint Relay P99", + "title": "Total HTTP Requests", "type": "timeseries" - } - ], - "refresh": "5m", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [ - { - "current": { - "selected": true, - "text": "All", - "value": "$__all" - }, - "description": "BuilderAPI endpoint", - "hide": 0, - "includeAll": true, - "multi": false, - "name": "endpoint", - "options": [ - { - "selected": true, - "text": "All", - "value": "$__all" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" }, - { - "selected": false, - "text": "get_header", - "value": "get_header" + "custom": { + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 }, - { - "selected": false, - "text": "submit_blinded_block", - "value": "submit_blinded_block" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" }, - { - "selected": false, - "text": "register_validator", - "value": "register_validator" + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_duration_seconds", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "HTTP Requests Durations in ms", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] } - ], - "query": "register_validator, get_header, submit_blinded_block", - "queryValue": "", - "skipUrlSync": false, - "type": "custom" - } - ] + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 3, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest Head Slot", + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] }, "time": { - "from": "now-2d", + "from": "now-15m", "to": "now" }, "timepicker": {}, - "timezone": "browser", - "title": "PBS Metrics", - "uid": "cb_prometheus", - "version": 1, + "timezone": "", + "title": "bolt-prometheus-holesky", + "uid": "bolt-prometheus-holesky", + "version": 3, "weekStart": "" -} \ No newline at end of file +} diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json deleted file mode 100644 index 93b649d80..000000000 --- a/testnets/holesky/grafana/dashboards/system_metrics.json +++ /dev/null @@ -1,853 +0,0 @@ -{ - "__inputs": [ - { - "name": "DS_PROMETHEUS", - "label": "Prometheus", - "description": "Prometheus as the datasource is obligatory", - "type": "datasource", - "pluginId": "prometheus", - "pluginName": "Prometheus" - } - ], - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "7.4.5" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - }, - { - "type": "panel", - "id": "table", - "name": "Table", - "version": "" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": 14282, - "graphTooltip": 0, - "id": null, - "iteration": 1617715580880, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 8, - "panels": [], - "title": "CPU", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 15, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "percent", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 11, - "panels": [], - "title": "Memory", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "hiddenSeries": false, - "id": 9, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Usage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "hiddenSeries": false, - "id": 14, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Memory Cached", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:606", - "format": "bytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:607", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 17 - }, - "id": 2, - "panels": [], - "title": "Network", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 18 - }, - "hiddenSeries": false, - "id": 4, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "hideEmpty": false, - "hideZero": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "sideWidth": null, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", - "hide": false, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Received Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:674", - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:675", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 18 - }, - "hiddenSeries": false, - "id": 6, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": false, - "rightSide": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.4.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Sent Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:832", - "format": "Bps", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:833", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 26 - }, - "id": 19, - "panels": [], - "title": "Misc", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "fieldConfig": { - "defaults": { - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "id" - }, - "properties": [ - { - "id": "custom.width", - "value": 260 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Running" - }, - "properties": [ - { - "id": "unit", - "value": "d" - }, - { - "id": "decimals", - "value": 1 - }, - { - "id": "custom.displayMode", - "value": "color-text" - }, - { - "id": "color", - "value": { - "fixedColor": "dark-green", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 10, - "w": 24, - "x": 0, - "y": 27 - }, - "id": 17, - "options": { - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "7.4.5", - "targets": [ - { - "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Containers Info", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "container_label_com_docker_compose_project", - "container_label_com_docker_compose_project_working_dir", - "image", - "instance", - "name", - "Value", - "container_label_com_docker_compose_service" - ] - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "Value": "Running", - "container_label_com_docker_compose_project": "Label", - "container_label_com_docker_compose_project_working_dir": "Working dir", - "container_label_com_docker_compose_service": "Service", - "image": "Registry Image", - "instance": "Instance", - "name": "Name" - } - } - } - ], - "type": "table" - } - ], - "schemaVersion": 27, - "style": "dark", - "tags": [ - "cadvisor", - "docker" - ], - "templating": { - "list": [ - { - "allValue": ".*", - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "definition": "label_values({__name__=~\"container.*\"},instance)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Host", - "multi": false, - "name": "host", - "options": [], - "query": { - "query": "label_values({__name__=~\"container.*\"},instance)", - "refId": "Prometheus-host-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 5, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": ".*", - "current": {}, - "datasource": { - "type": "prometheus", - "uid": "cb_prometheus" - }, - "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", - "description": null, - "error": null, - "hide": 0, - "includeAll": true, - "label": "Container", - "multi": false, - "name": "container", - "options": [], - "query": { - "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", - "refId": "Prometheus-container-Variable-Query" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Commit-Boost System Metrics", - "uid": "pMEd7m0Mz", - "version": 1, - "description": "Simple exporter for cadvisor only" -} \ No newline at end of file diff --git a/testnets/holesky/grafana/datasources/datasources.yml b/testnets/holesky/grafana/datasources/datasources.yml index 91e10e2b4..10df84a1f 100644 --- a/testnets/holesky/grafana/datasources/datasources.yml +++ b/testnets/holesky/grafana/datasources/datasources.yml @@ -6,6 +6,6 @@ datasources: uid: bolt-prometheus-holesky access: proxy orgId: 1 - url: http://bolt-prometheus-holesky:49090 + url: http://bolt-prometheus-holesky:9090 isDefault: true editable: true diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json index 6cd87cbc6..1b14e8d5e 100644 --- a/testnets/holesky/targets.json +++ b/testnets/holesky/targets.json @@ -1,14 +1,8 @@ [ { - "targets": ["bolt-sidecar-holesky:8017"], + "targets": ["bolt-sidecar-holesky:9091"], "labels": { - "job": "bolt-sidecar-rpc" - } - }, - { - "targets": ["bolt-sidecar-holesky:18550"], - "labels": { - "job": "bolt-sidecar-builder-proxy" + "job": "bolt-sidecar-holesky" } } ] From 4a9ea79179d48420f2a25d9332885fc38767d83a Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 17:14:31 +0200 Subject: [PATCH 193/272] chore(holesky): update rc image for Helix --- testnets/holesky/docker-compose.pbs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index 362dc1c2e..27034a7a5 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -64,7 +64,7 @@ services: ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: - image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc1 + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc2 restart: unless-stopped depends_on: - db From 44d2bf00529fb211956bc06cc554de21e1d509e0 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 17:25:00 +0200 Subject: [PATCH 194/272] fix(holesky): remove toml volume from dockerfile --- testnets/holesky/docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index e0c54cbc7..756708a95 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -9,7 +9,6 @@ services: entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar env_file: ./bolt-sidecar.env volumes: - - "./bolt-sidecar.toml:/etc/bolt-sidecar.toml" - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" bolt-mev-boost-holesky: From c8e19a65e4d60f7072f8b2e18897f6277a669a4e Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 24 Oct 2024 18:16:48 +0200 Subject: [PATCH 195/272] feat(holesky): cadvisor dashboard stub --- testnets/holesky/docker-compose.yml | 11 + .../grafana/dashboards/system_metrics.json | 829 ++++++++++++++++++ testnets/holesky/targets.json | 6 + 3 files changed, 846 insertions(+) create mode 100644 testnets/holesky/grafana/dashboards/system_metrics.json diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 756708a95..74bdfc514 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -28,6 +28,17 @@ services: - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus + bolt-cadvisor-holesky: + image: gcr.io/cadvisor/cadvisor:latest + container_name: bolt-cadvisor-holesky + restart: unless-stopped + ports: + - "38017:8080" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + bolt-grafana-holesky: image: grafana/grafana:latest container_name: bolt-grafana-holesky diff --git a/testnets/holesky/grafana/dashboards/system_metrics.json b/testnets/holesky/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..ebbad9c48 --- /dev/null +++ b/testnets/holesky/grafana/dashboards/system_metrics.json @@ -0,0 +1,829 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Simple exporter for prometheus only", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 7, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "CPU", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 15, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(rate(container_cpu_usage_seconds_total{name=~\"bolt-.*-holesky\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Memory", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 9, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(container_memory_rss{name=~\"bolt-.*-holesky\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 14, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "sum(container_memory_cache{name=~\"bolt-.*-holesky\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory Cached", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Network", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 4, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Received Network Traffic", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "Bps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "id": 6, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Sent Network Traffic", + "type": "timeseries" + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "refId": "A" + } + ], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "fieldConfig": { + "defaults": { + "custom": { + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "editorMode": "code", + "expr": "(time() - container_start_time_seconds{name=~\"bolt-.*-holesky\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 39, + "tags": ["prometheus", "docker"], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "bolt-prometheus-holesky" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "System Metrics", + "uid": "pMEd7m0Mz", + "version": 9, + "weekStart": "" +} + diff --git a/testnets/holesky/targets.json b/testnets/holesky/targets.json index 1b14e8d5e..52546f538 100644 --- a/testnets/holesky/targets.json +++ b/testnets/holesky/targets.json @@ -4,5 +4,11 @@ "labels": { "job": "bolt-sidecar-holesky" } + }, + { + "targets": ["bolt-cadvisor-holesky:8080"], + "labels": { + "job": "bolt-cadvisor-holesky" + } } ] From 3fc56a5497227afcbfac91af5e0c9dfe3580af70 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 12:17:38 +0200 Subject: [PATCH 196/272] fix(sidecar): test/CI --- bolt-sidecar/src/test_util.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index 6eace3748..c0bac0ddd 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -90,6 +90,7 @@ pub(crate) async fn get_test_config() -> Option { "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY", EcdsaSecretKeyWrapper::random().to_string(), ); + env::set_var("BOLT_SIDECAR_VALIDATOR_INDEXES", "0..64"); let _ = dotenvy::dotenv(); From 656116c691e175c02e0fe8ece1c92b768736845d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 25 Oct 2024 12:31:11 +0200 Subject: [PATCH 197/272] fix(holesky): restore commit-boost observability previosly removed by mistake --- testnets/holesky/README.md | 13 + .../grafana/dashboards/bolt_dashboard.json | 236 +++++ .../grafana/dashboards/dashboard.json | 984 ++++++++++++++++++ .../grafana/dashboards/dashboards.yml | 13 + .../grafana/dashboards/system_metrics.json | 853 +++++++++++++++ .../grafana/datasources/datasources.yml | 11 + .../holesky/commit-boost/update-grafana.sh | 4 +- 7 files changed, 2112 insertions(+), 2 deletions(-) create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/dashboard.json create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml create mode 100644 testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json create mode 100644 testnets/holesky/commit-boost/grafana/datasources/datasources.yml diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index b78a75acb..f9924532d 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -224,6 +224,19 @@ This will run all modules in Docker containers. > with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be > configured to point the `builder-api` to this port for Bolt to work. +**Observability** + +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `commit-boost/grafana` directory. + +To update these dashboards, run the following command from the `commit-boost` +directory: + +`bash ./update-grafana.sh ` +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. + ## Native Mode (advanced) For running the Bolt Sidecar as a standalone binary you need to have the diff --git a/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json b/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json new file mode 100644 index 000000000..ea72e6d2f --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/bolt_dashboard.json @@ -0,0 +1,236 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Metrics related to the bolt-sidecar and bolt-boost.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Bolt sidecar", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Latest head", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 4, + "panels": [], + "title": "Bolt boost", + "type": "row" + }, + { + "datasource": { + "default": true, + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "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 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "cb_pbs_constraints_cache_size", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{__name__}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Constraints cache size", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Bolt Metrics", + "uid": "edxnwlpgaw934c", + "version": 3, + "weekStart": "" +} diff --git a/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json b/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json new file mode 100644 index 000000000..94dd06265 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/dashboard.json @@ -0,0 +1,984 @@ +{ + "__inputs": [], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "11.1.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": true, + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "green", + "value": 100 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 61, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "cb_pbs_relay_last_slot", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{relay_id}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Last delivered slot", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "text", + "mode": "thresholds" + }, + "decimals": 6, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "ETH" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 78, + "options": { + "colorMode": "none", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "horizontal", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "cb_pbs_relay_header_value / 1e9", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{relay_id}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Last delivered header value", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 12, + "panels": [], + "repeat": "endpoint", + "repeatDirection": "h", + "title": "$endpoint calls", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 12 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 12 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_relay_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h])) by (relay_id)", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Relay Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 12 + }, + "id": 43, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"2..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Success QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": 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": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 18, + "y": 12 + }, + "id": 44, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "editorMode": "code", + "expr": "sum(increase(cb_pbs_beacon_node_status_code_total{http_status_code=~\"4..|5..\", endpoint=\"$endpoint\"}[1h]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "$endpoint Beacon Node Error QPH", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 0, + "y": 23 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P50", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 6, + "y": 23 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P90", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 6, + "x": 12, + "y": 23 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(cb_pbs_relay_latency_bucket{endpoint=\"$endpoint\"}[1m])) by (le, relay_id))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "$endpoint Relay P99", + "type": "timeseries" + } + ], + "refresh": "5m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "description": "BuilderAPI endpoint", + "hide": 0, + "includeAll": true, + "multi": false, + "name": "endpoint", + "options": [ + { + "selected": true, + "text": "All", + "value": "$__all" + }, + { + "selected": false, + "text": "register_validator", + "value": "register_validator" + }, + { + "selected": false, + "text": "get_header", + "value": "get_header" + }, + { + "selected": false, + "text": "submit_blinded_block", + "value": "submit_blinded_block" + } + ], + "query": "register_validator, get_header, submit_blinded_block", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-2d", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "PBS Metrics", + "uid": "cb_pbs_metrics", + "version": 1, + "weekStart": "" +} \ No newline at end of file diff --git a/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml b/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml new file mode 100644 index 000000000..db50699f9 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/dashboards.yml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: "default" + orgId: 1 + folder: "" + folderUid: "" + type: file + disableDeletion: false + editable: true + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json b/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json new file mode 100644 index 000000000..93b649d80 --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/dashboards/system_metrics.json @@ -0,0 +1,853 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus as the datasource is obligatory", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.4.5" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 14282, + "graphTooltip": 0, + "id": null, + "iteration": 1617715580880, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "CPU", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 15, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Cached", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:606", + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:607", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "sideWidth": null, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total[5m])) by (instance)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Received Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:674", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:675", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 18 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.4.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_transmit_bytes_total[5m])) by (instance)", + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Sent Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:832", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:833", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 19, + "panels": [], + "title": "Misc", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 260 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Running" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "decimals", + "value": 1 + }, + { + "id": "custom.displayMode", + "value": "color-text" + }, + { + "id": "color", + "value": { + "fixedColor": "dark-green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 17, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Containers Info", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "container_label_com_docker_compose_project", + "container_label_com_docker_compose_project_working_dir", + "image", + "instance", + "name", + "Value", + "container_label_com_docker_compose_service" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Running", + "container_label_com_docker_compose_project": "Label", + "container_label_com_docker_compose_project_working_dir": "Working dir", + "container_label_com_docker_compose_service": "Service", + "image": "Registry Image", + "instance": "Instance", + "name": "Name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": [ + "cadvisor", + "docker" + ], + "templating": { + "list": [ + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\"},instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Host", + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\"},instance)", + "refId": "Prometheus-host-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 5, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "cb_prometheus" + }, + "definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Container", + "multi": false, + "name": "container", + "options": [], + "query": { + "query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)", + "refId": "Prometheus-container-Variable-Query" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Commit-Boost System Metrics", + "uid": "pMEd7m0Mz", + "version": 1, + "description": "Simple exporter for cadvisor only" +} \ No newline at end of file diff --git a/testnets/holesky/commit-boost/grafana/datasources/datasources.yml b/testnets/holesky/commit-boost/grafana/datasources/datasources.yml new file mode 100644 index 000000000..6d29d617b --- /dev/null +++ b/testnets/holesky/commit-boost/grafana/datasources/datasources.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +datasources: + - name: cb-prometheus + type: prometheus + uid: cb_prometheus + access: proxy + orgId: 1 + url: http://cb_prometheus:9090 + isDefault: true + editable: true diff --git a/testnets/holesky/commit-boost/update-grafana.sh b/testnets/holesky/commit-boost/update-grafana.sh index 1f832d32b..4e5a16695 100755 --- a/testnets/holesky/commit-boost/update-grafana.sh +++ b/testnets/holesky/commit-boost/update-grafana.sh @@ -1,5 +1,5 @@ #!/bin/bash # Fetches the latest dashboards from commit-boost main -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ../grafana/dashboards/dashboard.json -curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ../grafana/dashboards/system_metrics.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/dashboard.json -o ./grafana/dashboards/dashboard.json +curl https://raw.githubusercontent.com/Commit-Boost/commit-boost-client/main/grafana/dashboards/system_metrics.json -o ./grafana/dashboards/system_metrics.json From fefc31cfc939a98ea723f4a65cbde9183f40f1d5 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 13:48:41 +0200 Subject: [PATCH 198/272] fix(holesky/docs): broken links --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index f9924532d..7f11ad361 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -6,9 +6,9 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) * [Off-Chain Setup](#off-chain-setup) - * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Docker Mode (recommended)](#docker-mode-recommended) * [Commit-Boost Mode](#commit-boost-mode) - * [Native Mode (advanced)](#native-mode-(advanced)) + * [Native Mode (advanced)](#native-mode-advanced) * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) From cfa8e46dffc6745d09fd902c384a1b308428f89b Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 13:57:44 +0200 Subject: [PATCH 199/272] feat(holesky): update networking & default values --- testnets/holesky/bolt-sidecar.env.example | 8 ++++---- testnets/holesky/docker-compose.yml | 9 +++++++++ testnets/holesky/mev-boost.env.example | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example index a094ec52d..8792c94a2 100644 --- a/testnets/holesky/bolt-sidecar.env.example +++ b/testnets/holesky/bolt-sidecar.env.example @@ -4,17 +4,17 @@ # port should be open on your firewall in order to receive external requests! BOLT_SIDECAR_PORT=8017 # Execution client API URL -BOLT_SIDECAR_EXECUTION_API_URL="http://localhost:8545" +BOLT_SIDECAR_EXECUTION_API_URL="http://172.56.0.1:8545" # URL for the beacon client -BOLT_SIDECAR_BEACON_API_URL="http://localhost:5052" +BOLT_SIDECAR_BEACON_API_URL="http://172.56.0.1:5052" # Execution client Engine API URL. This is needed for fallback block building # and must be a synced Geth node -BOLT_SIDECAR_ENGINE_API_URL="http://localhost:8551" +BOLT_SIDECAR_ENGINE_API_URL="http://172.56.0.1:8551" # The port from which the Bolt sidecar will receive Builder-API requests from the Beacon client BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 # URL to forward the constraints produced by the Bolt sidecar to a server # supporting the Constraints API, such as an MEV-Boost fork -BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" +BOLT_SIDECAR_CONSTRAINTS_API_URL="http://bolt-mev-boost-holesky:18551" # Validator indexes of connected validators that the sidecar should accept # commitments on behalf of. # Accepted values: diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 74bdfc514..fb009da12 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -56,3 +56,12 @@ volumes: driver: local grafana-data: driver: local + +networks: + bolt-default: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.56.0.0/16 + gateway: 172.56.0.1 diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example index 8815677dc..942149d0b 100644 --- a/testnets/holesky/mev-boost.env.example +++ b/testnets/holesky/mev-boost.env.example @@ -6,7 +6,7 @@ LOG_SERVICE_TAG= # Optional: Add a custom service tag to all DISABLE_LOG_VERSION=false # Set to true to disable logging the version # Server settings -BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on +BOOST_LISTEN_ADDR=0.0.0.0:18551 # Address for mev-boost server to listen on RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup # Relay settings From fe21cff2ebeeb080b8c25a6cce542ceb79f59ddf Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 14:06:31 +0200 Subject: [PATCH 200/272] fix(holesky): add custom network to compose --- testnets/holesky/docker-compose.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index fb009da12..66ab25379 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -38,6 +38,11 @@ services: - /var/run/docker.sock:/var/run/docker.sock:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro + networks: + - bolt-default + command: + - --housekeeping_interval=10s + - --docker_only bolt-grafana-holesky: image: grafana/grafana:latest @@ -48,6 +53,8 @@ services: - ./grafana/dashboards:/etc/grafana/provisioning/dashboards - ./grafana/datasources:/etc/grafana/provisioning/datasources - grafana-data:/var/lib/grafana + networks: + - bolt-default depends_on: - bolt-prometheus-holesky From 16ee99b9893ba88a1b52bb4353a342061036edbc Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 14:20:14 +0200 Subject: [PATCH 201/272] fix(holesky): typo --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 7f11ad361..c2847c66f 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -344,7 +344,7 @@ to configure such sidecar options properly. After you've set up the configuration file you can run the Bolt sidecar with ```bash -./bolt-sidecar-cli +./bolt-sidecar ``` ### Observability From 14c3385fccb28670b5a41c289ae872aeb80411fd Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Fri, 25 Oct 2024 16:00:17 +0200 Subject: [PATCH 202/272] fix(holesky): genesis fork version on builder --- testnets/holesky/scripts/run-builder.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/testnets/holesky/scripts/run-builder.sh b/testnets/holesky/scripts/run-builder.sh index c1bc12035..4af4bbf93 100755 --- a/testnets/holesky/scripts/run-builder.sh +++ b/testnets/holesky/scripts/run-builder.sh @@ -28,5 +28,6 @@ geth --datadir=/var/lib/chaindata/geth \ --builder \ --builder.remote_relay_endpoint=http://helix-relay:4040 \ --builder.beacon_endpoints=http://beacon:4000 \ + --builder.genesis_fork_version=0x01017000 \ --miner.etherbase=0x614561D2d143621E126e87831AEF287678B442b8 \ --miner.extradata="Bolt Builder" From c0839baf57b884e36ec6bfe67e4f18bada124d09 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Sun, 27 Oct 2024 10:11:41 +0100 Subject: [PATCH 203/272] fix(holesky): cb port description --- testnets/holesky/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index c2847c66f..0b8e83ca0 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -220,8 +220,8 @@ commit-boost start --docker cb.docker-compose.yml --env .cb.env This will run all modules in Docker containers. > [!IMPORTANT] -> The `bolt-boost` service will be exposed at `pbs.port` (18551 by default, set -> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`), and your beacon node MUST be +> The `bolt-sidecar` service will be exposed at 18551 by default, set +> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`, and your beacon node MUST be > configured to point the `builder-api` to this port for Bolt to work. **Observability** From d78c3c7135e631415ccab779ebbc91816dcb9dd6 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 10:22:28 +0100 Subject: [PATCH 204/272] docs(holesky): document cb status --- testnets/holesky/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 0b8e83ca0..c4bc81e6a 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -168,6 +168,10 @@ can find at `http://localhost:28017`. ## Commit-Boost Mode +> [!IMPORTANT] +> Running with commit-boost is still in the process of being tested. Keep track of the issue +> here: https://github.com/chainbound/bolt/issues/328. + First download the `commit-boost-cli` binary from the Commit-Boost [official releases page](https://github.com/Commit-Boost/commit-boost-client/releases) From 3840e243e44505dec9e4fcdee4f61abd567676a8 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 28 Oct 2024 11:18:10 +0100 Subject: [PATCH 205/272] chore(holesky): re-add bolt-default network to all services --- testnets/holesky/docker-compose.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 66ab25379..397ce0e63 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -10,6 +10,8 @@ services: env_file: ./bolt-sidecar.env volumes: - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" + networks: + - bolt-default bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 @@ -17,6 +19,8 @@ services: restart: unless-stopped env_file: ./mev-boost.env entrypoint: /bin/sh -c '/app/mev-boost' + networks: + - bolt-default bolt-prometheus-holesky: image: prom/prometheus:latest @@ -27,6 +31,8 @@ services: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus + networks: + - bolt-default bolt-cadvisor-holesky: image: gcr.io/cadvisor/cadvisor:latest From e2d47cff14404540845cece34078c7eef1544c7a Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 11:38:06 +0100 Subject: [PATCH 206/272] feat(contracts): smol changes --- .../eigenlayer/depositIntoStrategy.json | 4 ++-- .../eigenlayer/registerIntoBoltAVS.json | 8 ++++---- .../operators/RegisterEigenLayerOperator.s.sol | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json index f500598aa..7a00219ce 100644 --- a/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json +++ b/bolt-contracts/config/holesky/operators/eigenlayer/depositIntoStrategy.json @@ -1,5 +1,5 @@ { "strategy": "", "token": "", - "amount": "" -} + "amount": 0 +} \ No newline at end of file diff --git a/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json index dd38cee99..3bf10125a 100644 --- a/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json +++ b/bolt-contracts/config/holesky/operators/eigenlayer/registerIntoBoltAVS.json @@ -1,5 +1,5 @@ { - "rpc": ":", - "salt": "0x0000000000000000000_salt_value_000000000000000000000000000000000", - "expiry": "0x00000000000000000_expiry_value_000000000000000000000000000000000" -} + "rpc": "://:", + "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", + "expiry": "0x1000000000000000000000000000000000000000000000000000000000000000" +} \ No newline at end of file diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index c67b7ef06..d96da34ad 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -29,7 +29,7 @@ contract RegisterEigenLayerOperator is Script { IStrategy strategy = IStrategy(vm.parseJsonAddress(json, ".strategy")); IERC20 token = IERC20(vm.parseJsonAddress(json, ".token")); - uint256 amount = vm.parseJsonUint(json, ".amount"); + uint256 amount = vm.parseJsonUint(json, ".amount") * 1 ether; vm.startBroadcast(operatorSk); // Allowance must be set before depositing @@ -73,13 +73,22 @@ contract RegisterEigenLayerOperator is Script { } function S03_checkOperatorRegistration() public view { - address operatorPublicKey = vm.envAddress("OPERATOR_PK"); - console.log("Checking operator registration for address", operatorPublicKey); + address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); + console.log("Checking operator registration for address", operatorAddress); IBoltManagerV1 boltManager = _readBoltManager(); - bool isRegistered = boltManager.isOperator(operatorPublicKey); + bool isRegistered = boltManager.isOperator(operatorAddress); console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); + + BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); + (address[] memory tokens, uint256[] memory amounts) = middleware.getOperatorCollaterals(operatorAddress); + + for (uint256 i; i < tokens.length; ++i) { + if (amounts[i] > 0) { + console.log("Collateral found:", tokens[i], "- amount:", amounts[i]); + } + } } function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { From cd77ffa54e2fea32950ca70c7c725e0e6bd78223 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 11:38:20 +0100 Subject: [PATCH 207/272] docs(holesky): smol changes --- testnets/holesky/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index c4bc81e6a..c55dc81ce 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -610,6 +610,8 @@ file: $EDITOR ./config/holesky/operators/eigenlayer/depositIntoStrategy.json ``` +Note that the amount is in ether (so for 1 ether, specify `1` instead of 1e18). + Then you can run the following Forge script: ```bash @@ -663,8 +665,8 @@ forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ --broadcast ``` -To check if your operator is correctly registered, set the operator public key -in the `OPERATOR_PK` environment variable and run the following script: +To check if your operator is correctly registered, set the operator address +in the `OPERATOR_ADDRESS` environment variable and run the following script: ```bash forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ From d52fb5ac4be6fa56c6898003cb9c50a0d5a6cbcc Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 11:42:23 +0100 Subject: [PATCH 208/272] fix: merge conflict --- testnets/holesky/docker-compose.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index dbf2f6b6e..397ce0e63 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -10,11 +10,8 @@ services: env_file: ./bolt-sidecar.env volumes: - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" -<<<<<<< HEAD -======= networks: - bolt-default ->>>>>>> 3840e243e44505dec9e4fcdee4f61abd567676a8 bolt-mev-boost-holesky: image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 @@ -22,11 +19,8 @@ services: restart: unless-stopped env_file: ./mev-boost.env entrypoint: /bin/sh -c '/app/mev-boost' -<<<<<<< HEAD -======= networks: - bolt-default ->>>>>>> 3840e243e44505dec9e4fcdee4f61abd567676a8 bolt-prometheus-holesky: image: prom/prometheus:latest @@ -37,11 +31,8 @@ services: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./targets.json:/etc/prometheus/targets.json - prometheus-data:/prometheus -<<<<<<< HEAD -======= networks: - bolt-default ->>>>>>> 3840e243e44505dec9e4fcdee4f61abd567676a8 bolt-cadvisor-holesky: image: gcr.io/cadvisor/cadvisor:latest From 34cf8c5b663ece69e5cae9ac826d264224112086 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 28 Oct 2024 14:46:41 +0100 Subject: [PATCH 209/272] chore: remove mev-boost folder again --- mev-boost/.env.example | 32 -------------------------------- mev-boost/.gitignore | 33 --------------------------------- 2 files changed, 65 deletions(-) delete mode 100644 mev-boost/.env.example delete mode 100644 mev-boost/.gitignore diff --git a/mev-boost/.env.example b/mev-boost/.env.example deleted file mode 100644 index 447b2fab8..000000000 --- a/mev-boost/.env.example +++ /dev/null @@ -1,32 +0,0 @@ -# Logging settings -LOG_JSON=false # Set to true to log in JSON format -LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic -DEBUG=false # Set to true to enable debug mode -LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries -DISABLE_LOG_VERSION=false # Set to true to disable logging the version - -# Server settings -BOOST_LISTEN_ADDR=localhost:18550 # Address for mev-boost server to listen on -RELAY_STARTUP_CHECK=false # Set to true to check relay status on startup - -# Relay settings -RELAYS= # Relay URLs: single or comma-separated list (scheme://pubkey@host) -RELAY_MONITORS= # Relay monitor URLs: single or comma-separated list (scheme://host) -MIN_BID_ETH=0 # Minimum bid to accept from relay (in ETH) - -# Relay timeout settings (in ms) -RELAY_TIMEOUT_MS_GETHEADER=950 # Timeout for getHeader requests to the relay -RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the relay -RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests - -# Genesis settings -GENESIS_FORK_VERSION= # Custom genesis fork version -GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) - -# Network settings -SEPOLIA=false # Set to true to use Sepolia network -GOERLI=false # Set to true to use Goerli network -HOLESKY=false # Set to true to use Holesky network - -# Retry settings -REQUEST_MAX_RETRIES=5 # Max retries for relay get payload request diff --git a/mev-boost/.gitignore b/mev-boost/.gitignore deleted file mode 100644 index c919b8e0d..000000000 --- a/mev-boost/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Ignore VI/Vim swapfiles -.*.sw? - -# IntelliJ -.idea -.ijwb -/mev-boost -/test-cli -/tmp -/dist -.vscode/ -/README.internal.md -/validator_data.json -/build/ - -*.env -!.env.example From b640654fdc31476df76fe57a981cc700422c3964 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 28 Oct 2024 17:19:03 +0100 Subject: [PATCH 210/272] fix(holesky/docs): broken links --- testnets/holesky/README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 07a29b06c..2f86cb855 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -6,9 +6,9 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Prerequisites](#prerequisites) * [Off-Chain Setup](#off-chain-setup) - * [Docker Mode (recommended)](#docker-mode-recommended) + * [Docker Mode (recommended)](#docker-mode-(recommended)) * [Commit-Boost Mode](#commit-boost-mode) - * [Native Mode (advanced)](#native-mode-advanced) + * [Native Mode (advanced)](#native-mode-(advanced)) * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) @@ -23,7 +23,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt` CLI](#bolt-cli) + * [`bolt` CLI](#`bolt`-cli) * [Installation and usage](#installation-and-usage) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) @@ -31,6 +31,11 @@ This document provides instructions for running the Bolt sidecar on the Holesky + + +[bolt]: https://docs.boltprotocol.xyz +[constraints-api]: https://docs.boltprotocol.xyz/technical-docs/api/builder + # Prerequisites In order to run Bolt you need some components already installed and running on @@ -116,7 +121,7 @@ cd bolt/testnets/holesky ``` The Docker Compose setup will spin up the Bolt sidecar along with the Bolt -MEV-Boost fork which includes supports the [Constraints API](https://docs.boltprotocol.xyz/api/builder). +MEV-Boost fork which includes supports the [Constraints API][constraints-api]. Before starting the services, you'll need to provide configuration files containing the necessary environment variables: @@ -176,9 +181,9 @@ First download the `commit-boost-cli` binary from the Commit-Boost [official releases page](https://github.com/Commit-Boost/commit-boost-client/releases) A commit-boost configuration file with Bolt support is provided at -[`cb-bolt-config.toml`](./cb-bolt-config.toml). This file has support for the +[`cb-bolt-config.toml`](./commit-boost/cb-bolt-config.toml). This file has support for the custom PBS module ([bolt-boost](../../bolt-boost)) that implements the -[constraints-API](https://chainbound.github.io/bolt-docs/api/builder), as well +[Constraints API][constraints-api], as well as the [bolt-sidecar](../../bolt-sidecar) module. This file can be used as a template for your own configuration. @@ -308,7 +313,7 @@ git clone --branch v0.3.0 https://github.com/chainbound/bolt.git && cd bolt The Bolt protocol relies on a modified version of [MEV-Boost](https://boost.flashbots.net/) that supports the [Constraints -API](https://docs.boltprotocol.xyz/api/builder). This modified version is +API][constraints-api]. This modified version is available in the `mev-boost` directory of the project and can be built by running @@ -427,7 +432,7 @@ forge install ## Validator Registration -The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only +The [`BoltValidators`](../../bolt-contracts/src/contracts/BoltValidators.sol) contract is the only point of entry for validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. @@ -463,7 +468,7 @@ validator or change any preferences. > all of the `forge` commands below. To register your validators, we provide the following Foundry script: -[`RegisterValidators.s.sol`](../../bolt-contracts/script/RegisterValidators.s.sol). +[`RegisterValidators.s.sol`](../../bolt-contracts/script/holesky/validators/RegisterValidators.s.sol). Note that in order to run these scripts, you must be in the `bolt-contracts` directory. @@ -864,8 +869,8 @@ If you don't want to use it you can skip the following section. ### `bolt` CLI `bolt` CLI is an offline tool for safely generating delegation and revocation messages -signed with a BLS12-381 key for the [Constraints API](https://docs.boltprotocol.xyz/api/builder) -in [Bolt](https://docs.boltprotocol.xyz/). +signed with a BLS12-381 key for the [Constraints API][constraints-api] +in [Bolt][bolt]. The tool supports three key sources: From 17dcc7f310e0bd2e5c6be46b31db8e913d9afcde Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 28 Oct 2024 17:20:59 +0100 Subject: [PATCH 211/272] chore(holesky/env): add BOLT_SIDECAR_ENABLE_UNSAFE_LOOKAHEAD in .env.example --- testnets/holesky/bolt-sidecar.env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example index 8792c94a2..993e00cb5 100644 --- a/testnets/holesky/bolt-sidecar.env.example +++ b/testnets/holesky/bolt-sidecar.env.example @@ -52,6 +52,8 @@ BOLT_SIDECAR_SLOT_TIME=12 # The deadline in the slot at which the sidecar will stop accepting new # commitments for the next block (parsed as milliseconds) BOLT_SIDECAR_COMMITMENT_DEADLINE=8000 +# Enable a two-epoch lookahead by enabling unsafe lookahead option +BOLT_SIDECAR_ENABLE_UNSAFE_LOOKAHEAD=true # Signing options. BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY= From b629e3829dcafbcf9759e7c3ec9b4da8f2a329b1 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 29 Oct 2024 09:51:08 +0100 Subject: [PATCH 212/272] fix(holesky/docs): table of contents --- testnets/holesky/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 2f86cb855..41ef0ce1e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -2,13 +2,13 @@ This document provides instructions for running the Bolt sidecar on the Holesky # Table of Contents - + * [Prerequisites](#prerequisites) * [Off-Chain Setup](#off-chain-setup) - * [Docker Mode (recommended)](#docker-mode-(recommended)) + * [Docker Mode (recommended)](#docker-mode-recommended) * [Commit-Boost Mode](#commit-boost-mode) - * [Native Mode (advanced)](#native-mode-(advanced)) + * [Native Mode (advanced)](#native-mode-advanced) * [Building and running the MEV-Boost fork binary](#building-and-running-the-mev-boost-fork-binary) * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) @@ -23,7 +23,7 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) - * [`bolt` CLI](#`bolt`-cli) + * [`bolt` CLI](#bolt-cli) * [Installation and usage](#installation-and-usage) * [Using a private key directly](#using-a-private-key-directly) * [Using a ERC-2335 Keystore](#using-a-erc-2335-keystore) From a4c514a87432d784ec037c9c0e8356cd1ed347b0 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 18:07:24 +0100 Subject: [PATCH 213/272] docs(holesky): clarify order of operations --- testnets/holesky/README.md | 841 +++++++++++++++++++------------------ 1 file changed, 424 insertions(+), 417 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 41ef0ce1e..3f6b3af81 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -1,5 +1,7 @@ +# Holesky Launch Instructions This document provides instructions for running the Bolt sidecar on the Holesky testnet. + # Table of Contents @@ -88,596 +90,601 @@ or **authorized delegates** acting on their behalf, to issue and sign preconfirm To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section. -# Off-Chain Setup +> [!INFO] +> Before moving on to the actual instructions, please note that the on-chain steps must be completed before running the off-chain +> infrastructure. The sidecar will verify that all of the associated validators and operator have been registered in the Bolt contracts, +> else it will fail (for safety reasons). -There are various way to run the Bolt Sidecar depending on what infrastructure -you want to use and your preferred signing methods: +# On-Chain Registration -- Docker mode (recommended) -- [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode - (requires Docker) -- Native mode (advanced, requires building everything from source) +Once you are successfully running the Bolt sidecar you need to register on-chain +on the Bolt Registry to successfully receive preconfirmation requests from users +and RPCs. This step is needed to provide economic security to your +commitments. -Running the Bolt sidecar as a standalone binary requires building it from -source. Both the standalone binary and the Docker container requires reading -signing keys from [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores, -while the Commit-Boost module relies on an internal signer and a custom PBS -module instead of regular [MEV-Boost](https://boost.flashbots.net/). +In order to do that you need some collateral in the form of whitelisted ETH derivative +tokens that need to be restaked in either the Symbiotic or +EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens +on Holesky: -In this section we're going to explore each of these options and its -requirements. +- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) + - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) + - [`rETH`](https://holesky.etherscan.io/address/0xe5708788c90e971f73D928b7c5A8FD09137010e0) + - [`stETH`](https://holesky.etherscan.io/address/0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd) + - [`wETH`](https://holesky.etherscan.io/address/0xC56Ba584929c6f381744fA2d7a028fA927817f2b) + - [`cbETH`](https://holesky.etherscan.io/address/0xcDdeFfcD2bA579B8801af1d603812fF64c301462) + - [`mETH`](https://holesky.etherscan.io/address/0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f) +- [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) + - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) + - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) + - [`wETH`](https://holesky.etherscan.io/address/0x94373a4919B3240D86eA41593D5eBa789FEF3848) + - [`cbETH`](https://holesky.etherscan.io/address/0x8720095Fa5739Ab051799211B146a2EEE4Dd8B37) + - [`mETH`](https://holesky.etherscan.io/address/0xe3C063B1BEe9de02eb28352b55D49D85514C67FF) -## Docker Mode (recommended) +Then you need to interact with two contracts on Holesky: +`BoltValidators` and `BoltManager`. The former is used to register your +active validators into the protocol, while the latter is used to manage to +register as an operator into the system and integrate with the restaking +protocols. -First, make sure to have [Docker](https://docs.docker.com/engine/install/), -[Docker Compose](https://docs.docker.com/compose/install/) and -[git](https://git-scm.com/downloads) installed in your machine. +> [!IMPORTANT] +> When registering your operator in the `BoltManager` contract you must use the +> public key associated to the private key used to sign commitments with the +> Bolt Sidecar (the `--commitment-private-key` flag). -Then clone the Bolt repository by running: +**Prerequisites** + +- Install the Foundry tools: ```bash -git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git -cd bolt/testnets/holesky +curl -L https://foundry.paradigm.xyz | bash +source $HOME/.bashrc +foundryup ``` -The Docker Compose setup will spin up the Bolt sidecar along with the Bolt -MEV-Boost fork which includes supports the [Constraints API][constraints-api]. +- Clone the Bolt repo and navigate to the [contracts](https://github.com/chainbound/bolt/tree/unstable/bolt-contracts) directory: -Before starting the services, you'll need to provide configuration files -containing the necessary environment variables: +```bash +git clone https://github.com/chainbound/bolt +cd bolt-contracts +forge install +``` -1. **Bolt Sidecar Configuration:** +## Validator Registration - Change directory to the `testnets/holesky` folder and create a - `bolt-sidecar.env` file starting from the reference template: +The [`BoltValidators`](../../bolt-contracts/src/contracts/BoltValidators.sol) contract is the only +point of entry for validators to signal their intent to participate in Bolt +Protocol and authenticate with their BLS private key. - ```bash - cd testnets/holesky - cp bolt-sidecar.env.example bolt-sidecar.env - ``` +The registration process includes the following steps: - Next up, fill out the values that are left blank. Please also review the - default values and see that they work for your setup. For proper - configuration of the signing options, please refer to the [Delegations and - Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) - section of this guide. +1. Validator signs a message with their BLS private key. This is required to + prove that the validator private key is under their control and that they are + indeed its owner. +2. Validator calls the `registerValidator` function providing: + 1. Their BLS public key + 2. The BLS signature of the registration message + 3. The address of the authorized collateral provider + 4. The address of the authorized operator - If you've generated a `delegation.json` file using the Bolt CLI please - place it in the `testnets/holesky` directory by replacing the existing empty - one. +Until the Pectra hard-fork will be activated, the contract will also expose a +`registerValidatorUnsafe` function that will not check the BLS signature. This +is gated by a feature flag that will be turned off post-Pectra and will allow us +to test the registration flow in a controlled environment. -2. **MEV-Boost Configuration:** +Note that the account initiating the registration will be the `controller` +account for those validators. Only the `controller` can then deregister +validator or change any preferences. - Change directory to the `testnets/holesky` folder if you haven't already and - copy over the example configuration file: +### Registration Steps - ```bash - cp ./mev-boost.env.example ./mev-boost.env - ``` +> [!NOTE] +> All of these scripts can be simulated on a Holesky fork using Anvil with the +> following command: +> +> `anvil --fork-url https://holesky.drpc.org --port 8545` +> +> In order to use this local fork, replace `$HOLESKY_RPC` with `localhost:8545` in +> all of the `forge` commands below. -Then configure it accordingly and review the default values chosen. +To register your validators, we provide the following Foundry script: +[`RegisterValidators.s.sol`](../../bolt-contracts/script/holesky/validators/RegisterValidators.s.sol). +Note that in order to run these scripts, you must be in the `bolt-contracts` +directory. -If you prefer not to restart your beacon node, follow the instructions in the -[Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. +- First, configure + [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) + to your requirements. Note that both `maxCommittedGasLimit` and + `authorizedOperator` must reflect the values specified in previous steps, during + the configuration of the sidecar. `pubkeys` should be configured with all of the + validator public keys that you wish to register. -Once the configuration files are in place, make sure you are in the -`testnets/holesky` directory and then run: +- Next up, decide on a controller account and save the key in an environment + variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run + the script and will mark the corresponding account as the [controller + account](https://github.com/chainbound/bolt/blob/06bdd8e75d759d91f6178ad73f962b1f4ad43fd8/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol#L18-L19) + for these validators. + +- Finally, run the script: ```bash -docker compose up -d --env-file bolt-sidecar.env +forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast ``` -The docker compose setup comes with various observability tools, such as -Prometheus and Grafana. It also comes with some pre-built dashboards which you -can find at `http://localhost:28017`. - -## Commit-Boost Mode - -> [!IMPORTANT] -> Running with commit-boost is still in the process of being tested. Keep track of the issue -> here: https://github.com/chainbound/bolt/issues/328. +If the script executed succesfully, your validators were registered. -First download the `commit-boost-cli` binary from the Commit-Boost [official -releases page](https://github.com/Commit-Boost/commit-boost-client/releases) +## Bolt Network Entrypoint -A commit-boost configuration file with Bolt support is provided at -[`cb-bolt-config.toml`](./commit-boost/cb-bolt-config.toml). This file has support for the -custom PBS module ([bolt-boost](../../bolt-boost)) that implements the -[Constraints API][constraints-api], as well -as the [bolt-sidecar](../../bolt-sidecar) module. This file can be used as a -template for your own configuration. +The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) +contract is a crucial component of Bolt that integrates with restaking +ecosystems Symbiotic and Eigenlayer. It manages the registration and +coordination of validators, operators, and vaults within the Bolt network. -The important fields to configure are under the `[modules.env]` section of the -`BOLT` module, which contain the environment variables to configure the bolt -sidecar: +Key features include: -```toml -[modules.env] -BOLT_SIDECAR_CHAIN = "holesky" +1. Retrieval of operator stake and proposer status from their pubkey +2. Integration with Symbiotic +3. Integration with Eigenlayer -BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module (static) -BOLT_SIDECAR_BEACON_API = "" -BOLT_SIDECAR_EXECUTION_API = "" -BOLT_SIDECAR_ENGINE_API = "" # The execution layer engine API endpoint -BOLT_SIDECAR_JWT_HEX = "" # The engine JWT used to authenticate with the engine API -BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. -BOLT_SIDECAR_FEE_RECIPIENT = "" # The fee recipient -BOLT_SIDECAR_VALIDATOR_INDEXES = "" # The active validator indexes (can be defined as a comma-separated list, or a range) - # e.g. "0,1,2,3,4" or "0..4", or a combination of both -``` +Specific functionalities about the restaking protocols are handled inside the +`IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and +`BoltEigenlayerMiddleware`. -To initialize commit-boost, run the following command: +## Operator Registration -```bash -commit-boost init --config cb-bolt-config.toml -``` +In this section we outline how to register as an operator, i.e. an entity +uniquely identified by an Ethereum address and responsible for duties like +signing commitments. Note that in Bolt, there is no real separation between +validators and an operator. An operator is only real in the sense that its +private key will be used to sign commitments on the corresponding validators' +sidecars. However, we need a way to logically connect validators to an on-chain +address associated with some stake, which is what the operator is. -This will create three files: +**In the next sections we assume you have saved the private key corresponding to +the operator address in `$OPERATOR_SK`.** This private key will be read by the +Forge scripts for registering operators and needs to be set correctly. You also +have to invoke the scripts from the [`bolt-contracts`](../../bolt-contracts) +directory. -- `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services -- `.cb.env`: with local env variables, including JWTs for modules -- `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus +### Symbiotic Registration Steps -**Running** +As an operator, you will need to opt-in to the Bolt Network and any Vault that +trusts you to provide commitments on their behalf. -The final step is to run the Commit-Boost services. This can be done with the following command: +**External Steps** -```bash -commit-boost start --docker cb.docker-compose.yml --env .cb.env -``` +> [!NOTE] +> The network and supported vault addresses can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -This will run all modules in Docker containers. +Make sure you have installed the [Symbiotic +CLI](https://docs.symbiotic.fi/guides/cli/). -> [!IMPORTANT] -> The `bolt-sidecar` service will be exposed at 18551 by default, set -> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`, and your beacon node MUST be -> configured to point the `builder-api` to this port for Bolt to work. +The opt-in process requires the following steps: -**Observability** +1. if you haven't done it already, register as a Symbiotic Operator with the + [`register-operator`](https://docs.symbiotic.fi/guides/cli/#register-operator) + command; +2. opt-in to the Bolt network with the + [`opt-in-network`](https://docs.symbiotic.fi/guides/cli/#opt-in-network) + command; +3. opt-in to any vault using the + [`opt-in-vault`](https://docs.symbiotic.fi/guides/cli/#opt-in-vault) command; +4. deposit collateral into the vault using the + [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. -Commit-Boost comes with various observability tools, such as Prometheus, -cadvisor, and Grafana. It also comes with some pre-built dashboards, which can -be found in the `commit-boost/grafana` directory. +**Internal Steps** -To update these dashboards, run the following command from the `commit-boost` -directory: +After having deposited collateral into a vault you need to register into +Bolt as a Symbiotic operator. We've provided a script to facilitate the +procedure. If you want to use it, please follow these steps: -`bash ./update-grafana.sh ` -In this directory, you can also find a Bolt dashboard, which will be launched -alongside the other dashboards. +1. set the operator private key to the `OPERATOR_SK` environment variable; +2. set the operator RPC URL which supports the Commitments API to the + `OPERATOR_RPC` environment variable; +3. run the following Forge script from the `bolt-contracts` directory: -## Native Mode (advanced) + ```bash + forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S01_registerIntoBolt" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast + ``` -For running the Bolt Sidecar as a standalone binary you need to have the -following dependencies installed: +To check if your operator is correctly registered, set the operator address +in the `OPERATOR_ADDRESS` environment variable and run the following script: -- [git](https://git-scm.com/downloads); -- [Rust](https://www.rust-lang.org/tools/install). -- [Golang](https://golang.org/doc/install). +```bash +forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ + --sig "S02_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` -Depending on your platform you may need to install additional dependencies. +### EigenLayer Registration Steps -
-Linux +**External Steps** -Debian-based distributions: +> [!NOTE] +> The supported strategies can be found in +> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). -```bash -sudo apt update && sudo apt install -y git build-essential libssl-dev build-essential ca-certificates -``` +If you're not registered as an operator in EigenLayer yet, you need to do so by +following [the official +guide](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-introduction). +This requires installing the EigenLayer CLI and opt into the protocol by +registering via the +[`DelegationManager.registerAsOperator`](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-installation) +function. -Fedora/Red Hat/CentOS distributions: +After that you need to deposit into a supported EigenLayer +strategy using +[`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). +This will add the deposit into the collateral of the operator so that Bolt can +read it. Note that you need to deposit a minimum of `1 ether` of the strategies +underlying token in order to opt in. -```bash -sudo dnf groupinstall "Development Tools" && sudo dnf install -y git openssl-devel ca-certificates pkgconfig -``` +We've provided a script to facilitate the procedure. If you want to use it, +please set the operator private key to an `OPERATOR_SK` environment variable. -Arch/Manjaro-based distributions: +First, you need to first configure the deposit details in this JSON +file: ```bash -sudo pacman -Syu --needed base-devel git openssl ca-certificates pkgconf +$EDITOR ./config/holesky/operators/eigenlayer/depositIntoStrategy.json ``` -Alpine Linux +Note that the amount is in ether (so for 1 ether, specify `1` instead of 1e18). + +Then you can run the following Forge script: ```bash -sudo apk add git build-base openssl-dev ca-certificates pkgconf +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S01_depositIntoStrategy()" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -
- -
+**Internal Steps** -
- MacOS +After having deposited collateral into a strategy you need to register into the +Bolt AVS. We've provided a script to facilitate the procedure. If you want to +use it, please set follow these steps: -On MacOS after installing XCode Command Line tools (equivalent to `build-essential` on Linux) you can install the other dependencies with [Homebew](https://brew.sh/): +1. configure the operator details in this JSON file -```zsh -xcode-select --install -brew install pkg-config openssl -``` + ```bash + $EDITOR ./config/holesky/operators/eigenlayer/registerIntoBoltAVS.json + ``` -
+ In there you'll need to set the the following fields: ---- + - `rpc` -- the RPC URL of your operator which supports the Commitments API + - `salt` -- an unique 32 bytes value to avoid replay attacks. To generate it on + both Linux and MacOS you can run: -After having installed the dependencies you can clone the Bolt repository by -running: + ```bash + echo -n "0x"; head -c 32 /dev/urandom | hexdump -e '32/1 "%02x" "\n"' + ``` -```bash -git clone --branch v0.3.0 https://github.com/chainbound/bolt.git && cd bolt -``` + - `expiry` -- the timestamp of the signature expiry in seconds. To generate it + on both Linux and MacOS run the following command, replacing + `` with the desired timestamp: -### Building and running the MEV-Boost fork binary + ```bash + echo -n "0x"; printf "%064x\n" + ``` -The Bolt protocol relies on a modified version of -[MEV-Boost](https://boost.flashbots.net/) that supports the [Constraints -API][constraints-api]. This modified version is -available in the `mev-boost` directory of the project and can be built by -running +2. set the operator private key to an `OPERATOR_SK` environment + variable; +3. run the following Forge script from the `bolt-contracts` + directory: ```bash -make build +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S02_registerIntoBoltAVS" \ + --rpc-url $HOLESKY_RPC \ + -vvvv \ + --broadcast ``` -in the `mev-boost` directory. The output of the command is a `mev-boost` binary. -To run the `mev-boost` binary please read the official [documentation](https://boost.flashbots.net/). +To check if your operator is correctly registered, set the operator address +in the `OPERATOR_ADDRESS` environment variable and run the following script: -If you're already running MEV-Boost along with your beacon client it is -recommended to choose another port this service in order to [avoid restarting -your beacon client](#avoid-restarting-the-beacon-node). Check out the linked -section for more details. +```bash +forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ + --sig "S03_checkOperatorRegistration" \ + --rpc-url $HOLESKY_RPC \ + -vvvv +``` -### Building and running the Bolt sidecar binary +# Off-Chain Setup -Then you can build the Bolt sidecar by running: +There are various way to run the Bolt Sidecar depending on what infrastructure +you want to use and your preferred signing methods: -```bash -cargo build --release && mv target/release/bolt-sidecar . -``` +- Docker mode (recommended) +- [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode + (requires Docker) +- Native mode (advanced, requires building everything from source) -In order to run correctly the sidecar you need to provide either a list command -line options or a configuration file (recommended). All the options available -can be found by running `./bolt-sidecar --help`, or you can find them in the -[reference](#command-line-options) section of this guide. +Running the Bolt sidecar as a standalone binary requires building it from +source. Both the standalone binary and the Docker container requires reading +signing keys from [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores, +while the Commit-Boost module relies on an internal signer and a custom PBS +module instead of regular [MEV-Boost](https://boost.flashbots.net/). -#### Configuration file +In this section we're going to explore each of these options and its +requirements. -You can use a `.env` file to configure the sidecar, for which you can -find a template in the `.env.example` file. +## Docker Mode (recommended) -Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) -to configure such sidecar options properly. +First, make sure to have [Docker](https://docs.docker.com/engine/install/), +[Docker Compose](https://docs.docker.com/compose/install/) and +[git](https://git-scm.com/downloads) installed in your machine. -After you've set up the configuration file you can run the Bolt sidecar with +Then clone the Bolt repository by running: ```bash -./bolt-sidecar +git clone --branch v0.3.0-alpha htts://github.com/chainbound/bolt.git +cd bolt/testnets/holesky ``` -### Observability - -The bolt sidecar comes with various observability tools, such as Prometheus -and Grafana. It also comes with some pre-built dashboards, which can -be found in the `grafana` directory. +The Docker Compose setup will spin up the Bolt sidecar along with the Bolt +MEV-Boost fork which includes supports the [Constraints API][constraints-api]. -To run these dashboards change directory to the `bolt-sidecar/infra` folder and -run: +Before starting the services, you'll need to provide configuration files +containing the necessary environment variables: -```bash -docker compose -f telemetry.compose.yml up -d -``` +1. **Bolt Sidecar Configuration:** -To stop the services run: + Change directory to the `testnets/holesky` folder and create a + `bolt-sidecar.env` file starting from the reference template: -```bash -docker compose -f telemetry.compose.yml down -``` + ```bash + cd testnets/holesky + cp bolt-sidecar.env.example bolt-sidecar.env + ``` -# On-Chain Registration + Next up, fill out the values that are left blank. Please also review the + default values and see that they work for your setup. For proper + configuration of the signing options, please refer to the [Delegations and + Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) + section of this guide. -Once you are successfully running the Bolt sidecar you need to register on-chain -on the Bolt Registry to successfully receive preconfirmation requests from users -and RPCs. This step is needed to provide economic security to your -commitments. + If you've generated a `delegation.json` file using the Bolt CLI please + place it in the `testnets/holesky` directory by replacing the existing empty + one. -In order to do that you need some collateral in the form of whitelisted ETH derivative -tokens that need to be restaked in either the Symbiotic or -EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens -on Holesky: +2. **MEV-Boost Configuration:** -- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) - - [`rETH`](https://holesky.etherscan.io/address/0xe5708788c90e971f73D928b7c5A8FD09137010e0) - - [`stETH`](https://holesky.etherscan.io/address/0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd) - - [`WETH`](https://holesky.etherscan.io/address/0xC56Ba584929c6f381744fA2d7a028fA927817f2b) - - [`cbETH`](https://holesky.etherscan.io/address/0xcDdeFfcD2bA579B8801af1d603812fF64c301462) - - [`mETH`](https://holesky.etherscan.io/address/0x91e84e12Bb65576C0a6614c5E6EbbB2eA595E10f) -- [EigenLayer Strategies](https://github.com/Layr-Labs/eigenlayer-contracts#current-testnet-deployment) - - [`stETH`](https://holesky.etherscan.io/address/0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034) - - [`rETH`](https://holesky.etherscan.io/address/0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1) - - [`wETH`](https://holesky.etherscan.io/address/0x94373a4919B3240D86eA41593D5eBa789FEF3848) - - [`cbETH`](https://holesky.etherscan.io/address/0x8720095Fa5739Ab051799211B146a2EEE4Dd8B37) - - [`mETH`](https://holesky.etherscan.io/address/0xe3C063B1BEe9de02eb28352b55D49D85514C67FF) + Change directory to the `testnets/holesky` folder if you haven't already and + copy over the example configuration file: -Then you need to interact with two contracts on Holesky: -`BoltValidators` and `BoltManager`. The former is used to register your -active validators into the protocol, while the latter is used to manage to -register as an operator into the system and integrate with the restaking -protocols. + ```bash + cp ./mev-boost.env.example ./mev-boost.env + ``` -> [!IMPORTANT] -> When registering your operator in the `BoltManager` contract you must use the -> public key associated to the private key used to sign commitments with the -> Bolt Sidecar (the `--commitment-private-key` flag). +Then configure it accordingly and review the default values chosen. -**Prerequisites** +If you prefer not to restart your beacon node, follow the instructions in the +[Avoid Restarting the Beacon Node](#avoid-restarting-the-beacon-node) section. -- Install the Foundry tools: +Once the configuration files are in place, make sure you are in the +`testnets/holesky` directory and then run: ```bash -curl -L https://foundry.paradigm.xyz | bash -source $HOME/.bashrc -foundryup +docker compose up -d --env-file bolt-sidecar.env ``` -- Clone the Bolt repo and navigate to the [contracts](https://github.com/chainbound/bolt/tree/unstable/bolt-contracts) directory: +The docker compose setup comes with various observability tools, such as +Prometheus and Grafana. It also comes with some pre-built dashboards which you +can find at `http://localhost:28017`. -```bash -git clone https://github.com/chainbound/bolt -cd bolt-contracts -forge install -``` +## Commit-Boost Mode -## Validator Registration +> [!IMPORTANT] +> Running with commit-boost is still in the process of being tested. Keep track of the issue +> here: https://github.com/chainbound/bolt/issues/328. -The [`BoltValidators`](../../bolt-contracts/src/contracts/BoltValidators.sol) contract is the only -point of entry for validators to signal their intent to participate in Bolt -Protocol and authenticate with their BLS private key. +First download the `commit-boost-cli` binary from the Commit-Boost [official +releases page](https://github.com/Commit-Boost/commit-boost-client/releases) -The registration process includes the following steps: +A commit-boost configuration file with Bolt support is provided at +[`cb-bolt-config.toml`](./commit-boost/cb-bolt-config.toml). This file has support for the +custom PBS module ([bolt-boost](../../bolt-boost)) that implements the +[Constraints API][constraints-api], as well +as the [bolt-sidecar](../../bolt-sidecar) module. This file can be used as a +template for your own configuration. -1. Validator signs a message with their BLS private key. This is required to - prove that the validator private key is under their control and that they are - indeed its owner. -2. Validator calls the `registerValidator` function providing: - 1. Their BLS public key - 2. The BLS signature of the registration message - 3. The address of the authorized collateral provider - 4. The address of the authorized operator +The important fields to configure are under the `[modules.env]` section of the +`BOLT` module, which contain the environment variables to configure the bolt +sidecar: -Until the Pectra hard-fork will be activated, the contract will also expose a -`registerValidatorUnsafe` function that will not check the BLS signature. This -is gated by a feature flag that will be turned off post-Pectra and will allow us -to test the registration flow in a controlled environment. +```toml +[modules.env] +BOLT_SIDECAR_CHAIN = "holesky" -Note that the account initiating the registration will be the `controller` -account for those validators. Only the `controller` can then deregister -validator or change any preferences. +BOLT_SIDECAR_CONSTRAINTS_API = "http://cb_pbs:18550" # The address of the PBS module (static) +BOLT_SIDECAR_BEACON_API = "" +BOLT_SIDECAR_EXECUTION_API = "" +BOLT_SIDECAR_ENGINE_API = "" # The execution layer engine API endpoint +BOLT_SIDECAR_JWT_HEX = "" # The engine JWT used to authenticate with the engine API +BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_FEE_RECIPIENT = "" # The fee recipient +BOLT_SIDECAR_VALIDATOR_INDEXES = "" # The active validator indexes (can be defined as a comma-separated list, or a range) + # e.g. "0,1,2,3,4" or "0..4", or a combination of both +``` -### Registration Steps +To initialize commit-boost, run the following command: -> [!NOTE] -> All of these scripts can be simulated on a Holesky fork using Anvil with the -> following command: -> -> `anvil --fork-url https://holesky.drpc.org --port 8545` -> -> In order to use this local fork, replace `$HOLESKY_RPC` with `localhost:8545` in -> all of the `forge` commands below. +```bash +commit-boost init --config cb-bolt-config.toml +``` -To register your validators, we provide the following Foundry script: -[`RegisterValidators.s.sol`](../../bolt-contracts/script/holesky/validators/RegisterValidators.s.sol). -Note that in order to run these scripts, you must be in the `bolt-contracts` -directory. +This will create three files: -- First, configure - [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) - to your requirements. Note that both `maxCommittedGasLimit` and - `authorizedOperator` must reflect the values specified in previous steps, during - the configuration of the sidecar. `pubkeys` should be configured with all of the - validator public keys that you wish to register. +- `cb.docker-compose.yml`: which contains the full setup of the Commit-Boost services +- `.cb.env`: with local env variables, including JWTs for modules +- `target.json`: which enables dynamic discovery of services for metrics scraping via Prometheus -- Next up, decide on a controller account and save the key in an environment - variable: `export CONTROLLER_KEY=0x...`. This controller key will be used to run - the script and will mark the corresponding account as the [controller - account](https://github.com/chainbound/bolt/blob/06bdd8e75d759d91f6178ad73f962b1f4ad43fd8/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol#L18-L19) - for these validators. +**Running** -- Finally, run the script: +The final step is to run the Commit-Boost services. This can be done with the following command: ```bash -forge script script/holesky/validators/RegisterValidators.s.sol -vvvv --rpc-url $HOLESKY_RPC --private-key $CONTROLLER_KEY --broadcast +commit-boost start --docker cb.docker-compose.yml --env .cb.env ``` -If the script executed succesfully, your validators were registered. - -## Bolt Network Entrypoint - -The [`BoltManager`](../../bolt-contracts/src/contracts/BoltManagerV1.sol) -contract is a crucial component of Bolt that integrates with restaking -ecosystems Symbiotic and Eigenlayer. It manages the registration and -coordination of validators, operators, and vaults within the Bolt network. - -Key features include: +This will run all modules in Docker containers. -1. Retrieval of operator stake and proposer status from their pubkey -2. Integration with Symbiotic -3. Integration with Eigenlayer +> [!IMPORTANT] +> The `bolt-sidecar` service will be exposed at 18551 by default, set +> with `BOLT_SIDECAR_BUILDER_PROXY_PORT`, and your beacon node MUST be +> configured to point the `builder-api` to this port for Bolt to work. -Specific functionalities about the restaking protocols are handled inside the -`IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and -`BoltEigenlayerMiddleware`. +**Observability** -## Operator Registration +Commit-Boost comes with various observability tools, such as Prometheus, +cadvisor, and Grafana. It also comes with some pre-built dashboards, which can +be found in the `commit-boost/grafana` directory. -In this section we outline how to register as an operator, i.e. an entity -uniquely identified by an Ethereum address and responsible for duties like -signing commitments. Note that in Bolt, there is no real separation between -validators and an operator. An operator is only real in the sense that its -private key will be used to sign commitments on the corresponding validators' -sidecars. However, we need a way to logically connect validators to an on-chain -address associated with some stake, which is what the operator is. +To update these dashboards, run the following command from the `commit-boost` +directory: -**In the next sections we assume you have saved the private key corresponding to -the operator address in `$OPERATOR_SK`.** This private key will be read by the -Forge scripts for registering operators and needs to be set correctly. You also -have to invoke the scripts from the [`bolt-contracts`](../../bolt-contracts) -directory. +`bash ./update-grafana.sh ` +In this directory, you can also find a Bolt dashboard, which will be launched +alongside the other dashboards. -### Symbiotic Registration Steps +## Native Mode (advanced) -As an operator, you will need to opt-in to the Bolt Network and any Vault that -trusts you to provide commitments on their behalf. +For running the Bolt Sidecar as a standalone binary you need to have the +following dependencies installed: -**External Steps** +- [git](https://git-scm.com/downloads); +- [Rust](https://www.rust-lang.org/tools/install). +- [Golang](https://golang.org/doc/install). -> [!NOTE] -> The network and supported vault addresses can be found in -> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +Depending on your platform you may need to install additional dependencies. -Make sure you have installed the [Symbiotic -CLI](https://docs.symbiotic.fi/guides/cli/). +
+Linux -The opt-in process requires the following steps: +Debian-based distributions: -1. if you haven't done it already, register as a Symbiotic Operator with the - [`register-operator`](https://docs.symbiotic.fi/guides/cli/#register-operator) - command; -2. opt-in to the Bolt network with the - [`opt-in-network`](https://docs.symbiotic.fi/guides/cli/#opt-in-network) - command; -3. opt-in to any vault using the - [`opt-in-vault`](https://docs.symbiotic.fi/guides/cli/#opt-in-vault) command; -4. deposit collateral into the vault using the - [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. +```bash +sudo apt update && sudo apt install -y git build-essential libssl-dev build-essential ca-certificates +``` -**Internal Steps** +Fedora/Red Hat/CentOS distributions: -After having deposited collateral into a vault you need to register into -Bolt as a Symbiotic operator. We've provided a script to facilitate the -procedure. If you want to use it, please follow these steps: +```bash +sudo dnf groupinstall "Development Tools" && sudo dnf install -y git openssl-devel ca-certificates pkgconfig +``` -1. set the operator private key to the `OPERATOR_SK` environment variable; -2. set the operator RPC URL which supports the Commitments API to the - `OPERATOR_RPC` environment variable; -3. run the following Forge script from the `bolt-contracts` directory: +Arch/Manjaro-based distributions: - ```bash - forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ - --sig "S01_registerIntoBolt" \ - --rpc-url $HOLESKY_RPC \ - -vvvv \ - --broadcast - ``` +```bash +sudo pacman -Syu --needed base-devel git openssl ca-certificates pkgconf +``` -To check if your operator is correctly registered, set the operator address -in the `OPERATOR_ADDRESS` environment variable and run the following script: +Alpine Linux ```bash -forge script script/holesky/operators/RegisterSymbioticOperator.s.sol \ - --sig "S02_checkOperatorRegistration" \ - --rpc-url $HOLESKY_RPC \ - -vvvv +sudo apk add git build-base openssl-dev ca-certificates pkgconf ``` -### EigenLayer Registration Steps +
-**External Steps** +
-> [!NOTE] -> The supported strategies can be found in -> [`deployments.json`](../../bolt-contracts/config/holesky/deployments.json). +
+ MacOS -If you're not registered as an operator in EigenLayer yet, you need to do so by -following [the official -guide](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-introduction). -This requires installing the EigenLayer CLI and opt into the protocol by -registering via the -[`DelegationManager.registerAsOperator`](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-installation) -function. +On MacOS after installing XCode Command Line tools (equivalent to `build-essential` on Linux) you can install the other dependencies with [Homebew](https://brew.sh/): -After that you need to deposit into a supported EigenLayer -strategy using -[`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/testnet-holesky/src/contracts/core/StrategyManager.sol#L303-L322). -This will add the deposit into the collateral of the operator so that Bolt can -read it. Note that you need to deposit a minimum of `1 ether` of the strategies -underlying token in order to opt in. +```zsh +xcode-select --install +brew install pkg-config openssl +``` -We've provided a script to facilitate the procedure. If you want to use it, -please set the operator private key to an `OPERATOR_SK` environment variable. +
-First, you need to first configure the deposit details in this JSON -file: +--- + +After having installed the dependencies you can clone the Bolt repository by +running: ```bash -$EDITOR ./config/holesky/operators/eigenlayer/depositIntoStrategy.json +git clone --branch v0.3.0 https://github.com/chainbound/bolt.git && cd bolt ``` -Note that the amount is in ether (so for 1 ether, specify `1` instead of 1e18). +### Building and running the MEV-Boost fork binary -Then you can run the following Forge script: +The Bolt protocol relies on a modified version of +[MEV-Boost](https://boost.flashbots.net/) that supports the [Constraints +API][constraints-api]. This modified version is +available in the `mev-boost` directory of the project and can be built by +running ```bash -forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ - --sig "S01_depositIntoStrategy()" \ - --rpc-url $HOLESKY_RPC \ - -vvvv \ - --broadcast +make build ``` -**Internal Steps** +in the `mev-boost` directory. The output of the command is a `mev-boost` binary. +To run the `mev-boost` binary please read the official [documentation](https://boost.flashbots.net/). -After having deposited collateral into a strategy you need to register into the -Bolt AVS. We've provided a script to facilitate the procedure. If you want to -use it, please set follow these steps: +If you're already running MEV-Boost along with your beacon client it is +recommended to choose another port this service in order to [avoid restarting +your beacon client](#avoid-restarting-the-beacon-node). Check out the linked +section for more details. -1. configure the operator details in this JSON file +### Building and running the Bolt sidecar binary - ```bash - $EDITOR ./config/holesky/operators/eigenlayer/registerIntoBoltAVS.json - ``` +Then you can build the Bolt sidecar by running: - In there you'll need to set the the following fields: +```bash +cargo build --release && mv target/release/bolt-sidecar . +``` - - `rpc` -- the RPC URL of your operator which supports the Commitments API - - `salt` -- an unique 32 bytes value to avoid replay attacks. To generate it on - both Linux and MacOS you can run: +In order to run correctly the sidecar you need to provide either a list command +line options or a configuration file (recommended). All the options available +can be found by running `./bolt-sidecar --help`, or you can find them in the +[reference](#command-line-options) section of this guide. - ```bash - echo -n "0x"; head -c 32 /dev/urandom | hexdump -e '32/1 "%02x" "\n"' - ``` +#### Configuration file - - `expiry` -- the timestamp of the signature expiry in seconds. To generate it - on both Linux and MacOS run the following command, replacing - `` with the desired timestamp: +You can use a `.env` file to configure the sidecar, for which you can +find a template in the `.env.example` file. - ```bash - echo -n "0x"; printf "%064x\n" - ``` +Please read the section on [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) +to configure such sidecar options properly. -2. set the operator private key to an `OPERATOR_SK` environment - variable; -3. run the following Forge script from the `bolt-contracts` - directory: +After you've set up the configuration file you can run the Bolt sidecar with ```bash -forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ - --sig "S02_registerIntoBoltAVS" \ - --rpc-url $HOLESKY_RPC \ - -vvvv \ - --broadcast +./bolt-sidecar ``` -To check if your operator is correctly registered, set the operator address -in the `OPERATOR_ADDRESS` environment variable and run the following script: +### Observability + +The bolt sidecar comes with various observability tools, such as Prometheus +and Grafana. It also comes with some pre-built dashboards, which can +be found in the `grafana` directory. + +To run these dashboards change directory to the `bolt-sidecar/infra` folder and +run: ```bash -forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ - --sig "S03_checkOperatorRegistration" \ - --rpc-url $HOLESKY_RPC \ - -vvvv +docker compose -f telemetry.compose.yml up -d +``` + +To stop the services run: + +```bash +docker compose -f telemetry.compose.yml down ``` # Reference From 4c1baea4d0bfcd98fef49bbb0340bae1214266e2 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 28 Oct 2024 18:09:09 +0100 Subject: [PATCH 214/272] docs(holesky): callout fix --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 3f6b3af81..2af1a3abe 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -90,7 +90,7 @@ or **authorized delegates** acting on their behalf, to issue and sign preconfirm To learn more about delegation, check out the [Delegations and Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section. -> [!INFO] +> [!NOTE] > Before moving on to the actual instructions, please note that the on-chain steps must be completed before running the off-chain > infrastructure. The sidecar will verify that all of the associated validators and operator have been registered in the Bolt contracts, > else it will fail (for safety reasons). From f0e18fa66ce58a831746ad8024a6501310b80e01 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 29 Oct 2024 09:44:04 +0100 Subject: [PATCH 215/272] fix(holesky/docks): broken link to contract --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 2af1a3abe..723b77d75 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -152,7 +152,7 @@ forge install ## Validator Registration -The [`BoltValidators`](../../bolt-contracts/src/contracts/BoltValidators.sol) contract is the only +The [`BoltValidators`](../../bolt-contracts/src/contracts/BoltValidatorsV1.sol) contract is the only point of entry for validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. From f55156c9d871efeedf78864d5c0ed25c3aa5554c Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Tue, 29 Oct 2024 10:30:50 +0100 Subject: [PATCH 216/272] docs(holesky): updates --- testnets/holesky/README.md | 43 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 723b77d75..8ce9b8018 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -97,15 +97,12 @@ section. # On-Chain Registration -Once you are successfully running the Bolt sidecar you need to register on-chain -on the Bolt Registry to successfully receive preconfirmation requests from users -and RPCs. This step is needed to provide economic security to your -commitments. +The first step for integrating Bolt is registering into the Bolt smart contracts. This is required for signalling +participation, depositing collateral, and specifying endpoints and other metadata to start receiving preconfirmation +requests. What follows is a quick overview of the required steps. -In order to do that you need some collateral in the form of whitelisted ETH derivative -tokens that need to be restaked in either the Symbiotic or -EigenLayer protocol. Bolt is compatible with the following ETH derivative tokens -on Holesky: +First you'll need to deposit some collateral in the form of whitelisted ETH derivative tokens that need to +be restaked in either the Symbiotic or EigenLayer restaking protocols. Bolt is compatible with the following ETH derivative tokens on Holesky: - [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) @@ -121,9 +118,13 @@ on Holesky: - [`cbETH`](https://holesky.etherscan.io/address/0x8720095Fa5739Ab051799211B146a2EEE4Dd8B37) - [`mETH`](https://holesky.etherscan.io/address/0xe3C063B1BEe9de02eb28352b55D49D85514C67FF) -Then you need to interact with two contracts on Holesky: +> [!NOTE] +> These Vaults and Strategies have been deployed on Holesky by us, and are permissionless to opt in to. +> For now, these are the only vaults & strategies that have been whitelisted by the Bolt protocol. + +After that, you need to interact with two contracts on Holesky: `BoltValidators` and `BoltManager`. The former is used to register your -active validators into the protocol, while the latter is used to manage to +active validators into the protocol, while the latter is used to register as an operator into the system and integrate with the restaking protocols. @@ -134,7 +135,7 @@ protocols. **Prerequisites** -- Install the Foundry tools: +- Install the Foundry toolkit: ```bash curl -L https://foundry.paradigm.xyz | bash @@ -154,7 +155,7 @@ forge install The [`BoltValidators`](../../bolt-contracts/src/contracts/BoltValidatorsV1.sol) contract is the only point of entry for validators to signal their intent to participate in Bolt -Protocol and authenticate with their BLS private key. +Protocol and authenticate with their BLS private key (unsupported until Pectra). The registration process includes the following steps: @@ -164,8 +165,7 @@ The registration process includes the following steps: 2. Validator calls the `registerValidator` function providing: 1. Their BLS public key 2. The BLS signature of the registration message - 3. The address of the authorized collateral provider - 4. The address of the authorized operator + 3. The address of the authorized collateral provider & operator (commitment signer) Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function that will not check the BLS signature. This @@ -195,7 +195,7 @@ directory. - First, configure [`bolt-contracts/config/holesky/validators.json`](../../bolt-contracts/config/holesky/validators.json) to your requirements. Note that both `maxCommittedGasLimit` and - `authorizedOperator` must reflect the values specified in previous steps, during + `authorizedOperator` must reflect the values you'll specify in later steps, during the configuration of the sidecar. `pubkeys` should be configured with all of the validator public keys that you wish to register. @@ -227,8 +227,8 @@ Key features include: 3. Integration with Eigenlayer Specific functionalities about the restaking protocols are handled inside the -`IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and -`BoltEigenlayerMiddleware`. +`IBoltMiddleware` contracts, such as [`BoltSymbioticMiddleware`](../../bolt-contracts/src/contracts/BoltSymbioticMiddlewareV2.sol) and +[`BoltEigenlayerMiddleware`](../../bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol). ## Operator Registration @@ -238,7 +238,7 @@ signing commitments. Note that in Bolt, there is no real separation between validators and an operator. An operator is only real in the sense that its private key will be used to sign commitments on the corresponding validators' sidecars. However, we need a way to logically connect validators to an on-chain -address associated with some stake, which is what the operator is. +address associated with some stake, which is what the operator abstraction takes care of. **In the next sections we assume you have saved the private key corresponding to the operator address in `$OPERATOR_SK`.** This private key will be read by the @@ -271,7 +271,8 @@ The opt-in process requires the following steps: 3. opt-in to any vault using the [`opt-in-vault`](https://docs.symbiotic.fi/guides/cli/#opt-in-vault) command; 4. deposit collateral into the vault using the - [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. + [`deposit`](https://docs.symbiotic.fi/guides/cli/#deposit) command. For this deployment, + you have to deposit `1 ether` of the collateral token. **Internal Steps** @@ -401,9 +402,9 @@ forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ ``` # Off-Chain Setup +After all of the steps above have been completed, we can proceed with running the off-chain infrastructure. -There are various way to run the Bolt Sidecar depending on what infrastructure -you want to use and your preferred signing methods: +There are various way to run the Bolt Sidecar depending on your preferences and your preferred signing methods: - Docker mode (recommended) - [Commit-Boost](https://commit-boost.github.io/commit-boost-client) mode From c140a6e874c59619b21a09180aa0d2ba46233482 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 29 Oct 2024 10:34:32 +0100 Subject: [PATCH 217/272] fix(holesky/docs): toc --- testnets/holesky/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 8ce9b8018..476329d6e 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -1,12 +1,19 @@ # Holesky Launch Instructions -This document provides instructions for running the Bolt sidecar on the Holesky testnet. +This document provides instructions for running the Bolt sidecar on the Holesky testnet. # Table of Contents * [Prerequisites](#prerequisites) +* [On-Chain Registration](#on-chain-registration) + * [Validator Registration](#validator-registration) + * [Registration Steps](#registration-steps) + * [Bolt Network Entrypoint](#bolt-network-entrypoint) + * [Operator Registration](#operator-registration) + * [Symbiotic Registration Steps](#symbiotic-registration-steps) + * [EigenLayer Registration Steps](#eigenlayer-registration-steps) * [Off-Chain Setup](#off-chain-setup) * [Docker Mode (recommended)](#docker-mode-recommended) * [Commit-Boost Mode](#commit-boost-mode) @@ -15,13 +22,6 @@ This document provides instructions for running the Bolt sidecar on the Holesky * [Building and running the Bolt sidecar binary](#building-and-running-the-bolt-sidecar-binary) * [Configuration file](#configuration-file) * [Observability](#observability) -* [On-Chain Registration](#on-chain-registration) - * [Validator Registration](#validator-registration) - * [Registration Steps](#registration-steps) - * [Bolt Network Entrypoint](#bolt-network-entrypoint) - * [Operator Registration](#operator-registration) - * [Symbiotic Registration Steps](#symbiotic-registration-steps) - * [EigenLayer Registration Steps](#eigenlayer-registration-steps) * [Reference](#reference) * [Command-line options](#command-line-options) * [Delegations and signing options for Native and Docker Compose Mode](#delegations-and-signing-options-for-native-and-docker-compose-mode) @@ -402,6 +402,7 @@ forge script script/holesky/operators/RegisterEigenLayerOperator.s.sol \ ``` # Off-Chain Setup + After all of the steps above have been completed, we can proceed with running the off-chain infrastructure. There are various way to run the Bolt Sidecar depending on your preferences and your preferred signing methods: From 5a2641131868e783ed984ecb10fb97dd25d154bb Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:48:09 +0100 Subject: [PATCH 218/272] wip(registry): new storage layout with indexes --- bolt-cli/README.md | 2 +- .../src/contracts/BoltValidatorsV2.sol | 383 ++ .../src/interfaces/IBoltValidatorsV2.sol | 74 + bolt-contracts/test/BoltValidators.t.sol | 87 + bolt-contracts/test/BoltValidatorsV2.t.sol | 123 + .../test/testdata/validator_pubkeys.json | 5007 +++++++++++++++++ 6 files changed, 5675 insertions(+), 1 deletion(-) create mode 100644 bolt-contracts/src/contracts/BoltValidatorsV2.sol create mode 100644 bolt-contracts/src/interfaces/IBoltValidatorsV2.sol create mode 100644 bolt-contracts/test/BoltValidatorsV2.t.sol create mode 100644 bolt-contracts/test/testdata/validator_pubkeys.json diff --git a/bolt-cli/README.md b/bolt-cli/README.md index 00d51a8c4..ba6a84aa0 100644 --- a/bolt-cli/README.md +++ b/bolt-cli/README.md @@ -7,7 +7,7 @@ The Bolt CLI is a collection of command-line tools for interacting with Bolt pro Prerequisites: - [Rust toolchain][rust] -- [Protoc][protoc] +- [Protoc][protoc] (as well as `libprotobuf-dev` for some Linux distributions) Once you have the necessary prerequisites, you can build the binary in the following way: diff --git a/bolt-contracts/src/contracts/BoltValidatorsV2.sol b/bolt-contracts/src/contracts/BoltValidatorsV2.sol new file mode 100644 index 000000000..326894357 --- /dev/null +++ b/bolt-contracts/src/contracts/BoltValidatorsV2.sol @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; + +import {BLS12381} from "../lib/bls/BLS12381.sol"; +import {BLSSignatureVerifier} from "../lib/bls/BLSSignatureVerifier.sol"; +import {IBoltValidatorsV2} from "../interfaces/IBoltValidatorsV2.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; + +/// @title Bolt Validators +/// @notice This contract is responsible for registering validators and managing their configuration +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpgradeable, UUPSUpgradeable { + using BLS12381 for BLS12381.G1Point; + + // ========= STORAGE ========= + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice Validators (aka Blockspace providers) + /// @dev Validators are blockspace providers for commitments. + /// + /// Validators in this mapping are identified by their sequence number. + /// The sequence number is an incremental index assigned to each validator + /// in the registry and is guaranteed to be unique. + mapping(uint32 => _Validator) internal VALIDATORS; + + /// @notice Mapping of BLS public key hash to the sequence number of the validator + /// in the VALIDATORS mapping. This mapping is used to quickly lookup a validator + /// by its BLS public key hash without iterating over the VALIDATORS mapping. + mapping(bytes20 => uint32) internal validatorPubkeyHashToSequenceNumber; + + /// @notice Sequence number of the next validator to be registered + uint32 internal nextValidatorSequenceNumber; + + /// @notice Mapping of controller index to its address. A controller map is + /// used to identify entities with a 32-bit index instead of their full address. + mapping(uint32 => address) internal controllerIndexToAddress; + mapping(address => uint32) internal controllerAddressToIndex; + uint32 internal nextControllerIndex; + + /// @notice Mapping of authorized operator index to its address. An authorized operator map is + /// used to identify entities with a 32-bit index instead of their full address. + mapping(uint32 => address) internal authorizedOperatorIndexToAddress; + mapping(address => uint32) internal authorizedOperatorToIndex; + uint32 internal nextAuthorizedOperatorIndex; + + // TODO: adjust + // --> Storage layout marker: 4 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[46] private __gap; + + // ========= EVENTS ========= + + /// @notice Emitted when a validator is registered + /// @param pubkeyHash BLS public key hash of the validator + event ValidatorRegistered(bytes32 indexed pubkeyHash, uint32 indexed sequenceNumber); + + // ========= INITIALIZER ========= + + /// @notice Initializer + /// @param _owner Address of the owner of the contract + /// @param _parameters Address of the Bolt Parameters contract + function initialize(address _owner, address _parameters) public initializer { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + } + + function initializeV2(address _owner, address _parameters) public reinitializer(2) { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + /// @notice Get all validators + /// @dev This function should be used with caution as it can return a large amount of data. + /// @return Validator[] memory Array of validator structs + function getAllValidators() public view returns (ValidatorInfo[] memory) { + uint32 validatorCount = nextValidatorSequenceNumber; + ValidatorInfo[] memory validators = new ValidatorInfo[](validatorCount); + for (uint32 i = 0; i < validatorCount; i++) { + validators[i] = _getValidatorInfo(VALIDATORS[i]); + } + return validators; + } + + /// @notice Get a validator by its BLS public key + /// @param pubkey BLS public key of the validator + /// @return Validator memory Validator struct + function getValidatorByPubkey( + BLS12381.G1Point calldata pubkey + ) public view returns (ValidatorInfo memory) { + return getValidatorByPubkeyHash(hashPubkey(pubkey)); + } + + /// @notice Get a validator by its BLS public key hash + /// @param pubkeyHash BLS public key hash of the validator + /// @return Validator memory Validator struct + function getValidatorByPubkeyHash( + bytes20 pubkeyHash + ) public view returns (ValidatorInfo memory) { + uint32 sequenceNumber = validatorPubkeyHashToSequenceNumber[pubkeyHash]; + _Validator memory _val = VALIDATORS[sequenceNumber]; + if (_val.pubkeyHash == bytes20(0)) { + revert ValidatorDoesNotExist(); + } + return _getValidatorInfo(_val); + } + + /// @notice Get a validator by its sequence number + /// @param sequenceNumber Sequence number of the validator + /// @return Validator memory Validator struct + function getValidatorBySequenceNumber( + uint32 sequenceNumber + ) public view returns (ValidatorInfo memory) { + _Validator memory _val = VALIDATORS[sequenceNumber]; + if (_val.pubkeyHash == bytes20(0)) { + revert ValidatorDoesNotExist(); + } + return _getValidatorInfo(_val); + } + + // ========= REGISTRATION LOGIC ========= + + /// @notice Register a single Validator and authorize a Collateral Provider and Operator for it + /// @dev This function allows anyone to register a single Validator. We do not perform any checks. + /// @param pubkeyHash BLS public key hash for the Validator to be registered + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function registerValidatorUnsafe( + bytes20 pubkeyHash, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) public { + if (!parameters.ALLOW_UNSAFE_REGISTRATION()) { + revert UnsafeRegistrationNotAllowed(); + } + + _registerValidator(pubkeyHash, authorizedOperator, maxCommittedGasLimit); + } + + /// @notice Register a single Validator and authorize an Operator for it. + /// @dev This function allows anyone to register a single Validator. We perform an important check: + /// The owner of the Validator (controller) must have signed the message with its BLS private key. + /// + /// Message format: `chainId || controller || sequenceNumber` + /// @param pubkey BLS public key for the Validator to be registered + /// @param signature BLS signature of the registration message for the Validator + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function registerValidator( + BLS12381.G1Point calldata pubkey, + BLS12381.G2Point calldata signature, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) public { + bytes memory message = abi.encodePacked(block.chainid, msg.sender, nextValidatorSequenceNumber); + if (!_verifySignature(message, signature, pubkey)) { + revert InvalidBLSSignature(); + } + + _registerValidator(hashPubkey(pubkey), authorizedOperator, maxCommittedGasLimit); + } + + /// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them + /// @dev This function allows anyone to register a list of Validators. + /// @param pubkeys List of BLS public keys for the Validators to be registered + /// @param signature BLS aggregated signature of the registration message for this batch of Validators + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function batchRegisterValidators( + BLS12381.G1Point[] calldata pubkeys, + BLS12381.G2Point calldata signature, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) public { + uint32[] memory expectedValidatorSequenceNumbers = new uint32[](pubkeys.length); + for (uint32 i = 0; i < pubkeys.length; i++) { + expectedValidatorSequenceNumbers[i] = nextValidatorSequenceNumber + i; + } + + // Reconstruct the unique message for which we expect an aggregated signature. + // We need the msg.sender to prevent a front-running attack by an EOA that may + // try to register the same validators + bytes memory message = abi.encodePacked(block.chainid, msg.sender, expectedValidatorSequenceNumbers); + + // Aggregate the pubkeys into a single pubkey to verify the aggregated signature once + BLS12381.G1Point memory aggPubkey = _aggregatePubkeys(pubkeys); + + if (!_verifySignature(message, signature, aggPubkey)) { + revert InvalidBLSSignature(); + } + + bytes20[] memory pubkeyHashes = new bytes20[](pubkeys.length); + for (uint256 i = 0; i < pubkeys.length; i++) { + pubkeyHashes[i] = hashPubkey(pubkeys[i]); + } + + _batchRegisterValidators(pubkeyHashes, authorizedOperator, maxCommittedGasLimit); + } + + /// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them + /// @dev This function allows anyone to register a list of Validators. + /// @param pubkeyHashes List of BLS public key hashes for the Validators to be registered + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function batchRegisterValidatorsUnsafe( + bytes20[] calldata pubkeyHashes, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) public { + if (!parameters.ALLOW_UNSAFE_REGISTRATION()) { + revert UnsafeRegistrationNotAllowed(); + } + + _batchRegisterValidators(pubkeyHashes, authorizedOperator, maxCommittedGasLimit); + } + + // ========= UPDATE FUNCTIONS ========= + + /// @notice Update the maximum gas limit that a validator can commit for preconfirmations + /// @dev Only the `controller` of the validator can update this value. + /// @param pubkeyHash The hash of the BLS public key of the validator + /// @param maxCommittedGasLimit The new maximum gas limit + function updateMaxCommittedGasLimit(bytes20 pubkeyHash, uint32 maxCommittedGasLimit) public { + uint32 sequenceNumber = validatorPubkeyHashToSequenceNumber[pubkeyHash]; + _Validator storage _val = VALIDATORS[sequenceNumber]; + + if (_val.pubkeyHash == bytes20(0)) { + revert ValidatorDoesNotExist(); + } + + address controller = controllerIndexToAddress[_val.controllerIndex]; + if (msg.sender != controller) { + revert UnauthorizedCaller(); + } + + _val.maxCommittedGasLimit = maxCommittedGasLimit; + } + + // ========= HELPERS ========= + + function _registerValidator(bytes20 pubkeyHash, address authorizedOperator, uint32 maxCommittedGasLimit) internal { + if (authorizedOperator == address(0)) { + revert InvalidAuthorizedOperator(); + } + if (pubkeyHash == bytes20(0)) { + revert InvalidPubkey(); + } + if (validatorPubkeyHashToSequenceNumber[pubkeyHash] != 0) { + revert ValidatorAlreadyExists(); + } + + VALIDATORS[nextValidatorSequenceNumber] = _Validator({ + pubkeyHash: pubkeyHash, + maxCommittedGasLimit: maxCommittedGasLimit, + authorizedOperatorIndex: _getOrCreateAuthorizedOperatorIndex(authorizedOperator), + controllerIndex: _getOrCreateControllerIndex(msg.sender) + }); + emit ValidatorRegistered(pubkeyHash, nextValidatorSequenceNumber); + + validatorPubkeyHashToSequenceNumber[pubkeyHash] = nextValidatorSequenceNumber; + nextValidatorSequenceNumber += 1; + } + + function _batchRegisterValidators( + bytes20[] memory pubkeyHashes, + address authorizedOperator, + uint32 maxCommittedGasLimit + ) internal { + if (authorizedOperator == address(0)) { + revert InvalidAuthorizedOperator(); + } + + uint32 authorizedOperatorIndex = _getOrCreateAuthorizedOperatorIndex(authorizedOperator); + uint32 controllerIndex = _getOrCreateControllerIndex(msg.sender); + uint256 pubkeysLength = pubkeyHashes.length; + + for (uint32 i; i < pubkeysLength; i++) { + bytes20 pubkeyHash = pubkeyHashes[i]; + uint32 sequenceNumber = nextValidatorSequenceNumber + i; + + if (pubkeyHash == bytes20(0)) { + revert InvalidPubkey(); + } + if (validatorPubkeyHashToSequenceNumber[pubkeyHash] != 0) { + revert ValidatorAlreadyExists(); + } + + VALIDATORS[sequenceNumber] = _Validator({ + pubkeyHash: pubkeyHash, + maxCommittedGasLimit: maxCommittedGasLimit, + authorizedOperatorIndex: authorizedOperatorIndex, + controllerIndex: controllerIndex + }); + emit ValidatorRegistered(pubkeyHash, sequenceNumber); + + validatorPubkeyHashToSequenceNumber[pubkeyHash] = sequenceNumber; + } + + nextValidatorSequenceNumber += uint32(pubkeysLength); + } + + /// @notice Internal helper to get the index of a new or existing operator by its address. + /// @param authorizedOperator Address of the operator + /// @return Index of the operator + function _getOrCreateAuthorizedOperatorIndex( + address authorizedOperator + ) internal returns (uint32) { + uint32 authorizedOperatorIndex = authorizedOperatorToIndex[authorizedOperator]; + if (authorizedOperatorIndex == 0) { + authorizedOperatorIndex = nextAuthorizedOperatorIndex; + authorizedOperatorToIndex[authorizedOperator] = authorizedOperatorIndex; + authorizedOperatorIndexToAddress[authorizedOperatorIndex] = authorizedOperator; + nextAuthorizedOperatorIndex += 1; + } + + return authorizedOperatorIndex; + } + + /// @notice Internal helper to get the index of a new or existing controller by its address. + /// @param controller Address of the controller + /// @return Index of the controller + function _getOrCreateControllerIndex( + address controller + ) internal returns (uint32) { + uint32 controllerIndex = controllerAddressToIndex[controller]; + if (controllerIndex == 0) { + controllerIndex = nextControllerIndex; + controllerAddressToIndex[controller] = controllerIndex; + controllerIndexToAddress[controllerIndex] = controller; + nextControllerIndex += 1; + } + + return controllerIndex; + } + + /// @notice Internal helper to get the ValidatorInfo struct from a _Validator struct + /// @param _val Validator struct + /// @return ValidatorInfo struct + function _getValidatorInfo(_Validator memory _val) internal view returns (ValidatorInfo memory) { + return ValidatorInfo({ + pubkeyHash: _val.pubkeyHash, + maxCommittedGasLimit: _val.maxCommittedGasLimit, + authorizedOperator: authorizedOperatorIndexToAddress[_val.authorizedOperatorIndex], + controller: controllerIndexToAddress[_val.controllerIndex] + }); + } + + /// @notice Compute the hash of a BLS public key + /// @param pubkey Decompressed BLS public key + /// @return Hash of the public key in compressed form + function hashPubkey( + BLS12381.G1Point memory pubkey + ) public pure returns (bytes20) { + uint256[2] memory compressedPubKey = pubkey.compress(); + bytes32 fullHash = keccak256(abi.encodePacked(compressedPubKey)); + // take the leftmost 20 bytes of the keccak256 hash + return bytes20(uint160(uint256(fullHash))); + } +} diff --git a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol new file mode 100644 index 000000000..bd485840d --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {BLS12381} from "../lib/bls/BLS12381.sol"; + +interface IBoltValidatorsV2 { + struct ValidatorInfo { + bytes20 pubkeyHash; + uint32 maxCommittedGasLimit; + address authorizedOperator; + address controller; + } + + struct _Validator { + bytes20 pubkeyHash; + uint32 maxCommittedGasLimit; + uint32 controllerIndex; + uint32 authorizedOperatorIndex; + } + + error InvalidBLSSignature(); + error InvalidAuthorizedOperator(); + error ValidatorAlreadyExists(); + error ValidatorDoesNotExist(); + error UnsafeRegistrationNotAllowed(); + error UnauthorizedCaller(); + error InvalidPubkey(); + + function getAllValidators() external view returns (ValidatorInfo[] memory); + + function getValidatorByPubkey( + BLS12381.G1Point calldata pubkey + ) external view returns (ValidatorInfo memory); + + function getValidatorByPubkeyHash( + bytes20 pubkeyHash + ) external view returns (ValidatorInfo memory); + + function getValidatorBySequenceNumber( + uint32 sequenceNumber + ) external view returns (ValidatorInfo memory); + + function registerValidatorUnsafe( + bytes20 pubkeyHash, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function registerValidator( + BLS12381.G1Point calldata pubkey, + BLS12381.G2Point calldata signature, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function batchRegisterValidators( + BLS12381.G1Point[] calldata pubkeys, + BLS12381.G2Point calldata signature, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function batchRegisterValidatorsUnsafe( + bytes20[] calldata pubkeyHashes, + uint32 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function updateMaxCommittedGasLimit(bytes20 pubkeyHash, uint32 maxCommittedGasLimit) external; + + function hashPubkey( + BLS12381.G1Point calldata pubkey + ) external pure returns (bytes20); +} diff --git a/bolt-contracts/test/BoltValidators.t.sol b/bolt-contracts/test/BoltValidators.t.sol index b8b169090..17f5187e7 100644 --- a/bolt-contracts/test/BoltValidators.t.sol +++ b/bolt-contracts/test/BoltValidators.t.sol @@ -24,6 +24,7 @@ contract BoltValidatorsTest is Test { address validator = makeAddr("validator"); function setUp() public { + vm.pauseGasMetering(); BoltConfig.Parameters memory config = new Utils().readParameters(); parameters = new BoltParametersV1(); @@ -49,8 +50,10 @@ contract BoltValidatorsTest is Test { // pubkeys aren't checked, any point will be fine BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + vm.resumeGasMetering(); vm.prank(validator); validators.registerValidatorUnsafe(pubkey, 1_000_000, operator); + vm.pauseGasMetering(); BoltValidatorsV1.Validator memory registered = validators.getValidatorByPubkey(pubkey); assertEq(registered.exists, true); @@ -63,7 +66,9 @@ contract BoltValidatorsTest is Test { BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); vm.prank(validator); + vm.resumeGasMetering(); validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + vm.pauseGasMetering(); vm.prank(validator); vm.expectRevert(IBoltValidatorsV1.ValidatorAlreadyExists.selector); @@ -78,14 +83,96 @@ contract BoltValidatorsTest is Test { vm.prank(validator); vm.expectRevert(IBoltValidatorsV1.UnsafeRegistrationNotAllowed.selector); + vm.resumeGasMetering(); validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + vm.pauseGasMetering(); } function testUnsafeRegistrationInvalidOperator() public { BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); vm.prank(validator); + vm.resumeGasMetering(); vm.expectRevert(IBoltValidatorsV1.InvalidAuthorizedOperator.selector); validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, address(0)); + vm.pauseGasMetering(); + } + + function testUnsafeBatchRegistrationGasUsage() public { + BLS12381.G1Point[] memory pubkeys = _readPubkeysFromFile(290); + + vm.prank(validator); + vm.resumeGasMetering(); + validators.batchRegisterValidatorsUnsafe(pubkeys, PRECONF_MAX_GAS_LIMIT, operator); + vm.pauseGasMetering(); + + for (uint256 i = 0; i < pubkeys.length; i++) { + BoltValidatorsV1.Validator memory registered = validators.getValidatorByPubkey(pubkeys[i]); + assertEq(registered.exists, true); + assertEq(registered.maxCommittedGasLimit, PRECONF_MAX_GAS_LIMIT); + assertEq(registered.authorizedOperator, operator); + assertEq(registered.controller, validator); + } + } + + /// @notice Read validator pubkeys from a file and convert them to G1 points + /// @param amount The number of pubkeys to read (capped to the number of pubkeys in the file) + function _readPubkeysFromFile( + uint256 amount + ) internal returns (BLS12381.G1Point[] memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/testdata/validator_pubkeys.json"); + string memory json = vm.readFile(path); + + string[] memory pubkeysRaw = vm.parseJsonStringArray(json, ".pubkeys"); + + if (amount > pubkeysRaw.length) { + revert("Amount exceeds the number of pubkeys in the file"); + } + + BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](amount); + + for (uint256 i = 0; i < amount; i++) { + string memory pubkey = pubkeysRaw[i]; + + string[] memory convertCmd = new string[](2); + convertCmd[0] = string.concat(root, "/script/pubkey_to_g1_wrapper.sh"); + convertCmd[1] = pubkey; + + bytes memory output = vm.ffi(convertCmd); + string memory outputStr = string(output); + string[] memory array = vm.split(outputStr, ","); + + uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); + uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); + + pubkeys[i] = BLS12381.G1Point(x, y); + } + + return pubkeys; + } + + function _bytesToParts( + bytes memory data + ) public pure returns (uint256[2] memory out) { + require(data.length == 48, "Invalid data length"); + + uint256 value1; + uint256 value2; + + // Load the first 32 bytes into value1 + assembly { + value1 := mload(add(data, 32)) + } + value1 = value1 >> 128; // Clear unwanted upper bits + + // Load the next 16 bytes into value2 + assembly { + value2 := mload(add(data, 48)) + } + // value2 = value2 >> 128; + + out[0] = value1; + out[1] = value2; } } diff --git a/bolt-contracts/test/BoltValidatorsV2.t.sol b/bolt-contracts/test/BoltValidatorsV2.t.sol new file mode 100644 index 000000000..062605a40 --- /dev/null +++ b/bolt-contracts/test/BoltValidatorsV2.t.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; + +import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; +import {BoltValidatorsV2} from "../src/contracts/BoltValidatorsV2.sol"; +import {IBoltValidatorsV2} from "../src/interfaces/IBoltValidatorsV2.sol"; +import {BLS12381} from "../src/lib/bls/BLS12381.sol"; +import {BoltConfig} from "../src/lib/Config.sol"; +import {Utils} from "./Utils.sol"; + +contract BoltValidatorsV2Test is Test { + using BLS12381 for BLS12381.G1Point; + + BoltParametersV1 public parameters; + BoltValidatorsV2 public validators; + + uint32 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; + + address admin = makeAddr("admin"); + address provider = makeAddr("provider"); + address operator = makeAddr("operator"); + address validator = makeAddr("validator"); + + function setUp() public { + vm.pauseGasMetering(); + BoltConfig.Parameters memory config = new Utils().readParameters(); + + parameters = new BoltParametersV1(); + parameters.initialize( + admin, + config.epochDuration, + config.slashingWindow, + config.maxChallengeDuration, + config.allowUnsafeRegistration, + config.challengeBond, + config.blockhashEvmLookback, + config.justificationDelay, + config.eth2GenesisTimestamp, + config.slotTime, + config.minimumOperatorStake + ); + + validators = new BoltValidatorsV2(); + validators.initialize(admin, address(parameters)); + } + + function testUnsafeBatchRegistrationV2GasUsage() public { + BLS12381.G1Point[] memory pubkeys = _readPubkeysFromFile(620); + + bytes20[] memory pubkeyHashes = new bytes20[](pubkeys.length); + for (uint256 i = 0; i < pubkeys.length; i++) { + pubkeyHashes[i] = validators.hashPubkey(pubkeys[i]); + } + + vm.prank(validator); + vm.resumeGasMetering(); + validators.batchRegisterValidatorsUnsafe(pubkeyHashes, PRECONF_MAX_GAS_LIMIT, operator); + vm.pauseGasMetering(); + } + + /// @notice Read validator pubkeys from a file and convert them to G1 points + /// @param amount The number of pubkeys to read (capped to the number of pubkeys in the file) + function _readPubkeysFromFile( + uint256 amount + ) internal returns (BLS12381.G1Point[] memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/testdata/validator_pubkeys.json"); + string memory json = vm.readFile(path); + + string[] memory pubkeysRaw = vm.parseJsonStringArray(json, ".pubkeys"); + + if (amount > pubkeysRaw.length) { + revert("Amount exceeds the number of pubkeys in the file"); + } + + BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](amount); + + for (uint256 i = 0; i < amount; i++) { + string memory pubkey = pubkeysRaw[i]; + + string[] memory convertCmd = new string[](2); + convertCmd[0] = string.concat(root, "/script/pubkey_to_g1_wrapper.sh"); + convertCmd[1] = pubkey; + + bytes memory output = vm.ffi(convertCmd); + string memory outputStr = string(output); + string[] memory array = vm.split(outputStr, ","); + + uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); + uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); + + pubkeys[i] = BLS12381.G1Point(x, y); + } + + return pubkeys; + } + + function _bytesToParts( + bytes memory data + ) public pure returns (uint256[2] memory out) { + require(data.length == 48, "Invalid data length"); + + uint256 value1; + uint256 value2; + + // Load the first 32 bytes into value1 + assembly { + value1 := mload(add(data, 32)) + } + value1 = value1 >> 128; // Clear unwanted upper bits + + // Load the next 16 bytes into value2 + assembly { + value2 := mload(add(data, 48)) + } + // value2 = value2 >> 128; + + out[0] = value1; + out[1] = value2; + } +} diff --git a/bolt-contracts/test/testdata/validator_pubkeys.json b/bolt-contracts/test/testdata/validator_pubkeys.json new file mode 100644 index 000000000..36449c0ce --- /dev/null +++ b/bolt-contracts/test/testdata/validator_pubkeys.json @@ -0,0 +1,5007 @@ +{ + "pubkeys": [ + "0x92bdf63b34163312fcd1b39f73d95763bd750bf92d1876fe8bd84d8a7e887e5347200ffde7d101cd2e4e51402054b0a7", + "0xab12921dc0681874bfc52266fbe74e2354082224cc87c3458a4976b744cde4294afbdad471854e0756191cea4e84fe02", + "0xadcd4698880d9f1701205aa37286ff77a34d96a44efadac4ab1edfbe9c05bbe5aef8abb6b5eb8a8426014838b1abe03a", + "0xaceab8cceb575de80d0ab9c2a98f893e7c4302b827cfb6f6ee89c17317f90665f10ccc0ed221d33327dffbd2dc3c3e35", + "0x9034592e614b14e31f1a10b9a56a941bcb4a6a693db07d774a85cb3ced3080b2578e7e3971e6d9213c727891434e8f72", + "0xb5bb5a921dab5c79e4f18685477e94fdd1130dd0634cbc6404978d4db7ba64170326bfe6ae8298d4dba482913befeebc", + "0xb600a3562441fadf3549c5c831c8e626b2b5021900c45b93e193aa27c64c5c6226bb424beb4ee44c7272ab1788787ea2", + "0xa7f0c41336a4f7a8e0f1990df1f2798a48b616c5d2e05c54af134a57875e5c32368d43f243bded41b306649ed25271ba", + "0xa65b9ffa89d5d6ba54e5f43975540e429fea3c25bcb84c6600d718c9ad5e0a12622383a0f6fc38255413b1017b4610f9", + "0x8b8a97d8a8181293c0430eb336b3a063fb747bd0b8118aaae3f22fd8f1121fb63ac0131a5db02fdeb76a4f6e8880c02f", + "0xb41fe265b0e2dd5d1601ddacc901ee73a21b5a4ea0d0650adaea9cf0e9d6d5ebba676f1f78e418005d00b8e78e6b941f", + "0xa702c4607ee3656bc144790a9c98a0334a84d1b033a76d063b0f955457421bdc7f21463521b085951b1ff255a755ea0d", + "0x878c2399bac2637a2cd1d76316f77fe5781c22044ff15d7b5d7bcacae2f841997421b39cefb3b10152d9e022463db4dd", + "0xa5793fe05b793384266392570451ef3224eb1bbc1dca1ed1c0a62c9716aeda396e2f088370d1519a5ed937217222a65b", + "0xb93e8a755f1f6f5e572d1eb03d4b8e191fbf4f6e1ed6ee9cbe1985850f2b671ee7da5aa88f293a855da06b4744287d01", + "0xaf941da572d975f3230f004d0ed5e677a02ca7d8bccbbdade608ae67198fb0e8caba296d61e888dc80ff752d7e41b3d9", + "0x980dc2e05ea3f06d86ebab66447d095543418f69261286fe2c8051ebcd24955937ca532cd7b0ecb10276b3f21d77b57e", + "0xa6d8001022cab3755a5611a5bda702fdad06e2e06a4456cf6f8fae9bdcb6e91aa120ad84e2fd515e4efc6250529c52a1", + "0xb7d9950de1e90465a7c460f3b693cce9ba1ed9c1a3444529ada70abffab149ef3ac00740a374ebb9d8aaf73d0f65e9b6", + "0xa979b97d1c1bdd390e04beda7d5dce53c981b9133140662e474db5863a001970474389acd3a3a400fdc173bfbe1c7750", + "0xa95eae9e547b269fd7ee72d16df80d1bd87ada5ab567676bcb4bafe23f377c86501a2ca37e9a5ee8e17a25bea36ce1a5", + "0x84f845281651ecd692d2cca67d0acd11b4364b54f5f4e0cffa8c4ad75b1fdc03db0799976c6cfddef692cd5f33883bf5", + "0x855f14a3eb76c0a4cec39e6105bed8abbcac92ecd989245d6f585108c8e5ebb7a34bb482dc25ecdd6591d9a590780a67", + "0xb3d2d7666daeb97ce18c2b13ef061dc8e20222f11bee17a744e46cd6674f94c1a3633619dd2a94667c185c33c913454a", + "0x90456b397ec7608cd9fc3a32191854a9447136aa7ad614f004159b61f1c122e22d3729304fa744a1d877ea439e7ef865", + "0x88dcfc2daa785111efb0d1f69424001b25a8aed9a8c0407d2a0dec077b56ee91dd512742eea0d7f1afeb473c5474d0c0", + "0x998d742aa1d4167dfd4f789699161acb8f4405648aa031caf32cb70122088d95db88029e8b24a16811efef93140cf4b0", + "0xa7d50d91831a8002718409c099931d6c83abd3a220d9ad19b5d8783332052356015f8b0119a09b9ba2f5ce50bc0f0c7b", + "0xb7687323ca2b491bd3519110faaec814dfe6b60770557c26223114f03bf76cb1e9c1e82980990912b67b2c45634282c6", + "0xa4faf18f6df9fe36d959f9b18c2bb920aaf2d454a2e7a4cd592f66b5c5d095137860b02103cd4e76a8a98a1031d85bf0", + "0x8e2c722c713c48591fa83a8108caaf1a76bfaf4ad5246a16406adf1fa8ac824cc31d2a7231e6c2cb45c413b48587a193", + "0x8bd558f07733a9505dc4fa274b7e99ed9c388c6df96fea6e59a261974a5d881848ea23abac7001a69782f933544191db", + "0x95db2b5d51b46baf468afe26629f328b7e8135e281faf447d58d097ffcc8c4d79488457d81efe7b310e3ecee6da2a299", + "0x996a6c903ca8e16ce15948525a34105f7e9ed961115142b27854a036a105522c50143002ac99237fda054f42016a12ef", + "0xad677d4f73fd77205b624e38247cf612af25d628ba3e825b9e1026a20f18643946f9732b0dcdc65005010c3000faf074", + "0x85472f7149c6f28202bdc92ceeaced1d5fce2e3bbb590a23412015a47ab09a552b6308d9e5c57a6bfe1ef2ef89ee5879", + "0x97f2812c6e7fc4c343d8755a15012dc1793850a4467b250d10258e6ee87ffa28ce546a5f0839e77b630686c730dc53d0", + "0xb65fef1c1d994247dc4db5fed3bc61fad9faeeda0cefd07dcefccb30b85c3c28bb46520e7143f2909eb6b4f3c60b81d5", + "0x8492e06b639b8aa393fe6c1a2181be8a768b4ebb2bb99c794fc9d7f0ab38b509ce2b7f7b47d4e2e840c361d8781a311e", + "0x95abd10830ec4a1e22e205d0ea1efebd8829ac145f35d34f3ff5a962e8ed975229f16d697896ebc614b41b1d9839d658", + "0xacb442c1e569b5a0024a1fd73b455ec89d78c18268d07ceb85af485eaf5a06bda33cc36f91b29d3d2f6a648920ca29a6", + "0x95e8d1b54a178b2b551d95c0a3071f43fd3a6fd214bf82b100e39d2fde3f452010e6f8dc7ccad7d9180405130a98edb2", + "0xb711c3def43664a6c0ef292dee3611148eb4f20bea462b6e31cd9b26234fe2453a6778f049e97b811a65d84601eac18d", + "0x945960ac8fbd282badc93a1ffc7a2b037e934b82cddceabce7767c19a54aa00a17dfc80654713e393d29f034cd1ac282", + "0xa3ea0e29031d6a4a134c305dd63c5792ea098e36c295ec6e4891d8c0150feca87e5808015cf991671c8163f977e6bc9e", + "0x90ad40dfa9273a27e19d8321e1b3f2e3ce5552f40b5e859b528eef02a61f781ed16bac7c1515bea7ccf434fc78d6288c", + "0xadaa939536a3efe1da7086af8090fdb0f029762a7dc0fa07e793c4448087b9e5f15c14a74bf9999a3e3cfa64ac13ad61", + "0x8526886b8fbb71c45d8bbb5194587146ea87b33c631b9059a5118173b9c1fddfded3acaaeff423679a5038d6d95f5ef2", + "0x9759d182df70a2b2bc99b2b966d0f6bb49724dc826a9e105713b0aff788ae41953bbe1adba8f11f3cf932d0e843bc20f", + "0x8fd5ac6a76614217e365f368c217a23edceccfb0afa2fc0eac72da160f08758faabb9ee8f3235e0f1cd56849243fa4e5", + "0xa513927d2b836bc82bbbdfa95f4ec5e98b473e701491b5e8f28fb56763cb16064964eb8f1dd777c3ae3594553687ab68", + "0x92b9866f87237e70516ab42e7df9f6001c048ef7d433c3ee3d30cc2faa236d11391c556a30fed42cc23a3564cd9cc934", + "0x91369340bbd70f6af522ca04c31bd568dfd944541133a73362e711302c83e8259cbb77f832b80611a9c2a85c6272b200", + "0x997bb355c2c3182a36695c555d2892cd1d95a2e453b4170b734e233336c1127c45cfa0494cea1b34ca73da4f698cbe5c", + "0x954dd7659fc2b16a5755307871355a7037fdc157e3b0fc8ffb84e0a68fe5dad8bc558d5f94eaa288af647ae382ed3d80", + "0x8355f176feb5c44c54c30a6718dbb8de7fbec1e78511248b8db8bd1daf63d59305cfb36eeeb3c5785269e43d88258b5f", + "0x90270df9cb9c333ee4d5698a0219dbd21bbe0276d3a15f84e789078a298bc081b8dd879bce817f8ebadd4ab11c7ee5ea", + "0x93d1349b8145a97e66c14876b9ce3cb604758a7658e124c593ebfb91f99fada3f5bb4012927c3306f7a7a51309aa74b9", + "0x84d1df980e1805dca56bf4f0121927d2413661977e17f90a62027e433f4253dce049b6cd090eabcd6c2d7d7f7b054efc", + "0x85a9e44fd2afa57621068a18297f6f7c73572bcab20286462696f2bee081842f3e72027b8e9ad256e87dd9b89e13b4ba", + "0xab2efbdd5ee6a0964f2c77abe1e2b40e7022edec687c220abe086506c8bf12cc43ef7665e32462fa120cafb0873fc2fc", + "0xb66f2f9e44297ee7d03aca605f351ccf09471ad31c0ac66998489aecb55099e028dee40e142f650f08cb24962de8d516", + "0x8b4876a29c7bffc36c09f2442dc6875cc08f005bb5fdb2bdb347f5dc3efe45d9e1d1a717e15f4d81ac1b31c1966ee2ac", + "0xa1e36e59ad53e8f1b693a1c3a13dda88f83d11080a49994b9a1c8294ca01a5501c418659c56fa4086d457fc49aa99155", + "0x9588067f13a6e5096d2189036f8a5de4efd46d9f28d4ab06f8fdd219be4414eee248100320211310a4f1736fb1c7f0bc", + "0xa8ab8d3c6701c1265af69fc725744308c3b5c6e9fad667efdf46be458fbcdd6f019a38fae0b891edefecefd60a66ba73", + "0xa1a03cc00690b7e0fad863f6cad63bc303c2231e8ed1377e4c5d8be7e8ed7cc43703c0c01826de5c386ab7f0c4c6b4af", + "0x8c4e94519c32976a109abddedf370d8b626f1d29b971fd12c1c53ab6885292962986ae333268ddf1325512b092a0dcca", + "0xa96e4d635cc9860e26731f40811fa14060e2a7216654d386c2050c59e38c45f2be33a21380765da0ce52c3f0944a03be", + "0x902d99a5a34a51cb4d5d6eea0a71f578aeb5efef200beae628172119ed4c2046aedf655c0e93507cd947d5fbf8cc6f1e", + "0x96c4f327dcecd8291d7e711407e9b0a5065fb0e11e03c1222d7b080a3d439c245c33f552925d4234941d99a878e295bd", + "0xb79eeeebe4ddcec4c9f16ee2c6ebfd1c5af59afc39473a4ec43d16e1cf01607efc880b4ef6e295ddc53cf45dbe7250f3", + "0xb53006b80c74642b76b24408e834b9fd43b7ff8e63714b16550a3d027cef8594b72611c3375aff030e6fc96f2a807b78", + "0x8fde0a4edfc5ea4e0e732a34a47d489663f7b7faded6b50baff4c5768037eee74fe813fa2850da4c9abf209e8b163ad4", + "0xb56dce06ba4494fe61b8f76405d9ccfddc81947a77a666cf860a2dff062355b83ef6957ae413f51542ba430754d1e8bb", + "0xb76f0ef842849603a3d357bbfe1270e1c0e7c2fcbcb13466bc4c92b88d2bbcd0a1e29eb9319377e8fe0a84484f14a486", + "0xabdc16a5856f7a25b110e34e74ab244ddbeecf1b461c96a821161c7b01d2557c521b0d6261c2f0583bddf1cb098840f1", + "0xa54cce7ef61c94c644df7ae0516abfd558b9a2a6c9294f74fda7797567d75f17d3ea59baab196edafedb6c08af4c4f90", + "0x8575ec7920d4fe76181ae65c23830b79cff106223ac43b8fabde80cda7c28b442d55ab1b87ed04e681983ddc33d14e36", + "0xa52fa8236ed81764cc86e5d66d64a941e56973cd087d3c390d70feaa89d48c974b8a1fd7a86876862843bd2dc86936a7", + "0xaa2bdea17bc9101bbdd26c0019aa782b24716d1826334b877df3a4a6792ae8de6fddca36466cfa74314e4c9062672579", + "0xb7c3a46f556f4e7fd0023eebf551ee1e3dee05cf24ad2da9b4302e13bdbc5224b694106dae3e9f9ae75cb19a8d86b85f", + "0x955623ca9981aa78baecbd10ed8d418cdcbc7eed2d8e326af48a6de27726d1634c0533069ff6ead0b06058a11278d284", + "0x8b4a3a65a9d36613021da38229d111e8afea425f85e575672f48518f585480c886f4ab02783c9fb71de724d9e85a4f1a", + "0xa7b1d85ff9427bdad5af80c6d0ce7a168596d257e469703e4fbcbaa0c9d3e14bd45900d4a904dfd113b6e8039eff2f33", + "0x8b79d424a1f72af0a9dbefd99966c8240e4df924599ebe8c010f49fb3e2a779aaa2da25d103be51ba8820365b3bb3dc4", + "0x953f755b43299cc825f58bbaad8309eea35c661e4f39de442033edc0fedaab94e9ee2da207121fd45d8cf0f194a1ea2b", + "0x8722a13165c402d757577441023606b196b13ed305bc79e7555e6729a8f6ae50a55e1500f4ec807c2fb8e06ef77cce3a", + "0xae3addd758bacc49463afa7144b074dcbdc55155432b2cdfdce3fe9432138477216266189fa43905c4280cd6f02e8cd0", + "0xab0be8dfa71c1a8b251f0dcf21da96edf23c253467bf984e8fe39e2a41eb24ecfdae27561ae092d501baaf7afe0ecf6c", + "0xb84c9c7ba796fbb30d774a1f68f5c479dba88c4b3af15e77263ed00188180c824712857c2b21c906c7dac4661b0c234a", + "0xa83ed46ec9515ae2d94561a9ba5612d3b10b597083858c1c0a009efde6967b4fd9664a81e4f7543e7290d4386057e75c", + "0xa460d7705d2e9af164df0ea91c525dd1619f8aea9c9d0f5a300edd65502068fdaa65cad988ea586c2bbd59ee42a5fb17", + "0x8cca027d91129c32f1b45b5025f263512489a83dd707317af95ea68df2b367bc56b81b16ec1e9a5de2a2d300c6e29392", + "0x93bc4090746aaac0c078f85a63109463a611c7d59bf1fb8030089ec1fd874eb57e03aa239040713f8103cc20dc4e121c", + "0xa4479aa33d11adcbca4b0906b125bda7fd110c648cb6c8902a9ceb2e8f64a3ca28604e8c85a7ddea83fc0f3576986ae8", + "0xa476d9a57502600df63e7293e11d9ff31a9cb399903305d1d4518e8431ed861d392afc22a6d3f28046f128de6cd24c3f", + "0x924a6b08d6f66994c77f6b27c3d1cbf1984b4bee341b38f9d28811b72f6cd3ac8f975371124a0a02be8790df51c4611d", + "0xb18d8010aaf945c226aae0db5b66f9a04fa0fdbf8337b83f2504ea3ed02ded75a405b3405058f4e64e1dfdcbb5275d1e", + "0x9413e0f1176803e5ae56b20eb3596be73284b330382c0ce72626c4605a1318a6e845ea3b7006fa2cbfb9d9799fd1bcee", + "0x87cbbfe6f08a0fd424507726cfcf5b9df2b2fd6b78a65a3d7bb6db946dca3102eb8abae32847d5a9a27e414888414c26", + "0xa3d3889877bd210898c9c3854d970236de00d9aa67f168f7a2f0c7ccebef493b515fe12e2e64799ecbbf58e0fbbaf08c", + "0xb9a8bc6346188006116a3dc3b0a3314356847391511f4292ed42e004c3b811779258cded2aeaebdc86570e1a196e2561", + "0x93d5ca4e18d86aea4ae7fc559dbf885dbde3590cd96b911f32e08700a4bb78cf80460cba9b162fd7fbf2a341ce259d2f", + "0xa0b17e6202b9f9b2002f3d136d4aab72112f2b6021022a5099ac695a5867a40ab42fc0ee7633ea2de42d14142177f6e3", + "0x925cf9e5959e324f980922ea1da24b4133066937ce511e19bb9af6ad53209e8eb13128859d65760038fdc77e28c2b3d8", + "0x836ffc0d0542922fe4e66a1a74c5a33d809af0ed49a85bca007b41dcdbb736f067145c0dadb5fbf273d11bbb0a353464", + "0x97a1c4cdba7b430fb989b43f850bdf906fe8f32547f9d6e807db07eb5f8322e7244d05207ea27bbe7d00d8bab209a3cf", + "0x9305a8279c9583d9515ffc924741163e735c35a4d5695e4f1507a3f1e8d33c8be6e6fb0ff864cee227ae561fb0ce42ae", + "0x95d4940daa1d0e323a6898974b6866223709cdcac01b61eb61c0419248f295dc3fa5ae77b3242fc78bc97219574485a3", + "0x902e2802f45de91478d58df21693ae0537879927852f24f6a1d2d92c21d82ea411fbe8934ad79817c1939d108b928fbc", + "0xaca4ba8895ec01527f9618b2745efbff3cd5febf87bceaeda0468801ec64392a68719b30edd94bb6a4946a8556b47f85", + "0xb5ba91850932c4ca9d1f6d83c8082136e7c003dcb2e3141e3742a856eee31127ebf147399a2713ed643710befac264a6", + "0xa75003c78e79e1a84ae4266adc6e4823766698b8faabd7d4edc8cf7870800d17a1230abd5e3c3e29590d24378aa48dfd", + "0xa129003f33f8d03e8e6652a5ebe293fcb3fa024a8e578537120b729af8fd351eb06627d9b1dab345653442bf2eaef968", + "0x8abe47b0f24880d689df045114627fa13fa77d7eb3ec5e8cdb17e526090b7aa553b8f5af613896b7d167bf91fbc1413c", + "0x81c0334988582fd25f624fa6f51c1ee0fbfaf02eb64127f5e642e9b2dc7139c06af4d4e5aecb6f39a0ed8dec22ecc863", + "0xa558b49436745828ea3ea78af3831fbb3268f3cd7cfc5fd86fb0f501f83e71220f1ac3b7dc42528e5e6ffbf65b27e5d5", + "0x892bea7aaf4eea7e4e9c3ba0600fef7ff56707a061df9ff396d8c4debbc5e508ec90dba153cb4a99d20349adcfe11031", + "0xa7fd4da3366d34721568bcdf89a281bf210782233c4439bfeb4f1c48b57449a897dea0ad559544342d060d6f982ec119", + "0xa5962341540da2f10a458efbb1b0b27a4be99530c65e3523194d3f5ded7dbed064545ed86e539b0eb4f1a19e6a6d6929", + "0x98e8df36345bfc717d02efa50b986130e3dc419674f6a7b5badbc589bce9a21cc5a4ca07edd34a6931d6e41d382ee3c7", + "0xa3fc3de72afed22d6609b9be315db068bdb840950657ba849d6f888ffce0557b3ffa30096bdea2f903a029f65869d5d3", + "0x8de626d3d02ffe4cff59740da8bd36ec542ab899d56f894f050183fcb99bb301f2edd08f5613d483366c276e9e587fbb", + "0xb8ff2377e330e3a8fe678be147d2fb04044869f20c6641cf461d6e090cad427b999f7d80ca098498740f933b3f2a5821", + "0x8055bf08dcc580718862399ee549982e97bcd022409fa1e17a4ca864113dc0fa566211f050d83bd3432ae32f18c619ba", + "0x8114604ca66956c8d7786c9cc1c876909883811c803080d5394167e67ae5a22eda4db485ccaac1d939a6bd879b41c4d4", + "0x8619d189b7dc7d48fcf8d46c3a4369ccb3f1b203cc294b7660e580cc379e8b013ecf8b61a4b6c376559347567933f522", + "0xb14c59d55575f1245e4d92b0114406640853bf732ffbc154278d8d16b631a28ab70cfb6953b2e8dc56a757453226edb8", + "0x8daf47452ee7c95f9636bafbac8fc6f106f038e113d5c08cd8010cc265de4f1916e4b9b845e8a2ab5e2c3a1e7d4375a6", + "0x81ead7087322dc8b3c1b9fc5aec07fb4f7327aa8af615c13225d83517b649bc995629423b0dbc50a6d955e28ad65e996", + "0xa62c613adfa00df46eefb037ad202898b12427c0cf25aea3fc2cb1aadfbc5460b44e61f2de2a13b606afd999d7395d32", + "0xabb2f3a909c077930ca33b2cbd7e7207a98046c4e05d251a52ace61b6ac8aa4347d004a64c229e42a4de9beb947a6109", + "0x84d5f23cffdd55c174e480eaa10d9c4bb1fb082173d4c4c19327203c15fcbbc5e1c79b90983e6901e5149b04ce67a707", + "0x942c9422413c233db94685186abbf94a724e70867981cb149e921cc8baa222420ea8f5284fbbdae5ec2dd1924bf5b379", + "0x8d2b563c6051f0c36b9de48b602601cf8dbcf4f24fee9e981091f376c2ee4b0391c10cd4a3ea14f423446f84142bab0d", + "0x93f4310dc9494a122406dadb284a33809c07243922c60a2f14a49f7a03ac16c8d595de2e12dd1ec83716df647c305e06", + "0x8b7f86805f3bc93a57bb197b7ec69f59244f4b92df5327793bc5ec5bde8b1b56b7712d91785eb22c99cd8540724fdb20", + "0x96328e0ea2943ec7693c02b7ba7b8aad15c555901163585ac6f13d0a3e27653517085f0f654a2dd292768e35954f2b62", + "0xa4102554251982f72a6f52816c64382796f6f8f2cbed5bc331a18344b6eb9fbc3fee74993848096bc247f9eae121ad90", + "0xa3618d55106461f466864de7506c30b1cc79a4e696bd2ef8292f9120c6f99c17e1076a68158a33628d1e6b10f352c00a", + "0xacbebf66008ff0e95417345d60aaf506b31171d5bf2efd817eb90ab9271873c1433006b9c9f2deb6b31d8a154e4bacdb", + "0xa2f85cde4bccd9fe1c9acf498068aef6770dd700d34c5d5c582f2334b6cdf0869df8c569767466764beba886d64e45e6", + "0xa3d57e5c041b765cb7796300b77e9adabf2363024f19d2a3855f35643b302736a5605e7b88a875107918528c24c8cd14", + "0xa254969f32f8d2c4a58052f9350e9348d3f8a59709cd4eef1f7eebbe4481653f2ffc25ecd9df2a1ab4dedfccc56f73c6", + "0x872dda5f77e99a3146d441ea2277202f560436d5d10891f024d19c4a60f39a9a3436969589cd76e8db4de2682d02b36f", + "0x85340d569f648582c759389b9d61249dbe8d1d0b746ec7bfe5d99165c9fb83ef5916fbe75f21c49afa333d348d5b3653", + "0x8eb46432e1452a09f02e22a63a35b4cd0bc35e07aae85240360329a83b502ec21764a83a366034eeddf20a244909e7ef", + "0x8a141b908f0d6eea1cec6736ca9d7f80f482c2be9f0ecf2f081b994dbe6c9774c323309cccb26ea0bf24ad038f784764", + "0x91f2a49ebf7cfe0269582ff418b26739c9ed4508d79b924c604cf3199edcc16825f256a70aa1706172945c3de079a9e7", + "0xad1dc2df94282812b6dfbd565d0b1561d2cd0de7b4ee8c7b66599ebcafa15ad8d9b773d081bdfb27afba11b3ad7feacb", + "0xb5937f8662d9717bce5dffc76acd5778a8812551509fec631581123f655f310b789a9bd55d318731bdc4ac65de7ae920", + "0x98059e96fb235d9f1d08b229dd86b6852e4f913bc168e8d29b3c5eb3271be61f4070f492c27a8e4dc1a3af8ad3bf0c74", + "0xadbbdfbc86d1c4cb9c0f7fb5a02d824d66809cb61050b1b055ec0186d6d404c533577b37be80200742a441182dc0933c", + "0x80b1ec46053174334f6613dc4e7fd6d3de073391307425a92d02cc4caee42d6936463d833ed09037fbffb2ea42e7a380", + "0x937d4c04b382051a7bb0901cbc7a0c54b843ed73087f676714a30f6b5c51c37f4b7bdedcfb94313dcd594f12f9ec7422", + "0xac27024ebd4e300729fa96e75cc82c629c2a3ae2d42f8ccf861a3518d4137acc1bbc9c6bf8734fbdd2a8bf8174d4e13b", + "0xaede90f9ce6de1b0aee4bc82975cf0bfdab96c6578a716b7208a7fd8a344f01ade9b806aae05ef09dfc6d8fcf1469202", + "0x947a357aac1eb5dea76538b4cd9e93e4e35ac0689911ae12e1f0bbac58feb3911ce35fe0d35419cc8035b040dd5557bb", + "0xadbae02500ed9fbf211704f36fab207ad2dc7f94ebfed2da36ae75802254015e90b7738a58ded3655736a647ce68f1fd", + "0xa1d8647fa7a4487cbecf82d071b89bb1703392d495f5d8aedc2d783448592fd714222b24a1475aafb253c81e822bb727", + "0x989b64e6afe35da1607f034202346bdecb4a7a15cd20adf9e9e63790d6389adbb9c8b2bf9c29041367a8e0687a0e69b0", + "0xa02009d162d56e9e8f005cf6c09eef2081093ce061c68abeb15b9388aa20124f1a28d8468d05088f2f38ed8c70cc3918", + "0x905a15ce7d663ec1c585e3782b34e93b8751d34a29eebafbf32661be9bd3ad30d89a859c0c17e163621e62e485b183d1", + "0xb4fa7881ebbde6a2b055b44f15ad965e4ed1e9b8d3b2aecd3a84d3d6549e82c1f3944858beb534781d8b9aee49cc12ed", + "0x88bdf91f04527aa90593607ea1894eca0125ba7b60619ba636633fd67ab72a09f1af9bb7ee659f7bef320881af879b53", + "0x843284a7de8d444b3de5e471a6df356656f44add157b1fd783caa708f45ac33eadf4d6acc52e3a43f8b9dccb0fd55878", + "0x920e1251e28f0f9c687f6c6568e66d30369f4da705a0946d24b3cef2a5f19bc07d6b6f8a1d3079a4e99dd02de00b7622", + "0x99714b4a350caf643b2af8ff87243c223643b655cbc4b9bf47206d7d8f56e93f485b7b90d932b389e86bd19028f927b8", + "0xa84874a6f4b5b489f3bb8f964342146d886c1e3463a6fad82865383ab21b13c409d49f04a21eada15ad721eef6ac7a69", + "0x909239a7f928b1bd57a948f8de9a0d42e4ba8247b62110122cfe19cf941da08888a903832be064150a14b11fded883cf", + "0x9803993002b9eb0d0d5c8dcd604942106ee04647f779d5af3d27b67b801b8454d3b63e6ffb158390d8aed0639f7e0494", + "0xb9ece85855a04b36843f70cf3acd96256fb3cdacb68f23977d4c4692ad7e3fa29a013f92d665ab262cea64724c92eace", + "0x88292f027aa4406d9b7afa91357964c285e96868a6d361d8f36591a34cb0aa54cbcf46c7af02817204bdb1ae0c5dddcb", + "0x93374b8984e54bb458cee788ea40c12143c6e1846ab89985baceb216e5a1c7e0f4a14a9bdb73c356e928b349e41ffd07", + "0x870e1098eb8b472cca0fca2278c696ec9b2b09031389476b638f1e689be53b7e94aa62609fabff80bbbc5fdc542cfdcc", + "0xa8094117d75c8d5d08080bd02b6e7836feb08a0aade00929f5027f4b8c823984986e21a90ce01f0603e0d2839c359877", + "0xa8bfe40c5fda5bd0f638bb10935aaa85b904f46fb73398bd9bc7b5ab27200ffa22cfff6644e0aa5145461360170040ae", + "0x83962287c1c107ccc313c04bc64ddf988a1a0ee8b4e0e9e452fb89a8a318365316133025cb0fddceb8d6822a5e149c93", + "0x9057b000e4fa4fb0b345339e3335ddcad1df09ada4b2bb242191c69e533075b3489370a4d7d02d319cffd0e2eee040a9", + "0xafdad6c9dc828162adf520ea8b200dabad59db0853ef91afaae9ef74643a9999b0075e417d9614c51b1eb4ceb8da774e", + "0x81a4eaf5af20f062d51c2874069f9640f6dcd42fde1a68e87fdbc4fb030c4453c79eb332089598b4b6a202800aadf6bd", + "0x8e95364aed048c303082c2ddfa8f36c608e7fd8223054e2fa71bd52b173dff969ba81632f85ad63eb04760ed262dda15", + "0xb37c68969e4b75ed2eeea1e20b79210b249cf9879652f9021eff60a95b6b9938f3ae831f73b2c2e4c749afca36c24315", + "0xa51c2304908a66f8735bbb51ae3c5a4d063f3654b91b81513d630bd198e1d47aa68026e39f12484bc9aae41658c5602d", + "0x94025c469904e74b36720520f50a72147ed62ec5d01530908357a3cc415648e2c49fa0206e225084fcd43a3c23625437", + "0xa6227306be47dc4f3f28620006dc9b42dbb010c99ae06060a86c8daea9dc97c7ea6d9640a38b8cda20c2c8c89a8aa8f6", + "0x8f0a4965743e23b72e8a012720f449aa37abf0df7b0c5285a9aeb863cce3a13a0ed37af2b3e39c4d8208f2fdad78a6f3", + "0x8a0b48470009bc51a8712c32e1ec8048f1c867d5bf1eed725ece950a9ea85f9ee62427b7d2d5d122cde524917c288de4", + "0x83a42e801c1bb67e43ec900490da4b1a63043a07ea32846e2ed49ff78a4b2cb23ee3a1448169dee72c26189a05361674", + "0xb5f0714fbb77d10ea8d5fdef34837e6b2a5bcb4c46f2f633f9d88d0a82929a9129eccdba48a36846bd2cca2b763688ce", + "0x88e874a6e79f37b0155876fab03261cc4faba4d257b7b4e6ac0ce3cc25ba4123f73ad8babed6cabf4736389cb5e9b6b7", + "0xac04ecf64100609be0e9a87504064bf1171cb00fa8c3dcd374a4af5b248dc572380d601549631990b7d304a0a81b6513", + "0x9763c781026f242bee291f84324af8b5ded783430436843efe5c352a8006fa1d1e488f1b96ddf34dc4f3d6cabbe67b73", + "0xad3ce0575866fb3742c34309d3bb8618c1ca5dd8edb87e1748d0b855735064596be6d6bdbfeb92728c59d6ade228a91f", + "0x8065ef2a3d26ce9a3d8dc728c92c241c1df4c642d707b68df89d6a3bd1a85137b7cc18acb6b0be4a3291720456eba1d3", + "0xafd4ea5c196d1ba7c9516a2ea3bdafbdee7db990b9dd673d2b764e01425fbd4502e34646401e7967a3039b8981bc3142", + "0xa7bccc46695ab79f2c0b0e12c5bda26cb31e6596f95cdf470f64cc2b4b6043ef3725b7a93681b368e470ee4d9485c85d", + "0xafb94bd6098a16436c04a1a0dcd9bbfdf3511dff9307af4e4a9cad5977514aae0fb1b6e2d9096022b30b891d3e0414e1", + "0x99990b1063a93a77a99b482401882784ea47d7c00ee7362f1f8462a3f7304c0a81d5f95c45e3ea32adfc709f463a16ae", + "0xafe80883b6ce41f392b2867c52661ccafe85389ca5a0e1f5690d34562ce95f6ddeb07d02074709ddf721b3e0e3f450d9", + "0xa6fb543d59192932364c7a203dab5e0533b34d908a336d2a7f6ac125b9487db754e83e02e422aeb264266047933dbeed", + "0x858b36333dfdb48b6f9a89573ea4590fafb1fc41fe7e2bbf5e81819971b5b41244f9f27de3a25df801f79cdfdff706de", + "0x96dcb80cec87c21c1d91103809c3e2fb5cb0ba3974a8418daa6684a9160bafe2790b88d5c767f087fdfc813b545af5c0", + "0x91c7085ffb6562118dbb23c0cb766b761dbe0b33cc6d9a671d0dd641ec208fe073ef45dedcbcacd4281d2dae328a1c3f", + "0x9455c259588d02dae393ed66754c62b44a611d6aa95e7e47eff76cc507fc28adc596f76b16bab846dd8cd3fc518aea45", + "0xa5be2992a402ddee02fbbc25c821d8df326210ee92ced2ce53cf2a79c11f59ce4d208bd47760f4f9b66d632a3590f2a1", + "0x974e9bb34ef34f4dfa96ec2988f0e7883e393cfdcf201a2c0453005a2bd6a445cdde0cf7d60165f019598b204eedf62d", + "0xaed972cf9f8a9175cc8684f8bf49256cecaad923fe634cccf7f4b37953c40f130ff42b9b70da915ee9eeb963b58a18ff", + "0x802ebe23a46f1ab91458ac916a122cad18f0690cdd5caa1fc4bc5406d7235cc6237daf8e8e8d4b2f4445b285017eb273", + "0x893e9b52697cc559576a98ce1be55fb3650f9d1042b9ab02ce44a0118e306f2bff8ad745fc0fa6c3b75d699866b015c8", + "0xa5d19f108e2e2f43aed1d1c27028ab555f356e8f398ff01b721eb54dba53003297d4a77b4968f4cbe9016f3390650ceb", + "0x84df23c544c815b157c7a9cc0db48c051b799fc59e699aa5aca9c860cf3794a64ab8e7be99214ea3093d143c660d1540", + "0xb7b832d70c46a9f7af893ebd9b2bd69e7bc8721ebbeac0ba8762f4cc0d4aac85008d2d76b395f707a443de5b197bf07f", + "0xb3b979879bfe741c735cef30099f74c5d4889dea177cc1f6835cb16881de87a2b3dd1de09c27400673350186962184dc", + "0x8a0d818cd120a7c9a279e8ba72165bb4279d1a80b11d91bc450dc4069ac3ec7385342601fbb76d30a762567c59dc6924", + "0x98329836c0f2356d68a1d9cbec741e434c81423951f468e07ed0dcb1a843bf31a8726b2efc253099fa2c28c8d87a1a18", + "0x98cfdb42bcb605209c3be79b19d4668491f44ca90715634c57c6f8e0b815f35de02c3ed7216cfb1b446f093d47f1a981", + "0xb61ce585ebea5bafeece72877d7f8aae89bc0110fcbff08030e84b930ce3f567d3065af2a3c307430fbb5b8f2c7d2cf7", + "0x93e54b40766d1fd12833b41f5c14a807a9a10394d74989d124bb8ca0b8007147d5c7fbd0a1e3beeaa3188873b4c9c550", + "0x82febf2a44dfca4da1d194bacd84b05107a6030427e3c5c78da551d1844a3319dc5eee935f6edc52c609a3b6d7b73c42", + "0xb1a3ac332fa824a94b3597ae8470e125a5dc11f587990c801649f1ee453514e75415ef2a306b004d453f90b6091ad718", + "0x98ba5bb225edf1bd23c65a0d790d8e31f1940b2a5c6c516242f76ac51a953ece614c30b08cb464d8bce7a04cd6c0f754", + "0x919b94553c2c4ba73ceee6c36a80f2429c1aebe893ac58bec848d565b4c13780fc05e44957af539db1ea13976b366184", + "0xa248b267743bf12af4fe8fe9922e0704607267400ba47231e9f81e38dd207e12cb1a4ddada158a0a7f3f60e913aa824a", + "0xa423a27cc34cb532ae13854d0b0da582316ebe59d60d9ad062e342877cae4825a2ebb17fe368ab6029bf8761fc12c871", + "0x992a2e3678c179e40b5ab444d4c04bb39cc73c8665699f74f536168fee129c521dd73a3d6a470736de52affd7781a153", + "0x8cb7b9a58f28628ca05b5c577a3de2c751dde49a5de088152d9028ddd5d8f96f2dc19adc2e998a6331c7987c6751382b", + "0x98f026f3a0012d102278a87198b3daf020029c5ca18fb7468606b182b385365fe2330c83761b9e84a1ad8d8359f46b20", + "0xadcdc79992b0fa0f566749748d5338793693009b7a0314bfe6420a5af68fbd5135c762d3c03b86aa4b73c0d68671d8bf", + "0xa3e99b37d01c520e563fd29284a712246ab1d8d305711967e91e53de1c9050c2e49f95ca2aa5e4ef52e1bfbad99ebfe8", + "0x99d85fddd83d0848d450fe5e0e40a959957faaca8e4a02543c14ea947eaff7fe2c91d66259227338b1a196c69ad0d054", + "0x981a9a1b0af053be1c94b3fae69d47a261b49a5f0a620896a3c91f37d49e995a54320d0b5854bc634df90f04bd53c55c", + "0x9661c7f84434e53040f98dac9eca720b134fbff06c71552b1f68e99b564ca8510daf9129002fb32da0887498df0990e8", + "0xa8c3cc00212ed742b124dc430bdfd91bd6a85718d60fd9836882b319a9010428a0c025f9ce2883c411332e03fb748550", + "0x804db1d3d90ec02e30eddabdd5f1438b2108236998cbbaa550094293cdea0d8d0cc36f8e991f35b4da471d9001d4e9f0", + "0x978b7bece071b623d237bcb8c81a8937e60037e728fdba0e6ce162581c801cd23a1d500437533e9409c5dc96af339351", + "0xa04f099bd8b027f98d11fa75d5bb119594ff1a75fb03e6abe87fb502c0fde29d2c5f721b5a98fd9c02cb6604c28b0379", + "0xa2985357c33ba9bdc3bc974e72d45ee84df6503be2e164a4800aac9a931bb685ad16a7453023411c7e210fb7a85bdaeb", + "0xaf47c5bb3bc1f264921a3b09f8c2bf0340a38446aa5f032457f1eea5e7fa64483f5294da79316d9a24f858058ffe55a8", + "0x934966a4b4d55b11ad5992019d369d0c3899fcb30f138bb97fb5ba230c126dfbff35b749c0812a76d9252700a1acf3f6", + "0xb3de68c2c646cccf6ed82de71f3f0e70095333cf2e7222293852f9e5a0b3e8a656fe140e6e7d51fc921a4e0d75f11164", + "0x8c59f976b1e3bbb99c6f55c6bae311027e19af6143b0487b49cef5238422bd211cd209f4488aa275ec7971cff6405ffc", + "0x931355e3f0be8b4d3562f2c45a586add124387a67145c83cb10655068ad5413d1a2ce256a7c79cbb45685241f0c6090b", + "0xb94b279dfb1268372e7933ac1643f6301483cfc9cd350bcd502beae44e705be8c59658cec15373202ae9283714bebd07", + "0xb481d0d4f65bf94fb3d93a095f65bbed759de95ec3b8ee0281b30204591b54ca0586761c888204f0efb7fca95ef52a78", + "0xb3a195175223f81e471f781f2ad8f15ccfc271e617a38abc39e6701f925475b90355f12e851d0b2baa71690590ce9585", + "0xb907c511e1bd69155f98957a94cb643bc547a5973998882e297c1e970713288d8a5fcfb1362ed11cf4a7e2456a64c11f", + "0xa3fea8547bcd67a96fde87631a7c8ff98634ac6abb0fa1d25dc6eaea72f569107daad3777d99f750179fb3f7fe292e5a", + "0x867d231e5515b8e7d3c11af94dc1e9f2261108e644f1f5c18693f8456b5be1476e59a207ec78c455aba1b6f807c7115a", + "0xa9fb3ebb2698e8466af606fcec6cbc98dcea56ad98a7c2f1a1912941677a4e4244f3cbceec8ccfb831c62b092475bebf", + "0xb91bbb709e8dcff17e39af9b5db6b5d90990cb874f92b4301110f10f9b8e25ddfc44173349dda6ba81f6e2f10e355d70", + "0xa9402fa0218b4c79e0eb9a3c7e85004c7b2aaa6a4c42578d9769d6a693a64d2f753a9cf26a95f49d1da457e4d3cf0c45", + "0x80b320a6b626512e93f9af3bfa7adeacc453559771beb5028a8d188bfbed5e9960780daa6a4bf3e4201999c9c4543e72", + "0x93594c507521485303b520dd2b12015d08a7c4192350cad57b8d1e0ed66748bb20132b81a8856185c52d1925c0f9d20a", + "0xa31a0efcbf148f98995c484e224d1649ddc0039cddc2ff9cba2bb78ee636a3cf7c47e3589d217fbf99d81c74f127fb62", + "0x818df6dd2181e5400058308f469d3308c723852c1db1e37ace778e709dcfa4df7e9291e4945866cb3f8af9fae0bcada9", + "0xab543e6990ca69516e1992d5ce1aade760e19d84a903e6e8fe3a35b1da024aea625ec62cc4d922a1de0300703c40d4d4", + "0x91744106b33ae0e489c65b85d27b64f92825f6e4a8c00dc13549b46cc758af9a82cb2710d3304ca5fc305740a36ceaa9", + "0x8a849945a609602e97d3ced9e453c7a138bfbfe7f52f57e2bed4bbc071c674eb7ff97cd8317073f197c8614b855c044d", + "0xa5f9c19e3c64d4b3fa7831b05eae6328830977af0211f074be3e6f5e4b0318f3a9bee9aa28d2c0e50211e5bb9231011f", + "0x8aa4df43da5ae3516ed1f9ad6db1dd9e8f564204bd56afead0dbf703a4ca09bc079084a08a5ff7ddd0fdfa5b526b8cf3", + "0xb8f8f400bcc3978da3130e137d54653384e3c8ab8292d69dcec3849d8ff5a3494238c45549d7756fe89a59d90bd86d1d", + "0xaa40bdfa885589125bdd215fbd7766a83f94c84c108a7b052cd55cc87abedf3c9f456173b5cdb414fe5541ac918a345b", + "0xb063f5fe7674686ec1d24078f1f2b8564592328a4b10b35d23ec5f9faf7a59e49cea56b39c6de672369afa6787929e9b", + "0xb578c4ff38834021136e9521efe188564858389d19e8c359c2b2bb71e1394e917cf149c40ba4dc8e60a0e26639f51602", + "0x94d016bc61f2e6fc903439f526e27967a7b0581bd1f23973c4f6d9907d3d9812618db5206804235fe30eadb460477e39", + "0x953f374bff83578bc2e19282da07b4c56736d116ee00c25c814fdd4c4ba1b2ab63350af1e3c6a3af64c82107e56fd4de", + "0x87f47a035851bfc501c467b55a070dee187ff712fea745610d001fb07f21970671ec7d0b0cd909f916ab50b479cc28b8", + "0xa3c2f8722327a1268ae293d2e35c457b15ce27bc6cccd5fc7935a033bb1a4559ec1d25b2eab1de561f5aeb5c0133a985", + "0x861c598f0336b03d3853a5a1ff807949e1811957ae7259cffc697c8373ea9bbbc54fa35b6e691ed5e3019893de078ba3", + "0x804b738228fce804b538682b6ac7290209cb64c311414c78d983dd0cc93e6845348246df0fbc2df6fe3520fc3ac590bb", + "0x84b58dc95bd2010f800d219c9b831c1d34a9579b0d7459384e97a250eaa79e2d6396014802f50f445b7648f10d1cbd00", + "0x88f9bad2088c2714266698217c94324032c549675e7e8b4a1e285cbd5e3e51bea91e20f049c0ddc3c34f2399d1ef7835", + "0x8ab46f3dfbf0dd4e9a5ecf000ab10f7338cbebde7a7b58a2dd2b208e5b3b7bdada1f743e1bcd268f7da1a9b95a377d90", + "0xb0039f64ad4d3601bab5e7901d9f5c0e07525e83fb6510f1c9bf65e69d642f7a53c98c2099503f9f97783e34a1abf14b", + "0x86ae9b8539abf1ce5b791cbfb51e7bff96d1e793a6395c7e193756e13f668bed19f9d7ae002833e4fb0561daa4b94197", + "0xaac9fa24faa08b977819fe8cd69f0b1de226e93a76b78ecb6adbc6102acfe557b221befe42d728366abdd0bc13ecbf64", + "0xb1aa7aba2a4db3f1f3357decc3f8cec1b1da4803a88cbb67d3c81be2e915a6ef521e41bed64738fd8f7d54f35ef73626", + "0x8248be17d1a937b6600ba57c459ceb27264697a88b0332f10d994e46176c747e97873ce4ac6a77cdece1d6c1a28304ff", + "0x86c5df7290a9d0aa693c07782f7664aabf2e44e048ba5506ca0e2b0d4ec8fb457c22bb62ed38561120063044c3fb0001", + "0xb59d10737e31e47270fdf2cb71ae938efe374fef21eefcf0c93143f8a88c479bd21c426c610e4ee04ea8ca4cd7c456a1", + "0x906057fb1e9ce15784eda57d07da22b327c94ea8b01b9c3ea8eabbb67223a7c029dc53de032484c49b6f0fb035d4edf1", + "0x95659a0968f26c2a3568b7a954b35591d447c49c2c6783df9fad2faa16970c4372b889634a1c2d14d51b5bc43d379b2f", + "0xb770c0da20303a0be685c86781326a7e311f3f7f1d531085e9a77d30379d95a5fe96bf0e35c4108e917b54238630a152", + "0xa7d688302b09c3c7c08d350e010cea6605a30e199bf95e1ea3c04c694defb55b39a3791511c01258b687bde73f3ee45d", + "0xabfc5dc4c5a5cda6f30a1b710b0b45b916aece59527f9961129f8266dcc562a51052b518a5ddcc4ecbb83897a1632d58", + "0xb8447095e8e53eabdfe8085b1a2990e66ec47f00c66464082b702a11cf172a8ce4a01c72305d85a13f85b79623caf17b", + "0xa7ced7d5cfd867ed1ad32f5de911d50c653154b18fec8a4f05437f9dd2f044012c10a9209412e46f505c9f52461a2eea", + "0xb4e732085af6fe3f65eade32ef38242ff3162b69540074d0b0f8a9c7094df26a6f3c13906e516866011106013db21157", + "0xb503452a662fad066cdafb37b6f4bb9ae86a1891cd40b5dc26af64fa94a6cb8f0269fe4caae2ebf299af0896e09a8347", + "0xaf3a1e61043ed54c06373100ebb9a57a446149362c419b8ae3bf40921cb248ec5e7122eb68f6b951a061b653aaacca46", + "0x8b34e48c403aff5d20f22463d4e25f0f3a9759dcc4b05ccb7cced8beb8a03ee30ab1e29baf297bb5aa0232df63bf8fc1", + "0xa5eeb7473fd290ba95485aead10a01f988e7799ba7c809b9f9fecc1c51b8e963b0224df2493ce9fab6cac17a4cce8101", + "0xb5deeb413a06acdae2a48c1f5e71fb5a642aad0812ef2e14eb95c8447b549e1c07655d008f4497582dde0c62831fe084", + "0x91c909a21c823c243f1b03cdc30284ec32e069f7181f58770602928696925f557066cc9d70b0e3cd0ff797ddf154e8b2", + "0xb0c31eaae13668feb8270af45c2175596532210928e792377fb4a3cef04f7c45a19bfc74190bcb01162fcbc258f123ac", + "0xb871c0016fed2413da87294cf7316eb468304c7ecfd8d547f2282cb51197e6d2d371c1f14eb48065b14b81f3b4a94684", + "0x889c6e7fe77d11a9164a9d1333ae22c5e2f64b97231e49a12088f417605c670b0cc53a084d917b179e46dd657bb15c95", + "0x9567236bb02d8633bb65b8c92caefd90eddf4aef084d9ee22fb203c349a7b34916b7d7407e10f381eb59ea379a3a3d60", + "0x991b8e7ed673dfc776992dc3ecd54e8abdc7f3d2cd956e2e1d04ab403b694c787c1a72f01528b56b5fd5f685438561dd", + "0xa26cb39949530c81a05960e08711e8200c477ed11c5c8bfc357956dec01dae6afe7c084ff95a1fd31b1c7eb1b2fd5dac", + "0xb46fa5a6eac33bac664cc37b8818a73bba9bf5d8a02017a5701f6c8b3bb472182565c402448edc3fbe208dd8d9c13417", + "0xa62bf3ec2b8f18866d5a8ae48fb99eab1c5f9488a3fded2d3f392e5f5313307162e6b600cbf1078ba034196fc4be4570", + "0xb6a880d971fe0c25db7cecd063f6f0c355805f0bba1c690b7ee77f92a1ceadac5d5b876cb17b5ea3a8436b7eef5ae4aa", + "0xa5a2b6f684c97f17c0b08d0d60934b6a74014e44f3f616c39e0e5f56d608f25fcb3c8cf94d4971e36339f8d6948d4a06", + "0x95a4a80021886e6a6fba25f185f86a8390ed21a77e24f763bb68fc1a800355875e6b0042e0b9162dd1334f79bca94388", + "0xa5f0ba413f8db3a6e6021376ad670ee2bc71992d068015590785bf9eb1f6815700dea0acd7dd7abc52a9d2cda79b4d84", + "0x886234c2435599f82b38a8b8c87db704240144e244cd94e4290b672667f7b68c20be20cd2f18fce7746a83ac05b7e22f", + "0xb9521ea0ecc2fde03d632aa0b650c1589e59a334a1af0185093c2540d108fb58cdb78d446b5049c4e8ec0561bddacd2a", + "0xb331d8545175832284c031faf25ad6dfea04b96cabdccc5cd893a599c0f8bc53bc7be9c4a4fada378b20273ca48bc184", + "0xb0a275e60541e689fc8d8aef5c3a602791979df7962ecc08d0269793e083aa17c66ba4d328d1752ac83e8a8e00f9dcdf", + "0x8a7ed50a96223ebf14fff57764a6babe0611f51090fb039ab0c908e13152e7cfc536c22bd5a5342735868f3d81fa2c7e", + "0xb1b21a588526985512a9e2bc98602e8f3d61de61c178f3508600452bd301cfc48d610cea634f214379296aa911667a02", + "0xaa585ad4baadaf3dcc2ef49e7f90a5fb298af5da6396c8cdb3504c1ecf363f7a8df49d3e90852145af188482454d3396", + "0xa8fba412e4d976a384ac00f869138b4e97656e210a85c28732927cb0cf497218d7c4925350ac7398ad9975b31947a806", + "0x914dc48df592b4d640eed1b442664c19aea1f64ae0b351e545a35a1de171739ee801667db7af7ff2ad173cc6973b009a", + "0x937e8ea9c11249f9fe9082f50d484674e5d2d8a4b31c9f90dec7d7a70c72e1b9487958f634f172de2f1c092e51859154", + "0x80a2c2aebc387376852431eb56c6f9bb667129594ea4a01b5c10d33bb7d577f144cccd1fe1684468234146a5915dd164", + "0xad1c83171702e1ddd50a4fcfcd43593959004ddfd75a0e45a73136691ac10dbe575cb890327c3aead4369e166a6fd267", + "0x869e7c690941d504f602be98b47529ee6479c1744e8118bb5678d24e5ff03133cb81b189afad53da4c62b92e6cc1d20c", + "0x923ecb8706e3a9ffdbad8f8ca859640688a3317de9a90e83bf7b5023a25d5c87d89950397d5f97ac6ee69384caf6752e", + "0x944d50dd47a9a08d5bcc80888fb49c3032aff9173b565ebdcfb2e85500e91e24610de414df1d17e57af5abb3d1bc0b07", + "0x801df8c97e0629bf11cad9eaae6cda7e03f1e166ab1b565b11bb0ac0e6d86c1732df91aa0d3d839c0c3b1c207af938b4", + "0x8ef280669aa5c0a9d7106d9db7780b47177be742f03220859c3893687d3260af37da2ce9028784528957b74164d908a0", + "0x8cc7a386d8d816f3adb9ec56edbebc62204fdc6a2e498f087fa1ce74bd5462f2b13926e7dd9f60c8bf6664e279190c3d", + "0x8dcf728f1c2708fe230dcc28c40534fb2285dafff748b63501bb0703f6068becbf803f3753821c6bc572eea1fb73d81c", + "0x82828a9be1e39d10a4c78e80c62f2e19afc36a01c29d30c10277327e1d69aab8c52727907f705d592067b11307dfd6da", + "0xb08d9d0480583c7fb279399f2117074b3d6c9cda3fec2f12cf813639f6198b42cc578d98f6bf005ea6ef42c51d663d30", + "0x86de0aaca64be810a0d7a7f865ca496a361b0b9b531032699e76ef1be700d9558df2e132d256cc75ef62ab3845d7bcb9", + "0xb999d1a9b90a7dc9bb5e5850d72a7aaee73d69ed86e4a8512dfccd67704c1ede4d2e3ea6e561a2bbfeebebd7f78e32eb", + "0x885a68bb75394d89bc21e24efad955166925ba173bf61aaa90a6c0da363e6e1cead05fa9993b29389e4bae2852c40bad", + "0x88673c2fbf72257aff5e2e5c1cfde1a39e121dd0700e40e336ba2f05a38a2ef5f062f13f9da7b7f4442daabd7a24c8a6", + "0x8d9f624ce28fd144c3337594c9b2ac4cce5163a6886f16b1eb9b0a1a63e9f12f0a64e95e44b3d9951f8532a6fb994b1e", + "0x8aafff3809721d7019cb3478ff49226fa6060a02ab639dd1dae20ae49fef06b28e7e77d4db6b0bf2e05b276f0e691b0b", + "0xa5097c51e5155de4cba8193b4f4c0eca5c077f9db799a4afecb6287f003ec162e7fb900a30bba3ade3884dd047d8eef1", + "0xb4c38a61f7330289652094d9a521b54bdf2e26daaa4b62f9ff6c9e414600801bcc700b005f0bece9fddca5a9192b7fe3", + "0xa62780e8d45581c6533c0bc4a857399f273aa094e24dd2673e5b94ea7d0ccbfb7e4fa0fc158151c38af5de03d7e4a14e", + "0x8a35c130397a667509ef9b6656164da20e7d1e33b1f5324b8a19599730cff466e4c4925f6fb7d6c656173b036268f443", + "0x87ddd734ec35ef621a08147bf9ed97ceb89927fbce6800aab1f2ba828758687bff90d40ccb92665d721a2c200cbc3c04", + "0x8aa3e74942a7acd9a2a23c8005056d78f489a04d8b77398645b578e0199a57a46180e00b4b249f49de1ae201d8059feb", + "0xa0304d399d64284ee132e537727a9435b2e338381232e90efb1b7748a6a3bc45639867cc26b5056124c1d9e328100886", + "0x89c574b3e7a2076ffe30bb0f6d7c0fb322fb4bf771918029eec9f7e85271e058a76a48d01d380976e782b67952d14c11", + "0xa83cb1b7dddba6d1d2bcc049bb90d92cda73b8d41f322d30e05dadfd84721513a03f3edc1a072fa8147816a7769b9dc7", + "0xac9fb6050d84452b89b2a2755e70c8ca33fff06e366e737ecb6f563cb126d73e0970af2b826f657fd6f8f143f5c9b227", + "0xb6a369865c2e05684165019edd71c92092ed59b7cb7918cb44752430d76284453dd2efabaec1015f82e3e72fc95b48a9", + "0x973cbebf78faad23a0c1c0aa93db15bc17601e517329ce26801b847f262b2be5fec980080f35de0b3298d70cc7370b06", + "0x80e70b810512c95c442d27cb755aba465c768c09700bc664a9f5ff9b9a546d8fb198e891f5553949731aff100c005750", + "0xb8910e9517d4b178ac047903a7b6a9c66f5cae4a80a169b6ec50dc4b04865af9ac88879c5bacc4e7f5abb84de69e8610", + "0x94ab8796498fe19a88c0f4083b300c2303f0f9968fdfd38b46b649f8cdd8c300503ea52228f2f8355778a26d43e9df69", + "0xa62c81e172aef81085f535eb22c74b5f2ee4f5ea2f841b1c52dde5d2878e83ed28b784ddac2c9b1a64ab40d008ab2cb8", + "0xb6e884c5ef7707b441a5d2e624d35e0039d3588f0ffbeb79a4100cba7e817699bcf6cc9a8af0b4726e68ac3fcc26f10a", + "0x8a4943d2304b1835a2f5a2a23b988cc1791e61c71753d681fb8d2df1b30bc285d98704f6ff6f400a22ec463fb7500afb", + "0x8a76b1190b6cd928e732a6ec72168c583e694405e95f13da908523b3ddc65bf6c3168416f1615b56e26c7aeda178628a", + "0xb622ddcc9283e6f203c683617efa6716b17523613de411e51dd2dedf250e766c16ac54a7e7679caa6b223a70a90de1db", + "0xb6545710bc51b3760279c588953d21bb8d30db11a20e989a5e4641cdbcafcf6e48e96a431d9e35167f3f9ff63e451d31", + "0xadb5f5474dd149a816cf80d778570b06e85381fa17d687d85026c04edac90728d2ed794b79ee5ae4bd8345982d33d857", + "0x948b8732fdc6894754f99b5c2c03171748edeb9de1e7acf7e4d5310f0cce24d2331566b07aa5745d37df57b1dd516738", + "0xb19abed6305082019f1292c78b17fcb03afaa60cc556d2f08bb959438ee45b26ac52b9d2cefbd03851b4dd9d9c103836", + "0xa896d6de27c1e201533f573cde03a88b806a34a5b3cc21dd48220cbb9f098ed31bd41700a3488e854eaf861af83e4161", + "0x84d9712f90412405f716465eee69f6be2f22e698da2a5eb42b9e39898c5a65d7cd912fd4acebbfe92f35799ae83fb6fa", + "0x863f954ea0adc5348421a4f863ad919e9f4bd8dd7c79cb849ad4add903141127490558c3fe35e805426fa69868a60ba0", + "0x8c90f25f474f9d4b71afed2539d6970a11486a2f4cf0c052a7572a312bfdd95d5ac03a17af8fcf65378d8c77782fdb34", + "0x907c3dc8ce8edf2866671c26f8b55d3d12e4155e11070824077dd13e0daa24cc655406b2a644029edb713d0467fb5fcd", + "0xa407e9e36afc12c8cceabe5aec3e456e6d4970d158adca6954de41d7a541218ad02073d1e7485cac25ff4e92bb08737c", + "0x911b0c52fd0bec3075c9de3ef6d46635fd77f4a3c3c6a50063fe32f60fd5072e6702411441e499cc7df22938f1597008", + "0xb819ef8c57f7a81fb8bf6ced58c2d26b5b75dab49e5747dadd58ab97f62f5f99a29378ab25f597d0e2682ed21fb29fa7", + "0x89a8d2f8aa775d28fdfae21763e40018e7d5e0f43458bc9fe8f6519232cd84a4cad1308d939a0d7c390bdf920642b442", + "0x9800483161aab68911ab3bea3f4ab3b46e262b99afe623c67f01bf09b0a4e535ad60d1fb5e1540672659316db19651fc", + "0x818f224aff602b44f87fa3c633b7e10b0f9bf90c7fa693939f39251f87a13b41480987e8182528752cb01b1fccc58e1d", + "0x928c7fcb43d4daea3cabfd711bb731fb2ce03e3a0d7cf38620929b99958260671ca3e615a06601d5f766d21ee5d92c0b", + "0x90ef96a974efd1a2ed3d055d8cb39ce9a39c1977817f6e1c82d8ffdeae934a51dd87991f2ee0b197e27d34f05b908040", + "0x8aa9fa4b7077ecd760a79579fd91cc8bc48bd0a2645c73f7039f7de5d24f1a3ce00d9823160b745243212309103e5ea7", + "0x97175c15c01083174e40b0389dd52a3942f58844432d4c6c33fe0524023ecc3de5f91373a6e4e1962ac6014a64510189", + "0x94ad964e890ddc869283893396fc2c7188a572961c07f09b7acc0274293906b0a4087c9f8921e6e25275a50bb2e253d2", + "0x91833f7660327758fe0fb593f8ec2291ab2c3670e2f8bb8acbe0fe423b2d3e98bb10639f5cb2278e99efcb3a31382a8a", + "0xb7e7f4802086acbb3a97b6fc0529f6793e613f27c6f7e329112c1950c052b77cb3b8d56805db7b1fb13a4e3dded668ff", + "0xb2bd39cf5d378f92a38d456dadb1b40f005fefd4f74d20f8e40e3899e497941f669c6d14a252ae9a173f1c2d0a941e40", + "0xb21b044720b24dbc5653d9ec81ef46c6a88bcced7cf7dbf81f493a109974da03bf704fbb785d07653a426e124f02c45d", + "0xb6ff9e42ed56b6ff891be9c728ee4fc5fa6ed104bffda13b59608fc5fd37ed04946652ec5563d44f4366d24b5038eed6", + "0xa54e4eace5380dbbb758f3115380b9812457da79786bcfba5e6e2ad93b94af030ee475c8529f7201ee372401000c9634", + "0x88dd48c9ebf2b72fbad9ded377ba3fd1c11cf8a6250f2985c54335e3722b8c6daf1df0343c8d968136ea5441916f70ed", + "0xb1faaf137220446bc5043e312120fb9470b581d74cac22b710686f7113ae308575e73ce7a9e7c1729fafa11ffe48f65f", + "0xa1af2e14faf776572947a5957c14e189a2bfeb3dbe5e73b2f7051849fac236bc13f025fa309723705511a70308b6aa1b", + "0xaadd2cde69f98ee77a7b8725812a712207f8a39cdfc419cfeeee2ee826c31c8c3fc77d9ec4adb6fdca3860d6b4e0f544", + "0x95a85bd8ecadad1cb14574506965db2da08c0b60b500b92772b559b490260f09b7714033e5f616bd0414acfab28f8350", + "0x92046567d215a11080e58b0550032a95782efa22d70615bbaa43c3d03c8474acfd1227c2ddd77c0a0ff4697bba211e6c", + "0x99e5bd35a1be52ab413184e5f44ea8dba74c824ccc8da5be3d28b9fc0a672d4ee490bd1fbf108802f7f8d5687417c799", + "0xb13a8d0bd5a2fc91c30ebec98f75d96afb6963b38baa23c8f34b14d36f1b3c70e001ab90022ec1f944172e48ce48dcb3", + "0xb3f117d8d19bafa2d44c47b0890c46f060c3f0de0b6de1d0574c2d87609abfd4ba96517395c4375350ec85e4c364f04d", + "0xa4a6f5986b10ea4bafe8512de8e1adbc992e843312cb9b8755518d816ae0dd5e8c4346212aba912a5d97fbdcb2037ee7", + "0x83e17779b0f231fd3f669edfffba750a0cfdb1be983fdd3b248020680ec6bff14cf4800c5c5104ce1c29e4d740eebc59", + "0xb9571a5c08b01454fb0c1158663ac11fb9e05f0746e52c68bef5b8fd5c04fafe6cc50b73219445d55d1f1daa7ed403a2", + "0x864fdeb4aaf8720054544c0b1c271254f322fe946798c822bc1172e43628e54797d1913d11b286825d75638ceb2c6a27", + "0xab2f519a74b8281f4d3a923b503eeafaec6b137116b0509ad0f2129546823044b098671c0307867a73da174c9c68822f", + "0xad101ddd3654deeead87a8573c15424891f3a4da07657e812a8460aca23feb43e8a31a8818e37fec536786ff32125d6e", + "0x8b0e038187f86c449ddb6794661e75f73cc9f3108a366c6f38f03efad8dc707178736c19f487ab6a389d00f6ab51bbfb", + "0x80c32a99c6431e6b14bfe324ada48053dfe32d6349626d6f889834ccd02f6fb661971aa30d5b74e807b1599d8528226c", + "0xb2a9e35736e1bc9246ff2e1f746b88a54a8e0c60fcd7def75073445dcdc39ce0fcb955d62ffd0b673e3951fd49ba40ce", + "0xa61bc1d54e3ce44b3b648e08ca65bb46f75ee4e7fb44c4a1c52837882421fb596feb2f1e2a92879bf61c496362ad4460", + "0xb77471ca0037444d8b77aa0c540abafea4f3a19a80f8d223f9a5547c5a1e6517327e20c9520f0d2f2e98ae4183432016", + "0xabdc5b8c408c02078ccdeae26761f464b0c5ea948a6a95a7f8d847be92c99e5089a5616b1fa9f51116b7e970538ae978", + "0xaa8bd90102437bbafbf2342951d7a6d62cf86426a2648cee40c75d8e41ea96007f97032e8b387766c540ffef641ef3af", + "0x9341880fcfa43b7540b7b2750ed2e789e86178476706c7f4cf0aa88dbff103dbcd84a93e49a4f0960f2594ca1c5e7f30", + "0xb95a5972b1687defd1acd5726bcc6c714249353fcfdfb1b3649b90da17fc74ed5848e4433dca970309fbf88828f20f57", + "0x962c304d86285c483c6dec42d19c22996ae65552ddeaa2a6f0b30fb881a902e2de27193d842bb6365be8a051ccb7d643", + "0xa45a82148fe15fb31dfc0ee4e87850e31399cf6b7835520c1ebee45dcac9bbbb078b1b23e87f471388ab4625c554e17a", + "0x88e7186eea47aa75d28284c5c0655f037ca7434b8590e411c0e927b3262ce4c4cbc46d623b5833f46539b29595a0bc64", + "0xa7aed9ceb1e3d743b4310ea09a3b4dc295497da754255217e479473bb201660854d50a1f9c20c3905a4a5fb4f948d1e5", + "0xa2d71809c013988f56ba45886a263b82504a77677d846a4ce88155a36dfeda9f389f340d097707f5c379e294deb70710", + "0x884fc9b03e92fa84f97d0b8f02e70a9651a98933b56aeacb544fc52323e45e3f9f2bc6bac02225c65f576861cef4c9a8", + "0x83762e8971746a8779e8842f03926700c17162f23c124425d947fa452f94096551ab66a5d2877b943227678b7c4edcd1", + "0xac2d2772f0a7604ce0e457d74ac141ce91417c5b5e7d45c50f702bc71ac682af6f8f93f0b43940f91e338860f1bdbc29", + "0x90f662c41afaa1e97bd7884804c91f8564bf2d041f27c929f7b34539e90fb9d6bd497554fe789ee88d942cf8afa5abc1", + "0xa14be2674cc8489106a0f1634d6dd35280d634e27bae05a552b39dc69ec23f4da7e7d5293727f34e05bfca86aa70f0f5", + "0x9305f349121e72328cb919ea9fd63293cd3f2e3a5ec684d71361779ffc3064a8126fc2f28f944fe42907911a04ca2aef", + "0xa55e63e58693100aa4c7f7ca9343f06fcf222caf03f297558de18ffe4fc20b50c2de078309fadf6d81daa42a13cee77a", + "0xb0582d29d17a2bbfd1f5603894edc2a766b5c54ee9c2caad9e10969eba8e6d7e3f11979e8531c7a10520554869d9af42", + "0xa6820cbd35d7bd5c15602ebede7830dca36d59374e988ee9af3bf283ec6cb5f21525317ac32be0c6ae5f703eca8285f6", + "0xa91f2cc15b67e258a900f7ea4b3d719d7fb5e76ab58d6662d20e30067e9bf2337c2168266947d62b34ad91f041d41faf", + "0xb4aac10399ccd36f606bc3bba2002d6941e37c093df247b2054aea72ef8c2f9ecf8dd1fcde997425ef141a539911f71c", + "0xa1a075ff032f65cfbd90244b9d171779f87955ab6aeaae2b65d853cc70c51a2d26b673432d11f91984891076a99b2ede", + "0x851ce1b7277ccf5dc3a92d735d8caaa4944d3664b2fa0cb99962f5ad74b080c00dda1ea29b59e6f09b6dff52cada2697", + "0xac0cc8a5dc261461dd9eb85e5e3783aacd8ca552ace8ef4f2290b97eb2d3683569bda9886afef3dac976826aa06e980a", + "0xb8d91ff844b86bad1d48a08db01b46a6a3015ee38a7aa6813086760a2f18c2384e925302b11c09c26a0172e2660930f2", + "0xb3e869f4dfb671a3becb895ce0389a5cc9a62f44dfcf344a4e647d2076a79f71af930e9e69a87db4f02aebc1f083850f", + "0x96e4a10a7df6dd4aad60d815d78f0477be48fc94b5a4573805e997710a4e2436687838c814f10bfa0f2be9ec5187c7b4", + "0x93ec25aac2a822b104d6451bbf9a4b50452822c34eb0afe08be9ef0da6efa2ef1d3d37b1fb12fc24358ce2ec7754af19", + "0xa326f2ea1f720483c41c6421645837c7afe496dcf913860c23e78240f734b2664b21a5e9baa86eb570fae088b691d0b8", + "0xb49ea188beef5c31c22058d68921207136867281b0c9cab9884f84847321581a734e92404fbf01d647304696f98320d6", + "0x8d69d2c31616057e811bdfda31b944ef13740e71ccfb56ca16dda8442764b7890b03d641d085fcb0f6a81d85ba366e6f", + "0x9624657ff65a37a4c818c562f2282ed24b15edcd597c3ac239ad6ac01a2cffd4610c376862bdbe6068288c3d236e466e", + "0x8fe312bb1415f5753bb0db9b25843d4e87f4a74b7a34ce0c88632cdac4fa7c4a59f3ae6d8202a6cd9a6e69fd495a4bbb", + "0x852ea6df6784f40826fb9bb28eba55009751db3182fe8a458d1d29fabdcd85383b81eb152167f505b11fa5bf4d280bbe", + "0xa08a96789f8b0dac0f95cd4bcc099b49b06c79fec31ce4e2d2c994c3ed296da8ef108f1cbc8848d02bd294aa07348b76", + "0xa9ede20634f77edcab07ee36ee093fe066762c1a263ffdd87c294937f1ce67ed4d55db1617447a43cbe9f35c8e7623ff", + "0xb178061605ebb9b04b697a62b8229dc82c449db19db41776514104de4dbf71516526967f17358102ccd7ed256abcfda7", + "0x82530a67a5f63c893255f42f5114b27bd96dc15e127e4fbc13447e265ae2d180a2d34392b439e7a914c82fa555929d35", + "0x89cd26fa6ff93b2c10e3eba0e296a970dc0e716e8665c997d1503827bbc86ab0b9d9481243b2570da3d111986cb3f5b6", + "0xacd6c476bf83ea66d1d4e24a4212886ca2cca6cb51dfda7786837579cef4f8924c6b52c24695c91cd98065b07d2f29aa", + "0x95f2c0d7e8ab80374351c0cbc1bd6c2021e7cb362550a242fd81d9c7469b03f412867659145890dcc69da3ca6ee78255", + "0x888c0b0a842632cc36ed57f8052e903dbff1c4664528d73426c7b0b798bcaa5fee1227b2081290d562e40d606c4d8cd2", + "0xb2ffcc14c27c7989f1681c7984223a07dac2c82ed8f3a3926dea7b09982f0330211517a6b5b2b5f423a82712bbd1f39d", + "0x89b72bc1c8a4fb694db4c357e0eee14244b420d0d182e3a3f47cb328f353f57e9d81ed0288f80e6ed88f9938a9af011a", + "0xb3d794def6f9bb23c3e08f356e7a65b88273c53aa08e9f7290a3a480933ac003640212642c009bd46ce357d892b8a42e", + "0xae226147d5dacf356fe6aa27f3a7330914694b8571d95d019717edf240363a16faf777b943a5b43db337a08205bd5268", + "0x93ce3c47f59569e7ad7cb42218b263fa6b73498181bddbb79bc6bb335f7abebb077b7381f846195f50cccf726611161c", + "0xb9c351a124c78cd39b51467cf2df58f7722b70fe07d65ca3d013fb7ad43b8c86fe5bbb1af85678f9a46ca5a0c01cc08f", + "0xb9e390a4d822802cb3726bac0ede3c224f1bafb3abc86d4325e5d982126faee30911c4e97ba891e20dfa905840bb9a12", + "0x98c2b382195000e5439a21fccdc1c7c31f3aa106ab9690b5686743b4a4ed99b0c6cf69a13d7ca6973122affc1ed8ec76", + "0x966ffc3be901c69b56ec0a90667c378e88de5fa08a48459f283d2a8187a2fe1a0dea7f9169cd75f2709d54fdd19e4320", + "0x8c75544a77ce08d8b631f8d1ab85ffa631ba334290576157ae9f7a3a42da3258578d1ba3d5c6e5c65d9748fcd923d890", + "0xb4fe0be1014ba4c08f1d4b139a3232f9482b5e7f5064fe20c85baeb46a9755778af5288fa05876ce15068be2ba8d42de", + "0x89f421fee510282688570d63fdcf4366d4d3df5f1675585ca64c82ac1baf5b3f328333bd7a59aea1700bd118e1ae82c9", + "0x81ec8bb50097cab7c2dfadd3eb7aaaef87ecdcefa608fee6288f0960745fb64efa37f7dab948865920465342211536f4", + "0x913c4cf900839ac0900e6784871bd46ec05dcd6c4ba652d306d15156b52189d4af6e9a8543bf7786d8d1552139bc4d95", + "0xb6dacff7bb2f11efa6b4484b2b32643199256f177eca1031cf62a2c1beb29d93fec6f314c8030b953b149a7274771e30", + "0x8a03060e7e3a386cfc162df4e3fd27794b24ec6e6340b2b596b869d6d2152304215f3ef3e853ca29ba0777e5c1c06632", + "0x8731d9f7d6a25e6094c99b74c655e9113707bb1e5e7af1b65fe9e2258c057656207589e1bb4df541cfcc775e0593e1ab", + "0x920b020577fd4ee197d9bb918468695d184660a1fc086121ea26247e36c7ce7f59957de4e21787848e6ca98bf7bd0ff4", + "0xa6ee30a3300d97a44698835ac9add50d0c29b17223758fd2d855d7cb511b666f06418bd45c0a1ad2ac9ed68b0b70326c", + "0x87470b821b43fdc84ac9a1e7788e3d86f00bbe137496d8ddf91a781bff6d6c232a7b429b98c3f10c9d6e1e795cf00feb", + "0xb7a03ff622dcf8e6218b2643e10738355a2fbab2c911c1112a9c8036825b4efff662801c71aea20d64264128b7b7d54e", + "0xa9dc07be1dc93821fa84949c88792af44e994e8ab03fc7f1a9639511b0c5a6cef8a30060fb2a8f70b1ef81ad6020e723", + "0xb701f1104b5ecb2a60f800cf61859245093bbb622ce81378589dee921e8963b49950687b177d066511b2cf8780a90d58", + "0x98f51e153be621267f2108365183f95cdf65a558c9705c3dadc502db072e5bed05b175813f714bf4c6d509a40a001a42", + "0xb8e785d2372410cd1b58ba36f9b38d5aab8675b9bd2d221890d6b09b14f8d96820e5171713138313793a4381c25ba05d", + "0xb7e1aa8405ce349cdef13732635c49f7d83d6bdb44e95a91a7762ff1d4aa1ba9faaaade69d2f15f4799ac951c081342a", + "0xac0d5a5dc051f700da3cb5bf42003e2c4d808c2f8db719f20ce7bf23eae451645574ac35b8d397e1c1c8262c637a09fa", + "0xa70708d64580261b090fd81fc8b4dd845153bbb39ecfa931f0a2b6b96b78c79f98b56e3088efafdbb81c32cee1620ddd", + "0x90b155f8fa299a78f76a769c40c73325450e331bfa6b07503d06efcfcf5ea3f64f3acfc8ff69aeb5f458da0b22ef98c3", + "0xaba517ed29aa23a6dbad9b1e26320fd8ccedf6e031197492e11c4bc5d59d84a5246e8755bf859fa077033e4ed40bb579", + "0xad9c8d712c544a016cf2e3834df1aabd22f31bd5372d5c2813ff68afffd543e01d8d57096ffa486b8dd0b21718cc197f", + "0xb40b9b0bb4b99c54dfb2076e51f2aec99367cd0d1fee8f39aeee1fe218efa558a83ac8b8d7a510f8e8129beb21813a38", + "0xa79c1863ba2e8b1cbc84d9e27774e94119c1df98617a50b0e3ce887a184aa5926f9b4d1baed4db9235a9d18c404d1bb9", + "0xaa74a5e347e68b05948e68e648b15fbb5299ba089c99f0bdff7f5314494388a3284f91a249d53232078ed635252344b8", + "0x886d1ce0c1ad99b56752d93af521b0c050be4a4b514052dcb1dbd5aa437b388171313728ebf727d93da4a03856e20fee", + "0x91b2927092ef682cc45b14917918f79946bb0bf3494e1f0d3d21980ef269e1974177cc2be3a48b801e9764d6c0773a66", + "0x82306344ba9ef3eb9e5181d6d47e76120d7313f6a7037b15b12cca30a4d0512e4ad10e3228e76ce1620e2e623d03dcc6", + "0xb8a788a73d61811341956313f19efb0f1ad3c66193d7dcf0f98687f71fedaa22e9b33b4fb8299ac1f1a4ab6211817ddc", + "0x864773128180c446eefd2c5b87245584338318e9c85ce9ce4ebcbf4a34a76621aa6519b43b9b49a2159766709c3fe3fb", + "0x980cbecfb14a72ec2195ef04d11d62d9cc39b4bc8bb0eb72cfb5cb626e6a2d1e3badcdd7723b105eb06441d296f4e6d8", + "0xb6ca44ee6362c36e42890843e9d07d105f94fc66cc06e0099e84b3f469b9c14e83d41e102d1e84d9e3981bc19d0d2716", + "0x897386f2025b6fb23bdc1368f8febdc6142f8c895068b908cfc07a25bd1fcd8deb0ca56323f9bda043ad113e175aeceb", + "0x9676396b72bb0f004ada223f1e3058cf7356edc25e854fdc90244be6709e15359cffc980fea60c79f4d91047c2ec6181", + "0x822f2b7095a19323c032f8c81d1527f20a3e26a3e7af0e51b7025c3042848efeb6bf5a07db32ef883450318929364150", + "0x866b246c9d57725f7213017a26fba1fb092a3860f594c0f20d2f22af170bd5bdbe53d4d8d18cd02a267891c5a73ea169", + "0xa39bf1eda6d7285fc70c19a647114dffa403f025d086788ade8372a420be53082048e47ebd78bd561ba69a0f45556b0a", + "0xa43372001050111225a9fd5aac21b0e8878a8b70a49737f3f77602b8d9e561d218818617a9c91160645fe221dd8c936d", + "0x82d7682ebce8b8a0deb2bd2b8a8310a24a9b0aef263b361689b747a757bed796778644ff5497654fa867c355c2d9ff39", + "0xa1030511d4e053d7acf0d2f0d555dcc7df205f5680c3b72479bb93a7d3078e33a14373e3c3065dc9932a2ebad5ebda04", + "0x9933efc7cd0ce36d796fcb6d52b50814b8ce9d3a63c0a063b8c064b05bd3d5b571dcadff6ee21b0d382ab53c01003448", + "0x8ae00bc5d8ee643cfd2ed1ddfa4b66c9d700ee5fdbfe4903faac2b83091cc83ea5103fca9c5ff48e65aaae13171b8853", + "0xa9930bcba702c426872e7e4ad9411ab0a29ba2ef8ebb3ed0f2321e9df791a00e1b3bf20bbfdb1d504f04bfe0d8245cfa", + "0x86365b8931b7281e2fbf15e74c83101ddbd3c118c9ff62642243bee23b91d23139c3ad30e2113eda710625fae90b7192", + "0xb7b5e856210a2f813dbbdb5760cac8f881e013ba8bde1ebba06c09fa32060b20ec89d7ddccca379f744ad819ff679d5e", + "0xb610eb96655c1a6dbf56e50a6a992df4330370d388ef7133d7b0647ee635213081aa273d4aad1de83ce366275a10d459", + "0x88613202cd338bacc8ba871b560dca01cf7acbc3dc7cf12bce1e311e097e6d0bfb7fbb4aaddc5644641b758932f8aade", + "0xb4cddf91efa805e7fe50c8e022a63070ccc36a99c0f710d1d469ecf9733cca08adf490be865c62d53ec1af44b6a11d95", + "0x811b918137eeb802734bc982484a923c7958e04a9a03706fd4cb50d6c16dc74b49ab424298a9e486f891c95254d63761", + "0x8d813698636e6de5263d204a2d41a2c651a68594c454adfa1cabc903227401f0a39b25a1090351b7a0a04046be714fb7", + "0xa9ab9f8d79a242348d6d1bebb6ce814376f271b99b4c83b9a1e58fa354176c16cd414b9b88503d0fd1c71bf682d37f09", + "0x94b2211bde8d9f1348f8d7d854d579ece8cfa4b827047b1be13d6335730316a27feada01add2d6035c9a0ce34f095dbf", + "0x8bc819e9252cd5fd4495a21371ab903bf6eb3ae4db0a8dc985b6180991f17755b6bff555f136f7047c94ffa33baddedf", + "0x9512e782b5c4fee3fbe3080051185038077c02cecd34d7a42e8cb397d45ee6ce097b9914c3bdde6dbdd3462714f0f853", + "0x9472ce774923ea90968c4042202e6cc4d39941ece2cf2ce8ae6f1f85cd0ccfae9d478b9e78804a7cc09bd19ab56297f0", + "0xb925a71443ece105fce1f2191b303683d93653b291ef9cb7c3eeff0c08bcafffe24d1cd48b2915c7101bd9e22ec901a0", + "0xad9683977fa7593bdd512cc99618eb44f1bd317de5fefdd2a77a86e368df4554fdbfcd3c6845b9530faaaa498900990d", + "0xb3023bd3d89bf55f4810f01972386d9e715003219ad20f462b777b759bc0293be7a9ae7319b6bc1d6d9ad3c9f29eba2b", + "0xa7640329b0775cf81db585ebe22f640b8a6ca536e4084ab0b7c04246483d1dcd8df468482902e9e9925ed2edcb631a75", + "0xa3ed905270f2a4dc5948a398de1eb2ee21da1f008f82777fa17400e423110b9529b996caf5ac4e9e6286d93a17e39d07", + "0xaaf52a04e094f59afc29f26e03d6ffd3b3f5d46e46acfc551f57f093d829e3983a3939837e4b9d6cc4889172329ba6cb", + "0x975b22b5b5816d9d7bf26be3d9f4ae8489d7a8fb71f2af51d12bc129e14d41f3b061adfc18003116d03da039ff419852", + "0x83c079ec71ee395a24e2ec61073514cf43948332198bdf4299d3c4404e3c6245a37de626a87445749401143a9b8870eb", + "0xb740070eb43bed9d7c05f534f06d5569f35d8966041d4ddbe8c44c6a08261365dd4c882c4bf8e171efcdf7387c5544b8", + "0x84219686e80b850622f63ad47088e354566cf573ee1b022e93397dde7a321ebcd197ca5d6d87b1eaf0cdd609aa9e93a0", + "0xb04c99adbf67ef1658cd205422267a9fd0b06902ae43658dbd69363eedf3459ad7a50a04575952dc29ba209e12643778", + "0x99ccb24ea07859a03e8930e6345c6ee1959da97b05cad8f1ef7212842a64a9a24e7a99bb82dd3432d1c51c27f1ca42b0", + "0xae8c4182ae867936a03e1f57353a1fcbc6ae23bc7def817863dcbc91c8ab2ff2971b906fbaa3ddc5aaeccbebd746fc72", + "0xb546932eb52d5a3fbc402f420f23ddba4e163367c098b7052f16bdbdc1e2021f2e49c06e338beebf463517752524f685", + "0xa79ec1f01c99021b584d0b8eeb6ff698243903de2a6c354defe365f1d6cb272a194fe1205f3dc3533d1029255cb0f7a5", + "0xb0a7817a3672ce5509d92aaf7472f7815141ddfe16c76a67169b43d1f069e8a469e4584d25035dec66d7c57cf89a3aab", + "0xb4ea982d941acd1196d042eace7abc212e8b87346bba8655b976f755ae5ce2859a393f4c85cc3d713f5c53f98dacd396", + "0xb3d2cc762cd6df02734e4916c03ea93e331f270999e26ccc3524ef750304c8a2d64fbffb8e382109d2698e5eb17d4b64", + "0x9891437a7adda18b87b3359731c5b8a5bc5e476b804ac877c866b22ae0e02788dc98925d403ae456e65890c9b47e8f9b", + "0xa5242364138e3146df73ce107f50c4d09ac5e809d3c4cee33cd906e5dc0c83884469a2b56467346798c980e873c35e3b", + "0x845b9f10120e703ca8a39580f569742242c15d8f0eaa0d73296b91bbe9c39cff36ddbe01ebb9dda1e783cdd13254bbfe", + "0x813854e5ddb5f93127821b5c7e4e80cc721ac72d3e3ea116834ed131fc87c1d7fd045c38a287bb039109c4aa96393430", + "0xb27668a8af4bd5ed3f8ab35aa802c1effc11113cd8fe8a0aaf003d198545c88b1746c37bf6e2ff6422edb4f1bc8eecc4", + "0x955d13586ac29422c66df49e776c19e5c37e02fcfe6181b6608052d28ba7d8de3ed068be85f4c2b0526ea7f3bb3613e8", + "0xb0e324eebf4a2a173bda80dd0756efae852d1977fb797124e897c92a9f1ca50253983866fb1974915f5a83084b9b79a1", + "0x80f92a11ea79a40db54a5a7a7e546cad1ff15291a0aed0a50b7178a0d3879a3a6d0762ff1a532c53e0877b8132e7c3ab", + "0x977cde7e6c06e4ef646c342a94b261d7e8041c40233abe1b8b7026ded2d7885f9e4fa7a084b28335af5ac64de4cc9d16", + "0xb01a54e405b6a8f20353c5a90945af2d0f11630f893280c328410dbc3b2864cf369914e5d5417849e0f8950cc07998ac", + "0x8bee71eb6ee831da8a41b70c29d7eace7d1df538701656c1ab28e0c24e850f00ea4ffa3be67b64335037322b23778649", + "0xa09fbd34f3f76e641862230e2790cee6b1a4c0c98abad31f6edd768a4ecdd11f13082d317e7a282aa09b40b0b52c6112", + "0xa0387635b96379a082729eb48a1c10711b5a1bd49600b04c5366d0b25fd76a7a1ccc821a81e729fe176863985491bd45", + "0xb118afcb04eced40b10d7cfbbdf43ccac1889c56ee17e3dcb31f25b2ed2b26f7d517ea9056a19fd8a60e79f74089bf02", + "0x9280edb73019450394424f66612b9c3fef092f79a801d2d01681aef5b6522eaeb43ce20fcc111a5af086350f48cb2792", + "0xb0358a1fe6811054975288102de385652e6effc78b1d1fb1e3a97caa842141c74f77bb5ffbb3b46333fcbc8bef4c09a8", + "0x847a022f6361b1e7f6f8d0d8f7c23df98336b2ac5addc42011f7d3bacbae60ba24e6aa91b83a59593c5a2c84219b9312", + "0x9370013ae3ba04eff81a519fec4e6af5eafec302322006c8632a7c98c0642a7b8d7073fa4e66b4721e94a9272f0e7d08", + "0xb5ad4ab064272829fc8863a895c5fd7e92c03e9a9cdce4280a19932ddb332092b544a37eb0c7af6525d33086a587ce46", + "0x82112aa5b82542ae3c0796853b6955037acebd4db4a30268bb584dd7175661b3c1576992843cf6f5647577816499e589", + "0x88794f20c49237061fe2fdfa90d667df7462577c324155a4c78f9c032b353e2e23328dfce4a02230cb4e8ab2a124af10", + "0x8a872460be84c64df082e24f093df2d3d75347d32f0b97e742741f8e83740f6d9e2f4ddf96a6ea6afc408656d9fc6e56", + "0xb9400029fb0c3c0677b931bbb41563c2c5ba1f05146cae0fd46d213a0722526803f0e292002071f79bee77fe88a46c1a", + "0x8f9142e3e668dcb8188e2297017302ab8514b73a7d116ea7621b0d4aa918a470d5f2f1c38362dae2ed3010970f274dd6", + "0x8ff33a042018c22cf83d8729caec8d4f59486e40b6298f1f146dc8527e59117d13d2721da08282761b69ae95a2251f24", + "0x88b233abeb43ec284ad3b32699771eb5d4afaf0d6e5cc1e792162bb74961c74ba4829737fe99ec26fc6633d29ff1d315", + "0x84aa5fe174e8e1e42b011c7b0f0cba6760be37e2ebe75fe89cac6138546bf6432875c21a76aa04e576fa488b7afd5c6c", + "0xa8a885e0f3d981d6c890ee445d6a39d9e5729b85ef61fa36b0e574ea6c6e7e235f5b8a0cf8b446fa56acbb236c8f2ae5", + "0xab3e0c0da5dcaada343e8f0617c8921f5cc0f9a97349c78e4862b8683d1a9291793795de74037cd39914565d7a7f8096", + "0xa3f2f3f82e6644f94e3a708703c9b8e09bcd028866b9cffd9232f36285ea48333b36db36de595d34f10cbf5506f0df0a", + "0x827454c096f09ab0d3b12395499228c0b0783ee1f4c849967dd8f323143e4cddab42915e8a6a474c83452824be211a7b", + "0x8e3690e8e1305a670856ff2050c82c7740c2cf0e5fe9a5c3f0720a03f95d086ccca89b42d902494feebd98212e6751f3", + "0x9293d31c15f1efb53878207e341ad759cc0da68e2fad26e173dda7ac34696f0020962278a585a5d70fd523ed4d1b1370", + "0x8d8e47dcdee418c67e19f74811dbfbc243ec93fc3ccf4d74060248b39f9f34792c1012854543d52c92a442c091bfa87e", + "0xb79a367bf4fddd8dc6ac301aaef8cb019915c471e63d29b0e8a73a6f68dbc7a430010c544240d896e984d95704d3e796", + "0x94ade117e2181f8a8ddb711da5eab5a284792d210081cebeb47d5e1e2f13fb45798a64f476c7086ef0487cbb8d4b57d6", + "0xb5f8335d471877fc2533751718a2648a396d53981305461530a9531933da06d7f8c3fcc04f202e00fb342a6356a31f48", + "0xa8ec9d51bd2598994a09feb163c5ce724287615a79cd1f20072a8ef059b12e7f586d00adf543a0fdfff3e0f6aeecf3ec", + "0x95277667bb4ed63955852b1aeac648afc0cbaed3f1b29b2f3a247d20ffbbbe910ef45988722226f9a1899e34587ab00e", + "0xaec7ef85cb3b191b59ba9c835ec3fc9d64849becebc6ed812a5b4fd9b3820f91b17f7bdc1cfd76ee618d49ee426baaa4", + "0x81a05b20b5d1848b33f1c0685e56f917db328a8fd6541355a2f77d59a29bce83e82e2fbe24eac0859405c86b2d20c00d", + "0xa528d0032932dd58243d22e75e9bb2577c4df4280e4b1aae14792c9adc8c53505c91751038ef3dc6c33529f06abdffea", + "0xab7aa83925d20776028f0d6e6bc567c6a8eaab6ab0f69d16f49ef1577f5d668f9785307225856dfc36bfafda855f486b", + "0xaae8e9ea056047980638c3d69bf0228484254e709927ce3418087691645611750275b16b84d35271b799c7f3d51dfcc4", + "0xa35347e0726363b592eb51af5fbe7fd5eac3a63de5323043b5d267e5eae55b91afcc07df9651372aae31b8c8f7b86f51", + "0xa10ec915c22ef0c1262d13e2dc9dff2a2cf102e18adaadbc8df4d81caf8ca5e90a43173e2db1268d8541bffa5008e9db", + "0x83a6e54de9c870a31eca5079a24a115f310b9c024c65b666e7c0c684bac4e56edc1f3995297e9f018bfb2909b615e959", + "0x8efd47f236fc40ea652206cd1cc988acd4ec3b5cc62838f1a8c4a8491e97c69a4e52c8de381d60ab62f11baaaf29d201", + "0xb3c2ca3706c210b90d12aac0f73330a74f3e9dda2fed2f0200636d7c166344e8251d3733eb434a6c2425f3d82e95e5ab", + "0xb7db8bdf7adb21e6b56ec54efd581193921f8b1daa114912dd0c3d66d0f4a80576c55900a0bc8818d71ae6a2dc21b9d6", + "0x8af15c4ab75654332d0f7c70f2f04755ac13ff6d7f12439e87a28fa8c06085f4d4fd2ad552eff7ab9f3ebd8372b0276b", + "0x8fca3c13860e83c117715f469724ef711574e1b407bfb426d4eba9d618c8ac76c5d30d2785b79bea02cb4fb450d1f081", + "0x8ec607df55b25315120592c8c1ec5dbad5e9f6509fc6316855817c8fb545288e772d719a1f7e6c74f000d6fcab5f8258", + "0x82891b1c3fac351c0c89df158db7de3a3c23a6abc748fac0988b680319dab975fad2833d6335c89508e4ed6aa4476604", + "0xb9b2c65fa934e803c4fe6f84922e3effa4efba17f03d3ca624424fdc94d79b58c5051def1a65139a53d161f3ce917398", + "0x94fac78aa92e691a379c891ce52dc3c8d80063e668b6f4b2dc27aa7c57bb00d3d4eda89292b726ff22c12c1437a8fa36", + "0xa27899e81d1bff89264b5c348055c58aa2f58baacc8ee49b66a0ecea077faa11d1c8bcc8f3cd7a2665682ebb5ca22ee1", + "0x97edad6c33abb0d8ea32cb4c37fe80fa44f814d74976e9a65e0e1ae16d5959abe0d72c57f2afd37092a3ca8d11469f5c", + "0xadd26346aa4b24a793193d9d7c70e798ee4e2c9e7031c2bd3511e8e99e2a01e5757fe767c8ff6da981849ac702b70173", + "0xa7d98cc9af68480a43ebe6ef395af27063a268e6d71236629b38a2403c2c7d24590efe132ebddc7f984c9a1cfe2f6c7f", + "0xa3d1932ff4e6fd05777f6849b0e1640e57b4380781a9a095402c74f0cdc0ce8c97794b0d00a73ec98ae22a609feb8e93", + "0xa5dbfa693e140f05c42fdde80e43ffded6dc789d736852b479530213a0964483d377b4f52a9f45504e7dbd1b8c1f4def", + "0xa5e479a5028c83c390fb076ff9a5ea721df86b34224659a6546ecb52953220455ede9781e3ac13a69926af52187e1e89", + "0xa2d80ba7740380ec2cee318dbc9c2f7416f4d72a658aee81f2257b94ce19eeff3986efbd0f12f230753da68c68ac9c86", + "0xa3d45486101504603c2e65916aba55b679f2a368515eef65a48dc307af53f1bb11dbc405f2ee37638a51c063f78d46ff", + "0xb4d7ef5fd20a86c680d3300d5969841b0b81e5c329f072899fad1a71b470a3df21103fef5aa1e93374259ec433a0a89f", + "0x93691cfabbd9701905d8df6e6f68c009cd113220ad18638e83a5da2e3cf06ea2f6b515f5efccb3574c4b5e786abf4638", + "0x8aa98ed1c0b326d021f8f84bbf658b467f6ea728ecfe57aea5b6300a9ed4e6591f226fa08dac7a318f05aea89cd94193", + "0x99213c140d5135865d5a7b18be2a6ad2ee171c5bfca7954b73c59ae64c16f3814f48efbc0ff214811c48add4b2386f90", + "0xa48d37250828147d3e56d0b253fe01d6ab2040d734acba61fac91b5552c2844060525271ebeb6c96e908dd7dc0a48dd3", + "0x95075648e872dd8ca91a5b7a65d8b415aed259fceed57ce59e9ade1c49ee4b74bff31498c0b0e74b7e115a0caf8947d0", + "0x833db5038a6ffed84f73f2c909e4e0197a81ab12cecaa47429f6677a51c6c61f184ba8fe3f8f0c43ddbb50bc44354e28", + "0xb86aa37d2400751a34f985cb310d5074d36c094e100d7f768c9dadca455a3adeab849e635138f76c99b5162b0ae5c174", + "0xa0b0e25d91e0f981bace6ec4a451b1d28b6cfa11da7eaa1c4ed30fb1aca7dfecf7311a6c1b8f5cb1d3afee13a8d0af3e", + "0xb746c07ed218e366d455b10d2d6cc860b23935e1a4c358b7e10d142e36a9ca4ae11159398c0e90bced64ba30f7c35558", + "0xa104e4969ae8db887d8be9d681caec0ec79677ee88f7a7ca8fd361ee59f8d9842fcfa0de8988fac654a10671f34c94c2", + "0xb38d95118aa0d046476f55b132b45a9feeae98c03d06fdb1a6352a22819bff98ba516b31ad2e01c38cc2791228351492", + "0xab9896c16aed4cb4742fcb0c5abef9a37c8b15baa7a1e832603dd12bb444f46ecf80c79ac6228d466ab5dd0b75437ebb", + "0x85807cf2b397d6c34be327ec626d4f6ecc751550b381cd02d9b9330278cc78c399ea48d8b318e990f3de12c346abf702", + "0xaab44c483b8bf8c087f3a3787bce4906765d94549dd9ff97f606f554c44676920ef096ac985602d04b709f632eb682bb", + "0xa659b94b3393520b977bb54aed9bd7e57ee508630803ed904072aeb6f5a8ae5281c74f40b432b1c9c1939958a3d91f07", + "0xa036a2f136191098c41471993c58b55384db313adf272613a61d79e8610070b4da824af652fa42fc0699ac7b05a7084f", + "0x8103070d070d1a862f70bcc65fbcb1f8c247b2c263286e0bb78940265e511f658ab67f1d28880d091c0161818c4247af", + "0x85e8e7c16191d04c064a2612355469f9584e9b5b7b428be89884b18d9c561701801137e4921c60aad2eb0649894057cf", + "0x88a5daa5f8e6b0d7e10a272e9810c4976c6ea9a1ff719a0319fc9a4aa0d5f95f3170eddde7819c4a077e2df8243f2a27", + "0x923e49f17ae06c7bf15dd3e741c9aecade31c0c1b0d1212838eca1f1ed35e39f7051b1325152a55485e5a773815d1271", + "0xa39e9594188d4086353e38fc7cc3fecc92a34e0dea81b6afe99f24f78f888b5b2f9721b3fe3c551bc8c5ca6d181eb687", + "0x94956e364fd34dc9808f0d04f72ba533beca98437efdd1d2e67a7a9ac8ff7d6b52e9142a15ada43d4c7f7b3e1412808d", + "0x81e2ab4a5bda8e50e0dc85716b3c2b0e2e50a828dfb8c000ec0535a5c2f73947d0445ac2f6d55c8709b6b7a7b803a95f", + "0xa66ee80568bb603fb5006483c79df216e0f9c17566789155cb2b5b0d038fe6b543d168d21ed7a3fd1b3783f59eda9be8", + "0x99a7c0fbe49f2928cf32745b06e27f6aa7f02074a31d602a517dd3be7a3eff38651374f29cc7d0e07f7aefc3b6aa78d2", + "0x8a8fb21b2347e90bde9096de12fcc287c5e67299b6390c44a3fd0aa5c8a3f70afbd95bf0c44eb131b0ff990adbc2c539", + "0x82db2dac35cb178f882569f3357df5b657a1ba76274538cd68583774b9a1e07e2acfc04daacf2f635047cd3b1fcc604b", + "0x808a738b1133ada8642511960915f23b99ce5006eb1b76e8a0ad9b4c3ae2cc06b7db1501e1368f0429cf4f5743c3f627", + "0x894d85ac0647d7cba39a90493d55cd5b81e1161e80072f7e54d5af9ee11039f6907fff44580914436726931269bf5e78", + "0xa1941950ff88ea8375f701d96c135ce22c40b2f937e4ed549d55f3444fde0f93e987b53b690a648421f81b40b2cca585", + "0xae5d0b0212cc1926e8fc698b40e40f1780f560846a2ef961bf876f1568dc96d50c5fbad95028c3f8545db95e8407f03e", + "0xb458713ccb823008017a8caaf0412a76ea06f999f669408e2ea440e93a20218bd7e26acfd4d037b6ac89cb2d5d9cedfd", + "0x8c73cf97bd1af6971646a8a61623b059ed7a67ef4b687956c932d70e92fd866783c3800d9e5eff24f74a97f10e88177d", + "0x976dda791115628bf2415a94285b1e2da8eb8a2a5a82b690f30c0d3272eac2fabe33f638ac722f4a367e89831f9bb169", + "0x97a51b9e717e164fcc53aa0ee1c4dffa12f6386a61a750578725b272a7be1ac32889ac8deac8519e084d9f5f755e7dbb", + "0x89f2ef62049e5e07269cae50aebb7d829cb3d3f3ef1c37d9f2e4c17bd7aa5c3c4e0a096f06d6edb6b92c0db741977bc1", + "0x991b8dda6eac32d371884063616a648ae2495833f0712f0115b2928c75afb5b11538a2af1cad2c3c0b0b1ea4d1cd964a", + "0x8efa54626eee0672299b083433d033a0ace4353c5c82b848e1a70f6930cf68bbd70a41136a3615ab4a15b86de692234a", + "0x97d73be00bab548497410aec663bf3efcbae53fed6a5ce4f9d99fb2bd2f438b37700c26c3aee61bc854f3e5db4e80447", + "0x89b3000280f1563da9f6d8c666913603e6c89958e57863768de199d1755bc961fccd78e48fbcf68612d369bfaeec8e11", + "0x96f7208b110e981c3bfb0a3a74f8980cb15ac09a4a771752af97189b093735d8298fd56251345dcdced37ab7d27ce0e2", + "0xa21e01d617d4e3d9a2c763857bd899c9cbc78abb6e682167f1c55b1e79583010a9e30721e0df0a8c6ef37233b850751f", + "0xad5415b2046d2339822c6aaec97bedcb6040e32c92d99811a4ac13031c18f9ff8737f53294bb594648bf9f9f8a9231ed", + "0x93ddc0a5cf59f29434a756e8b728be75adbbb23308bf4cda3332c9555726dd63914627434d89f8cec0bc8e45db700061", + "0x8ee80c00803f24697be30b19ab6d4981f170c6f545686927095edf466d51286a911d7523564c02ef5bd2f21ce34965c1", + "0xb898b924adeb8d2f00a301cea255d2852ebc8a9098cefda2d2be3630fc52e8ea3bbf2cc9f5b06c2780ab9f6e055a9dff", + "0xb17cca0478b3921d07a0d1b83820ed5980876be6054be7c5d868182d189f571c4e76606e6f98498763622b9488abc1c7", + "0xad3673ef0416b265ed2f1b544ea9e6e0e234a519b2b19c48253a608fee94c2f1370f240f0cf6335cf34d23215f8d7498", + "0xb539b82d1a7a4485baa3914c8405e4c027dc897e34aff40c7f1475316d055560051326259e3231153b7ad63cbe6f0dd4", + "0x8d2c74a2673bf71cb5e485b6604c1f6a9c27dd4e1767a2dc87bbf88d3ff57c2b48bba1f151aecbd66e52c531d6f986cb", + "0xa380219df112c01dcfb6026f4aa176317ec300f33010c437348291d4c2f86d91027df0446a2c601e0e4eb1ea912b23f8", + "0x889fe61a9f29ee5e84d39dc4ac584e0ac39a4990318b878a61b88d6c5fd15baf88f3948f2832758dc6840b426fc2a3a4", + "0x86c6923360f460ef824cc87319662da89175f60dfd006e48dcfd9877388d25cf78baf9b90f51e2b02ffe7690220b4bf4", + "0x8cfd8c1675fbc12e9a93f633bcc5890f4d0e90d44ebe70b9bdcb2dd8f856f2af4e10b219a3bfbd00c111723b15725132", + "0xac390eb48a070f100773882e9cd2f320b0423ca21cd457ef73dd21c8e146401d56dae0750cfe45c3a95347b2861b4f76", + "0xa426bae8c32648324b6ce8bfd656644e9cbc723493f63765a3aa14b2c0946b2cb6c6589077faf7334b4ec077aa987d9f", + "0x8d3d115caa45e301b5f381ac18c84e5be43ade56a3d098e1cc90ebf998484e107d28badf5ee937f5c916d04789a09ba1", + "0x8946c0eb901a92e84cf7d932755976b3adfcd7e7c08458fcc32fd7e7015e174bef47d41265f3da0c85256c982fcc9df5", + "0x92e2bd0d239428c6dd22fd09fe0c0d5de3b2825118079f9832ea22100689ab5c1bfd19285502a902003839a97f486e2c", + "0x82c51348db2c2f5edd2dfb9db9bcbbe3cd708b3d57100b0556ff4acd820644807b4577031b67843e1012a336712418ed", + "0xab8b69d288ee48ea067c80e9dd1b5e0415b761fd1b57ac0e31916b46f7cd02dbb84c3df75f30c61a0df645d7b35a02eb", + "0xa1cd12576c92e5a392576a0af7d7cbb4dd658bc7ce9acf678ede63613a9a3912ba613ffcabc6a37471181ce74d4fa0cc", + "0x95412a54fa66d2168ec3b0f4b65c2fea1618088578bdad9a1c6c7e8020e3deaee929ac72c07b4d41fd2e061dbbcc7d7f", + "0xa990fce60301c4aab85e7e48a6b765fe389af8378a64448eca69c88ab97bd13a717c5281bef2ca22dd2e2035451e28c8", + "0x835324fd6c7348f5b51eb34154e38af85e28f3f88836821e169e5f1f34d131e23babd2be4fff9f9b028bb933b6fbf724", + "0xb4f2295fd119d35b7a9239ce4a69527e299d7292e57f5d33c5b1d61abc6f37df0d3a586a32a084bb6532d51de2e30d00", + "0xad4d175d792cde46539dc81b1615e4b0b17effa812eabb9e7de2e97690c4153c16988a6f7acba2d13a9c36d04e7b6ff6", + "0x83d1567ce2f413a09fb5d8bbb93784595c7ae71f54b2a4c0f7875fa6c53975d91ed26dad1d736f399ebc1b8f680ac834", + "0xa9b2171322fff1115f058009aa91e59f7d239a718055892505482bd4d63d46a2c8cfab739f574cef0dcb79dca821ce35", + "0x8b7a85a0d0f304b8a8e2bee130fa6b60684f34674f2cbf6bd3f85543762a4cea292ac68b6a1e4b7ef933fce803928d79", + "0xa1de01a2522314fc36c9a75465364a248e633db42bf9a754f6f1cd7455544f99b5d97e2e5795f6322a744e39f8df5b3d", + "0x94a534e14911f278fda07f11df32bda572f7f3fcc91c382cf278c3e024a779ada15f74990616c7a2666e065353ef7dce", + "0xa6523b670477b6f75d663722b728b3d85dd1245bda911bd34e833a8baabf617806a7eafd0fd55db4c7a53b3885087b17", + "0xae18c8f474f86f7330fa21048a5967193cdff49ae5d10bef3d16086787ff03b3bfe7c402dfeb751f57e1089c847dea48", + "0xaae4f92d1db4c6637fe5bdd62cab98b26429e5622436722af64240f9959eac8667393041f69106f27270e385f917ee8e", + "0x857b6f4833c80d344d5014873aa04a5612b8c89f0b98c3ff7d1228412e98eacecd4bca018f6bae93319d385cb4d2abbd", + "0xa98c5d46378401ed29d406f55753538cb6d474d21bc5d7460a3760dc89a60d1055083afff01dc082267ac1201b79a20d", + "0x8ee06904977d5f208db8a98f2f8dbd725e8cdcbf0ef4fbd4143ec4904800f134e51b1f01a1f1ea6de219779c04cbb270", + "0x8f54d5c8c4fc7842480f59f9947e43fabf1af6bf7a9dbef1d7ddaecc7a6cea77765af7ff0284d987efaa20ebf7ecc01d", + "0xa20c6ec39d39a00f72bdfda7cb5829d877f0764882b1bbd4eb5bded9503e909062fec28178d9e88812dbc2b2defd412e", + "0x81d34967217950f8e068c58a85a58a21d46fcaebab4676fb0aa283bea122e599ce2765bf07f3742da29e5373b62bdef6", + "0x8e3fd5787b3ec7e96019eade56104ea71c264a9ece44cc7c44750b6e5a81529c01cbe84098da544e77aa69266f73defa", + "0xb5244e1d7e07c322fab820722138f213b1c66a58d5cafb4e88fc9fc4a6af356bfbdf223ab37fca72b5476c377e738796", + "0xa5e1ad61d1151e1ac4c8717cf115a83c2af0051d7fc1ff3d85c50934e58045c69fd54d0f960484f3ccf29f3ae12986e5", + "0xb6f7644376a54927f3db29b67f4d769133efac3d88b45e2f53428f4ffba8abe354a0927fcd1722cfd759f9728b6e0c20", + "0x99a898eeb17d0c5b3ec54b4e889c784586ee5503286b4b6d12d8ee280956aa3d0f67372d8aecb85ba66ea7e7dbed0ded", + "0xa7da38d7d82980dbe7e39933adce8d3b2bf71ded21e3092ebe78da8794fdbc22f71d14c5bde25b86a0fbeccef998bf92", + "0xabce2aba5bc056d90271c7f82d581689798fd3faf2661a27b8208deba79099e68954327cc1682b72d45b6c3d2763231b", + "0x9264863f5a8526efac7474e8953ee97a525af5db33509fd37dfc49685a0500a49f5d8db386df6c3365c1fc2bf38bcf8c", + "0xa69bf58595b524d3b72f2367d0828a968daad66fec0a3a9c8426ccad4a0894181379463c939c82df5689e5e376d80c0e", + "0xa476455c5ab00246a0f5467ec5da418d898a6fec674ea28f3abeb9e62185df4ab7cbcddbf5da01fa05e3fc81c217066a", + "0x9327c1bb735b0ef0ddef5a508e00afe3d9b5eb7ee2444b580eb851d576164119df03a44b746a5b4ad51993c0031dc2c7", + "0xabf8192afbde91806f8668eedbeaae87f48d7caa4871c8c2d774b83fa6953e39bf7f310c01f2caf83bb6c868ab38dd06", + "0xb9e519a5abd4522d33908012263e08919d3f018b6b1b8ef301d541bc3bc8510d88d9f3b80aa9b37aa9cfe0287b4714f0", + "0xb051da19fa304779912fb7a5aeefcdafa1ed1877f112a07211be5e8298fb30b4f55dec2b8d95d9e878ebf1c7d0028704", + "0xb36169e317150d174ccda4ace5faadb681f1d69a8548116c01c7d2ed553fd383b046ebae17e5fed6e7c231aeb5fbb37c", + "0x81688d762c1e996cec535331b2c328d5f69b9f1d36bd5d4d53e1f8d034567c560c02b07eb41d6037203894ff567a888a", + "0xa2f5f306d6e303ad425cef7316bacf3b6c103352cb123d90a4bc4b1053ac6c1826b33f8b922c43be10bb068ce048b5ac", + "0x8fa24ce3aadd80fe6928dcf9c43f7288bbb19a4aacd610f62b9c64ffb2269297a6ba3cbdeda3a8ed3ec594829514b69a", + "0xa9e0ceb08dd91f710fb5b776eb8cc02bb6d23b2342127ab46703df761e340b97f903d5e02d450ba8c625380edcfd8411", + "0xa45f9fabfaab57f63bc0791d936376d0bccb939d2515afbb2d7f6abd57161caeea81fe3a7954c22fb08b9c17b640b5a6", + "0x8f68478ac4737da272241b75e23d9f190deadf2fecc1ee4b37212fd457cb70430eef5712f3f1356dd79054351b013eb7", + "0xa1de88168516726edd4e7630b13c9b505cdfb248b1b91a39e9e6c3fe433df76d05a8b5f3284fc5bff014c12b5accfcf2", + "0xa4293fa15618cbb2c121852dc3abf1ae63c9b309221cd06d505414a27eb393a20cb9e94ea5e124793541b4cee6a7ad63", + "0xb0176ce314f091a6718ac726179fc50cbfd0c86e4b8f083478974e0171d826e5dc790141c2ede7daad0eb78cf8b2490f", + "0xb172f63e6d945a42d07dfd3b85880a0fe36715b391e241e8c5e0e4390f92cc652518ccf7944428302afbd7f3da2a5cd2", + "0x82fd588ae3429c906b01f9566ef43ff4a7bc6b82b1a244f6c3512cfc8fded19d7fea8e23ec42076595567690140ea178", + "0x887a206ec1099b5a8babfc1827f62f54813b06f96632979ef77302436ff60ac89aaf03671ee8ed59e521cbdef17d4624", + "0xafc9819b3178aa2196f3151651b2e44785e3a593c08dc872212023102e38c2f12ab3b94179d3cab8d6e8b403a78d3a1c", + "0x8207cd60c8ab5f82ede55347aa60f9cf1cd5fb914c3263ece2e8b1ec629111097575e7573a82719e7740da95c297a623", + "0x8271d3f9cfe61b7352666a7481e9956e4a3dca95ada10b5c85ab0c2b5dd4e7170cad80d3ecfaafad82c7fce630403627", + "0x8e211397c64a62a89d431bb6308653b3e289efa48df6fff3062ddc743ee01d2edaaaa305eabefd1f9f5078b6fa9296a2", + "0xae9408735811571fdba21d175236e9a76b632a979db89bfe3d7c19cb560a394bbaa0be590875b3ed79f1db07261cf159", + "0x95d96feeb7ac2510903659e3c6b0f6bc93bdf8824036e04f6d552df3ccd10124764fdc0ce4460fb9995acfc52f157441", + "0x925f73e81432b6041f688ffce2e59a99e8f9d40cbfc08a126861a7ca751063f5259029a2b970c319e48b0cdd60816c6a", + "0x933670489d45788f7951991c589a9b789614357ea90136ed95db5cce69d6c8ffb03af4df131bb04f17e3bdb7f24896ad", + "0xa3d26d4dab78367148ee7cd43d4c095f0e218a6a14f69148cd7c0fcb05a570eedce0ecf3f2b5c4a92a2a7b6dffe18afb", + "0xb68b4c89a53d59276f3202d60cdfdb750605115bcbd8cbf2d0d2678df847860e1f2b003dd3ba7590a2b146610d212b64", + "0x9311238780ca76c2336c39a7536d97ba4b43befa2d70289b5f56d012eb979495750a9dfd2b187d349a1613fa1df75781", + "0x9642072d14562550958bc1f5ce3c6f43337774fee12a8e4b1e50ac297ebb38f0c6f7ca5ae771389aae27f5aee665b851", + "0xb44a8885fa78e7ab4e7e14c74bb50bbdad73d68dad08c7540d378da9ae5d0912d173c57e7310fae6dbaa16594e1c3710", + "0x8d845f79090f8879e6eebd5445e51d7cfcb0dd2fd15c00a44982ec954e95ea6d190f6a7762022bb231fd10ae813f5b67", + "0x9497666dfac0b749760c7a555138a5fc1042c47f43a85b6bc8d56e402d1d9a1e0fb1e19e32355e8a1a276765bbb37dd9", + "0x8734d2782d67cc7bd842985461cc010c22210b4037c28f31dfdc5fedbb7e5b26ad6f9442c26950fe14c7969f1d9bf447", + "0xad73db4c8a0f4143da4c12a7b54d57620f47f99696b46c19ea98c97fda3142388209c99f8bf91d8ed99e16e479e88935", + "0x95af163a8b48f0c1d70665be8482e354f37e15dd275ed6c3119cd3900b44531a4baab2f5dadbedce2cf9a2f3e034f664", + "0x88fcbfa429e7ba6dddab947d7b5368b0a49bede01884fecf2dcdb0a96a6933b394b609e8337586953fdfdd5b8e0603dd", + "0xa8d83435b8da5a453c59000fdb234f577e2224b4af3b229f8d297ecf6ad8c88da2113379125516dff23d16d498134111", + "0xa64101a7369187cd5f86f340067eb49bf026f07195988e333f1b677beaf89d579aecaa4d0b48957b48a5574831d30728", + "0xb124ecd75042434932649a4967b6dd17064e0b50c38ee8404c82c3ba9ebbdaf45edfc2b15ffab6f54959023aeb793ac6", + "0xa5d2cdb9f11895d3abb466a13a7c297b9dc05caf9c895790daff78ad88015b44beb8d1dee446fdb13b845c611dc348e0", + "0xa8d38ca444da1a0c5a8ef25ef7f143e9e955b20b0dbf7268105e11c695413e0bb49eb458dacd0fc696478b7ef7e707a6", + "0x8979130ff476edcde9c50b6b1f23e572e4386cc950fa94fb6395136fbbbe3eb45ca7c35f51dedc4287e90c7aac37e272", + "0x8ce1c858048681226c90fffec205ee771325b8ef8811df4a98fc96657b7ddba2075c742d0c6be6aefb9d675437f8a56d", + "0x9633f379da207917fa90af7264c00d348bf854fa641a4e261dd1ea8e03683138c2432c3d8dcd6ca43e4a2b03a8dbb296", + "0x99681e9e966d14a101cd29b2c310304db3026bbba1f00ba0ab7d712a239f8904b21a1b80af60b7d2e025fd71f121b766", + "0xac0c625b1c5cbe07623faaa5200cccd289a542ddf63e30d21c9c4b610c554abd629f36797dfad86e912a85bdbf0a8a43", + "0x80c7668af2929e8fd77374f5cc59a986c083d79a08c78112df353974efadb9d274c4e2521522e1cb4594afc1970c94b3", + "0x921f58dbcc41d96612eec584ff8869bab5effd256c633a4b91a3c779797f68bbdd006a30b7e5c0768da7d93ccc16c3c8", + "0xb34cab9f522e6eaa2a81bf46cbf6c48c426db075dc3ad61023abcb411ca2b675fa8b69fb824830ca7f9f1fd647f51412", + "0xb30a178f5fc896f77e15a16b8010e61cd99a6d521b3ae1266bc1cc430ec4651b8820c28bc9b8f4887ec9425ffba0e914", + "0xa40f6e4cd08636322189bfad39c98c75d545e117d4d0fc496b492c85afd47355557b68f86bf5a8dbf0a78bbb73a71229", + "0xa2333a2b11dd5342dc9e241412ca6d86e2870ab455413970b279c155da2e466aada7454113c74a5b79d7264e3bfa7020", + "0x975f02ca00e44711bcd493220221e2860320c280f63357fc5b76b4dac5e5475ed4e00f8a9ca39de5f23f0d72eb168cf8", + "0x8fb8d64010e5774ea502ecabaa4c3a07fb0c718ff647264200de5ccb4f7475c1813066322db344f352fafac40838cc14", + "0xa83c1afd113e7db5e89fd4f88036bcf8552c9df8695b90918a3fbf1122c8d1de86dd742890a42f0dd39cbd5006ed6415", + "0xb17197b1d0078a2ae3ad610b0bdc94444ec038b36327f6ece38c401c0b9e544007ef3a4d66f6c53a402147d2c02051a5", + "0x9452e086cbe8ab038ce80f7dc80a91df662aca5de694ac33bbf3ac07f497633ed218a5359d7f4f482d1547b43f67cce1", + "0xb7ecd74ccff11fc788621045da8ad99e2baa1b38d0b995ec26f1c415948674dc0139122301a1d590125d1f7384d0f978", + "0x86722c2a6da24ccf76390167971736aa713372e6de2bb3ec6b37da7dcb9f4bafd9ec56348301b9a422c349471a92a68c", + "0x8132a96cfd9d16fa2b2536dba1fe91e7a3f74b84e1778483cac27e4700567073639cb639f7f7ac30c7b16b219ded43fd", + "0x8708d6405c2e69cf5e5a400db07682859c399fdaedfd921ea7bca030e39833b4bf404457dded049bfc9d76e0b7f61200", + "0x983c4984f1fd4f952cc1cb6874cc2d6808513aff3e32d6846ff76988895c5b9fb8e85ef813a2f6c60385f5b5e7c59abd", + "0x92691e7d7b5a7b19a095dee6e540721d19eed2ef129554dcfd0e047d764787ee0c4a3a83ae7cc2e18ce579cbec9cf20f", + "0xb7a9b78d91aa1f877ad428ed243f100d953ac02eca2dfb8288e60d5d1ef65166c15b32e5b75d3ca9487376a7f11e7a0d", + "0xb827d3b4ee3062b93420eb1a93fc365f08b432c651cc28d4c8fd44a09374465fba07cf09d3ee40b7533dda993fb1121e", + "0xb9d42244c343d3c43a35b822c736af53f71727db5b01e90e708eda61c984715d796d88b7f7a9f2c36b6d43e61a41cfe2", + "0x83249152c6ad0562f8210e0b2c9b34642da76102d1ad2c4aae2e8822c64d192a877a4b9da75effaed7d615b57eb6a760", + "0x823d196c0c331ced409a1a021f2c905f0b1fb014b2cd6955f5bd67a00987414817874b7928f83b3f1f017bd14958c0bd", + "0xaa3e6d09efe979a3e2fe2e14b6e7f6c64bb318c8f2748835dd664d2b2707fccb273f650a409f3138a9abe7451a1399f1", + "0x8a764bbeac0a496bd21d4300a49bc49ade4512ae52821b2a77632ebdc499ae66935dc46b2f190a2194154ccfafb0567f", + "0xb352af9eaf180533f6ddb19a8b028309d4a843ea7cb68a344986e7b31d45ae329f1b31f5f6c99f609603827e32575c8a", + "0x96a3f2a25f5960e264d02bd1811689622fd0f4fc7b730d39f93cc2f86f9292bd5b0192770963c400e7a9156a0cd4ab8b", + "0x8684f5560af9f59f803618305386c3400c9c191106ad2ec2baaa19777fc95ec5006220c25232fd99d7d9ef17b929a7df", + "0xa2624618d0f12cf2d708d5648a678e1a25388e44808b7905f3f98c696abf64f4685cc8d1c0bacd161e1be9eec611c5de", + "0xad341600c64b0be604fe08544ae55c93152fa13bbc245c7eb652edd3d0304d237798ba0d53a20eca99ad9d22b20cc713", + "0xb08059c6a1bb26bd489954d9ca62bc74250dbf007fda047f08e4b56729c9e45a40d47aeef013eafa393af3c6bd941488", + "0x982a3d0a950074b85101828b7a52376ea1e9583ab83c48c2924a7ce61e5b5191975173bc28cbe92f90893671259b3f8b", + "0x8db3241e1a9c98afd5ef31c5d16690fc894579beae4805bf50e4403f8f9d958694f9a688b8b66c7509870bd209b2a192", + "0xa7ce41dc455c56d2d3bddc00ac1aadebf72ef9bb34692b4f2824ea4c781337d3887a12b419d3e4d44dce9ad465da0977", + "0xae65402d78bb5b72fb17b86ca99852a3c7b9d62683b0773ab9fe3aa074cf9ba16aecdcfdc338363c73bec81574959503", + "0x964fef3168784b2503521b59663647f5875aff199ebe70413af241821f6d7a7cf2ed375e21a7caaa606378a435d1e766", + "0xad2fdfe7ecabdecf8d24c79fbce2d9a0d937305125d43b0e71ed8f50db3529c30898aeb335c39de5798a9c309b731a7a", + "0xb51ef9973341a18fb146900df0c12f4339563b09ba6fdeacef65827ba44d09a907349a5f7e405431c5631c0d9ea8d6a5", + "0xb1241131485067888aff41aff45561ae44c1cbdcd6ae4fc21b7c202183c6a0b10a316104650628451f2ccad4cd9acee3", + "0xaf5767c5fd7d8e73fac848f53c667c7b329e816370c78e7bb2f1e7ac9a87a0468eb267c583ff5ddc42acad6f9759bb26", + "0x8b19e0ba6f0deeb103b094c7e184718249ea341b46f6fda2687fb39bff70469cd6c453182c802abc461ccf94d838e3b1", + "0xb7832d8f9cf4d9b64719eb9455034ae33c65051a2ed7a4b7eb25a78bc4fb6ac9ba958fa8307c1e3621abf1e93e26aa83", + "0xb3443321e8904888cdfe35d082ec102e9f827ce57c120be71a1e424dac256b4fa203065201ce02435fe79df8bad9026a", + "0x98f10697324613f772d40d9138a159e6230a2e625dec25c7aae91e895d9862f33acd5efd472ffbef56b68c1cec8097af", + "0xb4e7e1b9e1cfda71edf160778d20de7cdbfceae6147956b4493aff3c5386a9d668b0aa4276dcdaf62f2f354993818cda", + "0x938112cce572f267e080a5bec0dcd69593e89e9350a2516cddedea5b22c31f4033d1034b09c86ef2cf9ee7de4c2a0326", + "0xb667e697345a68714c66ee0624e21713bcd3746db48ffa0538964d2328925bebd1cc28b366f4c33a818c8277a7e75f31", + "0x80a2cf6e362ec166db03aac175dc7ff06bd23df80d37e2bad7c9ecbccaf58b667bc272930c2027c9320391980cbd53ab", + "0xb5bbce665857951a5143a5db951e1aae97bff2a6a5075a58545b6481c48b71e6c1833a9e16cfe611e2f6ce7b12b26765", + "0xb3ec32f69523aca2261cae182175e9c24fa6d462114fc870d3636dad714ee4df583b747884cb1f8473a41c5ee9c5a6e7", + "0xb2f2749f9f81337fd00279fc3fab4157a59fe6f2b11805b2a8a2f1b7085be38d62517a2be6258d8e62e12049050a78f0", + "0x972b54aaf788c2c5c861fc754d0ae6a51a8c7ab5331fdf008ad16ed3aefa652f19d68ce9d91966c1113c0efe94645098", + "0x9391b8239dcf64271e6a3e06691c1615d5f1d6f3f99d9ce6cd07721d258f819dc30228bc76000210487cd126aa88439d", + "0x8937ad2acdfd6e8b0588818c710ed04cc5d9f554b3dc2fa3de01a508d05059a69bc82b8128f6d4e1f06315321672c4ee", + "0x994e3e905453de6476c7b3267f5746348a4a49c8f43373a958ed326e2a44f36cf80d3e6fcb6f1829128220023d73a0d2", + "0x9926451552c754a9ddd25adf9e5982f5aa435db844372d4dc3b8ba459089598b446df633ddc2a6ef68849df9c089cb53", + "0xad1e11502b1033579ac79905b5f7632a23e6892aba18174489b18ee32464f8f89bd4a6d07364757feb4c6fdec03bd6bc", + "0xb2f86f4885e33a0ffdec848b679081db153f3ddce6132ac4f44e1a35bcf7f8cc2dc369d554e168e7d0ae88ce093491f3", + "0x80d9e416d99c52bd7f3dd2c1d34c73b8943a3d20d5ea4876e822d13f6ea1b78afd2d6401db3dd0752b0d870ed513e5e3", + "0x9587742889f615ea33a3c3d50b93002105fea3a1d1c29d325197d296cd0de2c0920a24c4506a3a489512fa96402350ce", + "0xae15176a8639f1cf3512905591b0fc4b9842ee89448b90fb58c76017e93fc89a898185e1cbb9121f4897cd3f28357a24", + "0x89dd6900cd2cda46d13fe3c8c22a2303f19d5823c7b6f73aa1cc08b272b92272d68b2db5b87be518f3611e68140f0fab", + "0xb1b712693328a9e163db72c991dccb44348eceafacc47a936d9db120200a8d8a8b80b326a459261ae3be266cb2b9685c", + "0x922b5aad4760b10bf367f663a2f33fddf6aef662129cc641a119e6a90e91399619a25a75e55128b59007c94896a9be8b", + "0x8d1b5908fc693802ec8fe37ee2a28c4a94bfe6dbb7e5740768d019a7b5aac6f7746f631179bb4d7ba02b40386211a84d", + "0xa0103bab8c982832b8b1c4e490e6ba9fff0be5f987861042bfe8bd2705f763c70d6182dc0ade97fca5b98d602879fefd", + "0xa761e78cf33a8b17cf036bdbd13f04d124673b9537413dc4e29b735bb63681e780e8e94e6991698ed54e775fb0b60955", + "0xaca84e07dc32378ee31a6e35e099b3de94994c0fe55241319b27337689e3170c253138ff98e86278abbc4afc6fb54208", + "0x946efd70224b1cb72d7612d755dd59e375e3a9e42d3416f752a81285f8dfa7fc606e2050c379061ebaf69b6893f03735", + "0x85bc55353a417157650405964365225b38f64bea668a16e51e1bc1583d9238543384bca3bff90d0e21965c13a5a2f51e", + "0x85421cc22c554e0c287bbd34ee9043babbcbe0ba95afc1fffa490d033473ff5c99cf36677a188ba71426770279017dba", + "0xa822a515e88fb15ed0890bcea9f2c1a8525f1aa8e53d1728ec18a697663cf18338884fb9b8b0e1bfbea9fd79144d3a55", + "0xb5a27c50bd445f60f565eac0d548422ef123e0f5bac8a6f997910fb7b397f8c8165e18dc3e20cfef3925ac2f66de26eb", + "0xb322484c59605f3e60068eedf8cff2ff65dc58f2a6c5d6cd848eaf677fabd9b47b612fa9c3297a291c031891f834b58e", + "0x8318d4d58aeb9e0e986e27abafc13c04e530705134d6bc9e4f7c87e0a44560d4fd477105768d9b3c306c895e11c89328", + "0xa474a690ecf5ff219849179bcc7f74d07edfd44dd137be7423c2c1cae85ea58f0917632fe6b5850453d37170c9bd6acf", + "0x949cce8399687e146c129318dbc95bd80ff57d5e645c22763e23551f73c882dc7a37359b17d337acdba5f74526cf1d8d", + "0xa157c463eb6af9d52b166ef463c56f2fcdfd5ce062bd5567c7453c4193f09ca05cd4aab4687f01fc3346c1bd1012a54c", + "0xb38121f70577f5478538f32ce06a6e2c54d8ea3aed48b4e882004041074a859dd6a0a878ad0185dedbd1a1e698654cce", + "0xb04c536ae8b86b023a0215d434910410f204863f7c678f4f9a771805ae73a7e3337403ae67789cd08fad940690f90d0a", + "0xa422c5f7eaae74a9ff9b6f125cb7155a53b8a042b34701b9ef229125a2cdb2003f147abed8668e16b5324b382bce751d", + "0x8159196cd397cb93697664d79f2c3dc793f0448f6c0bfb6f0065d71bd68f15ab2888deef3f183ba81e1347395905ec12", + "0x8c6a53befa3cfdd57ae0937fce631368f4a43a100e5c7c195a956ab275824990789694c428c478bcdd86c4827f0eb158", + "0xb307544c1ffa8795fa26cf418bc98e70a827b9da21fc19da24bb5a4e83ecfee4f6aa67922965676a3fd06290a852391b", + "0x8b281808f713234f26149f1239c5e2846268396ce25ec586573a0c7c48a60536f70ee122a2f0e399f3cc8c5bebd543eb", + "0xb5ed464b9e482d6bbd249da9f440f0b8d9e86284a08e9f29999af81490a38b891554589937f0ca81f0b64b3f8905668f", + "0xa7dd907aac35ab3ae50854119c3a5c12b25e1afb71d78ab7a311421a471c78e0c4e2a50e555027c5a6ff39e34490a28e", + "0xa34aaf442cb474cb95455b32ce9c8cf3650827faabf6add5ef699ed655a604e098ab5712cbd7110b9a62bde53d0741df", + "0xad47b8171a66824b4c29bb24ab78b2079a184ccc96fb36d155f8379507c29945f8aef710347b923505d79566bc565ab8", + "0xb40160c5940ef10eadfeaaba8799bb0ee2b258e812fa339382a57f34e71afaa56528667003147c5f6713e24e5d4e83af", + "0xb40bdcc7ade537ae475388376090dd88e5d737e749ca4044a461d5312e5e76513932c9340f4b6b0adbbd2e28932ce588", + "0xb2c142930994ff639cd8a8ba9bc5bdfa11ce4ae5c6c85a77cb11cbd7a3f3804bee49d1f2fb6e402651af1decd29a6e1d", + "0xb929c66d7f8c644b33f83302f0608ed9000854b7be0ea19f7e1b1287ba712b2db5a82ea04f98e12d920b4df98e335be2", + "0xa96aa201874f1cd1641120f36ed2dc32e8808f657591eb69c7f473d7660a1fcae7a7c64da29be72683efe23641d3e747", + "0x902cce81b167fc0054ff98f8a4ed8b0301f41be53420d00f31bda4e827df507a17dd6dffd5d3b366501074c40047dcd7", + "0xa6a3bbef2f6d45f87178b0ee3dd738e865a73f44de94366a0b9405a80e120b98bce545fb909659664ec28f7d60c3fac9", + "0x9218be02c2bb9416ff85c57a28d56977c8d54d5a5688d5c6bd367549a0592e324ca3c8b66a96f6ecbfa7564919bcbf5f", + "0x8bab2fa3073ca793c5ecdd35ffef4e67ba1b21e069c6196fcc9199c60930ab8c1d7494365da171c14b1f79bc358ec358", + "0x81422e939abff77260af4fdebbb78576ba55baa1c5286b634d53e6de853decc4d45acd10721aace1997006c4b3cf692d", + "0xac9ad58e5b16f02e9fd1f51fdb7355a737af3ff852519e3d8245c123295553ae307190bc6e3acd159ae7199e040ac367", + "0x92949ce13f7bc97851de71c950fbecc85f33389091530cb3d9a8bd6003f60f123fc2ef3d928e33301069d5e80c4069f6", + "0x8bcd7fdf3f14b78a38af5e48f8d0761dc77aab735f2e8e0f65a30bf24ce9ca00a9796f0d79f89b4848881f7e9185ad56", + "0xb8443c0edef3327dd04ea0c95847c8aecaecc1bf547a15843c8497fb6e1b7d0d59edff6cd791a7f7999f386a409646c9", + "0x8c9562cafbba484678f2b28d31e191bbd0a4c9f5f06f2bb62ff4858afc9737ea613e4170d7846d3473977de73c061411", + "0x99cf0648dcf2a652d4619f3a84e5bfb1b0b40c6750e10b7b6ec8abe172518cd344ae746c2e22b7bd123a3748b9226e14", + "0xb2b3a6f8fd8074c67f686b2564ba4c5e9a30156f99f1a725046937e1dc0e9e7e4e962b17593d70dbc12ae4717ee3e74b", + "0xb6cceefb811b674d86ed9353ea729e025f32f91d4eb5716ae11dec00f33c74cb2e809c9d06e32f9503e677838f4f1a6d", + "0x90b89f0ffdd2e3bc054cf926b4b51df2ca68b273b6394c3d782dcb0c834b8afe45f8348b58d12ab8243e536b7f15c1be", + "0x962f3d652eb43700307b6bc1a465f121b65b6952a64e646aa03e6d051f4d817a4bae9c66aaddb29b77f6741341653dfc", + "0x8e8b685fcc8caae5ea2ba6a28d45d8857c5aeb9faa9bd5920d4f12727f72a4bb4c477883b093133581357c9d13a325cc", + "0xadbfb8141ca1c4ba4ca422c968a282b840b0f4d473fc41ba4426e7bef349f01149d54de1a98760d19afc5b7f59d45fec", + "0xba00d59e0794e2a9f21433369e71ac21240faaa12dbce7e9c49bdb5032142de108ae366ff1f33f38a254c7e3d42af999", + "0x92b149afaa1529ca14271f118573a8721686ef4523afc4f69bd5c3cd9f4f2c3e7f2aaa5d315f34eb9cf60c4dfe047ee9", + "0xb56393bcc8493d6b4e38d50b14d8fe21c7214163545454e54eac5fd0656554642d1985b7c9f51ef130b7c7f3ba79ecc8", + "0x8fe38719738faf0ef718f8c4753b64ba6a4bfb5bfac07d7c0981fd415cb1f5d7c3b56d897b476439da3f4e99b53fa97e", + "0xaa4cd95a7d01784095d931b33370f99878e1fe91629a0169b04621d0c48769f8af1f99acbc1d879d6df72bad6da1d3fa", + "0x95b667e2002b99b11f666d3d3a91371c0d6f26dd5937e617415d30b597f5d80507562053efc4c40069fdcbc8b17f9a5d", + "0x961bc6e2e0d5e0da64724f2ea499a8ba3b58ecb82313eb49a7a563215b5eb1d5287e68edc6b7a2d633ce884ab08743b1", + "0xb9c7c694cddfd221064c2bfea97d8e3102d04c685f2d777747a0b02b33e3becde2e85b11615d36dbd4716083391e31bd", + "0x8fb5acd2a2840606fb4cbf5db81071605fa9471d3e30a6bce3e577ed42cb67e6c6b9654f6deed35f50ea94bbf0fc8722", + "0x93d0011e9551199636795c8698be9a0a188edca9365df23734ef6007bb3365de013f637c9d92f6fac584e183c23d15e4", + "0x84c34405f6c605914df7ba422034f573165c381ac51509ed6237911c50b5ff761e81dbaba1607cd1cbe2938a60ef28b1", + "0x8fea6c68710e864cd744aab19e5a8c97e3dda757e4b43029506aa4ac2fa4a0f8603582527a47d18425104e43314ca945", + "0x99588b905f4c04687d1cec6ef4059a972a259c33a6d3520b20d7ba37908919738a1e857e8c254bc039ccdbe92ebf0ac5", + "0x8dfd1ee1da1d601d7ff4ec8cbbe8295a8b2bec827c6cc7cc87b6b1dec6a15658e322487c1bd8f89d03f4cfe9bac11487", + "0x807e1bbfd716f1b22db088f57c966df1a30cd78caf9df220f7e74360477b4f23ca0ec70e4a66f18186c4361304809005", + "0xaefc7c3e33d64e25891012f8f67fa8de2afda6bf9fbbe87e79130525a926ac085266bde667d997bd7b2595d7803c0046", + "0x8bf2f01d82027cf8fb29e0e68cd835ca4538462deccfdda9e792f24ff820254e6935483bcd16f35b0eb1b7f71b65f6cc", + "0x83e8c5160c039a9e3c861be50a246fc5391dad3600e10370dc80d5eec4c659f9680d2c84f96b718643a68e48c2c39b29", + "0x8976de24473eefe1d0378c27d9c91225c3901fe38530a26a459481552dd506d50782517601378f1aa95d9bcaed64c098", + "0xb4473751ec4b434652c742f5f7f81e3aac9b66ec3f9e5b200a071f77f8168d26cf9490cd422bd7a347a517ed28e67368", + "0x871ccc01cd0d4659a366d407c366d2e1f5a70cc8616d1b65d97d6bc0049e98d1b9cc25593472f2c8fc8c9828993a154f", + "0x91a8a33b86a62819a3e0af9a4ad8a535b7c10159571f952cce3ea9ba88bbc2cd3bbd1d99e8eb95c9a7813b385f352b4b", + "0xb8c36c2bc0e59e94d32bc01e5f38a34008dc97bb8c5f5673bd8846687d76d2975d8d48c8087fa62c7549aa602a2027ee", + "0xb8ebf48309231169ffdd763f490980567938b363fac56af04ee85672cd9158199403be917bd76377883c99a36497a6a7", + "0xa8985d984e19c0d53837f5c534909eb63118077cadfcb9f2d60829522d5fc7c5b5934fc110af6ffc406c6af8b2507759", + "0x8cd96c0e16713b0010ee8cd86873cdc02d7d7d2983324a7eb08f88a5fa82d5c895b0da48210585a3495438bf9ef18842", + "0xa285271545db6b7efa47aad5f91afced2681e79002270920a22a076cd814c2c8bd9db3d5307cc115658aacddf6e830e6", + "0xa80fc12244905b86b973169539876d532891b7ef48e2f93188ef51bc0f9caefcaf10f6bb80a96a38f11f45155478ba4b", + "0xb392dcdb93270a7e3eba2d1184acac3aeeb3f0a6f190d8dab86a0b0f80b1ee52d52afd63f865ed7586d0d1daadc43d29", + "0x84a83b2140624145ec2d9d2ea3a79a0e68c3297881cab12dfa60c90790a8ae714133f403293575a710196c8bea841c81", + "0x8a14d06693ba757e975a5f89ed6cffa0085d1a2afd6d1f1fd5a09412638b708bdcc23b84f2f447550f3dd84c98fc5be5", + "0x8fc027fe68ad07e3475e47917b3d8b431f7d190f5adf76879cc5a8006b2a725d87e48e4afcde35b6ede34942eee468c6", + "0x9118b2f77abce07e1341aee9fd45ae1d6d43149f5ca1e477123f83542f7acf5ae79acda603288627eb042fd457c1252e", + "0x936eb734cd121b9fdf370526486fd08d67d6ee53a0996335b7ee097cfef6ba285f2012564fcf3e1c2c5a2b4e8b062ee6", + "0x8df8ca51069a69e884db19f69ce78ae799c38cbd3fd44fb520afb22b5764bab086afac87f1723ac64a8b7a3b22997efe", + "0xb2f2366558a32e718cc1bc088286867eabb823abfe33173b2940c638492c3c25f3e132b844d3423cb8699c3a87923836", + "0xabdcd586a0d8559a264d7ef80466f6df422f3ae11e4fea288e6fd1ce29428202fd96b5a83f2684caa4800a41b5cd2ac4", + "0xa3dcbb73dd80a85436503f510bd2acfe54d5f13b4a1d806aaba8d45104f09d8f5e3d5ab8d452b9faced69e04d5fec457", + "0x86078e53ef955147658cba34c1ecad34891cc1ad30e3c219d05b862a73aa8b02dd44c105408d8ebd9a00437c57fb2b6e", + "0x93b0494657d1d1b44d8764eabe1680ee4f9cad38a0d47f3de4874c200d2be252474f1da0a4c44464eb4b0e2a0b9dd910", + "0xb7c1351ae013fdc814defe8053288d0705479610087b2ee245b24be725b85862497aaefed103639457fa5a368c594e89", + "0x92670694e6c06966db72fea01c5f18b57494fe3efb0da1e1f22dba13cd029973963ff55f9b248c77af5092a3f0b9f009", + "0xb9467c87c7634b6ae951d568c2fe0f21343548f24db6afcfaff2ca56a47777c744cc8f83c15826588f7c31287a4cd3c7", + "0x870d45135d19540d7b8e5cbe7de32a4a94ad078d3ef15fb2d0eab734a01d6ed403a499594da4b4305a9ff6ceb9acbc30", + "0xa7d980a4078f561d34239294b9e263474dd061659a090763f013babff5068aaf3e885a395657bcbdd2a8ad5939dc059c", + "0xafc43dc3698476073f76ef94ecb8b7158e4ac1a922802f0a86ce14e5746d22c077739ce690cefdfe7b1ba4dccadba53f", + "0xb3ea6ef90f8830a130094d76970294331b9ddf2b3f1d1e8bae77754265c12f3cdd54f09838ecc2caac9ba7c9bef295ce", + "0x871a4cd5deaa8123a8a39155d285880abe6c2739ec34026bfe9b439de2037c34a65a538837ba00a223c0a3c46fc52e19", + "0xb2a8d26d5cb1d4b4d8918869b4b6cebc968f7dd37b2cc213dc06aa6019b5e8338b6ef0161c2d8447775209531086cf24", + "0x8e1b5eff3b2f35ab12bc7afb6ae03b144226fa2bc337a027351a8e1438baf75daff843fd4eb2153946fa27b3f0c6e708", + "0xb8df6bd90793c7f21c8539547b239f1a65a3a5a8cb0190647c3dc64cd209a3a5768ac409b5baabb8d3d2ae68c6eb2f82", + "0x948693395bd969831c03ffb4660104e487a15d658c28bb59e7b99998a93c9469d29ad64920b3680927850e0d4dc5ed20", + "0x83260970f78ce2dd7231acd3ea3c6d937a95f49decef2f523d7ac63745243d0ef80d32e87a30e099107680ececdf788e", + "0xa58bb52ce055afe615d8e0f2be8d20309256c10def4736c2e033600039f66b6b97d1bec0d8a4513e59cd24b29164576e", + "0xb4de6c3d87034c2fccadbbef302f9de80f7e5307fe45ed59fc88d8ea91c8f23deb4771333dbe9e3160282ae73ecfdc07", + "0xb9485ef06487ec6442c2efc4173548bdbc35ab21e19ff8704a45d27193e3ec6e187a4d6a261528b500261d0aa262db82", + "0xb1fdd430d8f65a963b36b78042fd42ca57f7e9edf0cd1ae421dd1aae232ca44e99199bca6ed8c5883bc29513ccec37a4", + "0xa39092f5b9e26f9b6b8fd353a5d97dc08d0b1ac506ed7365db8d4518bc6c078fef2186ac02c5aee6d11eaeaba63f8b4f", + "0xace061f9e63b025f32b5a8714cdca6378dab6c77efa7f30580ca8d88f4463746160cc6b6f7fe7603f714629acb4a61e4", + "0x9643dd80f745a01cd0d887ce20d67ffd14da89c810043aca6955da7420f9ba9d00e08140dd5b757fced3008bc46633d3", + "0xb25b7099e54955f175eb216b56e0a24c6ac512fbac20716e296a062faf1914bc540d6db5ab2d466f8a436dc0113c410a", + "0x805b6b4e7bcc3211e2d3374e39ccb024ad37d5d8cfa641783bcbb1632f9bf186cf017b212ba0004ed60ba4961365d960", + "0x9143ba9e6041de33322ed973a1279e6731e94de30f1316c32f9e690dc034cb07b0e7726b1e96b30e0d112a7f7f674d8e", + "0x8a9a41ff5d2999bdfaec50b8c72ca7f99bd790db239f3f2b7bdc4db07478299e1437b8001456c783d37f19b83755be54", + "0xa2ab069806856873a58b186fcb2d3bd406f280423c87970a7853d505cce6b6fe4d4b215a769e70527fbd182da0fba475", + "0x83051496d9bdf38eb8009868eb98d5b640b344085eef4d1e60af6b786da78b1ad5c48d9aeeaeaa64f38044db5170e108", + "0x98fedf57d82a6d7280dc3e582bf693bb6c18521dcc0ff95aab4d03ed1797a3308381539a73588c3a9ce48f8d2fcd5414", + "0x90a91a7d6919377a1d2461e534536799eb842a613c95264b2ec49bfe70dd7a00b05acaf3ae7a8170cf9e53e80ab32934", + "0xafac6d59632462e9a99a7c541488eb6519566fb7e1fa99e3609f22cf5eed064c1876b39bb800d0587f075943aadfda0c", + "0xb14123c7b494bceb21c9146a8a71407f214b3a81eeed3cd90b27c4985491d55c2b50bfff06f5765d80828383a6effa03", + "0x930bb1a28497233a7d45f7346b14a4e6010be3461c417936f900505252cdc2ae2f0466c1b2e6d21d7bc09109dde8f274", + "0x855d481540ea6feb975f9aaa62685593331032d4bbd34bedb7afc98a5ed8a7edc45c2d6e10b27ac610ea0adca6fe4f92", + "0xac4f88177776470018b36f7ea8b6724ba269d3f813eedcd11658a933dc2acc9b6779b3002486bb612592e8c90024199a", + "0xb4b8b237bbd95d0fe78418671dee7b75cb963118201500efb2fb0807c02883d453ccd36a9a6ee9f5c5aaefab85831172", + "0x99ca352a77ab758e877d80a34eeb0444d40fc422610451baa735c13d83c4f94682ea3608bb2819357126bc775f341a70", + "0x8f8d2a5fec98e5abcdd274795b02e1e864d01241fc8077ad103dc19ef0ea5a8913d2cd51b40ffd9c2d2b5925e22460ca", + "0xa0584650f56eb49d235e616a6d80f6930387f6240fd3e8c8f60c029c3239eef6208ba7d54f10bcf52fbae644d5f44c41", + "0xa8fbeca6b52ebab7ea44d627fb1d5605114d6c8c9fa0052220042546afc8bee276e0e88f807a74b92683e0a55d3d2ac8", + "0xa512beba35702952aa35defbba20f5c5b9e833c0cf95744fcedb13d88d07edbbd920c340cda3292eb561288560149b51", + "0xa07653bfd457684fd3cad92c9fb608cf6161987172492fc6847b5cd231dd86671a9128dd44512d94d2fa5fd6a9bb6d35", + "0xa1c76e24d10fb223dfcd7899ed3bec45b688e955e6ce01615f6c395818fc8967d8d8fce87c643b5eb68813e7d9a50eb4", + "0xb7b1786e5ad6e1efef832493d57a86e4271134c6930e92f987e63bf0ec9d7c68f490962f16511fd61b97fe0d95365120", + "0x84cbf902bf20df10e8f2e23123efb5ebee540ecb5367e02a26ce9ed2e58caa677cd16bdf20f0e000c0ba0d8a6ea6d243", + "0x978c3b1f469ebac4aad69091c81270529c73ef686cc03f4ec1197610ad2bc9b5cba42057ca9692934422193f808f31f2", + "0x8d04a2584b04ea0ed16660cf3205779a6c94a5745e307b3e8341641bc6dad712601b435fb09bc484ad6bb411699a70c8", + "0xab6e99657e3b1f197109140f8a55b6a4949ba41a5291bd522097ebc667ff2759280851dd90852ca049af78d455a20c93", + "0xb28c22f971e5585ae27922397145a297c5a371a9b02317c93d2ce92365a98c685f661cb605d214a8b3cc1890676679f8", + "0xb16c4c8e9cf5a158a4bed07a8900561d912f44bb251c9552e2849f2e069bd361ab29a630d689567689731b4ba3bd8fea", + "0xb358549204b3b8f8388466b4d21ed559a9eabada0ea0922c61dc142e7f0a5c1f4b6ffe57034d5002541800d107c20996", + "0x83a2926418b2f237a13435b859c6c7df92ea2652b4d54aa0670f47a8ccdbd005b500c81033a25fdb655e9e5a94e3628a", + "0x975899b5fbde67b86962850be41889f866c29cc2e88a4d81b1419637b9a79a2295db5769fd87cc8d51525e89f9c626b1", + "0x8d0e41332ddd96ba3c6191b1087aada1ec164aa3c5240b7d374c4a55df1ab556e0bc2ab2d327ce76d3d2bff07e097494", + "0xa994cee27bc284ee1b9aa0af772ced8051a6dee9d802b4730571f74a7fbc662fdc2485d0685243c3431740a5aafcc87a", + "0x8d0b551b80c19d5fc9b7228e1774e54ad02adf95e9b4722dcb53e31654c5338155bd95bc3538233628ec001e57f2dbe6", + "0x884f7fb1698d539647be23be8b557181fa0c0551b4c1a278bab4b43df5f807177f8dfaeaedacc152e90f93ba9065004d", + "0x8877faa63c8a95b113954e55eeff2e05f69e54910ebf7fa7a7a3cb176a50ace24ec3087e3d5c96e7170cb17dd8cd1e63", + "0xb97104248e2733cefe9f921e1877e708a0495d4cc68452c0d9208c76a3b6b28db5292dd8cd1cf78510d238bf55dd9db9", + "0x84ba91b405f716597fc9a6ec175344c04aede174c16a9aeb7b2512df4f0681a3c81604cad2142a54ca1a11a67e0e893c", + "0x8a630d0c78e71f8c4461e4b8a61616457d06c287fd6fedb125c142e753f616006a52b14e18eb32cf37866a935ea670e4", + "0x8abbeaf79bb43f33eed5627cb3a94430ec8d1b793f7dfc9d5fa51feb4252b00289ad0ef4d5e9144e83d2b1acc91fb62c", + "0xaa98af5bb459ef80fbe23c4d2fca048924dfabfd9ce921be1816f7bb3d6445b46803ca02bdd79894fa1560a3439280c8", + "0x8ecdff7c8a24d68dd5bbc108cc6c9ac8d897c5b00f477ed85d5f085285abd447cd505d1e13373bc7f3bce897ab48fbcd", + "0x8fcabc47edc4a75119beb25214e207fcd7f1eaf9ff01fe32f17e01b24b3f5ef4c4c4823441eb47827fd9f2a68cba76af", + "0x93a4e1ba8bd1e40d5dbe72f760901c6662792fdefa7c7f35ee7b4edaeb6956d9137dd3ce82d6f1ce4072fc2417fbc6a3", + "0x8e0369cd5fd41c7ec443bc3ccef7be14623fad249a5c29d7794ee56d9c4c7e793ed57e4ce1bd8d44d4d367692bd94ae7", + "0x94e5d83df75e315a496f67fd847e0115372fd7799db0631db146bcea32b98913ab1ee95436f45d6a88d7e0a60263bc1a", + "0x802d153fa9b958fdcaaf0837a86c0b89f396bbf4163d4118e791fa3a39c2be6c2c2cea824a9c22e07d6cab805cbf6a49", + "0x90f482f17e5e2f7310ff449e1654a99a7457e9ff1b6ca3c5d3e744fa47e43bb7c66e68c80ce38f608a670f4795b7c136", + "0x89b4056fa85d9a2bdee2b0ccda8c2a3d8acda9348673f5c7dd22caef94d2a69efccce05f2df36c8c7dd25bc48bcd65c4", + "0xb96158157e17753973a0444924bea5f334d14f3b38b5e510ed4219a112fd6fd6e7882b64d3424099f0981aa6c8c9a3c6", + "0x870a6da6765cca46286d2dd04b7b128e93344ca21c0ddcdbd94bc6ac135e2ff9632677b0e30d98edb46b0d76a71dd772", + "0xb25ab38389075667bacb944a8769ac9c9578700a45d2dd2017797a23e1d3cb755021078ebb53b33f9b5baeed39117185", + "0xa72d9f9217ed6e8207cf0c5c48485af08a1ca2efd9ac5f54af3994fb22e059b96de1d0e1478995cd4ed069c8377e6852", + "0xa0dd2bf68ff3963cd7b5534384bae90ba24b39984eeecc17da7ed7ca2ec4da786e1d50b3673d9b1b595d20bb79774a9d", + "0x8aa655e92aeebc22c44153a82d2021fb0246f21b5b04699ffa3089f0ab0e112c90986b5bb7a06e18ec470b65f8353029", + "0xb7c4391442c77fc622c4173ee3715a09ff0f067b0c20cbb0755d5adffc59e22dc825485d0168ca0b44a5604f14149b12", + "0x81e91ce5c0e96b9b3d8a87c5ef819f16cd22fa71385f5ac40469a0496cac1a52b2e38a7629d4bf8eb63248d1b15af607", + "0x8a38f48abeca7c816c8a3a53be75c3ede96c6f1ab0ae1aed0414e472769db3c35baa20bc6241975ac24ff7daee577eb3", + "0xa5f867ea6892cad22217e4a4baf375c2aa3dc45e6838eaf1dffad925e4f30955d25446d53e5c4ac0d69db646ce0b3cd1", + "0xa2152f82367b1f82866e01deba995de3cf75347d5e7f09036901ac37950d8b9028f6ca524a9c2eec16070a55ff4046aa", + "0xab9655a129245a19d5fddb5097b33fb92859ee03dd8cea09088e8073e7afc9d9a67000a47bd2134027efa3aaee12a3d1", + "0x8a543364d22eb2311c520e6e4739baf62396bcf223126d31e303bd66f00e1da78a34f525b5405366511dd5da18d7397a", + "0x85544c7c7a99dd7c7c19ece386279e02cd98aa0764f60dbd91f6fb90f2e6c4dcd6e56258cff32f6d6ab87400716dc92d", + "0x8a80b80ec57b46006a8d4880ef062ea288dfeafe5d9418af235eb1463443f7fcd6febfd7c9128d7efc33e20383a4fabf", + "0xa838126697402180d0863d813a52b99095c09d9ddeda8be9d00bd5f6db55ecc76ebd6236bea594e2743032907079b9fa", + "0x896b669ee59e35a0abd87a638d739b25db7775140493aa8ebc70ab1b5d8b3a7b7a88cc2e7d99255c5512b4ff70109139", + "0x817c0b5b431f41ca510938c29d44a50055547ca191c3e03dae693dbd5343d8042de364b5238eaa358d38bd31c30d2035", + "0x82ccfd22ea723a0272407c4a3f0a535b38bf75706276e9ef497544b1fb642aa46c7e5dd544aaf7a0ddd92798e36522d8", + "0xb3a1f237904ac1368e1cb5bf436f5e0687c1b6f95402a18741ade56db658885faa3fb0d5b207a265474083b246e3bf2e", + "0x8674f593ac2065840dcd4a3ac9d48829a484bda987d6359f5466b155a7a88a7be79c656e88e5cba30de2d58b72495a9b", + "0x98b4965e284115ba1f8ed42c53a2c750acffc32202e119f4c2d88a55a5685b343eaf792d19284b131c70f9bfe3f4b1d3", + "0xb4dd21199bc503636c482956296fdfdfe5f89eb50cf86162106ee0bdc6825737cfbf514aa0d0782343e511b362eaa22b", + "0xb8951997a9d8f37ece1a4933be6c1d12251a991e9fa70b1e7d54ef11b358425b039f7b5d85ab97ec9440d87252b60a99", + "0x89bcbf5c03981a9cc372fdf6f92af68147e38d7cb1ae5be8612d89dfc6da2234cca2e9775c80d3869a245500301df3ee", + "0xac06a03b895f06abe3327ee2d86c28b5165e465ebed6edfa322286163404f91a4615669ea02af35c2ad333a8edb9acac", + "0xb3c3c78f80bb12535165bdad60d3e7f520c296a2a0699c77f9ed8d0244ae573213fcd7150cec9b9e72477f0d0313898e", + "0x880bd666c4fa218b96620910c9e6417bdedf16fc996b9da5eba02e3f07b0230ec96a298201e2ff9939c4e8d2f7f185b5", + "0x8a32d3544e28071ae498e690f76d98010054f284d2264ff008d7a7f0d771788dad2ea0ce06c83d452da1e20b73196b75", + "0x84dae334bdd462fd1715b63d6b29ad9c89256f20966515a3c50cc7ab70e50b7dcc852dc560589f77859d8e8f42c6aea1", + "0xab85e1be69c701c8bd57ffeec712880359388f6a7d085961d0a0df9a9c909d4f68cf8eb39bd2475ead8d33c4a0fe821a", + "0x930df9a06e21bd078b84792d72446019d2e56cc1117511764d404ca14ec4725189abb1f3d461eeec6e011c41f1b22364", + "0x804463a8e390aa9fd3f36d3439c9920c6692e1426ae1af49c5927707938173b326b76c7900d0effedb923a9005f471da", + "0x8c1bcdac3e28503d6c4e0cbf3069bc34f239ddea4e8e95d0385c64f52c13add6ee42fb3ad38f305e22ea280edb6d3290", + "0x83e7ca5eb58fbc9c8b023d1b0596061a475b9dba15e052e537d61698eed600e3830cbde66a3b08dac397f3522b53a4e1", + "0xab91b95058de129840f4321ffde86b71f77dc95de832958fc01f5051bd52dfd67fc46ce9a624f0f923f2d632cef26ada", + "0xa866c7550dcaf1f2d0d1af30fcac7f5e6ad74c3c76c875f17f35e9505da6271b1a7fefa1f6aa44e9e22ac20be1c72287", + "0xb91941881180232eb84bfbe4397f04507beeb37343c5002989ec603da61cf73a81248c7240ad8c55dcf2a9da31341b37", + "0xb85b3917068375731b2a54fb873f8e453f774a5fc5a779b806a5749c8362cddde72c083e46bde9207564145b2b662c20", + "0x9722d53369e762e64beb9dbadebe7ad5c0203da142f438bd7f352c8ecdb1ee1b4651d03d01d5e7b68739d3ce9690c541", + "0xa02af5f4918f21ce5d32022e6e09a78a32a45e45ace5e60bbf6a97f457ef9ee9567215e96d9789a2542664ec72e5b1b2", + "0x844bc91cc0a41200c07b36124ee7e889cbd9be9c513eb238993d90be1f6f7af1d678d3e276fcf828efa512408e6c75e4", + "0xa56885339a28c579edeef3a98db0568ae15aca2dd324abb65fcaf7d10163a3e1ef025eeab71a63f264f2f1a46d423143", + "0x84b8b044da40cd52079fd0d39ad43d95fcbff189db6bc4b47a88f8e7fe2ce83931f7faf3e0e116b946727b26d4df2790", + "0xaa0dbf6096c03f65e27dd28c5e6509e88f8092ddf2d465bda9e2b4be3d1fd635481ca81626810f711075dafe727eec4b", + "0xa12132b0eeb8cde2d2b05074af56765550f7a5004034e56bf39ef49ff9add78dadc5d2e16c080bd99e61015a7a109e57", + "0x961991316a37129afec79c6fc2fdd830de653dc190b31baa0f1da945649718c7abdef907ab01207b14b3fba9106a5535", + "0xb1cf38b15b7638ed11923245dc936917d3bdf49e8fc4030021435d31ce2cd1da9868de002febef4c944d0c9bc63efaa4", + "0xa8fba0eed0fec245c8d20f23afcaf6c9b71e77f44980c63ace535f20f499cbe3c125665e0e5f14bec6a36646e23e80a8", + "0x8ade9d84504f59ca2b56a023928854506168d32d1167f71b4916bfef40deb8395b5dcab54aa7843edbfb1001e5f50f20", + "0xa3bd9f6682a8d255e075f2cae95472940a56855ecde9dac8552c60c775022d9556a28a0f6ec2226965b25aa678511e49", + "0xb86b7683115353d394ef525321157614b3b9aadd390fca43f2e3974622bef4c458d042badb6fc2e8d3a1cdc256b3feb4", + "0xa6c778dfd41d19e16b1628bcf39e298816203547af75f98d3519e8deaa8be2c8a47093c6f2ac265b08186892b1d49e59", + "0xb225560c6a0e2f8d8649d8e6cd29932cc4bdbd977710ff90cc145a406b9d4f0bc9629799c9c7b599971eedc8247a5187", + "0xb6eb93f0df983278c4b6991a64a70acc54525cfb9943cf6c5c639cf6a7f3bae9ab43bacdb414ce0f05cd2ebd3851e625", + "0xb09161fc8902eb06b39c00e1486caa656f37cc0d9dc93a39930a14550d5b604ffad580ed7b2d1798aba23dd326519bd3", + "0xb01877008d09dc3f67edbed0d61755e2529fe82fbaac77a262faa9b9f5ad66b80afcca5fc1114e9ac336e67847699574", + "0xb887eb4c04a06004761c399cbe7e1b92bfcada2b0a52be2220ae2ec6b2a0509e245c9345192995c96772493bf6736814", + "0x9733ea403c02003f1a7c9686d0386d87684d90567c2e6058de939493d3e654ad11370156ae03822e652986c1646fdaeb", + "0xa4d0cccc157346de6eaf2ebd080305b8e702c0ae0ae27e64e63ff7569a97bdb36546daeba909c6f27047dcb1524810dc", + "0xa8c711f84697ea71f69ef7369b92784c0ae0ca486632506e3e135c3a85bee1485f8d4e361e956697e1b957e3493ac10d", + "0x860e876c6303d18e3671fc293b0b0d8846005aebc252d198d87eab2cc89298dba802718611815401fdaa10e352397901", + "0x8cb8752292c189e11607a9a5194f5cf1f0dc855323bd20ae5e27e0d695cf21a266fb6492228e32e8450f55a909cae51d", + "0x823303f5ce15298e791af17aa9b5d8939374582188f40a62bc35378a80fccd9a3b33de5f272201b4f3e887d87079ad25", + "0x8c282d8a780ba94cb853da4cf2281516483a20a29a2556dfe1052ea482feb7eb7d7e94fe9568c4c41ae9c36bd17e91e8", + "0xae7e73f2e6ccd2b8c63137f462a00a4de00c5c68d28134bd0a8c41989adb574f910e2ea56c029c30d2782f0dcebd4b5e", + "0xaab7e2b5c6d1ebc76036d4b1db8a4774deaceab23fcb1be917e3feb3bd8708a258795a5d7ebd44d390b05ec905870cef", + "0x8df8dae2c57282da8aa71c508d2b24dba59d3a9816d7c0c84bebce6f190f48b03701c97b8f57c0a8dc8482f48001dcc2", + "0x8738ff6a8cf4d5a6e9d902524fde112b51b103aeb0b9f6fcf3711d63845f585c5acd68658e81dcffadfb5d4d9b67aec2", + "0x84b0f111267e220d8143eb55425c0beae01124b21e03103e262e2c59a3dbf251a60953c9cb4e4943179c3b2863ecaf95", + "0xb7219556eda3adf7467dadf5b709663e1f52ea2c8011c625a0d87e613211c76dd022e5207f965d6fe23b0137bad48394", + "0x9506dcdc38cac04251de5f45c8df7d59a9736d6806585cfa6a7fc6ab3115e0fa2fd330d7317e57ff0bf4f75fb3b47bf8", + "0xa669c0a5ef038f2dd34a5b87657026935bc10898d9e52e1416a63ee45aac3122a7aadf29e15beefcc9f56155ee7f539c", + "0x8b5d3d3f4da39625a33fae60d8efb5d67013baa9397bd46b297cce25f1d7985345f17e506d116c52e2066e5ad8b43ef8", + "0x868d3fc4946a13e86eb9e5f1cf668d4408004ace68745da6e8c0f61f13bf69cdeb86d8d2c9a1a81477655e8327e14c3d", + "0x845d9186fc1769852efffff9c741b8ff8caad8ac3f9945fc31b31a43702b1e9daa14f6fec0c03cf50021736755d5d549", + "0x8ad0bc2551a3f7cb76081f756acccf99ed15dbe63499a6215327aea2a06bb728da3915d7329d7fffb482f643ca159fed", + "0x97758f751ea1e8190fe928c391e2d44072e1d76e2bce38a8bfa9a50fc58bc48b9a5875d32bb8ce3b8ab954056d0c820d", + "0x969dbf693a4b55cea86372dd868b1484f4e411cc54159ab95f2977e6e176bdd1cc6096f729bffeefec600a870ce789e7", + "0x878fba373e72f862a556b836902cb325b5a5baf12a5359261e57625e20b826da4bf1dc3788d6c25c111b82b7d76e8c79", + "0x98b63cbfddace3447fba17d36e5c6e36aa6e6065253891cf3158fdab20c77840f8cc1166dd35a5b8f1c9bad1112f4859", + "0xb4cadd986e498f9046f7b74f364debf6f3ea82fc31b99be5a0f4de770c37834e2cf8daeeb331435fdce89ac6e93f45e8", + "0xb19577b8727964ac8c271d3b7ae6032f242a1ab231ea8258e47661b93547778a5834b4dc172188befec47475872139a1", + "0xaabfc70dfac6d1e76e6251f127decc8316437a9bab5b73d3414f59059a40a24cedcc3157a4d562db249c835ede2ad096", + "0xaaf073c368a22cd011d6401a22a19af80a38b1d100674f019f4a64eff1d243e517903e02712fbdfab02dbe8e3fec02e7", + "0x8d913556a1e12eaa45b69b4cfacb67421087d6c2894c43940060d9596f7d02f8f5e193c621dbc8b1a3b191e0ee438ec9", + "0xa2c81017acf18bfd18e2462c32bda572d18c593a04c6888cee5f591b34af555bdbc4e7e8aa69ddec700db60f9235faaf", + "0x8cf702e6220507ba55a2a42620f87a89eaed13bbea2670687e4ef3cfc20cc475704531c0d225e84782c6203eae68ff8b", + "0xabeaa4b301fcc21895887531b9678d60c1ed980bdfd128083ef4512341fe0db1a4b3a8888651d6f171840171ce37bb50", + "0x8553c637f9b8195faf878b3234781401ae2066b331504373fd8e4d47f1ccd807a5e80cbdf4651087450bcb7a786c8fdb", + "0x99c22fb632ddb11800329be1b70ce74e1d302b3d21561a4faa955677b416b84e22ad89b5fbc711861c79403540142550", + "0x8b0857ed043af43200c4798eafef62c27c82a7d37fed92289a67823bd81f0b420920a7ef0d17cfc51a8f41c6235b190d", + "0x80b4c2bf53dd2ffd38bbcf729dc968b50d393fd7faa7cf2f30d9fe7e367a615a3cdacecdcd73db8a955bdfa94fe23325", + "0xaaf1d829b6750e9bceb7fa59d5156312cb1eb097e6a0dc691da94ab479259ecb93d45aab5deb6169190063bceb6a8d4f", + "0xab4ef2f480142575469f7e2068b366df5dc2b63cd3cc52ef823379ca5ff8f4421bf84e0f9bee50dcef81ffec3597a703", + "0xac216892a3556bb1e0d4ea2b3e8078276cac1832359bf02ad80b524b21b127ed010e56b3f9898d74b53384ecf256db6c", + "0xa30c5d1e53d48db752977856a9617f54ef31a39c80f6792284f5735cb5c1c0c638c658594c1eac8d39337803a1310b09", + "0x86da766512f5f338851730b27550e0e7c6bc2c33a9f80f638735a66e118d38546eb7a99c583818cc462508830532e480", + "0xa3469eb361ed8a56a1ca1d71d742971c48bbb3132f5acfcd57e6df0b1fd1b8d6f13183abf7c1da249aef3ce1c67fb03e", + "0x914d052497456c4005bb9375b70e24504d89d06e878bc516f0271e7696644dc8122023f995507a8db202267f84b74302", + "0x85efc18d7943d002b5bcff1d9a8debbe0b7bf7a42e2b1b77b71bd69a2f0683d9268041eb97d8d50301e56e3f3a617a98", + "0xb35132ee72e0254db5e4ec8ccb54d91f26ac978e306f195b7b805ae8da63a27f563d97e075677dc25f3cf5509b7f5741", + "0xa9f5c07c7a6dcb09c2a180ebb7a2b06139e186c8ef7cbbb5f3ccda6a2c423c179c37fcb40b5ea5486db2047537d4f981", + "0xb8daaecc5b97166942a3ccafb1862deb673da2f93625a5fde14a966699f0a788da09f41214fdf63942c1ea4cf0523068", + "0x99804002395d6d2873db6e705d59f4164736a5a4a12f58965bf9e55d288ccd1ae011f28ab9e6ed49845ec638d43cc0cb", + "0x8bd5c1db540b01be7c32a7e8ad9d982fb03b0b388dbad854d2942a206fe300dcec6384445e9f0996537ab23912c24615", + "0x8d168b1f5eadcaaaf3ac7b904b5a671365c3e2136187343668780a34a56ba4d4ca906e9e1df8591daf59b3e4e75dc327", + "0x935f0df3b2b823fd75c434bedb31a0cc8e30b6e842c039c12d23fe68b202569124e8ba240491094e387307658927e342", + "0xb5e89add8b7e9c621102852a777bd948b4f745a026f82c30f4ee69d0afcd3dec43e098b01f8da103d733803db5208f52", + "0xb9ef494be80e66b6c26dbc7b87311b259e4021f37ec64708ef6bd3cd8568ef1a63837e919bd1a37585247f18d646cb93", + "0xb65e781b15524d2d8ff4011d6429e001613bdc0d3bbdf4ce82d0aafb37a795bd25fff42c40d0a51cb310c2c8a92a1b97", + "0xb2c6f1461e349cbe3858f5ca6c267350a8e6f26562e825c2c2ac3c631e5e775467ff8d64853fd5e4d744a46981e313fe", + "0x83dbd9db7a72b6542f253df074bcbccadfebd04c343f85d150cfa459541ab16a833ed6449c71dbcc7eba71111b32d6e1", + "0x84ee5e357a775b7f9dfd45144592e6cababbd7c6781dc0e0b5dd60b69c9fefb5c314f2a4b6dc380c9fc6e71f01144cf8", + "0xb7db0fe98bc3aa883b39223d2d65ca9bed04a2d990d38b15790b9c06a06a55748f8a9e1b0cb485ab2939c8b1ae9394af", + "0x8414bc49cd32310227fdfb2bb0fecea90fed69fda5c20174d4bf03fa1f4fb03fce9cbdbd16bebd294629233be8c68309", + "0x9093a4bc206f3be88c7ca864415ecc80e1ae3a084da19485e76b09cfca799e7807a5c6aa7b9873e958bd6b0f4621f8f1", + "0xb821fff2ddbebcae9fa51b2ad365dfe2573a71c4c1856d49110a2b6f6125ec4cf0a61cfb805de39134c55b6ef0fa5637", + "0xb2d7df863e7b687f35566facb830844f3531ace9e4e73475238f14cdf6ad8a05335fb6b67c7407b97a0734272b67c503", + "0x9069fd0b9fecdddb5958df361437424b0ba48c990c0cf61249f420707dc526c600d19fb4d3b8b9ab4238bf9b29dc6e7a", + "0xad8315f18b2c1e0bbc2ffc93fb7a2a0bc0dc0cf99fe621292e1df06c5731c6b894b76ea7cc3aaed95231cd9ae949f655", + "0xb41d264764ec5819f49f0b8f6e0e7d1e856ef7cb968211e9292f80a14edc0f11b1fff9fbd754ec8108fa8bf245383b39", + "0xa15c68f75ea0bb4893560ec293ace16bbe95e7fec25b2426284ab357b2b1839b66887dbf1b3ec3a46a9dd1ef91eff2a8", + "0x91064d4ff12e68390a33d9a7eb7f3bba5320c7abc5f68d65e1e986364a8971ff82342179ce139d33f3530e4c3d9942f5", + "0xb5526338e3a0e985555e87474d46c196cfa4d21e8f60a26f4bae27c9ca31f159bc754e511006fd619d8cb801c63d0d3a", + "0x84435cc84eaea5b6f3a6973f2dc6cc88aebf5ed0dadae08c19d6cb3e054ee5196a507cf5eb0761f70950da03382dd036", + "0x9581337b8063f3680a2e5da1ea15a65bc4f0e42dd273e9f1aaeeb25f6616bf4aaaecbabc588c4d82959d86312ca6f998", + "0xb461033daec7690c578dc02d24445c19e1ef4dac345e5dc0ef91dd27a3c15036b776d8accbd4a0a4c383c85e36343360", + "0x90ba7c6fd4b503b793fc3cdcd408d1372bbe7302c4b7a2a414de1970c3fd5f27876ebc4d9f727184481f79b3f3e3d492", + "0xb00a1ee4293d28d061aff3c88df7ca3b554a69d5a44c2d7a2403a1dc122cea284f2df6081e199129e59a219733f3eb4f", + "0x87c42b2111efbee23f55d0e14031b5d98838c9ffb118ed2be80b023705c877d5a6a434ff670869b6290ae224112ed742", + "0x995d3158e1fd052afe5350fa8c3b06682bfe1980ec95f27b5fa1410ae85fddbe5a275a300b0bc7e4b4b307930c6ae433", + "0xb8fac169ce0c7f4bce3ecefa0376960f7b09822990de4df922324af4c2f389feae96dd35d5879db101a9d3ca894f7657", + "0x88e219dabb475b107b152140098fef89e5c621aa047e277ae43e21c6e4b055161e1745e88e61a972e538ba77ac11b0df", + "0x983c21b69202ead2afe04bd6ef4a9a0add4b9db79194fca7ad015bc9e6227175170c1588ff1e9de9f107dccfa1c1ad19", + "0x8a78f5db40bad4160cecde4fbaea02b682807184232c391101c9236029c96f72c6c477f65a9a786b56fd00ffe32bfece", + "0x836602d4dc58082c7883decd3ac3162c9d2e94cb6bd5311b77b4a4964cdb5f391bfe34198d41164fed4237926a817745", + "0x8992e65dafe75bafe17415d8e7b665ee0b0bb9dea5b670de908367972bdd1b1dc75b4b0c1c4751735da265703df2148d", + "0x9081d1911ce255e22a27b44256d483f7afd21e2d6367d8ff033869429fe96015bb91d8279d92c9ea54e77a55fd356c35", + "0xa448a98872185e46f0881e6ce426715bd9a7fd0d74843666321d09a37e5e0a61283dcfdff7145ce6b974b00fb6394858", + "0xa4d6de379e70cf41dafc6d859b08d9cc0e8649ff879512055db2e79fa471f3b486d933e16ada825847b13d5f83c905b1", + "0xb1985ebc4fda3ea5c768ab9e1239d5d66b3e0c0a9be7cbbbc61e1f1677e86cf1302949403b3fb88e470358b429ea244a", + "0xa7f4849766328aaf91932fdae4fc4f15fb1b9ec9674b11f8035647cf57af4cf420a13aea9d533f16b0da65df7ef4852f", + "0x95755fd02ea7f43c0c6d15daa586f69c17050d99225a0dbea970ba26736c327fd46d3865fb99342c813f4e6c44d2d78c", + "0x80edf6468794da8ddd4d00c6166aef4b1c07406082787e84c6b6192121dbe3dbaa916378e5286f7e0dac3684c127017e", + "0x8715ddd7e9a459c0b5f0fbbca38cbcf8d76ae3f3a13ab8fc4a9e0fa39c19eb114946dac0889e34c016dd1328f9aab8f1", + "0x95e6cbbde1fa3f3da1ae434e1c4556c19632f37860c08e7100ec464d302fd6ff2a12094b135695970c1bd4083781eb66", + "0xb92da60d51875c7682af4064b85e3bdac690958dbde9c170572de02e353ff7b97db57e936a1c88c50353efec8d175b67", + "0xafce10c9f9e8e95bc63ef3658dc6beea238c1261ec2fb55db6eea851462e93a43128200ec775fc6daf99d5d81bc6dd4f", + "0xadab6f75df840f701ef96b37a5cf580235da664cdeee302732cd54a5010c605d5cdd51cb98e3aeeffb8dba077f48818e", + "0x8a1020eeef054bf5627bab08ffc571d6543e705a7ef50c9b7904b9888c1746963d6c6eb953f191eef74b0254c76f425d", + "0xa3d72c42ee1d86bcf759b070774ec8832f941f754ada32d63e2fca7d1b8be9b7727833ad7c6c687a4e6bc05d5702fb97", + "0x866c3f8977747fe74741b6a5ccf579cbe6f1a9caf9e8177f749b056773983fb8c0364b2d9f93d1a7ae9c9cfcf59e3c26", + "0x91f7722712ff1cf0ac0cda2cf9d857e171f410278e348b36e170c18aef873092ae029c2ac66dbafc019b81ed060c459a", + "0x826cacbe72008ffc385c3488b380b3169cd2329b9838ecc3508211c833a55f2331d1c328ab5183bece05b3d934c38a0e", + "0x869a6aa60f6ee417e3d0b70ec439175a2771ef7ad0deea631786d20c3a5717d0015c112518f2074c8552f323fb82e2df", + "0x896d746ba9b23b134ccc22cc728a7e0864f8e05fffb93c09124375dc6075a7e58e3033d08a83e9bf844ab86254f83171", + "0x8890c589ba7a00fcb2fa420a3b52cb804a8faa9a9e227770a3d9c50dd69942df338fff6a3248b250d2640a31c582c3e3", + "0xa566438e035c5c4fdf8b81b1a6cd23aec7ad0e08ba3f95add195a96c54e559667aa8da68aff724a5c58556e7e9047934", + "0x88a6ce494bd540da1a3c8856bb83f3839abaa8c77cb1f4d8fb690c1108bc066f9ed04d54bcfe2db5e3961f966c51cd5a", + "0x8f5d91e5192e287d7df69e10271ec56fe74fe70f2d029af7ef71c9ccd52592aba3ac0051246c0cad95a90d4213f07fd8", + "0x85b5119e507f7447cf4972e76766b12820652475510071e19ee89041e0d09410d48e3e1d674fab34ae2f078ddc13da09", + "0x98d74510fc5539c3e63ee1fbe706497f5fc532a020d8218b25e82c94254536bb671821e2ba33e637b51253c289a2929f", + "0xb76ee9638bc63996e8eede281336079b9859c61535802875ef80b87f6cfcdd23b0fdf404735786c93e4ea4372925363d", + "0x98b4e649d11eb13e375deee355f05354d5683888985b36b5120b42341feaa7302f509010c04c78d79157af1c7e604f65", + "0x99598923fbc8c10bc5c2073b09dccf21b3ab96ea02afdc73d135e124dc59e1fb35efd45c88c6545f7c1392cb53ce2006", + "0xa21ef9689364046adb0041cb9028f0a9b7186ffbb13f1742de2080d27b85f8f6a893db074f2221dedcd6b7da3051d206", + "0xa78b6e983265ec8bada9261362f93602a29690ac2b5fde11d640b50d6d193dca8ab93aa2953d030f8c19e800d109bc44", + "0x909233ebe58bb6cbc76b3fd059b4e6649ce8530e6a0acd7a72fd6c900c1c0ef15bd90c60f95bcbf5543ecad3ac58ca0c", + "0x93dd7e82261a89ee46563a38246f379c5e49fbbf894d6cfbaa1c4379e1b26c0a36678db7da75b39b82a5a8f9d51000b9", + "0x81ae61fe9a795feb85fd6f5ee4696da90aa27cde10744b5d17d5c0b9c902c747e62c24911933f6e75ce78fca07eb8120", + "0xae96417c71f49bda80acd47db9373d94d95daff2dc5c260c3c2572a524f74a0c999b39bfa46f063c6740dffbf56ccc76", + "0xb2d608c70324f2cfb80b1e33e92200baa172746063e112407aaece2dbd4fa95f328df3d1c6bc62e1fa24248a829acb69", + "0x988e66de80a93842c6a516d726b3090e8d2c6c65752e673107c8a8bedec3980d62309d28ca42486b96a8a756f2683358", + "0x84a5b1bee9a88388bc18f447bc3a9b33f60a35ea886feeb94b0eb021de9eb97f3c7c302d5220f2464918640c30e8897f", + "0xa1d368b967774822a39e1c3c588a9c98e5bb590fccfbd09de80f234bf2680a03f7f00e6a976f586a6279788a992a5326", + "0x951f2b338ea405b9e0a3926798ca9d9e3991a9fdff7b2bc2a5616e1f727d9d5a190c0aab593a03fd99049bd980846083", + "0xa514a7f55b943a1f93f7624ea1853b4ee4ced2af81c16347573a20a003b64e95280e5bed3d5e80927e180ccff113b801", + "0x970a511cbcad0b8c4d10f41ec0e9efcbb71895c7ccdeb15ef94adf7125183706d8ef8922cab2c60054f6f771061fd0f8", + "0x8b4b00c29c8b694bb19f7fd559a4137639da8ff98a2d96dbc1a6c5b27e24dda46f50d9fd41d1a49a786dde17400e99de", + "0x961c51f6932c5f4149064e55489b615542da30fd506cc66da7679e9f62e3ea582a6aa770274263d4ccacef0e042b6471", + "0xab3e311a8c98893471a6ec696718a8f0c24eba7dd1d9bc187f8b244856b6397286583dfa2b3bfbfc19f0412162a7dd35", + "0x8028aa03f401ec90e4a1845bd8667d732769321c7684270d722f9c60cc21ec0197480ceabeb5bddd18b4187bd5e2467c", + "0xa7d37408ab35ef759228804a5931640bc061b1d1e6e5c4668b287cd36253931df3c460e1cec4a06106fa530467a30300", + "0xaec70c1e040c67090a40cf1d07f1ccba73a10f7a665d23b90eb927e91a9e0d16c1f2cf6cecaf248a29a8d9bce3c237d8", + "0xb271094d38b4512257cf868fb2231fcc7c1474c31d207586846b6e73c98f0ff4b1f51193bd19bfeb27bd23a2eae4ea52", + "0x8de8cf9a5b84678689a03fd8e2044644e99340b44b3cc0abf4b86ceb7307346f8bd291413289538582c43b894028d37d", + "0xa6842c51ac1bf09a2446f24ca4b9b0d6ed9caf04846954ea804ee4811cef0ccf853da73dd7d752a415407ba45993335d", + "0xa55a5ab165c460d288303f64aabef718439c72a0940fe68fb1292d85b8a8eddd0cbe830be9a9eae30ef0d6a54bfe0eab", + "0xafc878350a384dbf2a09684b5a0f9d7df1bb0a06cbc5393689eacbe57acdb46f094dfc9fcabde5e47ad2f33ce17a1342", + "0x8521844b9b2a8af214da8030cc90fdaac6895dd4498bbb58033996dd6865a67209ae259de811ecb243feaa6cedd991f9", + "0xaba0911cbd44298ef0a4b01ee7eb5792d38ae6e3dad2d1c41cd17ea4c26dc6753cc42568453b8fc1449518b81d938a4d", + "0x80b851c7f136bf6514af87426f120d0ed1f565aed92f017c3fa650d400a223e3ca58206742802e4a3121989d81b39d02", + "0x9133d85bbfba8dcb5362442359199b19264c46bce13f3a738a0e8733b51ca8a759e96dd74d715bd2acfe462d1d6e470f", + "0x8885c5d1be7bf1b57e59640dd39ab8cb588f3747a163e12ad5f93e07f6e5756463acab1353c94abe222cbd2178c13173", + "0xa87acdaf59ed72a771f0f715756165b18f83ee51ac424eda6991e1f20b0ca5bee54a50852bff8e7ece76605b8d23f64b", + "0xa15c540594b282f7b70b2fd2af5b26662e8be3043f45bf49ccac5a07a67d25f60928e733a0ad439c8834be49808ab1db", + "0x99693292e092dd2f9b24961a5c45481e0997601ab07055ef9223c7ce43c35384dbaa32b1bbe8a21b11d554ffe761443d", + "0xa30025e0caebd473564470e36613124f8afeef773ccd0ade4c66d4ffefe2d879a083705221e8d87f52c8652b01bb3cc0", + "0xa888c6a842658bee8425ba7d58616d5e55b948ff40158c3eacee8d89564f9c5f3803efe5aadcbc3a1a65acdbe2c32080", + "0xb6c1492a4ea1803c1ef1d0d2ce00151afdc508caeb6684efb8ed0a2070978405c2101325a764ffe096b5a158d96af324", + "0x8a92dd22b2609cce80a396ce723dd1e2d4cb11cea1cc4f8a95a5c5273e0bdb79b3526d7a69590a934ffe660964d9872f", + "0x95de285ecf40dd940ab76b180d5d61ea15cb095a0487597957d69e84943f8eeed79248b01dabfc0f542c79d96c7dfc06", + "0xa0f7739ce479b9f84a924aa698e1a2669a4f91be843e6aad7f4d46d3e7e5ffec8728e70e0e4117939ad04122cb45a1c4", + "0xb5eb108bc88762f5c300e2891f91391bb8cbde4eb02b591542720f53c6a9f64f3d10bb50fb9e8f30336cae188ba4d3bd", + "0x9484755001cacd43bcd50da0cfc212929c36b07b6d2f6f81386ddd71002251f88a6b7532a4f1f60c33130614feadd61e", + "0x8ebb2d841b599769ad08a2d49848c32837ad6125b0d76ef634448cab0c87241d458274f887fb3212e0f35b36d0da2821", + "0xa75f5643b1c15962c872621e2621ef989888f4ab9e19e67faacfd0f3ab966c49a5f32bfe4d141aa79423a264c6183e13", + "0xa9dfe0a3727939960625c0223a00446a238dab1b3a3213dd8c5066b440b4caa718fffebb63dda17b31fa38b2a178bb0f", + "0xa29e72102c846ddb34b0c0fb114fa35f8b602b1ae0fdc13c578901ad942bc8a9a28f401d02c0e9c09f7e337f79481324", + "0xa7b70a8add17830f6243b2da37260aa06ff1fa02bd8be34e540904b29ddf3e4f95a02d6107d012be57208e2a5b9b5230", + "0xa20ce6b51737bafc3439af1c43f9c59477cbbfc58ae633e8da3f1c1868864108769825f29619414b74d8d9a8708e7ca4", + "0x925365614113d6e662960f4fc9ac3aa69c8ffaf415292a001867e23e893c1fa0e2e67a3fb151e22794174b7e488a40c3", + "0x8aee47b4ea05efb96a35661a08cc2e25096c917dcfa54fb8ae6e7e35ee680209a25a77b2ccf03f374ef22b98cdad53f5", + "0xa375cdb9b602f136c64c28c7a092faf709ebae2af53fd8267e4529470b157441d2d578c203fafacfaf3615cb2e6dad37", + "0x86dfaa6a6203ccb8d9e774bb9fb314159ab7f6339680415b166b7a6e1e3f5306359275c1f0bd2e4d48bf4e9afb99d83d", + "0xb0ae459bec2ec3cfbf338db06c7695a574c5d650213bb363259c77d2961b92138a3d74b7d63cc85a577c229dd022af30", + "0xa078e7f8048f3765c7f8a634c1b028362d6297e02c201944545be50a68b733a9971430344083d471419cfb63438ef698", + "0xae3b8120af07c2ccfc2200dcfd7c36a046b1f7d5c1c2b44ae49256e0612217442463f65e8d321675e68f5eb9dc975332", + "0x8b12299cd6bedeafcf23c66733c61a890e89105be4f5328921ad62e4e8da9a446e0147d59d3c162b9f4a915f9eb58b1a", + "0x972ef3f6660cd7ad4db6dc1edf7f013e065387ed5ebc483ebb01523d9d7d91a6f3c9cf74e45dcf44168d1ce316247568", + "0x8b4c6e481aa11aafbf77f49094ee759e2a6cf6924106a5479772b227b45287d81e5c7cb8617d7ce265010da0cc86cf10", + "0xb304b303270a998ebccea47d52abac09a2e6fe90e9a93f3575f43e0ca4ccd48c6bcf4105839516edbf412d67827bbd2b", + "0xa6d5738214223a13df3eae3304d88261ced3b205f3f27e503273aac6a946eefea26202508de058d8e89dbc332c9bc1da", + "0x97116555f4e60794bfc5dee3a441fb830d15ee303b4c5702c13a20a03e9b8f2c5854a3fc72814076653e40e93043d0bb", + "0x8c44aae61b88413c612377139d923aeb9eb0151ddac3e8b0212c024e946111c5474626c023f4d91e62e2cff911258cc9", + "0x88f35c34fbd9d8b2ee315427af040bb1f61f708f1a045d1153eab936f6c998d25c257dfd8ce24af54b9b32746363c165", + "0xaa415683c9d1028f1125f167553a17858917e0e843b2d1815d55af59f841794aaaf0051dedc575e04ab4959d47ffb856", + "0x8a5c494e71601e8e2438d7d362211486cbf3342a1dc903013e73540f14d792480443b9f96660233463adc1792b99c975", + "0xb910e1baf71e5d3cfd9cd258c99c5942c53e97c712c5d9fa378bacbdd85370047a2c1148907c3117a384a644158ce11c", + "0x8fe9f49f52e1e80b77db6c438bd3d6806a00ea12f8a405b933e616e0878a01f27580b45670cb745a729856c7ca032ac0", + "0x89327bf560d7f318b2a79296f6e17833d0b9d33de122014c4716cff07d1a41f3498cf3ea17031c79c21d2d415d9efae7", + "0x8abba3e1ddfbcbcc51c57d51f044dd010800e6ad9bb2994c99dd56e3fabdb34709454cf3c67ce31fe15e4e05af9f3f0e", + "0xb76018239e7b52582b951cd212ae3b5f5e0956c6e323951e2e775e2a8ee1963687402896ca9070aa239e381070d17cfa", + "0x85f64136a8a767bac83e509d5d17599d31b52d1a1de322beb5e809c11c56684656ce3a021d234f597282b8d2c3bb6cdb", + "0xad41776d4fce0a8a9b0c9f5f5f9eaf24199649b3bf5eca4586c0bb3e6cbcef765a2b88c21ec5107170415485b4c2a2b1", + "0x872c3a229d1d59d50183fd8af727db6625334d44377d656b3635fd2be80aa1d01e0bdb62f85f79bc5eb1f0ab42b46ad5", + "0xb61b7255ba88e2d3c67ffee3bb8b05f02098447bb7843ad3603de25874d249f441efe0a743bdb69000744c1771f8ec20", + "0xa9d12b9fcdcc7ce326af191254644f813790c3a8e49b20f2eafa10267dafbfc8de3ff937f2b451e6c990be44dfef0c58", + "0xa436e1d3aaa9c96dae6d6906886c6c09d091667e4c1ffef4bafbdc045070e4d01086909babc5b5f3c7bd5aaa03e9b9c1", + "0xacf56bb450e1610324420b9832c6aba3a63a8e0c09d76ce30fc9aaf379b1f563b9c21aba618e9aba67bff074dc6d9833", + "0xa1aee041829cc46064fe4599c3ec5f0ec532588def29f6f38d8dd90e5c6c5ad0a89752faa5163b105c72d3b51dd51310", + "0x99256bcdffea08d521a574a1a99657d6accfeda97e94b2fb4b17047df456e4285b8a7cc6480ced738a98224ee8da1634", + "0x9751c40d118653080e3c0fa48f44a943720311056d47b45fa468f3914738e7469c9889787efc7ac8ee3634e836c95c79", + "0xb554994255c7357c4ea8f659818f605d1e1aa3b5bd04132dc933e720ce5fb861797197abd4885b155fd6f64ec4588bd7", + "0x980a6694873981b80a86542589d597030545f9ad65177e72552f97d79a2ca675def00a82f1e0741ef45a0eec53b9eefe", + "0xb30833d73e49c70ea50bf823519c9130deaa3915d2f911d3bc2e522d097059c9a2dff4f80f13ac3ff5ab007eb450b692", + "0xaf21e1749962c54488330dee07089ac07dab5570a0f4029d6261fa50dc573dca6821c5f0ed88b08ff90340ccb5c2f3f0", + "0x902df7e9488030ecfe4923bcf9a6e245bdfa90433788681c3e7886d49f4ff90efa8821ff323c7a8b959315b327678db7", + "0x9607cc1c89da72635a5cdf1682e8ba2aeb9d1f90fb53fae70b9a89b8e454d8114c74fb5deb3c3368d124ed51dc63a954", + "0x959a6b82a051ccd4a562975610fd43cf4af598d5bc8c88441ea90cfd9aae2a79157fe1417f0bd9071bf43524a10091cd", + "0x854f00913fcb10ad97a7c5ed00f674c5f427cca7e8c7bdeddcd67db3d3be7b349e7cea18360f96a22ab0f7d93145c1ea", + "0x972b0bc81a71f768c2c38dcbcc31c05fe33aaeb8e58d0c3614d550b96b48870d4e5f2ef11b834ab2df652e0501efd448", + "0xa3e0e80adcdc41e97d838d0703be106ac0e7a255697e0680e9c90ebe1154c9980331b15d2790fce89eb5e6fc35ba210b", + "0xb149c06025fe59a0fa53d1584930b6296e85112fb560153dd8d8c29cd4375fdc3ab51569d89f9c23b9d73237d8961e54", + "0x80fa4ae7f47911b28f79911dad99ca87656959cdee635dcee4ec626b2aca5c2deeb8a7529af9238823615f31427f6160", + "0xa759d890f6fc63aaf85fcd00780a6fa352ad3e4b18778bab0583893112523083072da81aea989862af9fd0826d8bf32f", + "0x813942b07a88b0f224a4d0aa63e65070aa2fe19cae6da8a93a4b308eb40957148d6d254cfb9a6d61587c34cc0563efb0", + "0xb30dc8bb5d23d97cb4fcf51c01e935610bbc35723bfe2be9e449cdd84f0fb7aa9aaff2cd09440aec55c0a9b68f02e6ff", + "0x827889c67860227f2a8b65fcecee7413dd0362ad6ee45840e834dd974604dd726f35cfb8144513edf1b65c10d0707747", + "0x831df0e213713dffa81e98b3dee42a61a1bc0a842d1829129ebb6dd455574034b2f510ea42520682ebc64b73cb49d334", + "0x85c209b3f8c8bcd542bb7aabf71c4813224266822d7dccfdacf867b623be01d19e0ac4ca27304e786c530b7f995eaef4", + "0x99915f6a98792e2527af2e3caaba77c4a386b2bcda5213515baabc5ababba87ce6115bdfc111b336bb091278ddf5ecee", + "0x8cdf6a17d6212bc0a9b3110e4b1c734c9f28f8496829a568d8230bace3277c29efe55bd5b51d0eb6f909aee20bb627a2", + "0x9427a35400b4ef015ff1a243f66ea0dc3ee37cef03ab5a6af65b6072f5fa1bd1e2caf0ccec9f311538f5175974eaa23c", + "0xa0ba58fffb42cbe40b7e8d6c08757dc8f388c68f5f379689e4888198bc0c8296f3f50ea3eab767d05b552f9357c5adba", + "0xb79e5533194fc0bf5dc22d16787cf61dcbcd77f89e8a15feffe0d0bf49a78051f5339c99019ffd319c3ac26af18eeaf7", + "0x9298bab45b7c025141fea29a8bf86557fa2a29742f84accff65c01215534439a660216146bf2180b4eb9d4745d6350e2", + "0x84cf4d0deabc9997dc7ae2a25c711cc6e59656ac13ed5592d1a9073328a1c04c408c02e94b1338bf6348b4b9aa4febc6", + "0x981148f9d0a1bba5d1eb22b560862173076c277ef9946b1231cfaa54ddba9172f7926820a0226b91bd4228ad7af5d3d5", + "0x860f98b2ac5b6eabbe2d1ed205cd7116c65f839f53538423a26b339683c8de74f0562134e24ea7001197ddec349ff994", + "0xb7a810988a40a62db871cc3b64bd5569e1df3254f004286b467eddb49ee8830bebcca460c4fb31bd77e62d6ed96120e3", + "0xae19a66fdd6344b57f50d85e966d94d74c7852ca42718da8d6c4f785ee2639c9a7f8b0f95d6bcf92f3fdd4e8c20bed18", + "0xae1ca7c846a4ee1c17787904f34e980dc8ce8e8dce44e1c310625ca0b2d71a4e3b243c22830340ea71e67540c58ae2e1", + "0x8aff828578125157f42fa75cfb1b20d6c6ffc6f914c95d73dd1a1051c89321df234529de0e805f1e4b7ddb3ba084741e", + "0x93364d42a9e597ac0e4c368e4374f97ad9d1c3af144801f0ccc56ebd96ec0efb9ee29092eac79d4fd87902e1ad279d59", + "0xa2354373b91b97ac1bb4909d1720be04d9cf44e7c4074e9ca642c2590ffc926c9f208255a880cad83d00e99c7d84b023", + "0xa24a9e36792033d88510246a6a7124f012f2c0a59db0fb8029061b09d0a614f089aca2fb4b84f2a04f5693ed49537574", + "0xb7fe83d9e9090c4e8f68ade364fa13a05a2afcd3371922426f6af80cbb8a3546ff2be3c571dccd7b7fdaab8ef8ed95b0", + "0xa67c40df2b6f535744370b426177d9fbc8086fee338315bfda88bb46ec643bc3ba66fe29b6ae366b3a876f237e64e2f2", + "0xae9b741e9d551e0f5a1b635a4d0c3530634552a1fbeb16fb360e094499947c5ebf7602f01cfc7c75d0fe8db5d2a7b43c", + "0x8a34411d1e4993ee5bc0a4bd0fbfe7b728769da27fa609d87cb7ab9c74e3401c9ec9dbf44087a66c4e353b1c41ace9dc", + "0x8eb387d583e341e0c7734e5d3111ce2aef8f3031abbf3c74f29d3e67f161f5fa73887a65c99589e7a5fdd98ad6063d5c", + "0xaf244e50ef36ea9d588d5fc62db6df75150d64d311c7088000804c1bccbed0c3e640d54a19870a36bee59e34863bae14", + "0xabfdfe94a9c05c799ad7ccc2a2051931252a12b0170ca16f39e89cdcf4e1aacf6efed15de00f382ed50895f0c2d5ab18", + "0x95f5d7ef894fea7ed5d569949ba907fcaa402a2fd10144b619e02978cb211a3554056d18248d61f373b69edf6c1ca941", + "0xb1ce9048d2cb21270886f81b463fa0967ee9bf67ebf7b23eacf95eeee0e4f708e1c7e095c052785fd0a08a9cb9529894", + "0x86da6f03a7e96ab000d52eaec11c5e846478b593242d3e1741f51419b7dacfaadfb0f4112bb079376d41ec55851cc6b5", + "0x96a5b1b0daa5a7398b2cc6ebdcab7148e852adb4b2c0321a106f64fa201614745b05343fd654ec776c5ab48a2bc8faf6", + "0xa66543b05c7cc4ab3df08c08e8e78fa1b69950b6db405dc63fee909ff46d3e2d842c457c1cd02a6e1899e0c73c610bdd", + "0x927aed373b01053f130c41d1e03569d4340dc8d293008a2da00662851579112e563466453d06d35b3d42650c301feef9", + "0xaa1f92351bb8ebef212832eb890188f4d7b9607a8d37224eab45b4f1f62c3ef418895e2a2c16a9cb9edce3c848e197a3", + "0x9411a33e3de25fe0940fa5cda4d854985408ecf3bdb400fc6d8756e0e6351e05b2bf18f69781d2ef0a4a41164623328d", + "0xa2f9f0c04cc911ec8bc707bcbd6d8fb29b0c2e4f676a1ccff0022a40e56e530820f65a6366aac8d5144575245cf6f593", + "0x89f758f30c38464f3b47a66b9e401bff5d7477991600713814b80b13dadef4dcb4372df07ab76cb697cbf3a02db5f1dc", + "0xa73c64835a649f51298c02b3bd28d3560d0855f84865941e4c845941f5d4ab68eb562fbf09d7c1a0b34fd51065983c79", + "0x892aa065d3c0a8303550553676fe891a3c7c78882a1f745da0587f21b065799ec00f9d3cc07cc3bf79629fc946961e7a", + "0x87cc6ca3e54af98e7cb48f0b9df68bf52dcfdfc12a1d28a9928447a02e86d8721d5421f704ad0b82c64bce0b046d12f1", + "0x835ede10413a1db671b8f6e7c65a499ff9dc699a96749c973d9d3469af02667e200ad0022dc75b37e1a335302cce54d7", + "0xa40cdbd650e0e7c0b6d74f1732f04c0179b51bab4cb9ab5635e8c001a6e5bf616a02e5ccc55e19af722af793d53490d2", + "0xad9bf89ce157c845e75edf652fb5f0ddf9668eaa8d6477896ea17c4285dcb5d2391df086571d8bd9656fd289c0dfa921", + "0xa6a05db266d7191922b58c0cffb6b9c6586d15fe18be59421f38b153324247bf3318f4b1279b2586346e1d7d179dd55d", + "0x80ff4b4dc048cc53ca55b05edf1d0d69fd4a55a990729221f0e94b1a081b415d82953776b292c4a09d8ebd0210985ec4", + "0xa0bc0c796384e676187de37b39d54f944818d4f2ada13bdf30c84057988e2eff3f362a0381d30d992f69740a8395a03d", + "0xb70a8334925d1647a5247acb9a33345c11dc6391cdb1160ddefece6e4677cf9618bc57cb57dbb0aa258a639a9bb30225", + "0x98a32e83590184150ff7dd0386b3b0d30c65e0c8e8bf1f651dc8daf2b83207a8bda013ec88b4242ec2ebc159b208532a", + "0x97dca7dd581b28f7ba494b4732d9866441739cb138dad4730a704c665e7c6da1aa6e38005142bac9a281df67563096ac", + "0xa058843c85e628c337b2f536435411493b86417d2c1b05fecec109d7a2090451e02ef357dbe1f3a64462f662f405177c", + "0xb3f070feb800c5bc647370ef70aa4d496e6aee01637ccc618ef16cb64a67e980e6fe0a10f568ab81b820204a041a6ca3", + "0xa5c7b8d28700126b3f8d10622d22eb6fd1d2b3a739cb9b7bf7ea418dfbd7297cbba808de533f5be2f63a4e53684ce2d9", + "0xaf18d411d7114539ea8333ae232b61291e19fc73453072c54670cfa61b4a6852822307a589d97367b62f7e07739b859c", + "0x9063773309b6aa4dbca2f9cc8f74d341be78f2c36d7218fa3b90f74ee7a253cf2aa01a9102cad310c086fdf8d07abf35", + "0x89e8487cf06032da7dc52ee7eb00eb3457ddcbb26d76fcfd2d3e08435852c97d005c26f21f8fd441b52571cc5730811d", + "0x8c9a437e50c65419bd380bd1e931d2fa7219c6ca6c29b7549f77479685326e2679ea440644d38ccfd186252f7c3b28b3", + "0xa393f1aedb4f9f50aa92a729db4052e529eb9b2cedcb96d95da38f00ea500a6a9cc81bbd929b40fc90db271ebc773c2d", + "0x8e834a76721a88970b7c4c3ec5237f55c8512497af7c74278d72dc26028f4a49428699742d17019bb5f590fca66b18ac", + "0xa386433e394e85bc1303ff1b3c25014b4a6e72059c57ae035b28ce7adb9f590e7479f7b7bedb33c4b5af10b529295819", + "0xa431c5e8a0a0bb965372de7740d87a51ea306f4ab382fbd17dc6d8c50ef7898ca4ebbaf46432423dab35d9411b3a6662", + "0xac9b5bd42b898b8950f6871830f92903e3b21aec96480c9c572be042a6b7a74232dd5376692bc844d45b42982d4a69ae", + "0xac0149bea51c8ecf57ebe6364aa4000cffc42f0b01ff37195473b18ea658e8e3101b2be408c0c8c0a66d2ffdeac4313f", + "0x8606667f02c5da0fe38953acd5d963129eb0ec600d0a6f149dd1f5182e0497ee2cb00f188048e407cf901cf995bae522", + "0x911842c66c3ee17871928c4b5b7169b25c2822cd9c3bf81b221623c73ec0e74eaad7a1290edf0d8f24cc84f9991dd071", + "0x849e5888f77b8192f94fe40df426ae75ef0902d076d5c3bf056baa958ac17612af18d2b69eeaa98d2a4e5f198916a574", + "0xadd1df84dd4528c78c09889f7f2bb00186667dd09b6a8155fd5e6558bf58fac7f2b7543be69bdf6f7cb56e75ef36d2e5", + "0x97b1b3eb8e74dc16c4da77a97ac477fefbeadae6f54a1d8db71cab82fa5ed39a42a44959fdb600c60c7a5e7963f2683b", + "0xb7b3396316ed1f12b1ccbcbf304b70048c3ecca476efefa76af4f410694c0d0de8939b4f07ec9d61ff55489edb58494f", + "0xb37b29079511d6f6695b6639dc4d2efea2f608754e13eaa30e4dd06efb55627f70117e1ad8410dfa1fbcc9b1a4d80ebe", + "0xa9e330944b6285dcdcaf24ab70c5625d43d8a3cc133c6d3ac7e60bb9a9554f5229ea14b904af45bdf7b26ef1efff4c48", + "0x86aaeb81f674f4399275eeede0cdf6c88a5324f69b0adaeb439f158f11ea2bf2e1d58f4dec42bf6dc44cf0971ed5e9be", + "0x83f80651427ace12d9dfdd62aec309d99013ae48c92395d96790f80529e1d00cc27bcb5b5ea9cf3b68f36a112b97e325", + "0xab02309942a345c9203b0757163581f046acfd494a0b8e3eeff1cb3b374a7437410c35ea3bbee56d2cf1ec7c9c01a56b", + "0xb132fb5d88b46b216e37a8d98436d24d4e11680805262217b35eb2bb4f9a7649f75c88a665358886c540abaa06a62683", + "0x917f93211f7e4d142f881a2df77488b54cb37b7547394d18fdc5e39a7a0541dc463feacb0ddc0390a05c3e7c6245935c", + "0x85e0d639211fb6d07fdf4eeb68f312e4adae567b50cf2819abe151cad0fdba679b352e344d83da9e32d8e5a23b232052", + "0x8f98b8fd54a1547a224bcd71228753a4854c77b97dd4c4cb9cd0db7b79be13751aaae9ff9265f57296d4381ceef900e5", + "0xa0ac1c2f4d3307f974865f78a976c94363a5af2672dbc03228fb5a917ab2662b646e06f6dd1ffb6583cd919f1db00761", + "0x85d59fcf62411b27c7e7d4440dabcb619c36531f6f1c78879eea8bcf51bd37ea21f31b07277f40fe906e4a36edf1bad7", + "0x9985882091d7b6cfa3f8b0f2ad0d83fb8b2a09937d919c62d0c453a64020e0db1171bee3d080fa55e7e8930178235d9c", + "0x83511a83dcc3ea35cfc701c82c9e7cfe153c071e0220811a8a663f697f973074d35eb467bad5422c4f57c8df697a2e99", + "0x95e89c3c15382a7c26b77a91c3810c2d5e4598251cd95f83301af212f213adfa275c22fd3ddc7e82c70f94a4f7119e76", + "0x85c53d69fdbe002fcf7b395230953b7dd43552942491c25ab40f9eddb4de272cf14081576af2d8d602664bba72dafdca", + "0xb6510a384c952ebed676eaee3bf9212b8d7b949cc82ecf207a56b037433672ba001c8b70fcab6a1766ea17e612b35b50", + "0xa98d3ac4062839d7903aa7379c87c4a6f468a3df0a71de3fa50745dd7c490016951680c6c5e69b6f038491361b64b4ec", + "0xab1e398ee6ba3e5c90052a5d66fc2cfc43ab83148b5b0a102a8610abe73a7765f24c3e82d9f6734f1b4f627dced146db", + "0x91fee9a0895b6ef7f5fb519d2a62afb3e735325d52066a8c1c6236298364e1482631d18d5155fa3c13b8a51f540c623c", + "0x9063b475750f7f8962ed1f3ad6747c3e597fc73291d18c9f1be93a2656d9f5c182b06304683e8eb90859cef398daf911", + "0xa7c796a76d5970ba1d85f50685ea852533d4cc0d6aa523b498c15761d715d8d7f3f681b1aa59ddfac632705d476b5dba", + "0x864b6489c45be9a94f6da3458811996e01c31e05a7f997f383e49fa2567645e94a087b72c7ac55416785c232e2c38393", + "0x83ae287d73a868d15a71d490c9a058d3895179325b3594d9d2760987bbc3edd849caa36d5dde8f9e1fb2bfddc30ddf6f", + "0xb62a29e71f713d0526bc094dcd040d5b90a807de3829920ecd4144014c5e776d544a170749a5c1869a3a4da188904e53", + "0xb05b572bef26f17ebed4b3c67e4811f9148b3ef467253b92f22a029854c34d2db42bae110da321037e8fb33e765f4110", + "0xb02d98775ad8b42dee008de80513c6ba5bc7de640d1164f7ecd17f8725b26de07efcef27fa9028ccbb5f7808b5736c19", + "0xae31393c711eee2e6c074d89e9d040ce15b8a3de3de767e4bd372f742bd5efba711dd5b584f11106452558af1bf09fd9", + "0xa999aa6e7fd1288124823d82798b0e5ce7078b532926b54031f10d8a5e4777a8099e79dc2fd7d4e5319c30e37793aea4", + "0x8919dca26ba7320ee5ee97375b4340f5cb26c94800e65bedfbfe24b9e0c68a26df91d9925bffa56a4cb148ebb790ffbd", + "0xb0ed3da3e786348ed88ae6f1ea98e7eb16c5345849f34c342767423363fc3f2a938a08bbf2823e7dbb377b5beeaa2167", + "0x95bcb835e1f6f5bbbd828247670909308060e160b779b4a610c5b7adf7c993836e070e75cd9dc0809eba99d2a492b3e0", + "0x9698ea51f4a43ac0ba4fe99da4ae2dd62a8a2d6b47f29d09330dee4f2cdb6c958b8d26aea8afc56334633193661d3daa", + "0x95ccafacc593af1ac778c8690a4963c20de08cff1c89137218d2fe2c2b284aedad0ae0bae5a2eee0b0731b52f9229a6c", + "0xaba4855773b70c2e433d074eab28e9145e26f4562cea41808537b0773f8d4b18c506b25cc919a62cfa460a904a39240c", + "0xada1308bf41b5ec254f9b143cefc85d23758d46b79cbeeb1eef60b5753a9e8bcff1369147c92f2af7b4e8048e35a2e55", + "0x83f059c852bf5f567a80c979c312e6128a07fc524f9c4e7178da86337c97d6399fe42289d797643cf9f48c21ee527a06", + "0x87fcfb71667f5a890766607fc6fe1dd34fcbddd2a4ff92db2336b3958586008a98ed876051bed72c4ee4f1b6d862c690", + "0x9472325c4bb40c0e4da1005ae80a31c324c334d07d03e6f4b3b7ee677374f408ff2dc8da3eaad2d047816a0877797542", + "0x93527fc9ba3fadd74e605b1cecae316da6a98835c377c9ea3e81cdbdf52dd984b00a343d54c95684a3616440394e685d", + "0x89e049ef5c0636468dfb5af79430d5c145d8bc5487d2cc8c1d12afdc5bd59d2b9b0c501c236deff99543a3cb04a2c62d", + "0x869567a1cafa6d69c43842c13f16524e164f2201afe4c07c234a46d37dc7197460b7cbdb2ec82c3b610e0e9b9d7b21b6", + "0x8066e8d6c3845a60ba1359ce3ef8af55e687480e11cb90fc667ea58fb8f3e4787c37f00d2565a81fd5ea2b4218bf7521", + "0xb7289195308d2d2bc4c942cf2fc6e30a688523c01962261c23470c5753750e425d5cd4d166a3ef1c63039528ca4e0238", + "0x82fb66fdf8af90c25798d795c7725c0ef3644097e07250c6e324ee31f457d4a4fe5f111395527432391ffd1c814798ba", + "0xa3bf44577b103c0d858f1818a2c243199c18225f7ea307cb249b30a33dd13783f11ffaffac40f36e93eab4d594b367c5", + "0xb75344ddee8f0dc4a75a9314d38ec03f8cbf142bec0d57e977a64844c2a3431ea1a2ae594c6473475b05f173a283574c", + "0xac5ddb5025e7616ee9f697cbc8dc0116bd81ae93f1847254ecb01ee96f0104334db6e6c71a8aaa3276c73a5500752d93", + "0xb44f92cb2b9206743be8f0b8f0c94ea6c10fe332457c2a0a100eb0d00317f12ef6a26e832e49644b2619d7c0f3fbffc8", + "0x8239e9683284d3f153bf5684f830f10bd8323fa2e5eafd4c47e81333eda0eda8e07d73588372bd7077b84d77239536a9", + "0x91a9ef2a385e7ea4082b0b51b5cb0126eb19c0538335dbb2e66049a39a017bc5fd077423aa77ab603342741bca38cc0a", + "0xa7dc2359e113a72983a15b1b82adce82015fa6fadb6bddf2f610fc6edf825225a2b1b21f2dc1ba64b49c9720796d4a66", + "0xb0648c8fc0cdcb619d1f42d4c735bf40189cf6f26fc647f41405a4d87acc9a32fa4c770224857531963d51e4fd79d1d5", + "0x8515827df7089c371b9c0bdabf1697624bd393990b5e15ae40fed376682582546e8e02941374ff994275e519a596fb24", + "0xa7f3273253f3158b6af8567b72a4fc60fd67d784a1e9fbf7241e9eaadb99448675d2c1b99b5fd6ad755e3192c6bd4ea6", + "0x830def0fc48420225a68cbb3b5839c44264183d4f8fffbe5ded900d7a6f70314a723b1a46f393ba273fa12a94359468d", + "0x960206bd6846bfcc4d19f683e45359fedabc7447fbca91e180eb9e809bbb907579e976cec690b32b2a5407e1fdebfc03", + "0x8de1057d7dc72eddf7303c4cf2b01ec52058e4b95f0d74a4df381a3907c30ac0f5d18f6d83e29151900752df97afe80d", + "0x9399434da24177db9e3b3d3d52639e00b98963f4d5429f543347dc2355d1e55340d5a5dee596b4ba4624d2ee42b02e21", + "0x8888c138e30c5bfa44b7ace138030a1a3eef885e17a88dc67700e82ea5b27a55375e368c2b5f056a4fc5d583ba8c4f4e", + "0xad7afa36eb2052538821ebfe6e65ce255b809ea06c7bdce61b63aa7870b8095c5320c69cedd7814ee4132a9b75513c15", + "0x9283e1fae83d04bd626bda7eaf532363c8dca924ec4e745f6ad674f8e5acab60aaadd06ff0bfb206c4b16dc819dbc28f", + "0x8434b711920556043d748aa766bd60012642a9c83d96f7f67e7a6123d744b0576eb0a4353cd67492424258f66bf6c599", + "0x973353ecec6d1a09ff92c8bf852a30927120a58d1027de0cf4b6253eadd770bb5bb012150449f94c3523ac2829cdbcf8", + "0xa598b1d452e5970e6f0b60b2dea6695011b558a7bed9853d456a52713bfdb23e27b772766fed1582122baad7ed3cdd1f", + "0x8f96be045bfcba4516041d6b4b3492172cc44266b23092275bd75cd4dec606d8972af0619d7ff5a2e09f93dae7ed992d", + "0xb16058d6bdea41ce0b4ac3730546da62db0cdea55eeef0bb37cf7605fc944e027fab4b736a0a226e716d3e7d7074f8e1", + "0xb201284e625b71accec61515be28036fc0c921dae0ee6cbd76e330e2d7e1c7020cfbb72abf1023386c02ca43e428f0f7", + "0xa7fea76cc6c9e0e64898908d4b77d8d87c73b5a641792120cb12ec24afbd660a4a4dabe9b667ae04b5dd184dbfbf69d2", + "0x90cfa7e7b6c4653483255593b1da3dace58891b6d74c89473283f437c3f80c5996aa95833547f788510d3dcd4b46de57", + "0x8c43a74381cdad800d42906724f67892164cd54cd6c357d9afad7d30497db0c7f840c8f4ab7647d639d48aa5e8edea64", + "0x9084dbf004b3fbe7993821124a21fe89ae6416480af36d479cb3b0fadd5fce72da8ff859f1ac82ae0be24bd3fda2e5df", + "0x9029586c7e8e45fe73150c31d3a2604663cebeb38f79949e710125b2c609af10b468e77d3c4afa0f0268f20bd48ee7f3", + "0xb6c82b2855b5a50047ca3502d4d08ac75830dfd946489fe9b79ac7ce64725012c84b2ef239b4c6961a7be48af7b479a0", + "0x8b6d49a9df207ac4a95b776b64bab847f0dff5fead5141b8008332edd41f4754aca02ba9fec497cdb96bd7eb04cd3b5c", + "0xa49126094f79e2c67a357a8f2d72ec8d21cacb2d2ad75cfc79ef5fcb6f1c8aa3c456d73a4ca05b851fdae28cd0436c93", + "0x8f67963b11333dbc0518f83ed7dd6226ec825c0839095bd23a97a08efacd54e583d7dd073b2db5282297327814bac0fd", + "0xb2736beb4e693c5482cc2e5e6edbcfc275f86e70d2a9859c7fb9b10f9db02c408c812fa59fe87d1fbb682cf668f3b926", + "0x98fa5ba9f431eb6b095fc306289750ea70e2cdeb68b2d7708fb1e57541329da6a6cc725d5ee6df7a8482690a3c03ea4c", + "0x8e878976643521abc71f70412c8add258f6aee74a8750d26401db2b00c94c9922926f4ca6c05dabb2b1c806a246e204b", + "0xab89531929cdd05926b7133fbbbf02f458860c352a359a065aab820a499c84decaec4d57854f9e7359378c6d056883d1", + "0xb4f727229c72554dacc491e137028ea990c049ab609d17df3e28db631e8c90b0809c1de7558e87afd2948295b796d87d", + "0xb1ac6911ba73b136703f9b90efba1a1307abcb9f15d64fdae3e509d624179f62dbca6540c9a2bfa88be8af4fefe7db28", + "0xb6fc6678e53d878239c0a6a67c7311b4d4423958dba685980e4c9e65a5e6bc48c841e88d3de45768bba63f7d768d38d6", + "0x93a462640aee1514c500972f1544097ccff5a1b51dcebed7673752925060c663d714127256e86dc909d467b0881bbaf4", + "0xb8082ba316809dcc96a8437a707cb0c673c1f56a9826560942f3d435bf17f17f94c7dc02135ee4c0cbf1a615f39de056", + "0x9512b8a4556853887ec27acc49249668994fee254a14a6b09d6c3c3d01f9cb1f9c448c3ad696f30b4b1921836b36f3c2", + "0xb8c49fae8da3939ab7c35f3ce1f4d1ac10fcfbe6945e0ed2cabd1040b445a7431be4f1ca7dacd12379e3f96861b46042", + "0x9917b197cc30eea299556f5415b73373063aab4ce71f6bcf52f7c2106f05aba3a70e9982b8e4db58d34475f084aaee26", + "0xb93f489f222859d05f0aa0ca0bb56ec7e2788355bfe4f87eef1a222726b7db257e45af09549b50960b818736efe50766", + "0xb70cda9b54026f77ba5b83c7d711cc6d9c4e68017e1d9ae4c73b91c0695dfc358059fdef5252f39361ccbbfcf76b39f5", + "0xab23570eb3216f73b5145ebf458b3c2f0376e91c46cc31ca2cf6c7a2cba2a3772d0a43d171c97391a9fbc193729e557b", + "0xa9ca1f9714d580f1c7d3965069fc56e438f91dcfd8e6af00178eb0252dd31f526c3c5d7c5ad380f4c2c5f7eede4ca848", + "0xaa76962e44660f82fb2b4aa4f2d44f327740030d7d48b3be4120367d61824c32fd6d495fca4e11404a3de77831d55867", + "0x890358c6710ef821c537242f30d9c80cb9f99c2c3e9917feb6e7548d481350a152b45bc3aab2d4dbe580580526bf6cdf", + "0x96cfe6dacc956b1ee9dcf3071adebaee2319253462006dbb012b2b6fde18bac2305e43030c7781950eb6b5085ff1f468", + "0xb85a2c5558f48d38689f2d4a58b8d3ce1e603df34e275b99a755245725661b86388369d84cf579adda7d51d504d52458", + "0x81c610f41f60d73472286fc1c39e7f27bc00cb60bcae864ec00dab49225c50b6f827e027afdf0b08dc4ddc0b98a004da", + "0x85411a699f15a4e367d08cf8242d21882b4f9670d70fba32639af9c82806b7e533113bd617a07d81103baf2338d197e6", + "0x8b40e3a11696d253cee52515cbe3704697513432342121230822268bdf1a9473a1fc773d202b5b3a1e0039474f98a057", + "0xb855c7b7f721f6c9fbb7ed86c80ce9a43f40184e30e570c96a18402717aa6aa957498dbba07b9cbf7357a84df6b6f8c0", + "0x98f46ac4d11befc56457c4efff575bd55d81e73eba178654d56cabcf1e06d8717c314d7f8e148dfde5cb1507dc30716f", + "0xa751748d774040c7ffa523e36d3eb2aaa24b691550909d5fc2cac9d3c57b80807880d80edef9bcec637437ce0434256f", + "0x962bdc73f7c911880a551843e213dc2e2bd21e0e85af1f6027880cce11ba1aa45c2bc56c948163a3719a74a0a6931ed4", + "0xa8c9df76e5c75df17443f4aabd66aed237310e0f5ccc2f37622776bf19c6f1ef0d6d64d5e0e03b36aa1e3bec70851f69", + "0x8d142e0b4762c2451e07f46dbc9aeeff87285baa85388576d5acf3813a47a5f906c33ae0dfb75b59fc5f8b11d2159910", + "0xb282b2bc2364b39b00f6aad1b0180dc8ff9d94a88ba9ac257967b9b15fd7fd78c5eaf2f45653704dd828e69076732f25", + "0x82f2be53b14d9210936bdfdd204ffd16343314250ac999e8d8599cbc93bef0ec40813b7b7caab9687fc90fbd60f94efe", + "0x81e51c60bfdbf367102947878b0dacdcceed1dc1b07d4176bf91cac24dfc0920945cd8486e98ce9c9b56513fd9d4930e", + "0xa02b4a2cdeb603775f609a6394001b2968174963152b45dffca43b423525075238220f55cc6b7543dbd326cbf75a5ab8", + "0xb93cd7ad45754c40d24d9a6cd3ab969d8a5c7d3785be5512215732db0e92e1cbf6172a6d36c79b524b519a2ac66cc5a4", + "0xb3c3904cd402519e0515b07e951209574c2a228b167f5714294541ae5655e4627ffc5859617cae8b5c7e7a644e28c9b8", + "0xa03d0da63b18912207b6e06544c051dbf9c28ffd143d6a7079c764ca1d2353d42a3a4dabd05d97e8c6625ebebba416b7", + "0xa2d53b7fe3719cad88ecca585aa05ecd2b3d8f7a4f069cfb7932b8aaa35ca20664569723731a7371aec34df4a6b64077", + "0x934d86c937eccb349f556dce44493fd6a4e64237c006d830fd8c52976de9fd1c38748793d9f1df2581efbdb2e27c30b0", + "0xa9c85006c11841514a5bc24f528e18b0bb951e819c2e7a041a072b1b93f10dc2a64aea9a478934dfccabe47fb9979189", + "0xb06485b61eb548502cf07d00ee2e99eb1cd2ff02452087b5871515994e80e1c6b30e7ddb457f209ae14c21ca57b1dcff", + "0x8fceefa6ad08e6d02fa59465f9a4769d96ac07fe0cc26c9bb9294e82b52ceafcac3231a7ca5d6f6282c7e56729db3cfa", + "0x81c8879aee3c6aeed49e4ec8c3ed7d1e53def392131b72caf4358daac76c9df4c616d8d278db6351baae8da5caf17089", + "0xaf994d8849760465d01447a8e783d6b7ba23024f1ab5256bea7a19513a5bb30c22d099f2e4cfeec603cb0deb620b11c0", + "0xae2822b697d5e627242aeaeef840eee1fde9176a28c48dedf144c146f4aa93377988dc1e73a8a576faff4eb6160a0893", + "0xb6673315a78a0e79ef64ea4a71ab21fe919f12a6924e0fe9d016f55034e2d57c1c8cce5af098184121c1fdfbf2e182cc", + "0xb591a7c147a21b77830158d9d0f45c1e81a8d1a96eec313e39a948746940593155c78c66e8b3e88eee488e3c9b413f3c", + "0xaaf781b8865a9412ebf88193e77de03ef6733dea93b3d3f176a05a681484c35ac74870943a501bd57dc1ad353f3ac6a0", + "0x89e2de915181c46d086f203f606ac638f5847a9211cae054febda0058ff17febbe843a6184628ce372bd6727134339aa", + "0xb386437654d04d5aad99ce90807d47aaf077d43e101fa14be6459a6a0f61835f116f0bc83ec79e85da6d128ca22d2215", + "0xb7f5ec7f73301179e9a75263a574ba084fd3655b36f3bc30642d72fe69befaf16a9b5771994a7f2228e8976daaf4005b", + "0x9115881edf9a5cd8f6a4eb1b674ed3e47dc778989784826562c9f38f119f0693c38f904b564a79a01858ee0e9126e4a2", + "0x869dcbe925f34305f35694be44e62575ad03dc27cb0301bff34632b02e2e6fb80406bb33f14b445f6587fea0865a9c26", + "0xb841622076c238b7d0c2cb5e118aa8a75081684a4b5b92790c61cfc96c0724739dd5687d458e7f3b47c6825806793c25", + "0x821e80442cd9386111b1614300138c8cc5c15ef220289c767cd99ddb20102f310fd7a7bd125523112cfee9ff1cec6d55", + "0x983ca5b06a597893609194a4d46041aca2b1fd53b138e5a9cfd1dea29fac1fc78e4a43a14d8785671773fcc3d0d918cd", + "0x88544182dab76c6e41cff178715ff8271b6d0edbf6eb1ffdd8858f8b3282a61816a24345eabca7e20204b444cd9f053e", + "0xa54f6847ff2a8feac879a8ed08b7f6ac8b2384870186bbdd3defe4078ef828a3714add128d981e0adb4ea35dc6125f8a", + "0x930e18c9e712d1fc415ed1e6e7626662135426c62b71b7952f87a952aba6f82a8b77e0a2f1ee3ed32dd1eb84e0f12992", + "0xa5d25d956388c0e9185ceaf3dd6f8099e9f47f58c19a8d477c408a57166ddff3efa712a5f4d862b8470db736b580a979", + "0x95e11c72daee1414218c0c51f3bb3b499cc71f64519e0b59d8a5bf4fcb32ffce369b8b1b3cbaa14ac09adcc9b1eb089d", + "0x84f5e776b14e4594cec3c6f7fb0283e0cbc4ac2968fcd3cca4ced63ee579483e62782c9ef5a0861043a949b97f4feb0a", + "0x862b645d5f5e053da2a989a601a2430ce2aaa2d087df1e651663f6b5e3a1c5fa57f004d827c91155c799077d3e7a2051", + "0x86484ccfd10ee5c443c5e27f9fcdaccf31a464784ffb4b785179b2e4af6a6fa7de2c51a27092f8c004fa9f06075b6359", + "0x918cbb8fa80a646bbbcbe665e71476a342907df23db75fca94f6085541f49e967f681717dbfb689744c06adf898b5017", + "0xb7e36a29282a9bfcea0984b6649a97d804120547b92222b13f000b83f13451eb8a9b186f4e9386b28a2fee6f63d99307", + "0x865aaac2b7cd57ac74513a52132841c2fff93831b9da1cda15fc5e6bfb61058b2083e62c1c8899e2ad8d3845c4b8e814", + "0xaa2f96d8e39ee38c8fc270ff1ac651c2c5679cbe431987bdedf32abbf3ca8debc50399bf0e1b56a60ff1116078ae51df", + "0x8d0209dcadebd916d07860f76d98cb9f6d960e305eccde76e7171b6a3c7c08d3c34b5b8ffda130e59513f35073f949c6", + "0x87ddf1492078c5df61a48ab97eb1f179a2b7784a17008343fc4c9d9374a2e7efce7465764a539d189e9ed00aa56f5dc8", + "0xa5d90ae8e7f49ddf2424501640596987ec477d7a8afbad7396b19b1fcbd406223b314169e658978d30c7e6f41f6bb247", + "0xb4035f4b1a37d0a2dfc6132a718851273d84b7163e6385798a6520e0171126c8cd6ef3edbd8e31edebc925e69e2c20f3", + "0xa0ce2773c4e5350d7d02808bc649b1ace7598911ae4166296eb2105570979afd4250a29a176a27f633375b69518e0882", + "0x8ce7b8fa84ba1e404e5cbd4ff505c2f0f48b488a9e81cd4a08af6259f8e970a675722048be5fea82e3b7e25d2a5b6b2c", + "0xb191d7c5a4e1129813c898773dcad87f8e4e4d140e7f253729b927ff83a5db2451609770d78fdf477130270da5ff8f81", + "0x9525c5468e9bf466ff04b3ab388b3e8d4d04fbf3a7099368310e2f12fe79e0c62cc2087d2a0e66a19e3c329dd4cd844a", + "0x8fd090b78171145d83225ac8669e08cdb04d08f451f65112f838a47dedc53180e92f7a7827bdc91726feb179e293e3c0", + "0x9671c009514911bfe57c7ebb34f4aa149e4c27e24a9e57ed76777fe600f6b6e3f3499d292027883420b56c47cbdabcbe", + "0x95dc795e680745ad3b31dcf95482563b25d48be5d6698555d8bcb013b240a7078b556d7936fa55fd6b2712509838516e", + "0xb18219f69306a34b382fe2967e295ec666a3c26404f38f971305435230317778b33d9bb68ba950b6e619d6749131211f", + "0xa6eec9dd6b0e3169c895bec042c924e30da1e6d7297412580a7e77b137eacd28b1fc7674dfc2b3f695554ec678db7268", + "0x84c2d66fb952d5e8fd21c237967e506dd17368ac82cb2bd153d68d945436daa68a0bc3016577d180b8075156ef76a52e", + "0x8ef52151754ed840c3f4448e1264c0f57e7e05215a74f2f7d7c7bf97bc3d6ea50ef219de2044623ec164c05d269beeae", + "0xa72f7058956abea15d65971933b86c0b4fdbe6b4ed9147eef27d1f1d893444cbc667b0cf6d97e9961b58cff347701e24", + "0xa4a5794e76714991c075739e8b4119fdf939455b005d93f7b9e2602962044b0e2aa48eeb2ad8037c5c3b6f2605ba2a52", + "0x8af87f4ff9e5eca9a208f76895af4c0b51d57c5107ff022fd7055f0985537f7d73ddc6d83ef9af04b20ed5f099432757", + "0xb746f58e64d5fd1bab16d3e759ffaaa3eabbeb0a0e2b46cbd0516d875fc2fbfdbb4a1932cd542bd672df988690589a12", + "0x88bc9f953849dbfdd3eda98f3a22adf366d9876c6870e75ecb1892ebf0b7747952450b3b553da6808f2c6f42419c04f1", + "0xaafc1169e3232f5166a59f95f5206192067f2b37ceddafa72dcebec7cd3f6d5c3759f4d1587f40d562a39b9f32c1e234", + "0xa9af5f0813cb4fdfac57692246ffbc7e210cf268e31186739e1df01ae6280d0c0a2a75379723eee1a7d8776c89a5d9c8", + "0x849c14cb1e89f194fded642c384b75bd321209824ef5fc00ca1b6dfa84e9b3e0b5be4eece480f0f3e6b212ecd5b16bc5", + "0x8e26d9d69cb7911f74b9a4b53361494043b38914364a0e358cc11bfcb344d0ae5ef70659946a33ca4b48cc0981c3092f", + "0xb90c9b1ea16392f172f60c6a506caf77534223f6941caf54e45f97dab384d12230b339caf175842714f78e0b7936f99e", + "0xb0b45212b095c7256a53dd14ff78b82f3021b6fc05836a7931941d4eb9ae5cbd663f585528521ce90bd719d4dff81953", + "0xa35f3195c0db98de9acbbcfb0ccf3250054c57476556ca4127431cf321508fedc660d7c58d3a97ee0f7aa0a47ba0125c", + "0xacdf448c33553caedf57bdfa45a251583c5b8569293a347744e3dc062d9202fa4c774e02d9ffdce1c535c465727e474c", + "0xb11f1c416af1ded336191fcf0102bc5b03efd9eee780519733e8537316e994d6ca6472ec3960e930751a6af8431cf39e", + "0xb33f2ea7bb5cddab4823066ac8ebbf31068760d6c8ab9c593f2a79e78e1bc23b3f07d97b8f8edf50439a1dbcb3ef2067", + "0xaaae065fc0b3419a927911a322edde429fb28b25a09a0dc2090082469c063d92bf1564570052b39395be1ae95bdad679", + "0x950229e41f2dd0903b0a3b733f5739d79394aec5f7306b200d4c926bec4c3ac54daf004ad4f4383880c0c63f319b0eba", + "0xb72a6c7fb006388ffc4005f111ecb5172ae76f86d0162c0a491d50d9cceed4b0e8b232ba8c4bc0f751b28ae91578d054", + "0x8001029c84072e1b291f4a7dfd2f1ab831b442149d3d0aebbbe15b915c6c8a95546bbb6aaed9c55da652f20e62aff049", + "0xad3b85bcaba600cd6e94955bbfa0a3f04979cd9a011ec9e549ec266840df4d83947520bd784496af26e60b239cea8df3", + "0x953fb0f323a0076ee59594a2728ff751bbf73253aec4c93049c0d8b444d788ee8480ae171db0feac828d2c921545400b", + "0xb03a46be9d1d2f86a7f295f320eafd06a4057d92558bbb8fac6f03b39164404a82320adb69cc1c18e8b1c1e1a9f03a1c", + "0x8a4783d22daba6b828ebf94a01b7e410eeed0cb806b89c13a12ffbe3a3798252932addbe2ae78bcf6aca11a8b46e92c7", + "0xa3e8a1f9d9206d4cd4a791d0ab21730b05a0c79961570b95b12b9b242c6d381f3ca9bac9872cfee0cfa9d7a9f344f28a", + "0x828a821e9c7307077c771998d31e278aa07f513bbaabb213c0af2b6cba2d2db3875c69ab2cb82f3d3326a6abc55d2e78", + "0xaa0cb8eb7e27dc3e8773f699598f3e4ba8326abec98f07da7c72787fe5cd264186c31f75d7b82785eed1a2ea96b3a440", + "0x9256b0dbe77033eee8c43487fa61755f0c4c62033619df5297e7314d7889cb3df29d56f17385233cba5695954f5f5b7b", + "0x97b7b9f4eca256c30506545b05162ea16604846a9eec36e76cc8e25024853da138c319eaa543bd8fc5089b218b76f9f6", + "0x88c5d096dfde6b28e2ba94f5b17e4b3be25f957a1ffc24df5a85692222804c3380f3dedccac31da71d2aa542669549ed", + "0x84717682bbfd2f33fddee6e5f61031da512cac94edbcffa980f63dcd45b095adca1431a3a2287681c770147cd26a905a", + "0xadd867cd200b2ae0e6095dea2ec3c9d7d7f6f74e7b772efa3ef4036f69443cf3e298112b10715a8bfd035c1e7406563a", + "0x852c15beabd9951fbf4647a06e15253909308688b0833afe8624f4de99b24e8cc261c7d1d5966c5d729fe4f57b2cc92d", + "0x83aa6aa8b34182ba29888f42fadadcf1768fac69b62116044f7f1ae32368e4319533e5f3eecbc1d08f0d5086af56f985", + "0x96f4e742383cad708c7c6ef769be2f376c53cae37ccd66a0dc0c4813192f1faa5a5e3684728be95b50390923c0e39f19", + "0xa2f808a0a73f24babf9b77819d181de483170283d0c8c2cda009d9125e9ff22a56bd7bd859222fdc949111ac4fa0ab0a", + "0xa1c6cbeb4197eb7744bc88221dc51b33242013c38b77169d16b9d2696c92faecc9dda2361007e90f7ed2d013dce868a1", + "0xb66cead4f0fa40f2ad350ed3f3781a08d3cd77a0cb43e22ec2067d84c7d806f1e5bfd62a70f698b82e6c21674fd64a34", + "0x83bca4dafc0dbca19633f8c49d56b573b600815a504ccee545bbd46ec854a63ac20d197c7791ab4045585282056bf2f3", + "0xb0401d80136beaef8b1f7eafffe60255c7bad0b2bfb43c31d3b05e699ea4e61a433e7cb92600ff3031dc1866bb9d3d9b", + "0xae23e379f4844d625cd5994bb4c1444a89f819990dd7fb46e9f11109e2c3cc5079b77b085b9c148e1bece9062c9760db", + "0x8cbc61598e455cafda73c42b47f3b185bc642704facca44a266af6c139ae546b47a24bd80f4fbaeb7fa0a3c2044fd6de", + "0x8cd2a0340629ec6b1a2b299839d14acfadcaf3ea3f4d84d99811c55d409db3bed042cc2cb52417d558cb8f15ef548933", + "0x8b70dde95ea869e36a66c17f18ddf719d30da2713269e6fefb38345f45edaec59d0640b397713689c417243643742cba", + "0x98272dd51686668714e0cf44684d5ca47ded1c233a54d054ec1acb6dc3f22f840e85eedff349c863a56732178f02af22", + "0x88e3ab41e226db0111612b451f116721a2340a3bf061826a4769be91bea3ae58500599384d9a5c6ab484f41c7f5e38b5", + "0x8f86acdf7407df7d098deb720c17007c5ce18bffa19e02620fec20107918d203fe4ba7c768a926354415512490d8baf9", + "0x9146dbf56050df9f5aed16be6f501d77c02d97655218a004d30c132793b769ef309e81025c6086fec4a85186d6a75735", + "0xb3c8439d779c0463f1366e5ad778d8ad1880187960c56a19708ba563e4c3ad8db0dd2f092ab6d28e8fedc3bd0bc172ae", + "0xaffe10a698052f0c8c200a97cf92c87dc0c41cdc02a491f0aa639b5f48aaa31cf334dc16b7a3855cc14ddade8d8da13a", + "0xabae13e44959831334a0d40af66b8c7c14fbe28b957b3adcdd057011826e40ea29dedf905609f2efffe727a92a62dd4e", + "0x85e86ab6bf72012fa4e33c84fe8772963b864ec54e174a3182829b76189ead54d01ab1986b026eabff2fde2fb31fcf4f", + "0x803d4e3689e1d603bb49818c0ca2ce80b1f2c9019241e2276fb284be5edfb11a826ceed3df59fe642f3f9803ea9876bd", + "0x8d25f238124b136f4288a3a3f8e05fbb217f840819de5669e281f295982ef05f53cf679b0937e23f03cbd59cf3df08a8", + "0xa5433af97037bb4ac9dfc5a8333c15f513f91070493957845b3ca8931ca8859515c24e49d98b20303f9fcf86d7736d44", + "0xa42a05221d64a4e8a22255c49eaee1956a6a8a5e28b21a22357a337e8fe2fa8f3519909a1d6d0e6c9a3cf98bac8b36ec", + "0x8785a544a3c0e1c956c42bdd040fdd377bed675a16075a98bf846aede59af03b44bb59dbd7265f41137327f29fdd6720", + "0xb223c2a9d6d91bb4d900bbd569adbfd343cf2c067d593a7b5207474284014a0905b655d912a7097b20eab1235e8a5a4c", + "0x992551b115f9bfe992e929469e38d10267cc672257fdaa031cc4f1ae649187d6d52f6145f5e09442ad9ff4663c77067e", + "0xae0b2b6d3dacba1c5e52b9b08cb0826c2bf358728ea669ab1810aabdce95277759f8af3fba2506e5882f8cf338ce236f", + "0xb6982f4cff2040a70843e6ca0a6a61187e2c513d8ee8a16387bde2ca3e41ef7a137d04c4228748c3bf8a4899594cab2b", + "0x8df26a1d30c71383d18799a72cf2f5312bf3d1084fe5bc107fcbe6095200cc3f51674b3736e8c2eaacfd5c15482d011f", + "0x831c9986724f5281da27be0e6e8ad1b2f7074a933489271e69871d3cbfd64f88bcb548c867243f1d94e1c30083fc9fdd", + "0x9753c5487436a7e47d23e1a5bc96479be16f5110f2e556a615589af761dae26364618853374a5f95bb647735e8c34b83", + "0xa9df28e5493088f9265cd71457f2645606abeaaa7518e746a7803043e649066e9257502efb59a273b3a5802f21cc3fd2", + "0xb1d74c6749b64b119f97d4a7ea86266c50506140634d09eb9c0f8fefce95557f4664f734c7c312a4f5f1cec28b061bca", + "0x8b52d5c97afc83df39296ac780d8ab5724e14c0aa56f4ca259a113692d330d59e161040f989b8ee7a0d3d23ef141d6c8", + "0xacf488f4ce9cabf46591b667fc11808d15810deba85c61e2527e81337bbce18f276300525df082aa5e02c587f89f8858", + "0xa30cd5382893362816ade57d95e3fb8a44ed7d86271a667730adaefd33083c4c52de09dd2cb7603f7e389a52d736c856", + "0x85473c1b01f4d0a9168cb6812b9c2beb18c9eeefd1645cfa854b939f13ef094984c7d4b3b14381fec6816c16684c3a96", + "0x98017e1481b1969744cf34e0b139ad34bca9c7d9d534a5030dcc037c8651df2dfe8df07245e8d87d87b8a4ca2cb7f0aa", + "0x8f40fc2cefc0a430226ac67a5373ba643e32357960274d8bd5a5be93b83e113156542cac211c9caf6489c6779c29da21", + "0x851eba8d94b826828b7813b145e3f7f0cbc533b124b8a34bc0c79a8e8cb5f4b71bad026b4b9db683188e7d77aa59e3bd", + "0x94cd179bab74bf299d6d36feea9441dc68f6bcc6be4adad4633d49f52db6356cf22333816a0a4e5c6185ecdaef6455f3", + "0xb279114f803f30da249fc015b49c130ab06afaca7277a104f203fa520511b0c602773dcd28b746e719610a9c7d80d58f", + "0xb86ddb3e73e4f9f3413effa2abb54b870a7d5ca0a153534a50e5eabe98645a908239baf5e0f223e296df59bdce59c728", + "0xa1e942ca189f826886a58e8e739d0b23cc376a7cf67a22bc83a5159cb5a0a26f1ab339a37648ce942ad69f488e831bdc", + "0x8d026db998079873eb4c566074e99e6a29f0c51ae0662e30c1d41a0fdc83b0970566dd9f246accd9b2ea29887079010f", + "0x85de1ef12dd4e6d9471916a65b76ff31c15556fa459770dbc29cc5f1c8b02936cd79316470b26ae29ea65ff513bbf568", + "0xb673fd6d53bac18c945d57b1b2cb1cc139969c3617b2df4635b87227d400d27ec20bb9608d8082318c5d8b3602e2cb15", + "0x84ef786bb7c8ddc2b7038154b5e14019eb34fdac7e493fd4e4b7555a748c0b121eae5b62c9c9e42a807986cc6b91e0cd", + "0x8234de0d8d0efa2978acd740470f60c76d367f135fe183ccb276c60b467787e57455907832d28ba55a8dbcd2f384a878", + "0x8b61c1775c4dd20e2d120a15537290ac04b0f631d7766219656bfae649b7cf5a294aca2fd531ef8793b61f3458db3f22", + "0x8d4dc85f3ce7646c817fe1ac40f381ddfdf87237a333ccd61fe0d540004ae138b260fa46ad421d3ad43e593cd3351175", + "0x8697e562f4d7166e808ea51c2a1dba390f102da807d82d0bea0bb1e765a17ed76a4a2f191f28e066bf60ca9f0b234294", + "0x9569943b687fd2e478195b0ea54bb8f052acbac6c3d1b27ade785538ae7a052a2412d699f2009a99c2912d2b06d683eb", + "0xa6a14b617f7730228b8056703d7c1006a5adf5e146522c76c421a9a9c67f9e3d7593d7877ce5409e178542423e1072a3", + "0xa43919bb197c9336d74d5a1705e5e474435332c424cbdcbcc82c2ce4108e63d3163e427d694e90009a43b758f018eeb3", + "0x8a89e9d0c6e908b3c4b766d0ae8ac901ccddbf8f26519e37feb1f5639864af56558d98a64de78f1a66257570b81058ac", + "0x8728b1ec373fa182fd6a7328fb2b909f80b6eb09620abdf6f524dfc8607b47a57972d267f9285be889e4d335a4c0acb1", + "0xa0c37e4de6b1db6dd2b796d5d7ed6d1a651f8c49fff91b9d2e4569b679ee3010760185219b75851035b5cf08821322d3", + "0x8bc544e5144a2847c7bec38b0a10ee385e40e062d5ba1ff75707898f107c6ddff086c99cfda7d6d9f220475548b41f47", + "0x8e25f17c67c669bf22fef7d544017c3cbba071a5095c4dda42dc74c173e4a7524d4359723b6eae6a814fabb96f472823", + "0xb747dd69567964f3c9d7373c516c6419deeaa4ceef728a408f7d4293c26552458b92c03a7e93e6c987c449791713d2ab", + "0x94bfbd088c9c407aa3835a5326e1ae7cf4f6745360ca1defce6c4ec276aff2c312bba2f8bd1ac19c54b5e750f4552bbb", + "0xae3d4a809108fa2bca72e06e4232da759890e34f70cfd1b15aa62de006652e4f56831d84eb90b46f0ca23f269b54c441", + "0xb8d83f7ffc4a363712219162bff7528580748c8a3281032b7495078380f26c42ff899a8c4eb4059e6d00eddc8d5c3cc4", + "0x96fec36e717c15e05e007d1036a6f08649014a391d62c6aa4b7032c9e47f5cdb7bfcab2f53e4dd35ca10577b7dc2e19f", + "0x8ec1714ea43a14f0baa2296c73c6b61103599902fe3c1f0a62525d9b4955a25bf5c58091f040324f27a1e0bb8c66af91", + "0x86d8938eb66d96123e62b66abcad6a17be55744cc66bd984cc0600c6f942b4aaf977d5ce02860ef036b6a14ff88c57c2", + "0xa32eb722e60848cba152068e85c3e67a979031e4e852d49dd8c12c3c7b5c2e5aff11f68a6e104d152e17570883c06040", + "0x82608c32658ca53d0bcae5405318f1ce99e30b65269ed44371e3669d68146f365d94989d54ae891eac60193442d2afdb", + "0x89a960ed65047d10d01c4435f9e5871a3558bb5756c389849aebfaeb221bb1c24abfbbea30f8f0bce54bc3ebeb91770a", + "0x80b0b1c80bdc6ba5755a8d1cb57e7bc6930dd3f7592c235e3fb1d245d8c8dca333f69028a8325d1e00e955aae9596db7", + "0xa25f805cc7bdfc5490fdec901ad233e6a79aed4bf4bba4f8e01009bfef4a5e9b16ff9be98a2f9ec603f1f30ef8ead1a8", + "0xa5189c8a72e5cae151bace1e27eb91685c4620bfdd002cc0863dddb740fea37195169fcbfeaad71255a44fe11c42dddf", + "0xab881932d97f49a0ba4b863d32ba891ba46008109a13b9208156734a3a0ecf45dfb1d687c95ab360591171fde65ba4a4", + "0xb40c223151ad29650eea1680cc5e2afa2589af21cb7d480557aea90c11636c404d0f1ec46cd04887f97b9213bd398338", + "0x98574fdb668ebe4b861168a9e546fe717016dae1f661240707930acc738ef84913e59644011b71a410e8601fc3d7811f", + "0xa6c02cb764ad7aa53d24b61a97a2e7de49f5e51bebc7ed14e3db2c636e7adbfcd5f355a8ce1d5fa087910547dbc12e1c", + "0xb0205210aa25ccb1b4ddf14abd5f95cbb21dd933b086c561b82fd259c0d3edf5ca7b5bd60a02697499f511e54780dfbf", + "0xb2f9395ae59f6b6d0f16875bf82d78d0298bcf60f451eb98d1ad8ba57c6a00d0eb66e73002e22d2a31558f40cc14b7db", + "0xac88382c49a200c8df26ecccca854d5286cc279c45fd32b566f9cc673c27640f6be58ac44120163929f9f98ce72b9641", + "0xa13cc91bfe66012abac5852989493852ac89346bd70d822efbc9f71c5f6ecc7a3656a07698da358ae7afe6088f86b228", + "0x8ed43b924e3ed0b6121618e9a9e67ebc58af69b6847a7898049af526a994e0dd8e0c724c8506f91ee6335039849e6bec", + "0x98fe66973ed816a575c5993a5efdf16abfa70e6e686482535ec5651c62f82af8e651a8f269526487dffafeab9865a15c", + "0xb6df2056df31bb37ddd2b8b7264d7dd3ac5eb8150f8d4cea0433e918b2413b0320b3ea21f91005f51345e3dd13dcbb67", + "0x9232e3e926ac958c2fea3fa6515869c7c1508fe2fcc8a3bcd54196bb5a76056e16190eb10557277cf3b6b88af52dbba4", + "0x8cdd37d14a3fc30cf141b3424151e263082bb7475ea710e01431e336e5a15dc3018794ebe97ababfede24aa03625d3fa", + "0x989623ac9f2000be90b4654a4ca9684bfa1c93ef86d8a1c9b1f15e82d71ce6e2ba15a2ee4acaca6057a314ce0631c5bf", + "0xa2450e645317a7d5dc37aa71417ae10c9b2fbe64320784f0c1a11d62a7ff13ace7ae6acaeb772a1079510cfdd00afa03", + "0x86bf02c454ecea9fa728f0f137f23b8cb7f8e94216af05dfaa1031007eb1baa876b99ecfb93975844d9878ba5a6a8175", + "0x84584a4a5a63c731e861fe1b562c57f45b9a962ce62f3637ce75ac386b40e53c166af592ebf97167e1c2c39c5f7b636d", + "0x8160f023463dec57ce138ffaa0311c6bd1d577b88ebdbddb49a73e60577fab67fa67b927248b5d332902a4e42af3bc47", + "0x8937bd96a1485e4b3cf559dbd90d75c6881ee73942e29c7eb3a0772737bd7dda7d12b57e4b0833575e8cffc850cd12ad", + "0x8f1fc601387eac48546ca11f33f174b628fb2ef8b9a21ce556044f10a4f9be4d12d96bdc280609ff6e81c48d67ab97af", + "0xa8532a29ee99002345574efd85f79b6b0425501442545944266dca3810a26205931b74a880a86b8be5f9fa7cb3b7dcc8", + "0xb02116956f2369bb96a40c1d8fde9bff06939cd1634b2788b81df2e8ba8e7e08399bc113472998e8c98a9369bd58f021", + "0x95c4a1f30bad7f995348626bcff303ce9bbf2c84d23274eb43fecb67599aac31043907281bc5b964feb20c9854cfd7e1", + "0xa72dde6d4b4b7c3087cd056f5ca93bde1c3de50c9392ea9f7d898da2b09bb5106ea26174969a808a16f24ce4e8d66c89", + "0xb73fc53358d00abcc3714e749b9d10be7ba176b9ba0b128c8350768fa3878cee9fff2231e47dd005d0ae74e2f4b8aff1", + "0xaffa8f60a3d83c59faa3d4382c146ad1d7c7f5d95372b8075a2dfe02184b527d377d186ed5975e9cfefde9184482edd2", + "0xa246ae081dc69022278e4ae623cd76a406db805f4178e39bbb3c855d2920351b9f5968ec4b9628dc560611c488b1b270", + "0x91de6eda8562fbb072acc7effa8218f1ca6db220dc7f0b04caf149aca078391c0d81c2284fe3bf1a176524a9108eda9d", + "0xb6c9dd865b035806e4f3c8d9245ca55c4b01c3bbe189a3853b8cddba68e794f3ac41c648cab876c9aa2f14d6e450a6dc", + "0xb4cbbc97360e88681c80a1c923fdd6072ad64659c6275d9529f444a9789ef2828e461d300da197d03c8ba2ac887820a9", + "0xb5e56d3026c16245bbbf0ff45ae2247ea81affa64d84b8392aa0ae661bb1fb0f00d791e41626744f001ad5d85876bbb4", + "0x93050ed0f7b3026d67428cf4133d24b46977cd158124698dea5d9793db9df051aa576a29d90dcce0d4e4cc2be484c716", + "0xb6d0ac1b98e9220b35032249a5476fecc33846163385cc25b10d525346c505e38eaca92dca5d27748625f9bb719a5884", + "0xb504f5aa66c0742b6c6c75073c7fa195a578305659d9e592fe25dcf198a29e3f9bfa039084a84d7f684d5985e500032d", + "0xb38855652ad79c41f4c294eff6005de1928166dd38403ec45f8a482418b1ff5d62ea69331e1eb8b9da39a43a43a99a10", + "0x99ae1f20fcc71530979c56855fdc4c6fb50243c6ccf571c2fc8d73b34216fbc91f0d6964f52cefe95c06b91768d500b5", + "0x83c44e689947655b255066e4c88ea7301b5d036cec640453b5f9bd3c728f04ce97958b79ba7cb3b59f01d14144586cff", + "0xa6c50afbb77eb91940b8698f16c3f33f29905bdcbf5b506b4d06f8f97674fb27fc26e3bd8d41a936c5ebef4692eeac61", + "0x80122104df988ecd9021a0310810afc4179ba6a393c27cc8c2e49f4e26d970dc6266ebec064f422df49f09cd0c78dfb0", + "0xa880f816809599420fa6d33e43e41a3e6f05c4cd89aeecfd8f1c0a70342df03674a22b1dfb48eed13461724c084007f9", + "0xa4160a352854d3c7cdee25bbf85bfcc5348b87ba0bb030704dbee55018dfe1ecc8ce94cccd34bc438b922bd9550769de", + "0x87835053cc12cd114f1a7db1995b56dfbc0ce2d7a96d97f4011d503591c897b6d1ed855761a9522ee5549b5727a40cc9", + "0x990ad15a602cf4feee490cf3540f51822c7e3a29e4e8a3de83483e21e54979e110958c524f4b77f3e3a8c9eb3d71152a", + "0xb600fa70c9d15e1acfa1399dffbf9e8f7651b9c42b61080c420754d19a20c75fa1b9cd685552ea1f3757bcce7bdbc6cd", + "0xa95e253a3f863110f6c03bb93974fe386c501a40f7769c64f4cc0e018bd7ccd1e0d5c18514a8d430141e60f0aaf24525", + "0x972caa7934011d49ba131572f9d7ca598658cad34113521822ce79e6a3de2e382178cf21277fa4709314632c895a0418", + "0xa4c9797108df9bcca381f3afe5421b204e11e3ef7edf9be600a50502800228727257d94e646f1e3c0ffa66b68618e580", + "0x9222f86dc2477b3fdd1b5ce2ca686563a378e87b5e2a7e49daf54aabebe666bf5935efb9a83be9ff8e31f4cef07b6c63", + "0x8e4619dde9ffa9263cc6cd54b4efa0fe9fe24f8eb9cf8602f0c722c9741a1db151cd2f2756a224b2fffef9c17b27ed45", + "0xb18ebd9f83aceff82d60f76c5081c68f3ab1d86408fbeaf68dd9a5acf549374946fe42550598f204296313fcd403a238", + "0xb91b3c017fe60382c6ad6d27c8ea8492d5d045751cdab05b1cb551f08433461a7e9043ee8e9d2cf3e78c8128c4a0beaf", + "0x904b63f62f391cc1af258db3f22f5f588756036919a304e17aeb233cc031548b8c66df53e8b489902785a94b28d59213", + "0xa6ad134459284c46f565c8c11ab5703f31a52502fe626413dbcc34f8b94a8ece27d34f16376ab02c3fc0327e046561f0", + "0x83fcba88ad5fd1970b28c49be1f87a96ce2e2e180a72766d7b27d3375faa3b0eda84a72ac548c26279e78af5e66bd6fb", + "0xa4045954f8fe027caafc97e7df74d13a9563d48f3e372a4e1f1d28dc36722c7f94b2b0ba09d1201c85ae842241a1cbfe", + "0xae358740777bc1497a2a52067cccd34f73a2cbb84e112c2b91fc2f6b33e319b63eef37991650692903d7a3cc120c8cab", + "0xa7778ac229745d3a075df1e6a79f0040acf73a72f59169c97b6385894bce44b91783d96a41e284f26c7e804645f0cec0", + "0xb107f1121f226476357f1b5ff3658ef9455c26bb43b7d941b7cca1bb858c04e911e7093c95072afdf8be6637a5b6aa4b", + "0xa372dab637845889028bf6c5ee2c88464235073a72cbb703fda1b3d37ddb352334dbcda97ebeb04d078fae31a1d08bc3", + "0xa50e559c5c7c08c61b815de2fff69a61ff713dcdb33e5a85731e7088f491dd993aba2a5ae6588327e30c360785217293", + "0x81f5eb92d4f947b9e094a50091b8b2141171f8f3e5231cd4a62e8e7b9157923be79ccf85815af1b101afb75d09322458", + "0xa1cbf2ca5aeddfc031605c26a6fb68fc517efae2a661ddfde04e059f128f6f9b4f1b82dec830127aa95891ac357628d9", + "0x818764f66d7d6e57961ef65663c3936adce28f841d90f9495164c5a390584035f2788b484656de136340e7be9be5b983", + "0x8f52e0bd3a1ab669bd6aa26f494ee95272f64e3255719dece0a66f70200417bdfb2f38c154063d16bc249097c2730fac", + "0x97bf0a64526dc7da86b8d4e09a7999ffc10ee9cf817d637934c30312ac27f195b2068fd2fa00fdf8624841e6a8a4f7b0", + "0xaa52b39de9d3ddafd60ee64fe3429681428e9cee204928bf03899b814337d801de241793e184712255b13b915f64d205", + "0x80ec50dc98deb2f2764f0f3ff42da906c33a6397002d40c41e363893bc025bf18a0875af148c64d8ebd35531c2e78dd8", + "0xa69baa73dc157773e43100f1c811bd6d908d2da5e0c4ed80f7cdfdb00502b8e4fb956ccdb2cea73f9cf6b11ae0f6aa13", + "0x85eafe6dc1d9cdbec6b3e530ee95d3bcf69b5361b68ca4b3f9031c207175dc291159505667367264a9f8f2d35eeaf281", + "0x8dd37a9f21ace46eb8ebe625782bb33de7477788e010916e897b6bd823f7aa3b1422e17b0bb5d9b7561fa6f467a0901a", + "0xa59257a0dd006aaf31a4367f527e1f2cc1fd0d65ccf18b71590c14befe586d06602341d71ad2d2e22b70046deaa3a131", + "0x8404d307adc1a88e423165c8255551534a111895ad1331fb05ae895e58da72c49fdca0c0955caed3ceb675c70b5499a1", + "0xa259a3a6018367268745406dd875dc9482bbee7f962c67c697cf242931e6a1461e4dde956ce8ca0a00c675e50b3296f4", + "0x95e06e35b4724772407fc071ca8671661a1317874b5bbfe4d11eb01729364ba0b2cc2bfb611cbe933a0b979997bdc0dc", + "0x85bf3e8d0b931dc0d043f49e2c0269f63b3a0e1aeaf1788a9454738bbbb40509067b7c014e75e5b09e2e0faeed7f41b3", + "0xb2f943ef30887ba41e2f97c5876977339e8f31eccefe1a4965d727578a26b2c642616c535f4919892c690b3beca18bd4", + "0x8e70f3ca7f53e7ab493e00c41183615b003666d46df5754fdcc95282c72b1b97eb6939e742500c0eb08b59e544c63277", + "0x8cd5a6ddb6c638ec2270e02197cec07d234ff29edea4db8caea750915ec247b21958537bb0cc3c9290544a723279ac1f", + "0xa075811366d63b3962c6f4921fe04e238283605bd2da5df3b5286310971bb3996b7e7f598dc43debb04531f3676e21b9", + "0xb40e78b0d1ddc4d157ecf76c52cb456f7530e7602ea78f4cce2fd11ccdcabb93d222c54f09072d0f5ddd91e0d5a30cae", + "0xb5e13b0a8419b921c42534ca7301b8951907f5cc28c424af2eeb9b6a8307f4a730e47e6c5bfde488a4d6d8205bbeab85", + "0xb5033dbe22e9075a9645b79c49a1ab27c561762902434ea62a01627f574d01fa2ccc57635e9fd0c93c633274be212e80", + "0xb801dbbf3c5da82137ba20dadc5d98d2f221985a0faf10c113c84e87714d60b6075d47d3ef04061cd5fd300f5c1b32f8", + "0x840c1d9d025f462df5b5098ca38bc8897cef6d5793cd91c4a6384c0117ad3551a04d320868c9c70c5a6442a4b8263a6f", + "0xaebf43631dadca3278544ec32759ef8b9c08f10ee9b983ce157919327e4b733679d839e74b645ece36450fc1fc512f60", + "0x97bf6efb4cd324a7fb0e0fc8a4acbe5b4c10454b930d1a3b4971986b025fc28d0dd136f8dc96326de29b6d91ac8b33ae", + "0xa2b882ac54c70d5613c4e558fa71e67a6bdf3acfcdf4d225f239993642add3ad130768f716d163d0e8d996513fb34144", + "0x831511f7024f07d8deb3d22cc56816e73f3a649b998d52e4ae7650d7357074aa8fa650dbbeaba531d4c8626b49b87b5b", + "0xb781413f1c507bc72d2a1d80259e4b1f526fb0f8f8e2a6d2386811f2d81f8a1b12d6c1a7a41003c5e8847d64537dc2e5", + "0x936fef55f655b6b6d3c44bcd2fddb1bdba882d064ad637d269ab1c668f6ed26ac7a2fcf268f7df861e290ddbb0c7ee11", + "0xae46e58a0d95ef27cd6b7b54d90ff51da912dd06cd0e4e31a3d9ba40b070030fd65d7e76dffa83b71ed086f5fadaa502", + "0xa03c7d9f02f99e321ac81ad5a629c47cc597e24be32a8081ecae8e7e15de9c6caf4ecf3fedb64c2c5b79561287fc96d3", + "0x85aa803b97d7d3db8494eff5938cadae97e4efc160fe0d2af0bf67b5d58b7fbee80bfea9843f1b76afbfeff4a0c4ef8e", + "0x82ab8161e168e3b743cec5f56e7306d52eaa7997eb09cc424c35f0609b72405e33f5e4cc7e006cdef0fb3dd369411dae", + "0x95ea6808cfc510778f3c20c5416c6fcbb965f4c33082b1702d7c99c65e02c39ebb3d375d9b3dac71537b933be330f86c", + "0xb6a84ff09fcbfd5f48f6013be313ddb6117328695cb214b2becbdc5ef352d7ffb2ffe451585f71b90c9c2c4c55237ec8", + "0xb564ff21d9c4231b35460d9519a783ff7e026a2e35c6aebbbd8f769e65c84fca977b323b9b8fe228b306b2ae9661ee7c", + "0xb893e1e10eb728c31323a4f118964b2eb1a86f8dc1f564036d07e8b58a2853bcf84696bbeed6acac73a66c66c5f73091", + "0xa706091fe836a123c1b99f857ca924671de2123639a26d1f58c3b33f9cfc8fc22da280046ec8b03994638cac3e64a365", + "0x968635e4b5c62ce07d6e654803d3dbb71fcb599323e2b122fcd45dc0310488e11783e8ff34dc3310fe7b399c9c3c9387", + "0x9767ffaafccd491cc0b7655c6863619090105230617334c4de4279e65d29797fdfe7fe9756f091e6d4065ed06b9be264", + "0xa45fae5fb1381edeb4476ec802f811369cd0512a49872110381780cd0a14ada3ed70ad519e2d42a986eb445eac2283d0", + "0x97b36e9610f66c725d555eecc2f9f3cdcfe7cbc7b8a181903165731f379b84da28ea6a26ff776f1fd31f7ede460d8002", + "0xa67924be063cc059938454a0d446a04ff01c3f4d62e752ea0b76524024626412170bf79527b85827352fd0ab2441c5c3", + "0xa9c23031470ad1396b27ceb2ef52cb1bae20d01fe19f9e1bad005a2f4afd973864789291949570dab60e3407cb6e9f31", + "0xb5c531f2b7c7e75b37cfce9ea43cd870efbe5403b35fbb91666cb6d2db253091a02254620a7c984b8964ec1a83a5ab72", + "0x889297bfdef24e673aabf54cb95e509bd7fd761cf6e4ee6b25b422eac2c72477f4804814c604d54b516658d4fa5bdb64", + "0x8c8e8a9a1c34d08197b30afb9177de103de0d7d683e36df6beea3183b82b99087bc56b719f25330bf7ce4acb5bd98e1f", + "0xb2c0ef0b8eba33f5274d385b2de75ca4f824fed44d892c59557f3adbfa548fcd587a4c1bd4197c89a3eac7ae71687330", + "0x8642c13ccdc92d9f995ac82f4fca0e331a0a1d095bcba5985eaf3ff100e98333416fc5910bf03481f20bff817de69c49", + "0xa4536128d3f1f0ea3eacd083d425ea0b1be16eca4dd400c1385f9bbdf483bcd68d969bf3a66a3e1819cebf0a6dd0a51d", + "0x86b198c98b890a4e28e718c8725ba85dbd42ee4ef184a5114b68cebe8fef7bf100a8aeec9f537ae10be70b8ee365deb7", + "0xa85305428e7d06477bec6bccb0d3bc45414fcbd08a9a2079741e350661581c07a722e3b47e3627e47233d3b3cf78c9e8", + "0x933740523229dc242c943f27acec6bbf2a887955c0d5820453a1820a90cc0abeeec69a98603717638ffc7306874dba70", + "0xac15ac722476c876ed8b5029eb044f92de4800a34cc3536ac9f21f97c3e85151e0d4b8e735396530e59b3861ed7dc173", + "0xa4e0f653506c9b4e7c985b22b2e0d3654817aa13718a220355b567b4fb547222d8d34268baf32fbf721818f4cd6e9aff", + "0x8ab102c68c6b6934261b363d9f540ad37f6b89f2695d3d4f5bf806edae7fef1bf70f562d081b86d75d5d15b22fb30e90", + "0xa8f0b814d8270e4a7d121649de13a8d1f60ada2e42e1471111fe62dddb2a8a9ac9e4da45bf0ec13cbd1921ebc38cff70", + "0x8ca52d04a538ba5e25601f8b8c62d5d2d4ecdddd47984649aaa4ca4e99da7c680ca9b010046787f33060f996d585e1b4", + "0xb6c02bef0d63b61e8d040b7699dbacc26669d8450f505fa137c51f732c2398622eae6718c6594c4c10d753623db43b8a", + "0x94487a850d727ad3847fca2dd4a05cc23445c101560bf8c44426383c9db3ef264cd2df52aeea552a541d2fce8a69c09c", + "0x85a8b6aa0f920914fe1d0cbe383222fe33834279aad63b0e2b5fe65fa6f69df58add71cdccef570a84e812d782bc1daa", + "0xac19d680e9aba31c786960550e7f0b068e8f12ea668d6c653cef093a3d6545c7b739b2c2b1b47efd616474af117e4d07", + "0xadf21bbebde3ba9d1741d1f71957c9c432e9c4194fafe6af348d6855e47fb3509a6863a5fde2d38d31841799f3309740", + "0xa33997a50c0a6b67b48cb4f1e53b4f019126640d02f5b65585edac3da38df37cf19d48fcd865b54bf47ad95caa4476a6", + "0x908fa207e04f7a19c1cfe1e3dd2cac4228ec5d35637a533a203080f9c491c74bc61ac0ba9af388a1cbeefc6187969494", + "0xa455fb2d5d5e08d023ed8e480128358b513130193a13518cfde068f9a3c6016ff26d5caaf81e744aede0415a73cf1b7a", + "0x871956b6e69187bac81041d7144694267be665d220e72ae2fdcfd4f3e3b479e3b667b0081ae164832b0a3cde02989593", + "0x8e8d9e726a4dce88a36d81e10de0138bb4912c5901eb9a8702b285d54fa800220407dc36c03789d037734564d40ccbfe", + "0xa3dc48f61c1bbc24a8639f586b9ef55d2e404e1348e838a60adcf66ad8d3f4b4dd4dddff74ca41163a64e00de122c433", + "0xb226ac329dc8ce324f0362ea58818b32786235135422cb3347663546262d1377274da3c93ad622912a1ee40f6263951a", + "0x84864f0ab846f99d261f78fcb38d129267ef81f7eaaeade02a931f0b9db6ed9523e267e442047d700d4ab8b54de29b0e", + "0xb24f72034d8dfad464e060b2902f3a4d46bc8122c528b815e5592228ab57507943344065ce409abb2c57673f815d085a", + "0xb708a01083963e15376a7645fee732d1b1d461c2e730501fb7110a9cf0dd8e482dcbe9c493e3aceea3622e9399df0b65", + "0x99465ea8ef2a591ba8ce1bdd6fb21333c4cb9933a664e0c62c7630d559c7961042dedf444d40af25b26cdd08652b94fa", + "0xa22f11cccbfd45307c506feaecc622c18e5c3e49e1624b589cadbcfcbdcd9c570774f455251ca4d2aed8e878880926a6", + "0x92f2823d8f66e300317f87ab1567930c921f2f9d69f8cf54cd1ab67d45a9d5a99379d642c5178a5f8a4a40b6ef4ec5e6", + "0xa8ff818f7a0b89c51e14f91016c4347a8d74b2b009e04ec11f0e0b092a786c48422a69444aca200a40c5af77b37c9488", + "0x94f8d4faab9d69a6362e6c44f189830667f7e5cbde5ac1caab119e70a1e4165b685c301f6e921e0151ec4f1c923f95d6", + "0xa841de52a499cd2c1d4b15bd8730c5487c854b13dde78e23c39f3cadd23640dd14ef975fd502fb213f6fb722f01d9a61", + "0x953921fcb7a82462409b4aa355db01de1ad4447e61f1aceab916a67421bcdd3f63c9f6be29781e3cd7a15ddedc7dd4c9", + "0x8ba10a803ec4a477f53b3f753455d91873f42e3a95f02844f269981938f5121e4643a0051bd18e0d1a4264c7d5606bfb", + "0xb2dc59f194bce9f9dc0d06b17800387fc14ee7aed82fe6e29689fad9362173a26121a9ad61be18809e9d1e3da4e1e44a", + "0xa62b076fa402b98912094af67562150c903dc17f3487255d824ddf20e25c6390e4f0af1d9c4074bfb595d7f4d069e408", + "0xb7633ae79d3aa10cecae5a7c72ed89f146a28d75a774c92e5ddd2acf938f964c30f471db99eb99880b3fee388c475d9a", + "0xa4848833f5e04c597ceb336f46a515d9f0d29a7c670e212f48e25a2e9829b8ccdf8c82498863bb5c130a127bfb5b46ed", + "0x88e6d420f6ec9085eacfffae81c11d92623ded81532e8d556e1319994b88e385e461e2d02cb0cf5d8d401ec5835e390d", + "0xb8e3315533aa61162174d4d931309a8d3ed25546311e496fb4cf0768769645ac04b455db53b0960e93ae96bbf17bf694", + "0xa06873f7c6297b11650abf3f158283181b52f566c15260f97ee97fabed537dd0b9aa5bf3eca7edccc7ad094799d936aa", + "0x943f47b3c660569e6272750f675ff23a86f4583bac90864a7a2b78cf2075448a169d029be12caa4e8e71e8052e111c42", + "0x93bb6da6376306b14cf0071dd91d2dde96afe56761eb0b6ff6a9ae71786bd5c9332087cfaedd5aa5822f293949bdcb9f", + "0x951ca9c56d52c3a5bc9d5506b4dfaf2a0f6800ef7380d68ea62b2089a266c7beff406cca665312a3b04c5270a84af522", + "0x95de2bfb3d9c380cf62101126e6609c8894a7e22ac8616bd3ac1109fbad127f19dba0110ae65215322dc4b303f0237ad", + "0xa595d7f14651ff648a3395d56f1f94361e8e2a8c44f7d46e8615017d0502bf87e73c679682c1e15cf26d1706b769afd2", + "0x8a874879dc75b23311d3b5a138ac9f7b2d3c74306ec5ebe9ee472d83214c911196b37d59d1de95d1049e4809f96aaf18", + "0xa03bd5d8620152abb785a7dabe10cfce7f9bc60ef643d731b44cfb85ed65fc3af8d873910197c6e1ebff7dbe8613097a", + "0xb12e834c9ae7e3fa8c3de8b469185a9cd0949d6e4ee5bf7787105344fb94c9c89afe1f795faa892c6edfb421845bdbe4", + "0x8955e5bcf1e80409ca475475bc087484713af961abc4b1177d8460557a3ec173f20663a676e96eaa7326db8a44ab75be", + "0xb04151a19f4697eabdff2fdbbf123869c6f3d3f0fc05d6cfe1964d2b7fd1d8e0c7667203a4d816af4ebeea07d7852dca", + "0xb2308bb86615bfbb1be23c0170eccbdff9695bbe08ece20603ef8f78444c752fef35b9553b10f9990fc4efff00c1fa5e", + "0xb562bd44ee90877d0e09a8efac1a349eefaa9bf3b8a2bd3757c207cb2bf62c7a27cb0ff739c0c5556927fe19651541f5", + "0x991c59075b2906da6dc74c11ba226554d08a08a923098e55484f48c28033bdcfc2e9acc23c647be28220415fb8ea2af8", + "0x916dbac51d94b45ad20498a9cdccb35eb7c36515fb3bfd43eda771cb8d98455f2fcde83f7695f02a137dec6349dda61c", + "0x859100f2157eaa923e765c5068d95d514ead29e7547c40964f5f9f7337425cdeda6030b8d41904684d943be6e2890da4", + "0xafe100abf528fef8457a10c663f99434b425da49036d6c5493b26d7610aef11de5cd2ff7428b7eb44feaa660a4392efd", + "0xb57c6693257a339daba0098f43a6a3183ec19322c768a42d0baf297491987f6f26e27a287517b50a0373285aa57459f8", + "0x8b0e307b2170178732d2a6b159cbb88a8d7feb9873ea6c9a375faa4bba9fb5348885b6746622fdf5cc73d809236f9c5f", + "0xa791f8d98d72177c96e1ce08c06b4af19b80a5716643d67b6d3ba8ad5a9e8b56a24f948c5206d468e63bf5a374b4dbf9", + "0xa5237375dd403ee08d357defe21a98a9568d63432b832e1e6319ef9e4128ca6380dd09f06eff1b90082b8ac50e24b557", + "0x81bfaa462051f1cdad3e80e383c6cf7f7c19f46e0783af052daef120b6a7ea922237c25f3a91d5558f7c53575e597887", + "0x992d37e54d3437c197dbd12e31bff2ae77fe34302a6a1eedfe07a7d4f73babf173c04b5a5d3ad9616f7f94f3994df307", + "0x985a7578ac66bc688bb15473853281c56a41eecc7220ca59dbf381b6f4be2442c4058a883d1978c0ac94e7b5ac475ed2", + "0x998b227ce385116da3a60b05d5292d7e240013dbe3b57baf0cad70cad393631ae18df1af74ef1f4709abacd0a41ffe55", + "0x81dd39e6e3c4458ba3c4cf5346092d38837c0ddc2b5f6eb486bae1fda9ebe581db842bdf6411748d5da980d989e67ecd", + "0x847bb2a0d92e21ec536570a57686d27ced4a94cae0fa66f200d976e11a49a97221919ee8cf17fbe9b7ef9d44d732bce2", + "0xaa9fb825e397f5d0c9d3667d2f2d9e1c3d8e88b327f87a8b18455026dcaa23caded2f5c5ebdc43e67d1b295470e0052c", + "0xa4c199146faed7c7dbb3089c4e87c275d1f79cefe23421c0826e535b45c3062870de65e89987007376862ddf6c39c26e", + "0x930add3661b05e51ebda0906d0f6b5aa4f118464476988caad7785ad5df9af6dea9fae0cb6cfd7808aff20ec32ff1f2d", + "0xb3e8db38da7fe8effe401ce28b5719272a225c0adf4f33bc1e92400a7c325192147bff19daa39cc8f18276f6ce825f66", + "0xb72f216a8edd4f5fb575a59d42e34d341d726d3cdf6a375900cac352908d6080e551c739d6fc40f094d2c30420f619e4", + "0xa52d1f8b2585af93bf6f3ca5dcb7d9bff19cca6ddd8c8f15d5d03f5d8b798f021e9debea77a4b7bd283bcfd58fc7f29b", + "0x89da898b770cbc14f892bd9ca8584adbba3f900c0d1eba61365d7a81c2a593154bace78ab6cce2c24c78e054a29ab8d4", + "0xb340b275582017700dfedec9b84aaa2140d6e2e02cfb25d4287795d01ef536a7b4b7af91e6175b76f06bf1b0ddc360d6", + "0xb3d8bcefe0c2d602b6b9cdfbae06f93d9c342dae09117ef9cdb6bd7ee8d5ebd16523edd73f8eb0f28579c53bea10fb47", + "0xb18e4ebc4a476cc6203910e3b5e7464478c72561c12db1e63e7b4aa014d2d273aa3b6cdbc7160162c19a14d01c97e578", + "0xb5a59ad8de5386bbfcc1f08b08547646c6b369f9354b867c11a569c13eaaef5b69340c912f632e1ecda4e3078f613df4", + "0x92d1557acdd780767ab2f7a80753e500649b6b21369adfd9622f4130c504554ab002acff60a2df145b83deff7817fe37", + "0x8e7ce129be8a5d5211ba77be885c5702d038202cea58c05809a878bafa6ff8c5c42ec71452d84fc137e05db4a6113ff4", + "0xaf21af60aca469fc0928585c61e3117b642a8ebdb3538dbdee31a539488ce02606410f8160279f61aa970eb3e935fb41", + "0x801ee22a22b6ebe7eeb462bc3d89eb8e02e6bdd09eb191d72e7cb2e996e2b67ec66a5312f8b3c1124a4a2e7b06c53872", + "0x83de3f7eca29e831e8464259a525e33b7ffc8edc8d7e9feafa08396e821422b456e5d2d668abd626994a9454b21f62ed", + "0xa5ab3814244f57ec59dfa62012c11a7756535fc2cbdd5ba49435990b8e07e7315f01b925231e657f4257b411848b45fd", + "0x969a4a27b949d2e3c17ca68b5bc9197dc1263d1ca27efafc97ff08c19169c0c96bd8a733e92c3e15fbe72e06e005f163", + "0xb96a243d50d1ffe5bc2f6f96b90d27e2ddf13d362fcd10b1d72daf8ca5ca292f0cd79fc000cc3e48169cd6a8642823a2", + "0xb9f11a210f08116215524cc50054e729668fa53bcdf6d39cdc1d99b0350f5c728ebfcea02af98e62bedbba3ac479b997", + "0x87b74cf328ba06e952b221da2336b81481e481a23c9f2193f54edcddf166cfaccade6e511effa0ca7b46020596daa3d8", + "0xa79da4eb6c6ddab59b9f15d4a92972e53adcf474a77dbe2a710eb026d0aa2e8a9130c7b161aa140c64c5034b42bae699", + "0x90bbc3e8ad544ad76328bb4deea6247f9b0aa0c206ef4d0a43994694a2aeae9f32ba8e83209e34c7b6e0309b95c6d836", + "0xb82d6075e79e9786f48602f0ecb1d421a3eaba7ec9ee63d33ca3225960c880968b09ee333747fe0835024b7181312a40", + "0x8d405d6b8ec42e8f0fd1653ef9616239cf4a1fb3c9eeabcda8ce2a976b5853b4b39526bcf6e1f5d5ebe6d235d9bc8754", + "0xac977ba79c8a5cec04301c490ef6c1dcafe0b7c19a2af873eb297fdc2676661a6ddfbbd05787172ff8c9baead08ab1a4", + "0x8d21013ede63382594db4e4b64d9e574bf46bf87ab90b81b742b558a3bbde87b8609131503f02cb61d48dd4900ea8da6", + "0xb9bdfc2185a0ebbdcc4e3be759d7baf55012632e6df875fba923556b917df01e7ccf5b3ae40547e5ce4395ae3405befc", + "0x8f42a0fc01908ffc6679148875c8d3735df044bb3197d9d51e83e041a67900398521126b4142fe60e1ce0130fea08392", + "0xa4e48e83dfc1f9dbd4caf85fbab86b3230954a69661df89397f2cccbcb01f81bf514f1acd32c112224381491873d124b", + "0x8d1c503b6f93d8172c67c98d815d915d15800624bb261030dc85799e9ad81628edc016ce2cc0638d1492b2436d5e0000", + "0xa229aed956fc07d6a0409667ed7365572e5ac3e1af051745143dffa66fad3df93d14dc4a53991d7df1a8070f1950f499", + "0xb135462ac367c16b3c6759d611274a127c55bdf62c06efd7530b37475b46a50f47cc3b78644e984d15e68bd8a61e9ac5", + "0x837486a4c665ec2b2f0505c5404a9ac2da00e9035e37d4165c3db5946efb705e65f2b9f07a12dcbcee20678b43a34ba0", + "0xa89dfa564971a25b61d924622835d08dfe9bec49a89bf3d77c49293c93c20288165f60c4dc39b579f3cebcb29cbcea24", + "0x95aafb928364fcc0530ae9bc9cc8127e2a5feb701548e76f3ed680857f365da5032f76d34840bb22d7078dacff9af19d", + "0x9583b569f5cf2e50ac51cf9b1f53d5080ef8a229f33cafe271fe605301c51f7b41231486be6f9155c1dc80fab4dbe323", + "0xb22bd18bd4fa502a868507b840d9ea04300a8224d7ffa950560b5a54dabd4a396a11b195d9739bf63e24661fa26a4455", + "0xb02f95a900842632fba0e3d0d8cc8a0db258d4531863806de9fe748a1077ce8bc2c0dd7359ab44c9ccd0fdb0e3cf6b92", + "0xac76aeafde53e38fb4181756e46764465b903b79a11c2654be93554588af826ebd0de38188ece9a69b5fced4f8b2e704", + "0xabd655032749f34ff417a2bd803c41401d8316b4528652167715f9513d80b2a573e21197a102771a83734d0c81912cae", + "0xae0ea77da4eee863135e405cc4ebf7562ccad6b916cd65f52854b4d430f2766ce76655552ce70cac76d74e9125df10b5", + "0xb5ea3882d65a75b159f9dcaa6a737516081f1c823e3ed20fcd98870fa8999c6ba3ea98916b9f231465443b91c2f02185", + "0xa1e614702c36c84b939fb153a789c9ef4be705c8c31510853d5ba1debdf0303edb4613a49864661bf5b27d77ec94af3e", + "0xaf3de9c77b3d86ef2d8518cb7a992aacad6c733ba367bb510cd28080b01d0ce7bee4206ebb90644e9ccc3a79abbab3f3", + "0x89f7208e76600e7405148f5a7a6d06672910b665fcaca92df19aa704557043fb02e47084823d67974b99cb0d20c6962e", + "0x80c79f1dd366ea2ea9667ac68cedb55aaae67bcee125cdf23551c984520944773df428494376298189d36a897ce670c3", + "0x8f306d55f0ea8442b99f70931c1d5700035b450d793099aef76734e2a6101c2da8d58bec5648f17a9b02baafee86fe2e", + "0xa732cb7657fe78c6d67b3930556e27f5d4ccd35d30dd262257b1a18c6280dc9f573f236baf18ed42b6eaf270b08dd965", + "0xb90c8787b7205fe78533f41f4933ab4623628f51d041877bf00ac649af12bc99942d46df4643198998e42dcdc5100841", + "0xaa2f4d3947310e454d6de799590bed7ed12fbcf521951e0ad287a26598d994064be50ad723e1ef0e7d5224fddc8afdcf", + "0x837378fa0b3e854142e5562e81086f21beec3c5aa4470db8acea44859a20514e8384debceb0a606c0acb025efc1d7b5b", + "0x9569c0e36e55888100a6ee5d14083a9e297458de84280d4b761312968b51dd4ddaa25819256364cc44356874b52ab5a6", + "0xb3009c81b065383ee10b37b4de4774ce8ea33621028e6391a13dd6f5c3c48c9ea2a46fad618bfc6d5a960eb579cf9030", + "0x81de48a73e859871dabb1ee418acb6644e1314412b139815d8a1cae0c88dc821c8e8adb420f359d664a143772a3e979a", + "0x8e48680a08418fdb37f1b77da2924c161d6e11a30f205f494cf0ff550c31bb9aa40f0211544ac4cc3cbe5b74a01a2161", + "0xa0b5753d0e06a418176eaa0d4b4e9c8a5f7182e68815587c2724b38dfd1b1e85c1c20fa809bf6eb4802abcfc00d4b4f8", + "0xaf064062bede0d6fea3d5570b16bfd8e48854a178520075d5d92fc5fec18a142eb1e2573f7faef706e592dc66f5258ba", + "0xa2a6d453ad98f0452d4e9c24357fe5177e8266c4c0739c4fa816d3c7c8c4f497f8f274a458d674b95ff1b34eee3002ec", + "0x8ed7bd928f712b23794b6b3db0cee3f7e7e384d0fc3bb967db59412dd9f6e1d091154fc3f9e5c3d4fbd81346bb1e5014", + "0xa790daad50367d4887a28e62bb733acd36dae54c226dc575e14e0d921b89be8a7d9a39cfce89fe98be5b895205128399", + "0x9155c262ed2c7da0530c024cf0a9ad07fa10656a3114c773b94c29d07fccfb193c3149daf97d3ad763c9e14f4d83610f", + "0x96c135b3f44fc8fdddc57847b05b964ce7de5ef353485e3798443c33642f8e3c9d40b176d2cea701b295842abc099e8f", + "0x850b1c907a4784d249949f06e377232681d0132e1437b836b4ac0296bd0802ff4a50542f5a854cc832b08e0e2f42da25", + "0x99a39be23efdb817a142458edf1568570676940fb9eacbef24552273a1a1a4cd6169b0efbf5558011a1900e809f0e682", + "0xac8246b48757fc6c8e8cb24e14b227aee35c3dd69bb1b6ff13a6aad59f5dd336c4982a42bc32cff3b3d04b259c21fd8b", + "0xb050e359cfd35eea4ff7023475eed42464f4ce7452f5ca6fdadedcd9014383cdde8157f5ad234ea5ccbc5be812ef75f2", + "0x98b3bd6e66ddfac03c4fa8f2d46ce5ccf1592e0b525c0f443b4a00184b028e6c67b6b5556ee869bd9b8deba364bc5495", + "0x8c42e3a801c1479ef44e57eb10ff697a13de1af84872ee64be666fae31e775db050f5643bf5a82342b2c0d3c6a7fdc65", + "0x8cb6b9b18f0d14be9270395312b70bca33d205d5a8eeecd6f94ed33f9e20eaecf4fb2684ac271254aff92105e4dbfd65", + "0xa882d124fc11c3f8a585a12729765f05df3125809fcb98311509dbcff08dd45f2f2689311700ea00920d633ccf67ec02", + "0x82cdfdbf2ca976ee2ae55ff19bb038c280276111cc348b7e0b1a43fe8b17ff66bb7b82f3d6a88cef2d9b9fc3e17bb2cc", + "0xb66447cb3398cb5814996c43dea115c91588e4faa307fb0b611815ad842ddc0be55dce4453afdcda719700304e784834", + "0x8e8c60d5cfba0563cd6690c901256db4167eb7e66518d07ef98da3307b6928a4019a3133899033e262a8d4c97051ade3", + "0x84f95b398e564881181be3792bb152bf0e653f12b14de1995f8253fbdbc9ebfe3c99169c00947280a2601c045dac2d24", + "0xb380c4d814d32728d74da14391fd314ecd4814f7a80d13eba1c16d66fb20adadc1238be8c26566e8502954203e97f3a1", + "0xa422a01e6fafeb7dce58ce8935c334f882321b8240a005534b274e2296ef5ddf290f2e83943850e5cdfd6999493782ed", + "0xb86a9cb7a904efad03c92df55a58b58951f94500c7da73d1129f598295cf6ee152079789d4010eba8295f5c5ad1ff97e", + "0xb639bc969c18d226505257c12dd5ea0f087da06efe3aca74359b10761d7db138f6bac78f5e0580ab5f38a859954376ba", + "0x89ffe66662e558712fa27434b2597bef63b443680af06ac4d8dfc650d3fc739b26168b0fe5c6df30a63666c236813a2f", + "0x85371e980beae2ae60f527477114b8016f3f2fb1ddd5f2d8c90bf9204733818ccbb2e2eb83f359e7fbd71d287c8900ce", + "0x8549a31e25363fd27958e2118e5afa64dd5cdf6c4153daa2fecf1d09cbb7da0914c6214eb0620ddcd14acef0bb17d9b8", + "0xaea233bce409436a1fdcee185e6757f8d824cd97df4f973d64b11bcf13683a6a44f1ef3510e204dbe835eb8abf678651", + "0xaa2f6d41df0a82ea079ebd350d033dc5ab1908e63daaab62c6ca3fae869564c29d3233491ae0bca6772827865a5fbed5", + "0xa7b38c0b4f7affbd0967571b312f05861ac5b2c08a5d5497888b42fa6a3151f702409f25c7192548fb1f98859054f1bb", + "0x960c23fe342d05a5d4109e54af8ff7f37c3f3b660e5ef6c0fa7a032d47c8dfd5267259289796f592b49a99a47162e027", + "0xa45a342c100916d6fc15151abffae8384cd0a1a6c50556fc90877b64d9770713354f2afb267ab05d8b5958aedc11c5a4", + "0x8215d31a25b83b956fa8e069d02b69033e043c80c5b32629d52144b2b1a41a09f08831e29ec564dce503fecd044834d7", + "0x86070d63adfa766c07753f6350b7df2c3ef141757fa32abfa0577d4a53065c938b445c6be30175b849ab166fb7c25e20", + "0x89a45215977c57d9a5fee6c06db41de1e18bf74b64ca1168cf9f5d6ecca087cb643836e5952fbabadb04e23e1cfb6ec4", + "0xb2580756d15a25ac9e5a965e0a75adc138f4fdb73e181d84f2d3cfecfb526ac8e5055e68d2e89da5096f3455640ae0f2", + "0x871df532402c9e596d7b74d166ba08312f2cbbdcc2da4bb763db434954dff861b1a39f7617f2092626059c7c677d2548", + "0xadecba68846d85a8cb3d1c76e3e1d290184c40b9fd346fee4cb7eae12a5f4b603d403908df7fbe1e6831c12006dcfaab", + "0x8d1a8b09988646b4626b3ed1dea576d262b8a390abc83b3b045418d2f86ced70d833a5f3c2e39f1ca824d4fc61ecc7ab", + "0x8b5af9f8480af89f8ff5913340784b39c76eba4a9d50af1109f3e8a4ad783f3a1575a286a20bfc4d366d0e1f2dbe2dcf", + "0x8467ff920f20a6ddef560c740007d0a1a8a13d1ec90a3402f268e46663e4117ba39b623a9900ca0c026ea148f421a50b", + "0xa728c26fa1e67ab15c2166cc60d5d07020ff674adcd62882b33e69fa1a847a6e25faab029e6e8d15bfa79dffe1b88ab3", + "0xb2d4d3b65821d8aca66ffa4f0090f331a44409416c486a49ed4f3c0eac0e945c247756b0a938495f0cb3ec217a488930", + "0xa96f40fd576741f4087048f8a9ba3b6c3ce63edfb949bfc8d9e2615e13096d1e60aa7c446f3add0f3508d94834e94a4e", + "0x82af5c64ac5d0e48056b1ebeb50ee965e6fed74c1fd4794a0b57d98f56bd0f795021d9f29ae9e74cd1faca45d2069b37", + "0x8351e7aa8888eda7dc97f1c63aa96dc7858b4b11e9bd73069c3185f8edbf51a38326542612aa786e08edcc66a32e0737", + "0xb1e951d9b081be6a6ea91a2dfe19f299b6338c710ae4b0a89c5fdc0090a2602528de8470723297e8be5fe30d693c5f0c", + "0xacaed101b3265232d9a432feacfd0ce610ba576a63ea8f904150e26dce615a9b002a40e2bd25940b33f2d7909f283838", + "0xa0a43a94e08c14f320f158f1af5ace245b38405271bf46c16c6422399603dd1003f805b81ea37660bf2e5200b024fa8a", + "0x90c66222d8621c0356de68076f3aecf6580a0c037f6131124dbece174f8f98313c890c92bd485078a7341ea1076bff32", + "0x8c8c28123a3def8e124b9847523599372b2579326c9f9868dcdc6c3323b23c9a8a5da62888278cf0c589e80be73e3b36", + "0xb65bf5e92755f0730ffc7bb8abbb70a491f70e123d3ad0431e22059d3929d2fe4b538d44a9b44c4a6218eb0ce3d6d00f", + "0xb8874e7ee14191bb4e6032253657d7277b79f5c0bc6466fc069c29cc048007ab272f733b4c3be330b8ca144e852fcb97", + "0x86da3c6d184b8d4e1588b69f5fbe861c2eb7cc4e6d164a5eb57a03f119617b9f3de8c076382416542cf3d31d3e229edf", + "0x8bb01752749674196a62ecbb4b656a2db097cc7290d22416e4303702b233ac02ca2a4c4576d457ee7ea4a5c4fd1291a5", + "0xb98dffe754a5352d95d732cb3d9c66b71f740cb48b0a2fe12a4a4bc07c5cea2b1b1ccedc25e3c86df076e6c58c801b9f", + "0x88ee8d954884f30a841aa0603eaaa2c1fdfe1bdb2242c45992c84a1e604804dbbd98f0dcf59315afc05261fd7483d7eb", + "0xaf2cee1b905d581905e50efe916f68fb9f0a2d965dbe7f9320ba3a74ecb756f307443f60b9ffaac6c15d3d90be76d490", + "0xb2c587d67b1fb829167b8e99cdbfd02f9a3da9b7faa5ef0b9fe86c4db3d388d821755de6bd864d259f028742b5d5fe4d", + "0xa68ce844261a7db7e3fb65185f07a53ec55e777f263b66dab8494ada163ec23ea5458478495c66283f301dd6d7e3b705", + "0xaf72fd1a165a7709a60b56d38bd51a5a2cd7b906224fe60cad8357ad7667889936ed9cc518c639ab0efb6f94ac273f55", + "0xaa0d1938d21b1925d0aba17d5d9f832f2d5112df6b363c8fd716a4b7cd737db59924e8e29ad3d387510433cc486b1261", + "0x940dd0a49c9890a1df26af4507867cab390801ba29eafb26f89275bb4b0da777e56e53c3d7254c16012259b0d1dbf764", + "0x972ab9c1ba6f2ed3cafa5ea4fa00995ee8dc56543918fd8c6831dd6b16ca1a208500f28ef0384469a038a8b378389642", + "0x87278160f7f655a9b35490fec573eb3aa48a64c914a8385710fbfe0926d84099e0402a21cb35126951ff9623450dc713", + "0x95a6a8fe984e41e50e479296d43fe22c8f61204019a1218a400dae4ec3592c0f36ff18bb1aafd10c8ad771e88c7b8350", + "0xb665114c06acda5bfb1b254f5c1c9e96e79d535ddbba3d307b6e4404a5929b085ea58b6739416b99fae010f21116a15c", + "0xac92c203b4932777a864fec93a96424b90c2dc13caa864562d2fea76704b6b65cbb7162f26bee427176ea4822ac82293", + "0xa581e2dfb119b8b941b9a6ea986873af9c92b31796a7d48508b4d47664a95af888a06eab840befb6ef371d5076f9759c", + "0xb0545f66c3f3bf509065cd26daaad20ada6b7cb4d67612fdeec69a8e9532c04db254eeb8aaeefcd26d4e4cb31c96b2fc", + "0xb62c9573e68e48526ea4f7d4895ae45c8935128d4352bc20a58e2871a4803b9c33658accc85c7b716be9c69099bc9453", + "0x8f2bb7b802eef1f10b6035c0d0c6082c5bf326e3605eabfd5b7ab106604c74ca322195a3e383fce5a1707ab9b7b6fca3", + "0xaf3a7b9733d297417be91bff08da838a6ece74b0a72acd0aa2f64e0cd22a8db3c4a1f079bff3715d6ac70ff6f9811f5c", + "0xb2c6116a03085f44e979842049b9d2a864589ddc279aacd0333f8227c00009055d1ec196c8c52d08eed522d44b569a4e", + "0x955b08c2681ea5b93c42d840c3638968147e6a168e1401b91c38f29fb7ac412721eb88b8df33c6274662df0489e2efe8", + "0x97bf864f0c9fb4418eced0be84ccd7b10d95fae7877cc8783e9136a85d3706f48c846a412357e7829871b91bf8824330", + "0x8548e7effc73a515b7881c80b5704ebe61bfb292caa3dbcef5d52e3581ab3e7fd30ef34ca3d38244161a17f7637d7477", + "0xacff006b415dfb79bf69d36f3e8de56ceafa4e5554e483f736715a7c61ba7ce23f21dfaae857210b229bce55acb420bc", + "0x86b5bf14073c08e4b5dd526cc9baab91fd1d2a9c4a78c879d3f2b9021311c494c2271b020921c150106d1422c46150d8", + "0xa27d20db99477670da461d7f40ae7894563206da62e1ecb56099ccd16e31cacb2b71159e93002cb444ecce3cddc92a1d", + "0xa56395a72a7ff858a94c1a2453178be6f63f9993aa25d07052fef9c4bc63f1651045d72b5698f289f3511cbfedc1bc10", + "0x845e4546051be3ef10108cde99636ff24747ea38c6163192e32b03242b46e317dcb87b4895cf6fac5937527ac619ad00", + "0x8205b3622bbde729dd158e5f330163b2f3b422f7909adbab0a2f82de83340b7880757601c22966e075a7fd3c38fda660", + "0xb9cc9a86c7faa0f7a469d3a51c5605d3b0f853880bc409f6e3d54bb6ca2525b38f1322d875c68bb6104bc1c826047e35", + "0xb72cdc6f7da4c10859fa4d5d632e467171dd00ccae4c00e7efe673b1b30ea011de76d3fa15412c11e085351c71ee44cf", + "0x98f402eb19b1c0c9e062a11aa39b69b4b8f7b3c8ca489aa4c62ce605b297d8d182528ea82778f48e6aa09afbcccf0e73", + "0xa4ca2b5490063685a20ad0a17b0fdfc2aa9fd10c465670d0ef8961dc1af0c4416cee2c450a74138deec6360e21005950", + "0x8890bf95938718d2b19d894696af820f18cfe31b7bc870743178010047566b891dc024e93833d21ae0623e0c352183f6", + "0xa1cbd3b4a6f5a19b3bb60366a4c5542172b51ac0ac6e7ab26475fa9b539a2b0de921a902e850f7780ce52aaa304bdff4", + "0x8a8a35a4e55797cd2d87a2b84276d8f6c8ac73ba2713e98acdcf8a9d8f1379c974a450aab52b65cf531d183e79ff468e", + "0x884bf8982ae11c07848796f8e285f0bfa28c3816076c36b095f89f3277cf3c4ddaf86b8a4ade0c68c20effd11689bf0e", + "0x9216ec84c3924454ab64ebc67a9f0a063c744b6f2f7484646be6676c51d331a871fa44a31bdd63e06a254a2710ccce13", + "0xa5a31374cddcee09f18a5ae9c136d5e8ca5f406bfb16855a45d11df2d6b8a84e81ee59cb9b9effd2f0ef70b2b31d8ef6", + "0xb3ae2cc6f67bb3ea4a630c892ec8364fb744d572622ddc244d8ccc9cecfa64b671674417d3951478c51ff07372649b8f", + "0xa614287805e9154fb16cec0e075fb36d64c79ce6f364673559776eadee8cd66e3b61034e9b0a2aea373b18939d7cdb06", + "0xb602f01fe26e6a7c502643c9db4a93e485782a6d868995000847935f29b2f5765de298496fe69d590fd449cdee0e67c5", + "0xa79311ab8fe2866b24e7a536d52900f7fec576642a86282cc43ef5934d131bcb808a4fdd3fd8eca6ece073947953b6ee", + "0xb161164e19b4e43a5a6961e1e5dea421f51833c7fb9dc356a480be7bd790e190facdcab188b5473bf400b1ca068cb285", + "0xa2e7305f2f49b83e7442022b8f8473b3860be3a1e7b3c223a64738f2e3bd988e118bfc52700229d6a5dd3a27c5f71056", + "0x8e3e93dbcbd73b13abfe5905781dca2907bdfad55e8f83a0c6f57cbe360e56ff3b9c2ad9e8b02fed3e8d0be176aa6b80", + "0xa7c5a383e25f3f36fa1a31ae0d9bf2d6043cbd723fb225b8121c4ba511b307459530c202a6138c56b5b61b912dd8ec3f", + "0xb839868db86ecd5eab079a90c00ed7b43c8e5f32394341c610549bdb6748516c6ae80017f02a5fd21d4b5e0f1f446512", + "0xae43b551ea1328135e7ffeb4e7b1eae13a49c502ef070048d238df50a9e2c392be7f13dbb621f452c66dabd662ab765d", + "0x979974f7ac8cefa380cf5f14ef36a45ad5439897c764655411f838ae1f82160e82423f4758b2c45644212d7dfc728c9e", + "0x87e088a12ff157929cbe104b56c4347be1bb4582ecca3dbc3a3011643c22eaa6d8a7996dd23067b2eb01b4dbd787d1f4", + "0x8ab1e55650059f4c5755b40255e6ba438ecfef2fd6ed05d0676e5226e35eb2be87d9085d339fd1510bf0cf4537a6818b", + "0x8a9dbcf07e844b65ed1ae8f7f320194ebeb111d191278c1d220a402bedf0d940b25dcbbc4287552b0def1bdaeb7aa913", + "0xac278133446322c14c86d1fbb90fcc0ccb15681fe8f4cc0ca2961d363881801592febcd7102ce26b168ee95d3749ac59", + "0xb050c6253a02aab533c8ab86b57b197bcca3198028373173569725a7bce9a9863b48acc083b72fd53a8c297d43e7e6a2", + "0x942cf07e75db331103d1650ee58a561c92932d290389f3030b6a31671c4154122b6eb55de5f7bcb41132f2543c61ef9b", + "0x80430a9beb01e617d326b17f0c45395ecfda6660303390a2dfd2ccf83ace6caa2b125496074d4424af98272bf852c54b", + "0x83a98fc25183b10463ac291dd1a7c2817bd545d1ea1ee92980e6ff8168f6dc760b1ba4b40c9ba819b38dfdda1d3fcf89", + "0xb8c9419b67012022721ab919b1054f6dc3ee11e345f1960f3ed27219dc36e23ea6c4892115f9d2fa9bd09cfc331a54aa", + "0x9296d005b5f7abcf5e2a1c0462527ae7505a5b5237c236864092dbd5110e4781865ace19b04aec7aae6d9dd1019ac3f1", + "0x9052be6063faf0bf0228d4e80d9305a2c73d72b1b6bdf9dfd9ef11eecde555b373ae29bec14127c0b7a75a4d715091d6", + "0x868cfc15672c5b489b3bc3107409f883a719959de8741726161db9e7412c13c65b6445a69b85eb2d0dd1c595cd5a2996", + "0x842f1675d59ea93eeadff627bcae3fb9201c344d5d4842153e1f1ec52142da056d1b040c2bd18d78036908622d629a5f", + "0x8a3363b237500ebc4158c1068540a8357b45f68338340fb9d7a6cb5846a21b57cd9530475a1ff144f76aad1a487eee7e", + "0xb92ed4019d37eb7e100111a079f4b5492376d5b0ab95c2d335f4834506f4bc42514e7f99e6e9dc2965e0c5e86bc130e2", + "0x8ca2c16803ccbbb98ab62a7060ee2da24911f37879a37cf90353ba2e87fde5ac031f3f5e6271a2d0bd80753d843fbf18", + "0x82ccf3fee08f6f1fc8ad4e232012ac63a91fc843f678dd59a11c6f43d904b4f7edcbd8a70ba204f1653884f4eb5c2bbd", + "0xb1360630823fc82e63de90ae893927f71762322a3fed595e83c4711e3cbec2be7d262ab5ca5ebb7736c83260954c4c30", + "0xaf7ce23de3be4488d5f0636c568421fa8e478714dc0055f7f5680cd6b8275965bfb9d3b59df63a4ba674d98f46960913", + "0xb559facc4cf9c15ea01b862aba62dfd8532bb0cf5c7154ed5073520f713970f4355b648583307123e645e40f1dd6bab3", + "0xb0cd35409095676a3cea65b57351f2736148bc4c7b59c431b2d86621aca2bd9af3af5ff4840b287d008f9608d1651ddc", + "0xb45d0efc1d99f9c3105185ef36061d6314381bb65b6f7896a0898ca6e445c9ac82e943aa38bab3dbd5e312d74afed1e6", + "0x8d87267f92c25d84dce279dd201c4b76c08c6c551324efa8ee4b70744553a1082b2a696034c5b9dc028a8da09a4f2c95", + "0xa9edd0806e7da3bb6f0ff08dee85a65a7cb2c2837e4c694d5633bdb4b9ebc7d551c786cbd1a15da20ca1a37bfc145d86", + "0xb454eb64957bbdac955d35023c85fa65c32fc943abc14fb3c14ea45eab89721195b21a08fbf7c15d3150663f1c53553e", + "0x93b6380888b529564e815d86f6783433b360ec2ebb5f4fd7ba7826fdb29b59da7adaf13bccd8735404988f907ea74ac8", + "0x8a33c0c9393f813f9fa8a8a486e12cad079531b892327d6493e17a1695fbbeb9586589090ecc8954fee64ec7658edea6", + "0xb5ddf14afa71f45a2479025425bf9b0d9146b562be0ed308304a35778f97c94e0ac2d6bf8a0d764da740cd86e3d07385", + "0xb9d13ec3d1a91292361b83fabb19682270132f90c70c430cbad521ca48d94b458b90c180d2682be82e2f584c21e3f9f1", + "0xa656144079bc94272d1cc7bc8875c091f71c28baa0ab52a7daa7c8a0270c82acdde293ecd98889f37d8bc3bbf93c230e", + "0xb2a2548de73cb4687290ea5c1efb150d593e786de442093d0f33eee62d66edcc8c5374b7187d133b5d646015574f8abf", + "0x989745c24be9fa9850d03af58f887c178cd4cdb22e4c87c53217a416d1509a052b3bb8ec15bb9fb339437eb0170dd7aa", + "0x8f775e193383460887650abecf3abd1e1c477bf6e3332c445ae815eca9a5f4778e0eadf52a28e63e487b3657476421d1", + "0xade7409380b20704da01522c82facbe48217067cd4671933265428298425f3eb2ca6fe88222697d5ec8ad43db041eec1", + "0x8efaafeff10da530115b7b543578f16bb2d9bb4fcf25d3f6fc95107da53fdaed61f8ab9b4aa947db8aecd5196c0fe8b9", + "0xa07e2d049010e004f78adefed17ea13b60c5b274f3375c54c263c89e569917fe4701b09931771c21380c7af017b6099c", + "0xb22dcaa5901c9a7da9f82e2f7cc950daa4e25847ab08f161ade79801f5582ded281559e26eb9ed09f29d631bf208b4f8", + "0x98b878eab2c05b6ec69e5d71baa873308c7e5abc34c30d7ab86a4c9790fb32c1b293112638f9829672da350a66f1e510", + "0xa5c2e63839807834a6bdedff23dcaffab7eab98dc55a235a95522529402e888e641d3dbf35df4ed2081a95c570ccee8a", + "0x944f50efeed25ffa1e4670b32f7e9e70ee01b34214f5231932f9db64fe9b131b12d351fe52eef8b264151d12fe1e8387", + "0x86ec7c9eed255f17d2c1b50235892e38ce3d3b5030d7810d8998316a009c4a4cc9b7bc1692986a2736d54aa4326b2bcb", + "0x9635a26ce4798d333ff6b34dd68eb978c11c8de589a556ae37f5f5a0c48792ec962dacaea764859aba790423757cb193", + "0x8cce947cc1c4f1d897de539278d5d5c46a6b266b9adc186adb732c72dfc6a77d7a350d4ec957b651b10e52dee2489691", + "0x99fc3fea6e182d76cdaf341d639ffdc3e535318ec487b15b8626ce4c73590e26dd1ada202ce9df29060fd9a5ed6752ac", + "0x8bbe81479c3c307b021dcb283da72c6c64e951031dec9ff1eaa8b8e40b4a7d792d9ed3120a77191ffadae9b862142dc3", + "0x8b6d4f35f4a9913712eb7255545020a4c52e9116de8f936fe068be9ac2f2a639e719533f5cbda873357a3167d2357252", + "0x905c03343304ae5a609462501f9f08c362d19aff6383dc2f5a89f418e0b5a7bc42ee2e865eb2af7e5ca3717a7fdedd68", + "0xa1b42af8793e566c0e309ce2bb8d98fae10876d3292511af3ff2962895be3be15b23d4869fb9e1d6ac8ea8bc32e2186a", + "0x92ee1a6c033e9ab806a66246cfe80127ae3cafad8a6ea3b74ebc441b66ea7de8d6a6ec1e9e6f6e3371a6ca75b29a8cb4", + "0x9454862693d1808215d8fb6988a510b416f2f5529dab05f357d6392e42ba40abc59ee23091f92f13acc812a1fcea4ac9", + "0x80e39495a2e193399f63328cf81e26a203d809d83a05e25fd6e4d0030f8e178712428769192eccc95e794bc0d0690be0", + "0x8e41a4f8e317ba0e5e379c336a92b8c37e7785a9b6b55308b113c2008ce42cf67489a13678ad91cfa34fa5e88986b73c", + "0x818aad2c8af4be2973e03a75a410b3c0449e373013b4782df65fcca6d2e39f4a8afb166539edbbb9423b8d5b66e23dce", + "0xaf70165b282b9af8bf737d5091b9ba97bba57e307828aa946b043fa700882097862dd0c0de4110cb931e327fc1ee7e30", + "0x8258326f6e26f5f217502f38d4031137887c1ba357a836609df398af13f813e3d29579e68bcdd8397534fe0a4d2b3dc9", + "0x8a5f560287ab01eb0e413ebaa18f0c4a7e3fdb7d2c421a8fcb46610c83123013be3c445506f86527a1e34d1ffafd05ba", + "0xaa6270cd9873b267426292fae9c45d4b4e72f3b7cc6771f083963a89ffb6d8b693a4397cf185b7c8682c04710e5be534", + "0x95075233cb2016f4c8af399ea93a18bc225bcf8b549afa700f3b7375d343b3ca13bc16eab5bee151c7dfc73ac159b0db", + "0x8c4035418ba5a7cc447a92ce1b41e6c8a3d41e078af42efbb29afdb2e189c4e2ff761232658df555bfd056ca2f78f6cf", + "0x92c16b3e22439d2e1a8805638d4f532a75d7ae5b84e668981b4949b6d9714a5d531730d5f74f767215478a66ba90b8a8", + "0xa0cf25b9742a3f9c6a8e16183e9de74b56da443add0b2e84f5abea2d81b62b2f6e730d0ca6b1ffea99e598931d2f0fc6", + "0xa26d9f6c66c8d9994cec3bd3926d0a0197378a2816498775a0c13fba42dd6762a606e62191a22fb56fe7bffe48951481", + "0xa14c40da1558f3ae595ed90540d5e137276dc02a67bbb8cd530ea218a6a32fa352134baf6bdf20caae88598bd92bbd3c", + "0x9676232edd4db99cf5de48d97d8ac42513a621980d3abcc24e4ba5e13505e254861ec6ed4933fc9cf437811109bc2372", + "0x912de087a07e0942bff513dc55abc17dc7f722d4e2ba19e799334fca536052a5583344e953d5199c037d567d9a5f56f2", + "0x97fef2b8c43d59b46a6055118e7fcbd13f6aad94862d393889254059f479e942706ce88ef64a08c3e1a9c452edb93893", + "0xa46123c48dc545ade36053cf4f36b09f88be2e4912f631278d0f921cb0e4e9e3423695300facb45936e5589cd9907662", + "0x97aaa72c1a11b74862a5e2ef443eb5b0542a5550e08a604f6c9f8735ae7298b858b7f5f64754c047cfad8e49cb65d030", + "0x883727435172f350ff952a1113d99678d70de4a8c751126b562c5b4b27ef200b9eeb24332d3dc40e664a51e3b6810943", + "0x8f7436ea50110b7d700af9b59b4b21ed5833a58102af8a0be9895f5811b50b0cee872f5f1d4fd09a21c50cd3629a76ba", + "0x933e90aec9ef564331174e289d9e5a24cd0da355c3b82f2ef29a1438e9b85bd4cade6944814b49523cd2b967ff2e8394", + "0xacfeb76000ebdfd1a37acc61a2d4a654a178a009cc8eb5b01e5f8a4015752a98ab39af7923f8b2a3f30e0f1507397657", + "0xb95d025e41cfa68e98d2b0d7a1a87f552fd492e45b3a694a4391008f86b269cda395f28cd0873888b13512de33c7802e", + "0x96d9b3a78d84a63be1a5e08317f1625b34613b0b031d8f8771e807b86dff005382287b568aa018e0f3aad6379bc1eac9", + "0xa81e36492a7febd2e8f80466cf8ca1007a59ed2d260c27f819ff4545ed0b142b33e94dbec7a3e4b11e631a4333ab877c", + "0xa45877bafa42392a0ce21e4ddde0b98d886e2959a4634fd293e68934fa00885e88e342915b30d54172739b4de8a793a6", + "0x945d9857944a1b14abe203eb371f028cf550217d9aa2317ce42295dde7c0efde453f07caeee75dfe9de8960fe6dfb25b", + "0x99580ccf7cd2d5586dbcc578ccb2f071ea38ca1791c21392509612d04d08e0dce1ee2f60810801e0ada041df9e6db675", + "0x87871259b2519f964e831270a4ccfbd5f3d6529e29c4cb6379af6131a2d08dccf130d4d375c13bae3c79e8b32666ec94", + "0xa5f7acbf9ee242f8190f6b463dd84010f22964d78d9e894cc71c1b65af69ea9f27d9915011af728701559af7f75a6453", + "0xa95090b1af54c7a27c48f3090b6d029cea729fd1cc188073c33e71f15a09b618450f4e0432a619401a0d284b02ab7304", + "0xb2f6db369add958203644917369eae3d01232e31799b01582693a539c7f33c1d556d2409b4213feb6d2b5dfb20631f36", + "0xa2b888e79c40c6476b4e739a1003ed224bc1ce62ab2a1b689868c080ba94deb7723a5ad4ee49b71af8bac9ceb278e33f", + "0x97d952c0ea5a0992dbf06741c9490e6450c86d48d8ec58aba5914eec1af4dc56e751d3f42ffcd2f221eda1aa8666c962", + "0x8f9f159227e5666aa834340b29351bfecdced8efc2639a9b4df0090e7eb2e4d27f8ba9dcb9d02e2308e57e0ff86b4f28", + "0x9333965853f81280b101151cd94098064036e61b435a8b6cee10b54ab27e07ffac8134d8978937ae1149241642f8dfd5", + "0xa0863fff45a561f8743a4dab596252054cc11b8b8daa038c00252b131372402f48c5c2629966ec9156da059110fd9af5", + "0x80ce4ac639035de799e14aaf14f9ff6f78cd009bb9ce11d00f0555029d83493ff4d36c7b1fe8de9a483edfce3a1cce1b", + "0x993634213448693835bd2e30bc26915ef0b2d5e8df5d5e43fbfec5f2c021abba04b6a2f7ceb952c157aeef4b2bdb19a4", + "0xb4301a6759e5358f809d4b5726880a25046319f1d02842ed479c2cb39c626ef2116a4b652e9b82c2cac5523d21c97f85", + "0xa2b9e815cd6ba6f8ae35a10872e1c87227d577433db3008179aeaea36a0623d15ec4c95b782acd8cc4908d189401f1eb", + "0xb2f71ef0b0aec168148d0d78536db139ee7c5120a9a56b084dd5132cb1ae8d8596ca315e1b63cccaa39878c6025e2c49", + "0xb3c136f4d5cc029b27dcf0cf72522fc3f58d9499f776979ede6dea18588491d6582bb54041c6d0fa1aa22b209070e47d", + "0xa949c289086d6653e1603bc1d082b5af5211e9d4e183f6385db049428096443b40c0c3ee0bd2bb0de919b878d7b5b145", + "0x8f4f1a3e814b472014a90e2ed1092968cc774b1f2f20c1b44b4e937a6b95198382c7cf29e8b604435550b42a22da44cf", + "0x8c575acd122a0845c682b9f62acf7d0983e6e85fe69349409d284fc007c3d3df4ef159e944f1bd683e175b7def12f37b", + "0x8935b9705f6fe230ac10b0552282246de331ffa30a6b15c8d1f687edb637cffa71ee3bc57e132cce8cae0352400e891b", + "0xaea230f04ca1a9b609736e6527814aa5abdc3657a2a061ec42ced317f61e67f4e955b91551830c073acdb4b57cdcf4ce", + "0xaddcde978d67b7d8375f94640261749aa54f6b9ce753329c03e250a6c088bbf3fca09b7a4d73afba6410f7811e6dce69", + "0xaa90d79b62eca8a56ef541945609604131c9d246e0e0c649e829e203994364e38d612c528cdd68e8e02c8411f5ae8d13", + "0x91d1ee0a99f6ac1084e6fc787fbab59b66c5de97c98d49aea16efc606c37ce3983e73887ed6cf1a320d4cca69d9edf1a", + "0x8bcb996cd739f145aa6abf23c5868910346ece3709ed85f8d1e71bdf96b9ff462afb932698ada269c16bd26e06a5b5b8", + "0x80a04a2c798cc72752ed867a3c2171632130dcd2e6629c46a078d0b60307ebacf7ec875e615106ae8fe6b999219bd759", + "0x91a42d02f44c69fb0f4417302df953bb06c8812961cfe7f75d014d9d264a65ae48b6709be0f85b75a469105640a8b350", + "0xb4774e5343510b54e0b51890c7fcce6f07e25e92659d10db207425ed81de6543d5ef7ead90d8a6400c6441377a7469c5", + "0xb451b587137f1b91b213ff8226ea941145901229d71e5889349e406c7c00a119eb580b7091d98758e8744050f08c942c", + "0xad209771ddf41f75a00f1915cc9d011ac48e6f10708a8ae76a4b9cfbc1cc09f333fccbd863dd5a66187d7ee15c7cdf88", + "0xaf532b44c4e08315561eb6fb4cea8ea902ffebb26fa35b0f0271ccac48afc0914dcee1d10c74be47929779db0e8f15a4", + "0xaa1d42c06ebe8ecc6d068d380e16bcd9bc483cf0e923d3cf0f80426873772aa5927039e3be90ccfb2803004690a9c58e", + "0xa849ed79b38542c794e95735c047885d49668e13d3919f12f23ebad31a3aa5cc5d75183888d03cdf5ef9ac9fb21a4cf1", + "0xa8ef4f736ff5aaa2a6ada7fb76312e6b6a765d4e34375e19af721e5392c582dbf223a3ddeebc1599fd86d83a1940b2db", + "0x95c1d4755cb3c9efb964071a44a5a0aee3d5b1bdb0e7a8432e314425026c9625d2d11bd2db7dcd4e43a5c1c833f0e402", + "0x99fea77b6d0bc62e5e4cd699836378d725bbe25e120f9d3fa42f43be424be614bac6352c4900c2ef86e69643be48cba3", + "0xa7c415efcbeb6ae990c093caf838129a349aa63a85c1d2a995e189b2d593d73e14e5c4be97a63d57e3d9a929984a6bf5", + "0xb926f815572196c4c9d0e782f2c51b62ad28320128b8c3c6e28c591b668d9fc80047a32eb79a207ef587e606a29c44ad", + "0xb6ae84adcf5b48b4ba8dc220b34edb81b5db0ddcf23554e14baeb2be7c5e7ed81496e0736f57563511180337070fcebb", + "0x95fb1553cbeb095d10ee5a46694d79a9ea71a62a131edff3910a5cbc112bc4f5f70c18dd134e0ab81b0492b4e0ec1e43", + "0x8ccdd6c2a7ad57c824cc933aef5097fa45ee14bbda63f9a3946b0e8678d9cfac405c4cd29e815ff051530fd696aed940", + "0x944ef26c0864cb55afc6581dc572a37d8d20fd84391136ddf2a33f4b68802c7cf2c48a13d5fbe6d05992b53d96867764", + "0xaf55573f0f29cf4877e2d284e65861c8c39e0bb2de826ee29bca3029cf8afe772b674edbc8841ff56229c3bca6c8e399", + "0x9461c25025e2a51b52ff46d085ef6c2b6b3a3e9e3a3cea08f0ec16e368d4eea7d248bdc04bdc5bfa856b14883bc94f39", + "0x98f96caafd39c65ce49c9862fd1e97a2805057ca673df9527d48b2cb43e439d50b29b172d7afd3d1beaf2f74e394a2c3", + "0xa0dbcc42c171dbeca328f6dc47f889079c69eda8ca462e0c75f39d7129776cbec65e49a9e2da1c644fd077fdea8f17f6", + "0xb44afea628310518eaefaf747aeef70c78c4ff4bdeb531de4c09c48664ebf2c13ebe59a683f685cba0b5600f7ba92593", + "0xb27b36137ca58190291cf5dfc155c5027143c1ab826bc7238d14bf2d629cb4a033b1efc7f07483e137f637ab4b0770b8", + "0x81cfa8cf783f5a82ea673fed7ad155afeaab1c66de9aa1a81e07320d17721d0ce080a00bb6c179170a71424dae9803a5", + "0x84ad00ea7cd927bd63d58282054765572987c62a3396d48f687761ee3f5fe443f13ebca31c96655a9e4e292ac3e2245e", + "0x8278ea90aef1dc2a0ec1b7d985dedd2af90b244630128c3038215ca0e3b8bc02ce15c4477c190636b2a4f13826135680", + "0x94b2c2d9b6b8c185f1dc05e098eb55222137175ff42ca4405a014602861f49940f6b863aacebbb7643ac6ebe871de53c", + "0x98de37a6fa313752a71d33e27949fa348a7e8d5aa48ca7ccb634ccc9b78035c15dfab3006ddb937bfd41c9233eb21539", + "0x887613d700e6d5d69feec76e49026fac17bec06f5a31320a32c67252695968e361b062a7d1887489eac0752e800ebf0a", + "0xb83d97e5d4f812fbfa3e2ecf026a1e4bd4caf8a0fd980a8ede60f4cad85515e6a839b7eeaa1dd21d64734fff708fe7dd", + "0xad026bdc5198e0b583039e6b2fa59c96c38cd0ad6184ece927acf04bf4befd817c8507204493a0a8db17ba41b418d594", + "0xad88fcb1b59573e9deae6fc596762b80886a6b56e6e47c08baf0dbaace905ad194fc77b9c99f4bc7ea6f88c7c4804c03", + "0xaa3dfd313989c13046df1848165c695b0630e78a0ce581c12ac8d902f299ec24a76ce5918b618456fb9b741f2ba11a9b", + "0x8b28ce65eaf29aea0bf656fce7807e58917aaef2cc352b41b2bab2323ef208c97620dbeb99793d4e76782a8dba7a3bc8", + "0x97b1a055c498bdfcbc1762e182b105308ef7e283ee9f4e25713807558baf18e6303896cc96bfee1e1bb52757151e7521", + "0x8ee386b5538dc5bf19bb4f1ca9ed644e59fd1214c249c02f3efbbda09fb3488448e9e1981b170a46250ff560dc0bf66f", + "0x90b6a18cbe61e2c4c8bc374373d08168990ab8c67f159805e82068e3649bb51ce0b9fd25103ba48b0669d0eaed40ed03", + "0x93e214526f1264966d823fb2766973be28cbbf4e445121cc5ba73dae8c836c07529c1911133115e7e48f1fdb18bd14db", + "0xb34a9f217e58cefc2868d40537dee385ffab78494533629d6e9feb0f52df1515df8d37e7ec3f26fd5750479c8c953d34", + "0xaacea58387ae2726ae738dd0ff93e06b8c0e93dbb8f1b0a87992e7be1371c54b74c475e13ffde68c82b85f70bf5da55f", + "0x8720e64e13711af79a51be271cbb7e854fc83c869617049e8f578e14eb3222b39148fd82dfa76f9e3d6ced8a818f3e87", + "0xaa664556401c7a66f2ed94fa99f1a025519e7cca04823e7a23fcd297bbe3a60756867688d053d38a29f272b62bf977df", + "0xb91a83196ac2b45b020a744bc25c29d049100fc38efae82e6c5a7c67f33abc984482c1e5efed257199ef8c00ede1b16e", + "0xb2e34c3d082fd17303cff0bb0b3916f818934d42118ea134f628982b06f030779f3fe5e1c80630ba77ef6249d4d3c683", + "0x8d655eebba9b9b17c3ab005510f3641dcd5eff7846685345e4bb511be0fa189e88d5732583b4c78a1e4d01943a5e9ae3", + "0xa75c61f51e0306219fefecc09d690753289d0c51e2f1ba4f593ea4f786417b16f21ba4a763192428040ec4d32c176e78", + "0x8f6413dc08496b0ee7465fe296e3be99310e443fa8ce576702490deb8515625d5431b2e6afbae8648c42054dab9d9a40", + "0x8de71dff66b4581792891faae48154c7db22f532076e77c5c1b935ac6de61cf5244633b62eeefa32cb44eeedfb702cb9", + "0x906b2dc0df32e8108ce563f42bca33468f610098037567113f3e84531d2e985dec69a351ffe089873c44427ff952f425", + "0xb197449a901b405d2822ae2c9634d8c42d1087765a9a4779f6a1b978bddaa1c7cfbd3095f43d0797241833a8ac7d1684", + "0x89a93bc4a71c76454eab797e382b75b8d75661838f0c431f01cd12fc6ad7920ba7dc8ff7e22a858a45e229ae519ff844", + "0xa7e4e3fcbaf3d131400b843a178f51069f505cd6bbc77625691d8d7149aadd548fa7dbc5b1be19b72098c1d3d93542cd", + "0x99f4ab4f38c76726c93943db140548815a7c097e89c3f3a28fcc4ab409f7f3997912ee3890de57aced86c2f7832e55c1", + "0xb0aa5aa4db1652d908f37d38cd76074fe622630ab3ff297df2574020a65d9d2d4dca8bbd857e06ee748014af2d087f51", + "0xaa2e0f02d41b695f3db39dc1ad1ac8fa951b65a1e9da53451cf379e9cbe8b4adbd18334c0fd76f1a8ac7e5fbc71c3ed0", + "0xa7b0fc75e09869f5cfa4fc927b9f44e9bdf3b799d34973773c525c6daa275babd839f73b4c3656043f2d723d1a597f39", + "0x874cfb908c14274057561e450851b2b0340c5ab8564f7ddd8c96305d77aea17ebdc79f48fc77f435a970292b8244302f", + "0xb366ad37ba8d0810320d5e916f4352b24899fb21ca6bc42e9b9e2b458f6985612b88203dd1467660e09569d7f02fc3e0", + "0xb59607f2df684d25e39cded20308de89aea09e158e3903a6bf369feba9f44bec13c7b20becd3a403c24e83404b83f26f", + "0xb7c8328b8357e5dfe2065b7746fd14e980e04b0db5c99f4d7f2bef59f80d3662b7675c7ec60b9af5466ef27daaf96080", + "0x8acf4a6df1fb95dba2827f796ab8a940535dbad7b702278bdc48dfaf7798106a455f2ee5394c18ae9771ec0523864d2a", + "0xb2a9a4bdeacd8425d49043b5bf9e1f015134a2d971d09810598a6024b8bd3b0d264809dcfc89ebe55886b45496af38cf", + "0xa7cf460f8dbcfe40ab53b782f07ab7da1269b8d0a1ca5d75cf054b463ebb663c27dea36691fe65d858f5c015f4de03a7", + "0xa2a9a399571048bb1450b5c91e05800f68546546f67d1e5f7520b8e6ab0125e53d4cf69423143ca8149ac610796d00c7", + "0xb4c9edfc93f53390bc1c8e88d9b295caf02c79db15661a006239d3f7cc30c3df436843f3c577a24f792b62f717d252cf", + "0xaeeaea5351e8a2b466539eea6e48b2e23dfda4a486ef6bd6ec37fccdd6adb790c104ae2264099014af6ef06e1bd94d95", + "0xb56b8ff89986e7c896a6245c02006d823fb1bb23ce60191fa02f002ffdbd593e014de5e4674a7d2e73738e8893a81687", + "0xb1858b24c238ad7e108269e98c12cac6c261cab9aa04c65a6f4bdd37f4537935fdefce809897d22c361f8b9a1c9ab2a1", + "0x8e5f2a9755171cb6f80160a83cda5ea9a037722956267bd42087605bdc7c3658a4ee3f9d72499b06eb66370ee9624e80", + "0x938ca139f5c9c2901bcc00a8db7c4f688143cf6fa33e94cce91f507c08f1c6cc306be2c41f5833ea92d239bbb54c64ff", + "0xa4a4fb45ee968981f5d9d8b1a3784abe1f558c7287bda44530be972a2c2f19e6a398ea82f42d25b2d905ac0ad722d471", + "0x8273c520a6061c2e7530d6ad022a1358a94f6fe51a2235d96c8623560c2336e1db787204f0c315f18613b4234144d3c5", + "0xa79fd845b1c9c6b386eb69fcaa849c22347e558ff42348e8099d5a98c2d31bb3fe0f62f4045034a6b45cb8b3c46c9428", + "0x8a54ea20af46b70791381ed5e0ec4d0173abcb3accfa563416337b24a96c0c6be8ae8fd01c1bc400a18a3763783b9559", + "0x827fce0ca9d36a2abac8f2cd89d3382d5edffe36fa8b51fe27d12d86f15686e8287027b404624e967d0e83d180468c83", + "0xa14c3490309604e0fac64a6181ca98508152cf92c768707adc948c11e429edb3a04c36df0c8a96c37c77f3f7653e24a5", + "0x89165a80b2abadd81c13cad2bfdec59d2bf6723447437db51b7cfa910e4693a71024e6bb2e678b601c1282271a0ae9ba", + "0x87fd15ca9747216aeee766e9f7ab08859f70124c381cb99d6ebb759c975ed9e8cf86935da5f0757fe34d59e954501c99", + "0x8b88fd4e5ce4960ba44235c331cde330816be0d5338e5acdcbfdb5cc25930f7b23afd8ad5fb03e7fd1c21c0798d2b593", + "0xb153d88bd3ede7b1d60d7704e167fc371e31f6a74d6ca35922fe06a7d54a91cbbc8880acbb318589886c17f07f547a1c", + "0xb6f047f0b47ce84fb15abc09c9c05287ea96e61fa1f99cecba884f3e5316b2a982a2dc72323125c657eb301e741d2934", + "0xb3977fc83982dd51a7093387abefae4f9c22394aceab334ccbdf86ade413bced6e654886f6c11c8140513eb3de12dde1", + "0xa7b0e4682744104252035e7beb8427c67229a0d3b0b3d3d157d88acbd132a0c1c33c01b03667d4bc832d217b7fc02c24", + "0xae7cb6a63d6b6d9671a4b4374d141e85b63de82a0986383f2101056a56574a92e66e5c24aadceb12c303968e5236019d", + "0xaea53f9db5e9db5588e81ab72efe709f6ae09b3c04bdd2d7a50a341541ddb4f6ff635dcc19014d7002f08ba8dab5e749", + "0xa7fa2f7dfc72a9da0ddfb9abacbb84bd527396358226b1c1fd342ff0f6730adbab27cd54b350c0bb5e2b0fed2ebedd46", + "0xac59b985100b90bb01701685132a87f3b9704f22b890f5ba3164ba3166f4865036fcbd94d56f46e0d47a605ef2381130", + "0xb164ccde595b0a7ed6cb93b79838c297bb6c7b0e255c08a465c2dc8e6766b9d4b07bb00ca96cba6c5f6611069e8ad0c0", + "0x9016e236fecd6e5ce9958c1dde4be762cc5279a44d392a53a4eb4811c6de44e3f38c1732e934e20d514a9158ae876a90", + "0xb5474d807daa8e0361f05693f399e790b1ffa6f3d838b08746a1b8b9eb0e80bf0e9e66fee7ec9a6e68777d056d8db421", + "0xa73c04f385f08ed862e07e90abc60247184094d5f7caff768c0667b1eaab29617fdd3500aa2850c0879dcc082bf9fdb8", + "0x8c18e3dbec4a031fe4fac58accfe7bc29c813307256b6b3bc56a1f35e6958bb891f2c450672618ef953dd357d23056a8", + "0x960f14b1b8b3b217ec042d4eb0b0914ad6f1de6ccd31362a665577fd85eb33ffc660f70eb528e8ac2c8a514f268ce11d", + "0xb090fb20dcf93f1a73cf5b05730852de2fb7194a2dc9f40d2a79af482beae4d70ac018f6c7e75adb6d6a22f8c633df23", + "0xb6ede3dc1f6c7bccf9a5c0f23737894c552cd22c37b81cca607267d33fbcdbb8ed6cfa806385b8b3af01b13909924797", + "0xa5b6eb2059b1c5f410a526b4802ab1cb61daa63566a761305553b06ac1fb0bb44aad48dd60807caa3e1ede0c287f12e0", + "0xaecc2ad94bd52bd9aa21ecd180effb9d424dd331675d1a90bc68301f6ed6f7611b9947f4f1ec36c40009ccafd2761435", + "0x8ec616f145f6841f97a922c7e67e7087902b97410ec26b2544478e1447e3ee19eb6baad004048ff39aa584a52ab16881", + "0x9394afc677de9abdc4ac3ac6649482d92071a24b188df918eb82d430b92bada1c22ebec623a00bdd5b2b9ca501f0b240", + "0x91070e3c23b02097b0973684b8087dde8d8b354c6f4a7320eefe42723d0704cac1716c7513ef5bd944b003c60cd24b7e", + "0xa98596d2116985b2d7b6b56665380582ff0755317a6970135ae4ae81e546041cfbb4e48ee7502e280197e17d6e7b6a1a", + "0xb4dd41b32905dcd3815b96ab333b98e2cd6d7be99e81605e0de1160ddf95ea3f7f0a1c9f30d1d8a6d41210b1f306cef9", + "0xb57997761053835954089f29bf8b6ac886f8c9c0bef0c20c8f635550b9139d31ecba650436d5877957d5aa79b8e048ff", + "0x8bf45a426e7a2372d2dc950827774e8848d3ebf93385c606007930468dd692d9e53c8505803983716a866b03603bb99b", + "0xa9d27facc5ac04653b6d86ccc5f305f918a1e022f9181c741e20560d3b23bf6cce718c9c7eed6f37570b046d49223665", + "0xae4cb874956b711accb47d751fe65333db5cb65e9bac4d6cda8c79e16f8c3b9ac07e6d454316262ea773b6bd444460ed", + "0x88f9828c1b4c6321732ba06372fc26d9816ed9cb9a4c0ad402f0411e99618017753b55a67996fce45d12bf793b91a5c3", + "0x99cd43fbad16a7af48874fd425e8d854fd7809bbceb885a1a56858bdf02c8862a7152172d9f092d7a9ebd82e9e7cca13", + "0x95b57c1c04220fb3760389e78a0663fd10d2bb93621a1b2bda509808f9f112b827c9fe460350852afd867627a2cca731", + "0x855e05530554cf74f3e9cb8daef2b3ce77157c07a50a35ed703d0813d4ef4d33f0d067cdc9fa9f023206f03970ed83d8", + "0xa6949982380c468194f726a817a3a1fa82b070f5bfbacc7aca12d4a8c4365ea187d403874f11d8bc3bdf583cfa9e0354", + "0x9581f1c7ab77f241519c99f06b7cf6b53dc397db35c56b6574f0dfd1089d886ec8df649275af5d1fdd9c13fb33fcf600", + "0xaad4dd79ab7e56ae0c126719f0d58d0f781eb5b8baa39894fd3f7df081f1dcfefda26d9b8d2fe574b43ace113525456c", + "0x8df308b48dcac88e1480439f2d5671cc1a15a324686a66fdb98580d5cd52b50728a1361dde2838882bbdceda698aa2e4", + "0x94713f6f42e3f82ddccf90641b5daab7d87ede495bb06472313c6bf1ece32c935bae57f63d2214d9ebba89b3ce988cde", + "0xb2eaaf76d403cf830aa76ba57dc1d051abdf28265621b8b305446cd21a612c22b73ba45e569d69ec3a10e319ddb7633d", + "0xa3c8f81322e435d3ca4050c6e29085df8efc695d5948d5d59980ac352b4dc61d23d566f535e4f394a16982fcb9b7ad18", + "0x86be2e6c776f7de1d77fb99bdd5723aaf3247cc521447488e8a7fcbf96b829d5370ab66438c3bd97bb7ffdd78c6506cd", + "0x942905c61ad876de0eb04257c9cb6ba84ca22f635dc55ba53cf87088f13c903c37c92daba1e209da72af8aca97347746", + "0x8ad237e6ec5d249be5b5aacd8579eb787d0dee30b72622ecced57e8d86d55cf0dc1dbef8507571dae148204af9efc5ed", + "0xb403c68ca06aad2cdfca6f5f7797131c444861f24d4276d673792e63164fcc3a4471fc2b4dd70b8831bcf38e8c431f73", + "0xb74d6bc991c53ed1496c5461dd51721884943d2c6cd54ba0630db374b259583ecdc6a6da120a3a762a7d957d64cdbb11", + "0x99cc33c8659fbfecb61ae283d89906cb49b313e4e646a859bdc7f5dc97bc1ba23e356a845dbd22fb0f47af8826bbc82b", + "0x9767c06f58ce677a6d0e75268d5b8cc2f3e997b33402772bd8df53947de1d00a65aeb0cd7477101c9190cf2867ab1f95", + "0x829b7094883b9e6c4a924fa7c4fedd0201bb3a04789d256e4c3ea565b308668a24da51d275aacefcf193927515892b82", + "0xa57873bc6c8a0c5f7591277bf913cbd1f8c8a04cbdd8d4f6a3373796b359f896a0a76214ff0c9ae5ce9d6d78225d0842", + "0xa3eccf0663a93b8512f5b8d8e04ffa9d29dac889eb8f5d2cb4fec78f8ff928fe0b390844c692fda89574c7cd784e2568", + "0xb1258f8cea6150aa870bf4d89e39775e72fbb04fbd2b3df2bd536af3507e999f145e15e44da8aee6c0fc5bb8e3177fe9", + "0x801cd8eb2cf351e3088be57bf9ed25ab7543b1291a63dbd901c0d2dded991e3b5a0bd0f188736a1ab93cbea245606ee7", + "0xb138029caa64d6e56fe185819cb56172b4d0a5ffe564e399d2ecbed15b99c9391d24f0bf2ea73ec2789fc97b12307adb", + "0x8a87e2a1c8c072229fc8e53ab2eab8a4aa31ffc754d796b96f761d914dcac668ffe93131897af959acdd2509fbf6d04a", + "0x80a4a1a4194d0b4635b426963c099407e994fee66643435e01f5616294bd64c585854636b648cb84bd821fb69790e0e0", + "0xa809991af39c3873a5cecb30bfe2074821d283c076ce6e458471a27405a4b8395172fcd5dcc0f8c79f0585a479ae3ea5", + "0xa3f5906ee8affb1c3b699ea377c0123f60b1b1e3667f30c2968b36ba4332b534ee51b337990eed18ebf249a9eafc2ca5", + "0x83802001343441ef0441509a35ed6b5c4f213794213058d23331794b26088ae2231c48eba5d00a8b2dd7cd9155d118cf", + "0x892db58aaebe5a43013787bb5a68f67c78940a52ddaf2316662c43a701502dfaec6df430dcc69163c9b58675c49f52a4", + "0xb7fa7fb8442d4cf4a501a61935398bafef17a2beeb1f8fbe0124f77d9031be34f38bd881601fc61268b34d673ee8ab94", + "0x9014902723d8029e83676d3d85abf7f8e042cdab29eacb09338a34db9684ca9b79e4b2c25fc5ef31f6a7a52f3b84b31d", + "0xb97ea760675c9fe705dbb5929713d5932e14fd1df886c24004a31f07da1a50cc973356721d74edc45372ce4f0b434f03", + "0xb8b1cdbb94ee02858b4c681d2b0cdf76b33b4175e3ab10f8d44342f20763182c9340c83eb3ee360b713085cd21c6ee6d", + "0x92e7c7dffb1e0f9db6b1d2ebb41b84dcedc8b8d0e18a99147ffdfd412c6e72a06e203aab0b6aa35a95b98e747000f79f", + "0xb4c307ee77c9741b8a2e78ff69df997459c9a172ba7f0ed28e39a1f2141102f50c8ef426db94b161ffc3d082afe14d01", + "0xb53e1516969055211f0d86c8ef3d579d0bad13a21a99a2b55f25be6b5669a6ebfbd7bb44941bceef8c938bb3198af6df", + "0xa849365c971ce295446ba21fdae452252c00b7cd5e7848cd440dcd96ecef885eb239b9ab8cf2d6363f4e010fbb790fa9", + "0xa565b18b4e14b978f43d6bcaaf118fd049b726d9b10c7d9b7cf901258a0920ecba49681faab1de51911d812997008ec1", + "0x986b196dbca7a72a6dc32f844863d7806aca53a1878b2f3e5faaaa641df404f2a660476b136bf7755040e8c94187c3fd", + "0xb0319e7a3114f237bee6f1f2c19adcdd9d22b3c68e9399a64e80e0e38db4496f17c3aa9af7059fe1ce8d25eb7335508c", + "0x8232a7d569065d741d1bdee02dfcb26b6829156437f83fbc25cae70b6b48cf4bac6aaa1d69507af50719ac077f714e6e", + "0xafc011563080a33886a956d2d62344cfed3c021f4e71a56b09253779c82486a03a40ca107f23b72dbcef7bbb904715cc", + "0x83c330e7b2f3d246b709c8611a049748aab06eb5a05a2ca3300e91267123ed4c459057ed60f5157ea67dde335416a28c", + "0x99d43e5f1dc22a79a904a01cbce9ee7f3683a771f3e3d78ae39c7309f9d13666f65186d4b63bf6f85bdd5394dd5178d4", + "0x85f50bd7d82440ed6c6e725b2e9ef27cca263f2d0c2f915cc4a2027e5936dd858e7790e536925006d5cf5655ef11aa21", + "0xa603f8f2de10220899098fe4a765eea6432d6158a2514ba6181093a60679d338b5b8d0b8b25b3f376f73c07471f84cd4", + "0x9000ab622dc3a3e474458e506690fd6f71a5b1d99677f060b7c7ff06421a6b8c02ac6c413fb6dbb433bff5393170b997", + "0x86c5bce277c7d0145891560b307c4e2708277db9c1e996dc2fc2d1a1e5f7eaadd81343a3cefd1c5efde9caa1e7e37c2c", + "0xb979c6353950fdee9e1b4d2249bcbafb67acf5882cdce48544a1012c7dcff069479f48503e51ede4c3e0434192692f1d", + "0x928c0170df67fbaa470bccefcc1768add6a7844d611eadd7ac33a4a98516747dafc79909a1000f8d76209d0954d42bf0", + "0xad9b6a823cdb574717c4679e3dd08224065b36d76c1a1ff780735c2e0ad700d70b3f460dceaea4c87165d5f7b63f3ec1", + "0x8c10d63e345711cd077c47c595869441248777079f05b5f4015355fa25c310f8ceec7f69494ad8742f25f3246300d26d", + "0xb21418d0baafd92de16f4330af151254c82b6ce86a6cf6739fca01a6e40bff97ca313a89b9cab368fc01fe35610dc23c", + "0x91043cf53f330ddb43268de3461daeac1abc2d6edfa6196bd3764252d38ccd0717de5690274843b0f7d6af34b821a062", + "0x8a1791457d6ac12e7fbd20571777f8797a8c11f36e6029a63a23eb45fe95a33d0edf43ce001417d259ab862f78caac88", + "0x8766e86b41100ed9c6b09b0866f8e336a397c3484bb8b204ab832c48992f0c6fc22fb618eb0e07e0b1993b95126a6207", + "0xb6f77dbd7f0b86561a74f5df0b08fa6fac7341414c8c10bb40571724de8297b25b49e269d7f0e75b04e2b8494d43550d", + "0xa801b02ecf138156c1def0945a69f6d52c6bd4bd7309949d5dee2ed00cd871cef4d2d3f6bef64155768bc86ec13989e1", + "0x933cf7e929f0b06dc052ef5905e5f310e9c601401e3ec2ea57574689a89c7c44567fc80f392d9671c9c11a994df43e90", + "0xa8590a3ebe336be55a46e80983ff4af3bd2ec567ed9b342ab4bccd41b5284c2257272df331c83be173dee323be412229", + "0x8a5c9763511a7573d292d90e0d132433394e705a8a73516b103f77fe18ccd596b09c0658c6c0a21a3cda5d1bdc5d1695", + "0xa87dfe4b0b3f53a3ae9174977c5be303ebbcf83c216f4c1bec2b79bac797577d6148ca508abf3c91d408a2ae7053daa5", + "0xb097ed5e3326e50099442d45e9a53ed39ee6db03346f92e0b588fdd7e753afeff3862ebe122ea45f56c8dcfa2165f9a7", + "0x8c1874a6f145aa4fa30f38d225e151ac594b2890482bd7524bc316bde5ac01d0c3a621bb506dabf6019be2d83c64b931", + "0xb29eee4ba5e892edbc15fc042e99551152890d88135fe51c0b83d95b2c7629d492ea707aa533262c9562ee51d9eab873", + "0xb24485df24723abd729d1100a6c46be187106c3f89226f095555b830dc13e288f88f1384867ec41e18d44160adae40d9", + "0x81d34daeef90cf509c3a836f3786e3c0bcdaa82b87c6b3ab223d94e75aea9e869dae2a2a12ff8dc773a150854c9b6539", + "0x8a7d5bbfcb20b2634dd8aab2fadf7577e0bd8301bb546c6f380aef3108ed4c4e7fc115313b3840278f7e5c5722f5365f", + "0xa498e4da28bfccfad936e990560304d20b00b78692fff6dbdbe38880a6c210eeff1ea15cc0faf84bd01f2e16ee32f79b", + "0xb751b233fce3ddc509ca5782838b41b89f722952d087eac638227599ef496f6d5c99627f895757550d91a29b08515e36", + "0x86c145d14d3d64c8a69a1c26cea2c327aae0d494039df1b6fdf1003eee76e7a8955694123f3da07fe1bfd52fc849be64", + "0x9636188c3f64982c6f687a4fed693db8d4e1ea5aea866f6ba5e481090fdb673b6ce73cc9a2aa2a3d3739adfe2901da6d", + "0x95401da04453b51732688c44e40f349c85cf076d02fb25cacce926d1fc3f9b3d1158891afaec2891d3c664195e915901", + "0xab3c3e6f92fd3858e9ab4fca37bbba4eb1fa949e7d3bc01e88be9474b61fcb137a7bc08abe9bb6993c149f6f5314c0bb", + "0x82c7f0f769a0c8b548b426aed5ed160d0d4c0e32f04e9a19a4e4350f43e7eac9b818891d7cacb38d74819a0c75b00257", + "0x89426141e01db10d31c369b548d8dd0f3a46cdfabfff4f2ae56f00fdb6bd93e83f2694a0921e2e1f4263d8af4e08c493", + "0xa6a82eadb1d55b0efa4fa1d84ad4dcb87882d56f06cc7eab9ab475046e5702dbede20898a8cea429f10b0d368d4a64c5", + "0xaa74e1e7e0833c7edaf9fd5db4ddfc8f69dd1d42382b2efe2bcc58ed81c7b1fc6e839e40f14a5f241d636bec52516f6d", + "0xa99621d00b3fa71ed3d18848021afe67946e14cafbd92208adcb7ae5a585942394ae23e4d00a9500db629d904f558d37", + "0xaeecdb07522a14185e87023979cf42c2bdfef2573b2057ae8795e4a8b6cda7808150937a4bdb09490bbe3edb19fbfec8", + "0x919e5ffb2a5f26b7a778b8955b92c71210d33d8335ccc74035ca6f9dcd75ad213957a9eb149c67357994d6bb338bda89", + "0x863c1062db086ebb78adb6c5ac0a25b409d05b5d5b584647185971173e15bd085941ed6009748fa385376568b793d033", + "0xb7e8d384fb11c743eb84556fe2e5878055a488abcc0853fc16925d192cfdece66ac75dd0e872c6ea108d19e16ed1ced2", + "0xb1115184bbbde445203c8ddc1b1a11100275dfac78a79b63429aef50ce3bd261c263cfa320a1d3fbab276036acad8097", + "0x9900c424651f0624882fdec0c4e202a38eaee0c100c07848ec529b06a22c9445709ba7a2b182bbbdcf5e4332f95ed32b", + "0xb4f1945ab42b1a377d41aaaad4dd084860552682e6d42cd513cb957f3149bbbf8d94a4fbe1789d2904b8477bd56e907c", + "0x95a0b9061da2eb6bf403d1c18d9e53e5a547c3259352f90b0ebbc8f4f0d84b077f3ac19f170676199399064ec31e5bc3", + "0x8a73a183ffd564f7998078bb96a39f76cdb0d703eaa05749b25667f8882e864b91360c4b4729d216ff28a0dea39115e2", + "0x856edaf1864f5951cd8646bdd885547bdeb72409ed5144de969261b840cd7a3dfa799a73cc61fbf6e692761a0a035d44", + "0xa5b62fe8d7a93e77b22813939e233186a4a084d2fd0b6aa312580cc64ade1e40099bab6093019fee6f75669a8bb618cc", + "0xb5b2ecf2668b1d97280c5a3bb0b054ec1a425b0d17b40943ff84a1d3ea293b133ce5d3aed2b496fb92a10aeb080430ba", + "0x8d1cd75ce64966dd779038c38ba45b5c157fbf96e63781af047935c29f3e4f549afabb000b58cfc8f8df86e628741f89", + "0xb39f193f0b3f0cccbe214181732b13ded818f7707bd5fe04d0955b6fac6fa5554b670ac794a47247c10e9cbf8fa6d532", + "0xa452cf37a76bcb388876476fd4f943780b8f8b387591a10ae8668eaa0ff8f7a94541617a99c29dd5b0eac40fed86805e", + "0x960bab6d5a6535ecccbf96d2ff270d3927d419a52567b88c6cfb04b44241ff924a830a049cfd92b11069c3ecd0600402", + "0x95fd06ad424d480304b93a6d8478508633f947315e2840559e5393c570a31711f077b369037d30429cbd12fc22e58ebe", + "0x867851dc1a7d72757a683fb87213edc9a6a32827e3de72fc2fc0d39e48252fc63fac874495e05308316015fd7eb79c33", + "0xa73c1d5be65a4ac9e1490418c6556dcba38ed0c8adfbcd46283f7e40e83bee849de63c6f1f055c3851514be62035ae55", + "0xa093f0fe28fa64736f20500d12e5c1eb8abf501d42842e843cdb99799510e3d2dad6e4aae955dccb96df01857ce9de99", + "0x98721dd0c7b90ce4f952c9176e6e99cd188d3c0dfb2ccda5fd18116660f9b954e733e0e3b4b3f684a3966072e797520b", + "0xb80c7152070cb6adc82a2e9dbaba85c9445b53ed3d46ba94e81cae2fbf4fd9880b3abbf24bc6963b147442c2ab818754", + "0xa2c7dcde54c5dfa82c6042b1be7a3f8bd169f89c89e6a1e5403aaa328c585b2480c8038429c04755cc33c246042c0046", + "0x8eddb4b399b3854ef38cc263f3d39f3616655e725b550689da4a12aeb28a4a1ae0fd103fc76901b9407731bd608246ce", + "0xa3059c10ae030fd3a5b2d630421a9f09c84a735c0139f9e03172c7fb44d37b9f2efb912c349d672a743bf75930c00557", + "0x9584dd490572d79a77a5743671bf00a1ae99b7c69a23e60665155dbccebdb104337aabbd31815e9415cd5e19320e411a", + "0xa37270d50f00118c3328d1c1adcb263229f6990e6df848013e8bddd280045fd2758ebe8f3f74d7ffb60f8ccf1987363d", + "0xab5e355b7fa72d85bac82fef996540a21ddf04bc4421b91c69386abdc99040b61a5cb5b290567d7191f9fd59b5aa448f", + "0xb7025c84abe5bb54a5f7814203bb04a3fe7c72318deaf9cacca358b79e357dd1eb3edc94f24f4fa9b6ecd3e44bdbfe7a", + "0x981b18a229e8d849aafdae0137da388a16d57b3f3b9e0814b9e62f37f8288d9c5ef7b75cd783395c4ae4e4ccf2b720ab", + "0x859c7e0e54354af5eb129ebf70d4cc2dc68449443d1fb44b20ac813329772d8e97c4c548f2b83d553f60bbc38a16fb17", + "0x90644a5371d7df9184f15a3af5c8b4fdc6829e70a99c5e06d0d7de018c7b043ad72677a09f24a20777ead3c58e02a962", + "0xad44f7b2749ca438f95e2e2ac68996f98ba6f6fd3c09f11dfac99310934f7b0278db452f48c1d5fd6925c4c64dd801e9", + "0xa2074755e48b0514221960546b0f07298e589ad865e0237308db6c6060dc589c6768f1ae5371fbea3fbcebdc2472c9bf", + "0x9634384e928f095d315b5a7711edbb3aaccac359e429ce7d5c70ee1b3f32e0e099528b210d3d341df5266a98d97083f6", + "0x8b8294ec782efa091028a6634e934b6adc1730f319ac23e2fdbae62da16374d63c7ef2ef020e7ebca1843e6f4e6c64eb", + "0xa119c2323270c3f39a2f87febb82d688b6a6e63555ffeffbb4d45456b6bde647b70032f62f834a390430d0e8782a926b", + "0x8e347dd6f36c32e6632e5189eb0856c3f3f2d63ffbae70cacc09d31ec6ca70caaf1b07cea1c658beab0d261a3150ba35", + "0xab338dbbbeaf672287ac4bbb5823a5fd715a55848ef61f37c97f134f11245a58252136fa7c65d34847abb56cf92ae3f2", + "0x85a6a244761cbfc74cd090433f7a29fc1e4992cc8968a644abf05ae855cc6d620893e1b75ab68d69e30ca608d733e657", + "0xad4d7e166fdfe587db7765be15661f92431d029581063669e6251d92bd00ec805f73f9fe7139501c699dc091cbfaaf82", + "0x868e27cc4c38cdacf79f18d3063cc1835970afeee3f559fff46479c9e7e6a6a4689b7b296ab35d7ef7bd2ffd01f13f69", + "0x82551cf046337683aae5cc483c9a488ceb32ac92c136936b9aff3a56153b5052fbb9cc626518819c1769bc87686b20a3", + "0xaf443552c80e358b9b5b33773250c046d4bb29320b9afdcb302c8d6e95ae1f055ca05f37d70bcc0953c96dc7b900a068", + "0x91f52e2a0f6bfc084bb0ceeb02053bd5cd9f97f95d21218447e5360acea36a7335776cb79a718e65b062295a4eaa52c7", + "0x8e131c3eb6eea0bfe9a32399a9a5b883dea5b084bc4f0deec93c88e72f435f038a5498b5cec6fb96a9bc6a57e7e6627e", + "0x82cf8a7558583265d981ca0b8bee0a7fd7c989f14c7864ae96926a8422a035a87b6d198fe4a0c3265f4a4fbd4aca6ba6", + "0x81f601ec2af8f17fcc71003cc964a2b07e4e42d8d7f2089723c27e02fb560da6e917bd53fa78811e7d5c5d6ac0c45650", + "0xb8f502fdf4227dc98f1a1f60a0deaf1710d3159f826ee6d97881ce344e1ce48e8b06e218e308dcc4851e5c144301e488", + "0xb3f8c96edd93736f8dadf7a90e940922149a6e6a2c864a75ea137cbea27d279d3ae14e7f74cd7e5991ab8575b7806d62", + "0x919800c30bb75029c87e1646c9cfe9316a17dac45316f51ac13766b88feed9b459d2d912b8e5e6a7756738e4b7d1b9d3", + "0x804e9e3dc58ee8e01bcdf845a7e0528ce48a0a2f458040e356e9366e2f5a726737c62baa8532360bfc8b1f9236db58ec", + "0x87b8fb4fddf3c7b5439d51b2d9fca7a2b98929d2d3791df5dfd55ba3f8f447223be2c0f33ab1ff08fe35502572102de9", + "0x836242697091b486c8ebe1f5596c2b397baf8a864d3da51b4b74ea8acf3bcf27e3d9339a4aab88d39895e46c18909cea", + "0x8c6dcb62cb9762d869187119b4e103a22a7e613439399137c98b6a9606ca98e4aae2af2fbcd0f6c6b28bb02e143f6f84", + "0xb74d2c7a0f416bc1a4530afee523dc4f82dbc92b541fdb05e9a7bfe62107c1fa79ceb9728648c113bfc8e8610c2099a7", + "0xa04621d173c81cc96a1b5b841c6d9739ee2120a5ee4d74476e0983f7d3aa925322cb120f966caaaf77f5add9674cefc8", + "0x854fbeffe65cc6c1653a3a3af37bdf134ec2482dcccb961afe5f386cd93849c2b0a63e3abdbffb3a4469f16940b90570", + "0xa202bb2fb34bc5984061e034eaeba586ca53f2883c9e7b97607e0afe8848262c79b4ddc1f304ef3e22ffd7eeb331f6bc", + "0xa0257561a67bef23dacca979b6bd2f86977c7fd4aeefbe6e619639ee1199b699660c97ae284935dc7d6fab2fa5a8221e", + "0x91a285dcea3adb760f9f3c60200bb8231851b686eec9fa113bce78929d8fe61f53028a1c049f0008b8cc0bb3de0f11c9", + "0xa9c84db6f03fe8ef3625ca467c98f07ada263cebde3ecf97b70d1dd8859f58d7121d0bbdf0c6ee31c9dea4b5aa8cfc3c", + "0xb56675bd712a47a2827038713a4c02d9956e9ca91aa7d39073700a876f576365078f9186e0c97fe881594df2adef3e3a", + "0xa3260dad8aad2fee096662d74763b0f3bc810c39e62387c39487c37ab1a4d523a28ff4cc2527f51dd032bdc87581e298", + "0xb8530e2ea5074f744e7aebf995677698d9a8ba9929f712a676be97eeebf157cafee7c7914e7c0e38cde1cfa05d49a347", + "0x9092bee214293cf212ece9032b18fc64f95ed9b3e6d5858c12fc7c362e20384d492ce4e244679523112a8fd48719ab0c", + "0xb4875ee7db17f3831a1d553fe1ac5aa17ede5c002c5e5445d63263fc1e674aa6dfb7947d11a8e35829451d3cc19fc485", + "0xb814bd6e5bf2e26a68581eddb7a551222707e74bde95e932c33719821cf1f1984ecbe5124118e87b09c92308ace3e748", + "0x90782e1ec47f50dee82d947652a34dd453d242e122f49a250cd3fbce4ece91686b7092c455379833f0b7cb3faa8433cf", + "0xb3d84ddcc9b64ea9daf3ed96b0df8e2433fd405de014af2b9c1363d2cfbbd6b20cee2198027215eaed3d26d8a6728df1", + "0xb206d768ffea08bb582c6b0014cd8afdcbfd310d723b2725d801c7ce9720e447d112f9a117a513ce7b7510df5b0dff7f", + "0x8f4a19465d2a340fcb354c9f49174fb90d50ddb2e0e15f7cfb1b3dac1527b3b51b14895b9b1fb03e47450bca3d2d5dcb", + "0x9150f63785761338363e77bb4ee93b9858d3c4a49ee2b23810c0426403dc92b92d70052c2de0bc3e7afa322899701673", + "0xaa2b146456434569255a30a8c119eea4616cdd1e5325b39591ebcda87f19a7847b8ebe3fe64f71c46dec3c5d97b11d35", + "0x8ebc239ebf2e74ab4513a2da21f562026740a520975033279125aab1c0230f60416a97124e3e8f4c195cfa382f319dfd", + "0xa0b0421e88c9ae79745614e04058e32ed5ba0cb93aeb7dea408fce32ec8029e780d0a48292561195a2c29a01d950844e", + "0x899ecb55dd8b160280cb065caee2c2fe439104d7bc37a494ad488f50094d25a1e6974a9a924eaf29a70c5b0c43907439", + "0x94b8e4d0e4013ecc99962cd3a5ae863d506fe3b0f7eb03e150382710131e7f68fc44bad917a93ffec2d5d75147e37c0c", + "0xa8d1965dbff1b01cc67ff2373dd17edba6f1f700ff64721e40874fac33c5167dbd7eee239af1dbe08231667b226d03f2", + "0xb501a1ca7d1189a0aac15466487e47d8338f59d9f536088f4ad1133ab97dcd0ef23b5b4edd44c65b67f5e3b6e170f0b0", + "0xb795ca316d177ee21832b0941d29e7c400bf7ed1dd8fdfc95e0cb534dbd2c6fc74a410f7fb510b39546a5a0a3f7732cc", + "0x87913f992c3a985d08a811d0ab81fae4d5e78f2e89e96a7cdc89b8c5345d768c3ce2b93d9106ffb7c14d81346cabaecc", + "0xa8aa9d87e6d371dcd7d7a900e4565568e7344d38207fe98ef8ff7d32df504bab29ed7e70abe57d8f9f6747a0057c2be0", + "0xa52e616228098c8019129bdc786472be7bd24bb2029f5a41880907388fb4efcc23daf7cfa9f040b827f876cabc374a98", + "0x896e41f19a95e8f8a60a4fda413372ed4da8856c74d61405170463e01cd648696de0b524154c478f3b9af9e885fcbbdf", + "0xaacb1ccb6d67f2657260f19f7b845c276d837d792d9a6ba4897ffce52b5ccb3b2bfe4d5701e8b4426167392ef586bb23", + "0x8fed964262dd727b77fa64e4113706ebddd5a3125fb0cb15d0fcdb958f4d6644d149020dcf91dad21dd88d4b90b14734", + "0x84ce3ac809882fd8103591c98b0cb49a7b4c340fd81de7baf16fa4b8d2c6b2d8facada762e8a0c78c7b6928ccc86a1f7", + "0x8072b830ff564552b9d125770cb059e6b9c3183e0578d174a68936b3ebb19a5eef2bebb5eacb0ac9c1ed503deaa2cbb8", + "0xb58800d2ee232352cd2513a640ff10563a7c32dee213483251ef7a789db754477f50db31d490b32ffc6c733cd5567ee7", + "0xb7585b3eef2947bad654008d03acd83da9db5a4b517c7fd32ddaeddf077b89014ff5c6b2e8e6374e2ef3efe501323978", + "0x97be5faa6e3ed4b5948531875e5fe360f6ce6b45b5b63c7b76d79d5bc09f09a862868f29f9be43a8a9393981477cec2e", + "0x97421f1f0c3326a71bcf51a99aea84b908a989ec2ffb4a45f1251a42365c46da44d3f951159909b576b80f4824b05de2", + "0x87fa9b0455dc433564a5291d1e9121735bc3d796cb23a35d3c65b967936e30f44f993bd1679aa599084ae5c31c8a804d", + "0x82ab2a57864711335c2fc87b04c40911a804021cc2fd96fd335b88705a72a99700763a56c7cdb49aebcf45cd9f9c6ffb", + "0xac4b6fd116dec0cf83071507d13645459634c5235e2cb0e3404461bc0c5b60c34e3f1b9303a643c295938386158c5eba", + "0x89402fb5b50deabd6e64fdd22cfe1a1c515cbd5fa506ea3782841f7357fe2cfecfc0184beae39c99d8e5a39bff10e337", + "0x89b8081d9c51ddb693a4959e2c8ebeea2c3655cec9c5cb852b3ea131e7ba734da837cf89ebb4dab1ad67504a7831bf25", + "0x8de15c3036569a912924487357c8e0fdb371ad374e0c888b21cb9c9fa82cf9a3840b0d2e7d0e15fa2a71f60e11977b87", + "0x92e6fffc597df6e29ca22dfd74b9345f21837743067dbd26b0d6bb63ad910f055ac588a0620a0f5900927bf1191c8534", + "0x83aa20c3319ea92978baa85f1181f8201b2abcd3eeaeaa7a05277fbe554c9e346c38a1ab665b22bef4f7d92918bdbb98", + "0x956685e6018933a7d3a09c51ec3fb3e29c9124c568282508ebf8dfcc3e1c294f831be0b5171666a576f2639f93c3b37d", + "0x995fc2d1961618181b01f026c1dd0b48e9b0347f61a7c37fe9e44caa259e8e1dbe8c6617f88c4869d3463fb39bdd51a9", + "0xa6c0bdf383be5fab79552b3082bddfe9dcab21d6467387056eb43147be116ec715d1c2b10dac921b1f9212ec5c70edc0", + "0x95cbdba467fc5012e94fc2b08046b1300326c12915d19c8e6ceeaa939e3a76bcf3059c11fa89e8c75abcabbdf22b1159", + "0x8f851813287eae0877a0b589406b0b46bbaf3ea5ff39d48267fe2308d2d953351512c1a5326141b8be76c8ffe1e53b84", + "0xb695d0d1e44aad17c8baa84d3a88e445e7ec551968d2231e0245728891353ef0e6b669a5fc159e9e58c057afab58d068", + "0x8978df337325f73180ca9c2920f2d213e87853dfea2a9d307503c9d656393a82ca339a13e6d79541744aaa49b63fb131", + "0xb7764aaa5702afd06a06b392a1ef54a38e0796f9597a004a965793d87bbe9542552e1ffd3e896b870da7f79ff4f0f4e9", + "0x8f592af8525143931ed2b71c26badd3b03d0e4e4c37c76f9dc5a2152d999d2607cbc0d9a0e2adb92f486cf0a01f58cbd", + "0x89ecdcb1f3ecd109699091ef262c7a25baef371317e09bb0d93fe125258498e661b6205bbb48d4e7b79e434fb0f92a7a", + "0x9276d432212ba13f84e971d2ce78c35d29b58c19bcf08e788903b57254009ac90c8c09d788f37caab315904dd9332031", + "0xa1b63f701eb2a7f855c44aaa6a5fb4ec1960213dfad01ba91ab511ed32e8bdda57fd117a56d4435f72d91749b6d5f09c", + "0xafff43f2e237767e53feedbfee584b9e037d3dc3e991cbf89ea30c695860c8bbf314bba7dc5de0c3fdc450a61a111bfc", + "0xa539c91606f6e72b9a4337cc8333cbab013896469851735dd389d75610e4c41fbe46a1ce4b30f42f45a047e13f2d9a86", + "0x94ea99665fef540c3afcf1c56e3b4d7f7f1f59598a45e077e20039dcc859a0a4ba8170a4fe49386b35f96e1c5d80b8a7", + "0xb2495fecd749870fce991722c8978178fd77a9a2299fde7ef9a1981f340b09ac97c72f1246a0648ccce34c01784f3a39", + "0x86572fe9e9d11ad20e7a2aa90d29a92cec37f1595e9b7d001efff8607af6c25cea7d8b8377fa5ed56db9e248a63db4b5", + "0xb773c936600efd37dda909e9c8c9a833b3669057813acf337620d2d51bf1c41e325cc4bb851532476c26fd0f05f11627", + "0x85b7e51bdf1590d5a326e39b6892c79c5a180c4c152c0f47e572bd9e056717a739dc1cd6dcc8118d0bf3504c07b029ac", + "0xb31763d16d6b37daff4de4bd172e9ddd47e377fc038e46c8f1f985fa77123258de067e057d70d974cf1ad83ce12679c1", + "0xb4ae881312fc528dd41a5adfe07e27a6f953b70748b54c3c3eab1e64c0f5c30c737286b87be4759fa06d6024e51f4ecc", + "0xb6c3912f21f980eec47d4f7e634e993a6cbc8bf4d3081cf777acde7d7bb7cfbd80e54180691d2fa60cd113eb1ab59a96", + "0xa6809b4d614b31a3e4b52cfd2f9bc28b96c001a732e862b77601fab71b0f595df8e9a182bbec070461d66b0a302e0b67", + "0xb393fa1fd4b13ecb9fcf847c5da130ee91297bf87c6c4fcb250962109cc488158b1fb38c136ca9422d8f9aba7c09f867", + "0x9144c060339565df639088edc162419167daebaedd38dca2fff04bd9665b0a37d7acf3b7f01680b3b589ff741cd4f65f", + "0x91521ba314bba72aeb2e6b4c6856b6d2744c423dbe3a654c296d5c9bbe5ea9543f527e17d73a71d0be9c9a52addb5a53", + "0x96addbc1ebcee740fb7f9fc5b7f2f91b92a91c43911ddd411e0e2af8306507325e95ec54f875536e7ffd25d87e86fc43", + "0x89fbbb7b66d6292fa5e7e99f3a6e34527df2c4f348d0c671bddc760b7f858cc9b0401eee0f611746b42e101efa54a2ca", + "0xa50c261389022a3b92e428d877ed3cedb5272a48186f729cec0da542bbfb168751d44788c772cd29fe5436d0b8f44111", + "0x9021309c8d5a4b0f13c2673da36a05be5c2518184e782461ba25458fe44d0ed1cf5d203f5ebf5ee21960b0eaf00fab39", + "0xb9ec38b171f9f8ef1a3d0d6a9cbe64c3d59807dc5bcb90bc67babdde298809989efcc4aed983b9a13440beb674a2ea65", + "0xa010e9cf53ebb4f85091ce2f2c27680809a53162228c3ba517c99879228347a651293f56303a8bb32d6e1b47c054c3e1", + "0xb723e1cc09d9f648a67499b81b087f4c3a3609b13cd7f14ebc6cc80c688760aa44692b6686f486a13614217f6d5543ba", + "0x943f475bb1fef8e966fe106224d4b0130ddac9ac6b38d38145ede95a69bc108175c909d3d83bed6b94def507df5cfff4", + "0xa7d357c0f0f44989cf7af2dbc21ba423f91deeb5dbc90c5bb64abdf6bb313ca0c8174027ed85a560ebe726ee51e152ba", + "0xa85841fc6c838bf554e850bc05f05c616c8f2a811c620bfbc102281b0cf3780a9c3daf4b1799fa349434ec1b8d562961", + "0x8531b2f4e667c91f7c2f19d85bea07fd2fbf4d512d68c6c8784e904dd6e46d622d98bb9d178917e48201f8a827630506", + "0xb26b8ab70a31c2c4787b4de0b937eb0c620db1e159ea562eb560b166759b2fd71c12dc59524a9b1ea8fd81857e44177b", + "0x97db1f594d9565ba4863edc16a6ba4150dfb1c48dcaff3ac10be03e4f41517f7fc57ca00daf2101df21b585416ecf416", + "0x80aed7fd40fa90a87cb341bea66378d0963f126bfd6899d896df7a561b14dd23a7339171b39e8720c6f7814c3a1bba06", + "0x85feda1ca5889dda71f8ad85e7c92d5144e5256513dc6fd8b63619ffb84b3d638a02d42dec282806672188b2bd1c002b", + "0x911fadcc44a86339e633537059d104b3b8e6009e70675e1e51a694af6a592b0ae2f36e74cc1ff5f8bba8a6789fe14c38", + "0xb54cb1382cf0075751bc5ded74ea5b3624124aa457f497229095f0bb19c15244c33ac9326f44dd873755627923d9e2f4", + "0xb3d8acf220a63e8ffb11d682e834f08c01113469017c9b99a937ef83086f5f259de74d13fb8788e2627466d2bdb24214", + "0x99d724879ee9f8aba660c11350c6c986ee92b9f2e897bbb5e2bc04ec66dc4afb51647dbad4f670a14c1a7e23d146e6d7", + "0x82e906329d7e13750544384a3e0feef7305e9d092107299f465f49966ed5ed5288ec2952b1174094f8d946af803c99ed", + "0x8bed014e86d5a5395b42c878ba67ec18b0cb6aee15840c72f9faef312209bed4316d71959cc3c319505f99c79615ebe7", + "0xa9365d77cf52818a7c40a12dd436f3c20a08196aa70d3a173900a629ab16354a5e8c65a2aaabcfd2cb9ded98412b4aeb", + "0xb436cb4aca5400032a03c8276c34b88b1ee426ffc412f85c712f3ad83b3cdf410742cdc74dab33dae09cd5677e250c8b", + "0xab135d50512d7f96079e2571dc962544f6328194e129498d8a2841539d6d2f51772d09e32886f41c1c08649714d770ff", + "0x9206fdf2b0f86bc890ff699e26ba2b636624b62cdfb287fbafcecbfdd2d6099724efb3120386746619c3716f1ff31739", + "0x8a7cd0cf3ff404e1a8eaaa3fbbcfe4f5bb4529a4ae9e3ce4aa183d43257ef9d133eba2d08e6136d3201858f77c3c6d29", + "0xa05ff88029c4d29b75495348037d6734f35e95995b09c7f734949ff1aa5e9a17d5ca63f84bdb608d8e36930d88ad5884", + "0xabc50ca69af7997eebc372ba65d0ee474c8fc36313b1332b83527fe7bbe823d98b84ebf8b30cdda4026c5fd9e63b9d5d", + "0x97cf9daa959b5327a1a32177c481158cc0f08c73a3f6b7d6932d28c3f09d2b050ce5e8d07d2f41080982bdacae6b87f5", + "0xaf2f9380a89810ade3dce3ac325108bb0f8c62226d0731aa028c9e956f935f725a229878d7d7068cd6048c8b03ba5268", + "0x8b6a6d68adcffa054ffc52d3eca41a6ca10c11cb5c69d10de1de695139aa1edbb03b5ab7d0a398c97dea699c1a40f031", + "0x892adc96a8232163a5dbbc63000119eb9c548b97ff882e33ba6c0ad6531b1e9ab20f8e21fd3974dfc7382702feb2277c", + "0xb80fd63608cb46059465689b22a4c263e159d1004163594492dc08d1fcf0e90d783f4b34175b68dd886daa3c8af77eda", + "0xb26323ed1a98d7919c8037c5fda2ed28f3d6f99883c3c22878289d5e3f03d248c91931db090edeefa410ea998468420a", + "0x8b93f1eea34794fb551e12a51f06243e246f60311b705a695550657b35dfa78980569371af809d638874d84de2660478", + "0xb55927b6cf421d5486afa6012faccb13b38bd9322c0937b045ff2dff6ad4e5be75fa703840fdde853749846527dfdb78", + "0x809218605c424f5c6be7eca495bf7fb10521e054ee3563ec129505f29bde0dc82405c0792bfd74aeec4787e7dae0a936", + "0x8f1bf33a0118e4ec7759fa0dda07cf4b4707d7ac2c023b47bef52d9c273d542b0b700ac250fee4859240fcb7238f3266", + "0x895b9e308163351d607a6632b22631c96c943a2ed601a6a47d66a72c014df4764a27e8d14cc6148fbeaf793d46a37981", + "0xa1ce2bf942e5c6441bbd028c444976318d31e9a1fc58ec51602883cc60342d16fdf52825c062d681484c320b5469c4b3", + "0xb26a4e73383825eef00e60829de3bc8008c0feeb4b951dd1a7d88462c838115db2d0932501f573537bfba1eb76a37b84", + "0x87c5ff9d7311b3fffe97417ca79d8e47fbc7e036a6eef9cca48c8f71bfa2763be993a35b927241d6582c932f01ea9f40", + "0xa550791ffc9e0ac108c541dae640dac999c32df304931df3eb80132924049ff18a72e42fe878974ee94819a5463b60d1", + "0x8e58fd4f211f464a7062c72edd0e443d51d1d7cf58b27dba169e22eb224c46b2a9c51a8c464e9d4a3c7fb7e60ec679dd", + "0xab130fc09ead1871d69b776c0a8b9212be7672be98ec97c9b47ac57984a95748ad75e4155675a147ba1617ab94e3ac6a", + "0xb4ccb73c69b3e4ec72ae99ee9ac3b9e676d1d619b2995a6aeae0d42a981c26e6ba68ed8bed93fdec64cfaa1da5c1b902", + "0x8868f881316c616b3df50d9f3f64a71fab09afe7447793814cd1b7f3540dc1df8acf769b3bc3d40e8cff1de84eff13d4", + "0x8f07f3c2481980e13e9c6007bee663a7bd4a8f1a932b6d4a947cc7c1e5530861f55d1acef27e14287f91d4861ce803d3", + "0x8c298938d17ddb5c045029844d2fc49f40267ec60b99c0020dd50c8f96c95cdc450adb2a172ba7e47f0bb19d5f8fdeb9", + "0xb75936ee4c3fdf3680cc23da98e01988c221dfed669bffee2062b24ce130266907668273b8576d108f5a90f1f175f3e1", + "0xaba7bc21b7f5f7bd0f61203cbd95abf9e76b2e27b321a439685099f24462ccdf73a52becae0865459815ef480b6f781a", + "0x91e0a7a998f1e632f8c7a9f55ff98849bc6fa46a103a282a75a0917470d7e5c673492aad98f3dcc64e9ef1a2d3e518f1", + "0xb75f9d71010df636d1d236328a3d43a5feb53a96f158ef2acd9978f56f21292412bc2be33271cb41db16bd6f715bd835", + "0x8c7957a6e35938d382513be944bc3f36c343e5d2105520db990b5ea0a6dc0b3f94c1b0aea3d945c5c13a369dced68696", + "0xa5eb1f93a8d56e95ec9b9b4d44cf27a19c42bbcc79c33094bfacaa6de3c76c4b119f6bc2ba8f3b1110e11883a2dcd112", + "0xade44ec7360f4fb33cd91c3b9a4f8e16ab8c727c84ebb2a7941a76ba031e724940050585290031e17c6fa667ac54dce8", + "0x988372b13228c0979b3d9d6c4318dabfd4749744672493b8df548bb12d2a077913a59c9de91071b2b2929df7a94dac56", + "0x90fcad074edc9af973630bcc85dc0a26cffd656008c09e0f46ca3fdaa4a5e2fc7fc8cf03faa3d456659163a6175a8817", + "0xb3ec16ab8d2b6ffbe088a2437bbc27420b92d13f5b3d014b0fbf82dd94583a6308a7f596aa86cded5df82d7a929c9d58", + "0x8b5266845882d9e206c98c096eec3500828fec2ae7c30b1915ed217efd65b1aaecca273f7fbebd178025d387e461194e", + "0x89bfbe69767e5498b759c8249751441d59848754afa5b82b1f1f8b9bbd8e1128ecaaf8ce4eb303e7eb8e1942836080fa", + "0x956c6bf9c4cc26aaa0a12fe715a219fddbc23f8e1fa7af9d4b8fd1b1a17aeda7547e87184aaa996851153dd789b6572e", + "0xa8781f563be4ba62ea603c6e1329d246f7f7ef671df33bed7851d6930bb83b8f88cf77f989df827b5f5a1e04be6401bc", + "0xa515cf3bdaf67b9e669d3ab653247dff2703b6d96c1f418428f9e7b413bd23e489e9f17da2d9a731d52c23e85ab3caf3", + "0x92fd01cf3d57fda20a06477e183054acb9288d65d2efacf9030721c3dd5016c56bba26d6a7f07d182b8a6fff562e99ef", + "0x99a15ecce3fc7406262a0a1bd308f24cd3ada788ae04abe7f04f0bea9adabaa19be9052a784d548c2131effd8a0b6243", + "0xb073200c8627ceec19fe4923b054298f2fe6edf7a8b4ee41df38d23f78118136a3c3143f32876364efce0732afa66475", + "0x8930dff5b7915c6c539699650f5b6ea7b24580067b961211dcd2f480a86f76751feabf89b6fc74c93da9d74595386763", + "0x963a339e80f31724e6fb8b7ebd8c62e4e6f9ec8523688d8208e76c862ec30a39da4291392ae8209299e5a93716bfeff9", + "0xb451649eb4f8099ed3e4cd227396160d30041c7c49814f722697013a72ae41deb6975cc8942eedca8bd796a6b9ffaaa4", + "0x99f7d62801a9850de113696b59cddf3629cc2e5cb70f8ccc02fb2678559e3b78a74e5d5668ac073f1c9bb7d33fff1345", + "0x8526a76329b71daa9a15510843618a7c25f68e0903cdea7135d216596935a7b88d67f88904debc39720848daa4778ae4", + "0xafc17ad1f78480f73eb9113fc5818900cbfd1c149666fdb2683840a27c27c810b6432c4f9d14269a98d33ad1ef784926", + "0xa8ca462c5d4987e751f624428559bbe69cf2eb4be5331e4b8dd83e295ccb8ae367f37ae9537350f21b97ff59c10851d9", + "0xa91e0d5b5c31290a1ccbfb41cd6d7e94e1667a52b44cbc3427d31a84ee707d66cdf7d9421730d8870276d76a3acca80c", + "0xa92c5c1b14d8779fd4fab22e4b11a62164516888fb16459fe19dc8f6d69c8f2183d45ff82ac03094ed33194242425a00", + "0x819dadc83e14e75f347e029ff942f39773b2da3c59edc54f84495fa2efda534bf628469c99883f2ece0aa04615b8437e", + "0x980386f843b682b55acb63961ec80febcf97313c7ab2ba51191b32c64356df45708aa3d30d0e380f953869480f0fa9d0", + "0x9570b6f06bc000f6f8b510a68e53ea6a8330e3c51805d6f4b919e522f6e0cee77eb3e7781ba3e87923a4e0872eee0ecd", + "0x9291e59452f3914b7d7427289aef733434853bb361b01316ac5752dcf9a204790f298e3f5c12fcdefa55073a0c014fce", + "0x815c6aa1dcc82133ac02e1b2024ac50657ef896956d0bf4c4946e8fc6076574f709766cafea00f571ac20aa196a7f648", + "0xa5ea7390384ba1a7a29857030e9801322b0034bd00bbab640da81342fc3da5faf03b3574584c04111f94732969b00fb8", + "0x86f3b13d61f3f2394212365498853bf2043dd3061e9bbee732c2861c9b65b6137b4ed13f87314367c47f31be69004bde", + "0xa21109444fe777e50b61c4594d932dbacebe29ba7d64240db74640db403725f9a0daa12ef67ce14ea0d9909334803cb1", + "0x80ba84d7fa7a4a6fb9ff130cc150c38de36fd65e137f7e8a03173aded694a77f1073dd59bdd3b37a730908a774dc89fb", + "0xade6bd1751b5391e71f9a56ec03bfb587f94ea94a4f57abc21405ed3e54122a65b3a7ea399746776045b6fd74aa021e3", + "0xb6096e5b5fdaeb5518ff480c743435e76ce0512858953527df23fff9b0a5a1cf6f30a4c2b7bb7a723d6d702a1fb94b79", + "0x8d8af27510fa5c280e74a6501f3f80bfa436e22fa494a404ec1c90c7dd1b86029ea4b373ba5b3dd424095d1d23fdc9b3", + "0xae42a2b1337afa4231db90fedc6def202c2b36deb52794cd59b80219307b0a3d8e92abe30bb732963e1fabec801f12fd", + "0x9741602294f5bef1acfbd265290de43b5fae8d1768c512ea0d86f455866eac220adb757cd497e18d9b1bcabb0900610e", + "0x8d339b14f64b203e8a5f7722650d7add55ebdaaeb060f83f384355a15b71b839b220e39f1451347c955bdf30e6a5e873", + "0xb88a1bd9d6f7c135e080735ba8cc5f0c70f21955a4c0c09091d93c5f7cd1e7abf949a0dc42031a67d44ff7a431b76612", + "0x853195d3e131d57ee96f795bf4f544e665556436eda80fb08408d9381720fb69c0742d8b38d49f406ddc93664c005de3", + "0x90e6e4015c233b49c86b2dbb92773f915a3e03091b2fe291cb80dca53dbd06b21b076f9efb271cb22baef7d9861556a7", + "0x979cffb3fd4e143d5b5e27d0e39e3fe76da5c3c3f8758559d04eab226157a12df09ba20d955b7094d2677bb65adaf9d7", + "0xad2d482bc69d106611f2913e725610121ff5ec2bdcba3dbef50c281d982ccbe9542deda4ffc8e0f8c69e67a4f2f15015", + "0xa48d1e0ef9bbdac24eae25e471363038585ef2b5db7eaf30a387c038eba69d679144028eeb096f0bedbe9cb514e4b080", + "0xa71a1fe32eee31c7dccd9dca779d5d4b39d4db3fcf71a758a60187fcb5edb236effb4ad94042260dd574e29d6d36306c", + "0xabf8a64bfc0409d2aa073f05cb74648439d6bde8256bdf8deccdac47ec278813c3d57437be9156d9089cf68fffa7d57d", + "0xa57760cbd4eece71568d296015c35f9c2b2129fccce14344ca4ec1a7612de62b1a02e577235e0713409c7308ac027fe7", + "0x8c6266626de9f020f43f0159d78d369c4991ea565fd4eb1b899176f28d2bcc60e2faffa6f807810b6f75d7e803576725", + "0xa3599def91b85e38ebd20c8bababa43ff83191bf760d4fd98f89bbd492f3a660e22e6e70d05151dd4e6797b93cfbcdc7", + "0xa621769c3e33a30b9e11a086670de7bcb29956fa218891a3e4776f783d402e52d5fb82a943086bb9f450b0e0bf519458", + "0x8f7e0a49c9334ef45869ba610e555ab7517349d09220f89a4b45908c25c56a2f04ffaaa3781eff50d8cf1d2cfec3f745", + "0xb3a3a2242e09533d1067ac1589125460ad51d9544c7dad59ef8d24b9aed90cb69de247ef6374c509c064b107cd3762ac", + "0x80a56a0ad87f0114e8939c4e471218480e27fa67b8833c3204289761c02f546122e0ecb3f0dc999ebdadd73cf5ae1f05", + "0xb7142f4aa9f42294b5e24c9776c5ad09ec2f7ef8a328e8d76d73d6e7a6b7d257e49cd70fdd9334befcdf27e0cf00a605", + "0xac3495470e43e3a9f67fb9cd562d3bd10e9da0a002baeb2ad5e55468d75001c64677f8cf70ba37dac9461f30cddc4e45", + "0xac7f2de25887e257dd46e7dfb249d1e89f21209e2f32718b9d8628f90b377bd6d13457848fe66421cd481a8b2796e1df", + "0x94260feb10cbca7fee9400c05f73346170a61322f9b56c7615ec4ff12d960111c392f8094bc20f1a98ea721de3cd4f30", + "0x90f6d7cf65a455674fd2a47ad7fe4183ec1cce9fbd19e364af52f5bfb7b2852a743c3e23d5fd9c957ad8aeb185aeaf8a", + "0xa16c8e9ea552a79ba6ac162f4c747f4c96cdd9233a69196ed8e82bf49d349225f5315b1a3dc2cc07ce99c87683d9517f", + "0x84cf8119a41f8be193ac49714e21dec9fe8dd76c4257fb0778c810d641a7c64ef71124fc7096ca5dd579e9f1d2dc0aad", + "0xaab190f7f9b2f1ae48f56873fc495c4933889a97b197a7ee8712a6d9aa734aa8e06d6fe67291093e6874c453c88718c9", + "0x90367778eaaa489edf16d6db833f0316947b0c9632b7b10cba309290b4972c245268c61a8ac5dd97a6096efc67abafd6", + "0xb6b352e8f78009803672aef97d9a63c9a895c926af217259a5bd580b4ef833441163a0af6311c527cb23738d17bd0ddf", + "0xb27228d5cb0cf71932d9b0347b3aafc705d4c7c268b0eb295d485e5568fc194575a066a50884cadcebe0c10ff0897f4b", + "0x8ecb5b5cfe0d7da4e8c39959a0537690a8166e9825e6cec8caa0a21bb1364503b060c31e6d83ab1ebc67d45e5ae87bbe", + "0xab8ade3f0368ddcef2adf3458d7993597656ed087de6e5f4a9a4e3f191cbf3646646476fb42084f5838ca4210c813468", + "0xb357be42435d9e5a8a5adb40f0bf5ef4845992d7e72f7c5ebdb692a9371234e9d364712dc026f9c58fba4d062d4b5c65", + "0x81a7b2ca56ccc9e6c1a19a9662b50d0df0197c112d06bceab4861f83ca4da4defe8ee5c23b24f8d9065cc6f93f42d312", + "0x85a3d3563f4556704b71cb20d7bdf7d4ccf5ba542417560a67970ea92f0611a68cd4c5196bef8ff7d66eee9051fd853e", + "0xa2b694924ddc433ca0d03d335eeff416b4383b5157399d76562a546c40e1559982282379648db9e053d36031cf2ca13d", + "0xa7b2bbd968c11ad62074051e93e22ca92c677e60ff78b4b0b21fb40a5f0e09a2e5888b479ec51525ab3ba28876ded15f", + "0xb822585ffe169e489638618e6e70c999dddc39b88f8b430574dc9bfc85cdd62700166700867f3b3f00befba4c26319a4", + "0x8f5c7925ef63efc2c1b92edc902b1e4d0889a4547a845c9a9fcafa05687ad27de96b828182689bace14fb5d34ffa6761", + "0xae02d3ee13f427b0e39870f64d8d9425a634ea2885445ce75ec9b82541621bfd53581bc6be4ee33400dcaa3ebde6e0c2", + "0xa8cd3e7a596951c5d27fd70c7fe69b45e5992ac42de7bbe8213fb5e0c23cbf82e0db0198faa4b442669c74e25f849027", + "0xb026c42d2b744d81d215076454b824142706fda84963675331ee734d466ff1d0acafb0893181bf7d7709ffa3555d826f", + "0x873f36ced40bcce571331e143d121945a6d17dac85624f2e529488640d0dbb1e52106951158f237b389c944d43ba6c99", + "0xb14bd145d6de3602e68e9e8bc64d7b69814bca4076142e2b2571f11255dbf3bdf9f57475c8f2553a556266c5bca38fb1", + "0xb5f6ac08313989645d8b44e6663c60b2515717c90bdb7fc5628d2332c9e5ade6f7ee8653e1b0c2e515eb5fc9fddb3c79", + "0xa44e8d8948e9159a63d3615cb9a05f87617de0d55d0a5c85e490bf6501ce88cb0cc5c94c278a8818288111f6f9f41aa6", + "0xa1d53d5fe56cf4b89a9cb464ee2cb75cd18edb6e0fe3a99856319cccac3e5541373e6be2fac98376419e1fb3b98adc04", + "0xb4811b9a95ff525533776787e006f29b2d7007e7c3cc89e504e3b3be1654d23941552ca4beaba63a6c83f094b2e51d37", + "0xa1a9a563103b99a38db981d96c77aacb058dc406ca041304776e60d2c049007d7cbacea3c5d9c4ba1c2fa08b66bf18ad", + "0x87f567a7ae62d8e87a057849e568760ee3745fea03800db178a639cf4719465ff610528cb541b95196f591a626d51057", + "0x9029e9b49a439bb4cedb1a4b2bb076c6eefccaf59ebf42494d9b42d70d192860de9b9cba3213943b3f589d15444b1c9d", + "0x8f34cbe04a41b9ac721b63c8b3ff120fbb36d66b936e729c9c10291dc1c3238c4b32d2bc4d6c4734215e33df42121557", + "0x917dd6792e94d0c344cfb345ad9793ebc3400ed570cd17936d7baf2a394c3a779a6263800691f6886e9d4ad1d0dc8b83", + "0x90e6c256b4fdf39bb42f8f3d0a49f4142e1049f6b06e545bfc7fb5a5653932f321adca30d3868b6f6f6e84b60dbe67a4", + "0x9435c8563975515c75f6c8d30dc32792306d0d90739baa1fb50e87bdd3fb42186d988aa957deafe7bf8c3da9ad33a6c3", + "0xb88034cdb349844f0527a7d6230e52700eccf00cacae16cc7d4bd45ea0c13185222718e3f7fe5c3a60959432e835505e", + "0x834dba7f1f56bffa6ad994fc46b067dd83b9af5ef770527e1e6a9dfd29a34733324650be28ef0ca1e7689b9811a3f3c0", + "0x813a6eab765ff7cfcdd0b6724164cd83ce63214b460b64e8ff859986373b328fb2d51a069d2ecb39324eb0d7d6e3a280", + "0xb7fa8afaa0054ba7c8275d9dc4caa994e39a93dbe99f57338338c4593fbfad6198b564abcbaa57b155355684c95b949f", + "0x935cc908788a30c42751c9021359794fc3c806ab746e8a67c661baeba62be092cb7941eb8ca717410f113a3d7411b999", + "0xb8c5f29bad3260ad20636e09c69316662aa799b87176d72f2c29d6dfc404c8c236d1cdfd1640dfaa10d3f71238ef24f6", + "0xb57a5e7cae5f644bad756079091c665ff1be2fc5417425db6a2a0eacc74acfe69375962d8df164515977e5c85841f09e", + "0x8bf76be9d1433f9eb1215e507c76363949c8c28f23cc2be16eb03d7fea510dd7264ea4a0bdb89bfb2189a7d4ca4521e6", + "0xb101f6a0c5c560f9345653e1486a2a01bbf6751ad6f808e536264aa73818ab3200db74e565d999ba80699ca133142c0c", + "0xa8707ffa8e0f7a26b1bd55c5b32fef4e4149d4f2c8cc5adb3aead6866bfb5bd5763cd5b07244c2e771e2440d77580540", + "0xa70908da277864933cede766c66bbb3502179db67d57a27311289ca6a6ddf3923b0ec100fab3474543f93660975e5d0d", + "0x93b956523a7260b76a02e3a65277c74299240464e0cf6d9bd9362988ef8f2cfe597ddfb157e2acad24e081c35c6f7bc4", + "0xa5f6452e38afd51827ee87938561d9a5cc2576892dd98b83c56924708b8a6b50c5a6542508aa782fcf78436c8b90362d", + "0xafcd6aff88570f99e97b1b962b6b75aea5e3a88164a265ceaaeece437e0a0630e666a090d0250f9afe0ad981a0bf2100", + "0x8c0e4e15c110a45aa73667fe95e49f5c51905684c23bf0103d46d34ef12ee6642c2dbed1d1558c76078b7ec0149d9808", + "0xa32b9d93cada33ddeda31150668ca5b275f95dbd1be0a17c8c9acf552e11ed6f7e863ae90021fa3fedf70867be36fe3f", + "0xafee78134c431533d02d0883baabf7a4274206d6d39aea23ca92520e35d1de2f1322050975a9bbc4a6b6a6378bfa6c4e", + "0xa82e690894156f4684f74036169b6c567f5a37d2aca03449a032677b31a12720abe0b4b61a68562a6eba2173e528a057", + "0xaebf310d8213e06bf5ff41dd389b51d4218accc3d110cbd4df9d031716383d50f9e7aaf1783301a72b857f24aec2ec80", + "0x8b32883ec3cc2950524ff955a79726705a17c340c674683de1d49f632fa68286fed5524c5d6c89461945d39c03266826", + "0xa73d1ffe4dfbd602049bbd9439de37a2ed2132b0684844d65611b2d430b08834e1fb7a358ba425ea0bdc485ef4062568", + "0x97fc665999d28a0a6bcc092cff59c6a9c5922f70b3dd99e1486354369cbf9585903602b5d7241043e92b41710e3c9b65", + "0x87d878b9f4440b3af84f343e36bd7e8bca6bc65e9ad69afb4c2bace917e5c251fe6fb0eb6e0b63844bb3e22a4c10cff7", + "0xad389e2c270980fa2d27e0440abe32c2bb79b67e5c9dffeeef835b71a382056240c3cbdad811bbb10754f94021385cfe", + "0xae79f64389ec2d0682a1a91c00e85336399dd55d65bb875a1064d81a155a6cb10f67853762a99bd998031c7996f9c252", + "0xa608a01bb906ef43be2b569ad64dd44d3dd07e49571f697b949ffc3c29aa27df5166c53450386f2822431f7c77a5a1df", + "0x98425db4c5fdfcf6ef7aa46ce100f0001d9b95acdfb295202a2c544bcc57cf51d8a1d0408b619bfb2d5b05f5dd7008fd", + "0x9446b89f2954b166dfecb6585324615e679a98562bf3bb9ca93202ba5fa9ca6ae7b01366c15d8456322d11308aaa7c56", + "0x92f6108e1c50187aebc88977fd47e2205afd88fb734bef1eadbef483374f92bb58513baf760728a60410883bb99f4e01", + "0x950b5c2424c58d213407dcf28cb93839cb08fac4b1b3395db69e29d96604102c73c11777357fbb2bd771cae31cc9f871", + "0x83c0d861de29e82044b421b7b79186d11d4238a5f94c0861189ca283bf9c6c5060c4471b2c488279051d951c328597a3", + "0x853d8a0ad50966a05e7553f487b824486a1bc659e66a2ae56af350e41fed81ecd221d56305a2b5c0911a1b347d2edead", + "0xa20766bfb0af26c7c0b470a739220884ac0fee96620b471797f8fb99c2d6a01ba05439992098a11f5d81b3535fb7ae6e", + "0xa33c05c11d765a198b891c3083fd0ce79f57f28315799b96f753a771d35c92622c4dbbe6bab670e59b1a347293ba0533", + "0x8c10ce0604951cf5d8a797e061d5bee550f5ac73eb757993bcd264e9b013d38a6c6b86818ef0d1b53cb64522b73cbadb", + "0xa2e6e6cfb8097d1284beb338fc442805686a35ed86ebd11ef3ac122ecc9851097a855fc31fb30c8d958d932dbe5ccdc3", + "0xb262c8c7e78cd5447d852d3793297a0e44123c93be1805e78c992f7b338e447feff7ca279477c2599281889940a61e71", + "0x80b2216dc439893f3898892f6486f7a63460a7918254d63859ac5ba657f9b25a97dd2cf59e4b157356eb9c6a1b524104", + "0x8102e52bdd67f3da0c17fa3a2fa896eaee397c7c6f3dcb361bc6686e0e1b4c4bc83bf28154fc27ad39df56a9025dc6c7", + "0x877a4c69a1ada10a9ac8f6151e3abc804d274358033b77a57e96869aa5c10235b304cae9c708029a91380701002d41b4", + "0xa4309db50612493243c99f731cb5c9a9cc2fe5005c4a7e06f24c34c55bbef92ca17864bc6652b99e20c207c27e597a82", + "0xb17c6ad1a159b578b8a7b1b9504a9ef3768f974d4ae91215354182d77e330b52d5cfe9082cf4a12a3d361c33fd6dd37d", + "0xb0f78d9c6a21efaa744751d86bb2b2fffc7a0942630a228860b7ffd4620e63d5d0c6c08524dd00601be0497ef966e3ca", + "0xa00ec29ef1e886405cf386ab49d6aa0d87fe919267ae4fe71e0c07b187911a58660fd2bc477915c40a4b2e469a8e2c95", + "0xa699c3da8552a265caad7259b9c6e65c52de904d2525da7cb7a86ff5d7681cdac6cd8215044dd60333b55b95261aa01c", + "0x8b30fa34c7feb640d4661c41a97c1e7adf4820a67bb88a43d45fb6e331cfb10549c55196b2072adf1e177ca12729cfa7", + "0xab0dcf881aaf54f8876ec30798d8dc68c15c21938ddf12a393edbf4d71f2221d843fd88a996fc3690ad76ddfdb263579", + "0x8cc9354baebebba31a795277ea0bbfa5acff2cd0cc4f98fbbf10afd7be3b2068f9724c6526d5c5354429420db6a2f7ee", + "0xa4a8041296fa07ef78351a4a9a145d4ee5e100b8eb0cb64c95da9553b831163498215cba3db2cdf58a52a5c0390fca1b", + "0xa3852ac447e21b65c621021a9886472635c089c48093db09eed4f637348c7872fdbdc9392dcf5f498ef1f9554136be25", + "0x895782b57feaa98293393cfd10f35febaeb24c5be59d8e9d5f32f291ad64157e9513219eff3c011526559ccb5e40fbec", + "0xa10840a8c3445e6bc514de50cb9c95f0e2ad34762d9cf702542ceb472c283509e258a6c7f39069df323587a27c45bdf5", + "0x8efe659d576b6d25efd842822855a7d3088ed5daac2bfbc9c9b8e9df2833c803d1cc80ec5e65920d5838afbd8617b0fb", + "0x80e22bb71013d6148dbe491bfce80cc8f37f665aa0d704cda7e54844de1bd960a045ce1302e1222382245e248d27e3f5", + "0x83b9775eec5be91bb5734512c5c72f7fd3f57057f4542d72a3fabca01852920f7e3720689fff281a6a04ae8d9f421739", + "0x83dfe9c7fc98fabe2f7605b601175eccaca82fdaa16eaf8ac4f7c07dbdd553f81d4b149a853a2eb49f30d07e638d3d9d", + "0xa20872b661eeb79898e502514be00bafeaa8f4d461c7594249ba6a7c852b284414099aa6db7de2fe7595c58057156707", + "0xb3744b97756b4c668a41242bd3012f498f0709bfa28b7b93384c5ac862a6ee664386c0bc2f4914efe9538b73c524a139", + "0xa03949f984cb674ecd90f48c0cd7225b9298f02ccab61375c00707012f9eb10af9f01f0b4b2c4f574203a9c5d44213af", + "0xa1b47d27da0e041fa4bdee3db1c378b03f6798bb518a7a288140299481ef44b95d74ce4ec28186bc4a44b3dc3d7a8a4c", + "0x868ff6705b2352339b0e461a7f60cfad9b86a8c47c6b307e303d22dbba079fd727db5b75d0e4a854aa862727c11a8196", + "0xa13c45fd5b44e12364d733a6d957fbda8293879b6f4bae4ea3b4a8a2e7e8f4219279024a31aae8efb5d8471060b6590e", + "0x88beae9ee7f95f8f34ea53def0d7d08a16202d150e9319665141c629dbb9f5e5ffe00d36f4d1f95074b11a6e7011d544", + "0x88b75edb4462ab10df70319966e6e982b3c5cd873a2ace9dfff9354fe9a68b25cffe2bc78e23fdf4eef4f73b0127ccd9", + "0xb9f5dff0896ea828a62056f0f34e6126b3f4fb9f3ad6a27b1faa7fa80c215e8ad7ac34b32774f4e39039858146a3a0bb", + "0xb423690a10c89b63969789b4ff4b9ada3c6d2b7464d0146ef1722b0d9c80de856c2f938359f0733eb4c7333685d96c74", + "0xb8e3ac747455e8727d5f84add49be8abcac1e30e8d67581ee93883e0cc09a62d693221ffde4e5070ed37b7ade257e9a4", + "0xa77f47d9f2e9a334af38632a570ad80f42bc3c51591f4e5ad5fe8cde47f12cc770ca87e8936f60948d48bb7b4505707c", + "0x82bb90485875f280ed5e4c4b21c5e6428762b87ea8acfd919e115383ed52dfd809e9a2671530d6d572cca2e0c89d6aca", + "0x91748775ff247e3f68cae126a1bb3961308adc5e187a20da41174086033b45676ff45aa4126cad5c5a2d6d38d7b6cb04", + "0x84660ad1b754a48bc3782a9ac98d7a0c8a7997ad25e4fc5e61d3c3752eac4dc0f6f1e6da262a3833740b7080a63ad2ce", + "0x8cdd3235c1c57e4c71b293c737535c6077139604b2abcab7b917d2616816d74e9c489837e90872a5a4cecea110dc9a39", + "0xb59704e434852e4b11d7cac46ede1e32b2fcd2d94e4e3da10f1ca7f7341fe6fe6eac564d011f38cf93fb562144f06006", + "0x99b877c04a06112cc55ea98a13731f410ab3684a641e36f70b9c39527849220de1d28d488d1837f3190d3f3a0af28c90", + "0xa617ac14ea5e74535cf6a3f59c3d78873947feff44065c4935753c8aededbc712c3d2a669da340aeff08be1786101f35", + "0xa2c6f0c62e39d63b9eaffc29bb8c01917cf009c3d94722d6fdf77868811d28ee171a479f3ff25e9bea74634a6dadfb20", + "0x944d40c32e9414bc235ec21432a14ba2bcbdfc25a670cfb4aa204b3f425b9a2843dc70616fffd791cff716bca0884e85", + "0xb12848639b870167c03ffec039dd6cb2ac28ae21261596f37337d688ee7c030633239d7d8ba99d6ce54cb3887dd10ce4", + "0x932c0c3640f501502b1fb570f487fde0b498a6c006fae495e6e33ca411b6b57d2d17758c33cb1fe4a6e5f538a9d28bf3", + "0x8b2221ce8aca8554f367a22f3f7a2b598b10c70edc7a9b5087938580279ee2b11b5a7c16fdfa5fc57f2d37dbb41a755e", + "0x91bfedd8898b1f2ef24fbac07329ac70c9d2b3b4657bc777a453b70b587057f875e2ba471552dc1c587608fae10ccda5", + "0x8db31216341a7f2289354ff1623b52861cd56f4212e7706fbaa71d5b4ce15238d5085d1fa58fd697769507f66cf8443a", + "0x8c2b83ff33f133d5357549484559f1a936941e493fd4f14277a06d4f39943c73683062f7411fb86117441673ebe74417", + "0x8abb32577ac798f99cb0f4905f902e7169bf079fcf721dca704cdb661331bf418d7d93f7c8805aa0a2350eaae5b93fdb", + "0x817de716943594a90ba46a5d4bf7e45afc3237f4ade8a84af1d17f5481f217d36453db3ffaa2cde711d604b5c53db127", + "0x87ea9af91500aefea5733d2286f90e865420f07a015d7a0a2929494e9dae9dc726f6246a2b8fbdd7c7035f97be47800c", + "0x98a215e6e429799333a828c95bee02cdc1423d737a7c91ed4a92d7137f51bdce5db42b5b1ca53f457db09fa520cd1609", + "0x876a50c93fbd9e2a163ccd8da9da65e4c0ca7f55d9e872cfb7bd42a410f84ca8b1ace72271b2c1dcb109e4220cd55b30", + "0x99fdb86fb54111a7afc35d6834b6ea70ee86aba717d3b6ac2321413d6b74f6f45dcda4a90d25b9dd0961d0e12daf9dd2", + "0xa5fa258d618e9aba3bab4a212f043514471ca9f09ff629cc20bc8b4a4906b1946543597dd09f65be4e183f2b278ff172", + "0x86e59c7db54d9ccc7b5ee1460dd9a60564e509461bd0c10b3e59b3d45c6acbdc881eb694838062b0e21ba41e5254440b", + "0x96067149471ca729a74967eb9b41d94a5856018d269671d3b81c880e8f77d6a8551beecfdf424174f58f2541a50fb3b7", + "0x8c96c2ad5b696151af52cce39bdc7cb0e910bb9a3657483c57e7d50a66fec236cff0a35c2149be4597c970251736f0bf", + "0x9366f6620d740c92a1a8a8ee521275de61578463ae4d76856c7029cc9d1fdeeac776ff1e3f89ed04816f6a8b4503fe7e", + "0xa9f7eaeaa6d1aa7f0a74e39e6ea05e6a1d65a00a7954c79662a767364968ff5e7a13faae48b1aaef78d42be592435a79", + "0x988039cd41f0880567c5eeb6e4f20549d361be9b9f9ac435a497a3286417627cffaa05d8dd7a3f44677761c00e38c06a", + "0x95f65115446fae21f31b4778ac310f05e8113cdb18ff7a5ec7cb1ca7538aaa4c7840ff2d76aa74a405e84d7b939ef0a8", + "0xa0ae61d480d57541ae9f74750d80262f742cc3ffc87e8341fa561ec4b3777d0cdddd9275081b2eed914120707946daac", + "0x9642c89dccb53323d94fe68b68718f2ffae01f23dc37892eb0e78f3cef5338a3f2bd85a716e8ac7b94a710208e6f9c8c", + "0xab0f26a435a624344c4497f95747e3eeeee56b33343b7f24626fa0e51dc294e6085e1a52bee0c1a87556f6917e7365c3", + "0x85edddf9d0e955f20316f94706d13ea5a233ea0a6dc4a808410a904ee9b823b8c442d5a68ec33b840ab6aff8755c3011", + "0xa0387d8cc221b270728b999edf6de0575262bbb99234e89613971b05eba87ea8695669ac466988e57ca4129c4979bb9c", + "0xad76e2a67d819b9f272a7180dc848aa66754623df973b5cf64912a1944613d176d3ad355e1856e83a5663d47ede43220", + "0xa32346699659d4a11c1fab8874edc6005192f2da29b6c0e34e0d9fd388ea9172923c18bc29ce659740bc280f8f2ada8b", + "0x940d6fbbc5620e1d73d9bb36981405b9b2b306b88aa3042a0139d59445f3b5afdd91584812696385b3902eac3d8c8730", + "0xb6523b935afd44350ae314be0fda6cb761a03de02dcba2a596a814ce4ac776ca1f3ffdfd27b9037925e8d6065b0c1ba5", + "0x8f9de2f8dc620ea76c69a5f735fca9dc5b05fa84608252c816bba4488dcc440124326a4a253138364f530481d5bef3b3", + "0x9088cb52605211bd0a66305630d4036dfdc509d55f93c7e552e5bbc6b385f88bdca96175175d6ed98ee211ae578c30ec", + "0x88d43149be1d0b0cfd4992bb374c92d2955f268b3215440d9874326d770e766361ca3893c1aebae8d8caf9caa9982741", + "0x828d0ef1285686e5b38725965d5e1c95dad4354e6bcac69b27cea1e7a04670334b16283d46fd3711d976f7c859ac6f3d", + "0x99a19ffba4352398b7013b510d30497f91a1c1350811dab24fcc97e6aa82e6eab72fcba552fde64117383bf13ddc0910", + "0x86f2dd89b78925808b9f451b84b50a61c5a6cbc6a08cb9898a3a5df3afdf420f5ec2a90962653b3021cf5a7f01372e71", + "0xb9fced424bbcd97dc46b9a8bc9867bbd0fa8d48aa0fd5572ebd98f70bf7599e5fd4e8750e5e23e2ef914d60c1c51412c", + "0xabad3f848717b429c21546e5840feebdba0e1971baa58ca356a052f7cccb96ad6798312ca40e4570350887d43f0ad886", + "0xaa1d05ff40cc3b035301e5b2213ae7c83fa5ac6724334d6b6db9ccf228bd89f52f56111ce1e78809cf54bcdb91944231", + "0xa666edd1d5898c0e1a254964851d96aed918c459363a525f07af9388637bfad69a961b17f88d5bcbc6754b146b0a3254", + "0xa767166fd912602ed00c35752ce92faeb9a1fd2edd1c2491ac5d7e2a90e8001ef771f14ddca3c093869499c441b630c7", + "0x9513015257702907c4b6d5555ddd7caafec379d56d681d64fc966f0f4c44a2fbb5752989187794669af3ada83e70cd24", + "0x872013a69ddfaedf361d514d52fd6ac47ba8558a9073a96e5b53be80fb10319f641ba1fe89e794501ee32b9d45bcb149", + "0xa441895421aecdc8065485af29107f9677c258011f14978cd6bb3bcf70deb429029c255a7d8235189c9d62ff32940e5d", + "0xa2848f235ed2c5a97c546a11c44e8bbad372fcd630a3d2b694a84f09fe03d4d92cffdedae49480c654fce1382a34dee3", + "0xa9855893dcda3fce7b02f1504d704b8974b4d165b56c8d47c94da87207e2b7ecbb115740381a2ce8363a200bd257b699", + "0xa6b682c6369c3bfa39f9b7f70da3a83a8a0b21e456d41b74d832e57575aefb92da313fedfa527ab3750cf170f3553c32", + "0xb3ec3c2961d2bb9b0da10dde92695d391dc95479011d655f994249199c2331b76b9c104f3fbdfe1f8fbe14b972f54c7f", + "0xa6284b365cf785e98bb427b49c48ae511102dcc180522dfcf2a774aff86f4a6c7950fe7b8e39b50b4ed262cadfee10ae", + "0x8e79294afc2712976bafdf5bb0ce0dceeb88f0004ad150b54bd91f0e5965171732b6e2aafae3066057f624f302942fe4", + "0xaf0dbf17ce8274f41ca37832e3954d34e0bc5fb12c0fd44664fb2b727142b05200bb30ab34f23cd4c116ccf8360be8ab", + "0xa7e1d55174c93b8b7ce1244c9b48b142cc0fef79b76532829ddcaf9ed2b824885424dc72136d6767094e9daa5d16039c", + "0xab1140c8e839b565a6e67ff48ed6421c97e99b49f32d84a9acfb10e0b0a51fbfc3cea735998dbbc5d9fb5ca0d35d9277", + "0xa1992ac680fb2464db41d5e97e771e1ec9b3bc5fa209ae8502b55a9d872b68a4397c8757fe194f10d3fb4ec78d1f30f1", + "0xb04dc696f83d2ada84b7eebaf77ff6c9755c616ca8380ff68da1dafb661d4e61bcc013b48c18bd2100ba24a8c41cacbb", + "0xa42e2617e2181aba813bf1e40e5a13d827e749d9e2b591fe87d9944e753abe01d09871674fb50355fd71a6e84bc8bbf7", + "0x85125f9ea0cff80ed1361b4808b3db5bd33cc9cfa6d1c272e94d644e338340cbb06e5a053173f84f844154acd97a300a", + "0x8c96476f3ebc306f74d481c3d2597b9e59515379c75ab628643c76c340d290cfe58a617cf28e4f6c93c64273412e3821", + "0xa26dc0bcf4c46fd76a6dee1b58ab5841453d252961229a2808e01911348442fc4c175408288704e4178e779dfcee6d01", + "0xa836a5f334bfd3a985f85f30c9f29fa8adba9de3a9a52ce94659ebf87365ce9b237fc3c420baf451d9c6caa95c9e2a0e", + "0x83b867dbee379ad9ea72c8f0aa3614e8a724b90bdfc394df8bc7e08caa13df4f9963a1199f5a9f058a2bb9a57bdcefc6", + "0xb9dfc174ef229a84dcb7ed8975a1b7d85e6c1b01dae6e127316cd5e815a095b2b24bd4e939a51fc8888068a9d8ec607e", + "0xb8362879e81889b4c0ddf646bff4b7fe0a3deec466d3fd90266a3e580932457a4c9c2d892eb0c1695bb215bf4f61084e", + "0x90fbba92c1d78a40020d2099cc2ef6a2016aced02f25346be40a7e2473912792c1c15f533816a90a525e5e6d6339f28b", + "0x8f76e5859d602be96ea46e6bddf96b4b0192054182f7c06f882244f9f3c87aaee79296e26096713576b64bde2d6bd96b", + "0xad3b3b56efd7f29cb35655b99da9657b40051b52206942ec637f6064a3d3155c614d325d3a342ab25f0afbed23fe9cf7", + "0x8daa1b11b55b92dce713f4845e3e2b1d0d968b8506eb8b41e9f7a2e5bda5f628ffe41497f6af97ce7ded183d7663655a", + "0xa2270430825702e5d9cd13b3b67f41d47fdaac775617afebff3dc767587b2114679b7a6c6524273885f901cfd61c4632", + "0xa6cb8eebeefe9f47cf32282fbca0cff27d16556ce674d93651722434608d6c1827d0416c056fba434864a02b64db8edc", + "0x9511decdf3354087d89ac30661eacaba1073850fe9eb2a6d6e417ada363e40511362122b0cc59b0bf3d36271ab04bdd7", + "0x977955aa3c40ac39d7d57b5619f743ba7ee385187fcb6f9920a51c51dc42fdd3236d5832cffe8d141267773b87546703", + "0x99c2304c81cd0ea78e360d1849d215640db350ef2f56245b2dbea68cbb6895c5bee887315f9379cab203e6b14d19d8b2", + "0xaa37f9e40f9befc5ded24c06ad49b88bc8e153d6ba925fe41d31488b671d82f8f298384cd27a8bf1733e5a9a1c586928", + "0x8d7282bfe7a91bc39aa0d7e2d0152fc8d94ff682ba462bec26ea3b218b7e1bbcd01bf3d987a0dc10543ee62a9b1df394", + "0xaf4c1839e41f4266515ac158849519f6d6f471eda74a15a37aedcd49834968b650fc7bb139b4e354d908e5de6bab1523", + "0xacfc4832428d2163f05abcbc6fea773e2c4e2a46488552c17691146a6a254816291b8c2201ca76bc6b8197cee01be5d0", + "0xae019e3893118ba884c2e07aa7085478f2ed557dd28c883462bca01091bbe2592c40cfb27e4957f8418f9b2ce8759f41", + "0xa41cafd668b3ff4a5beb5124b8ee4c1d183cb93189748cd8cab30d12ccaec6153f4841e723b87d249d735db3bbc6f284", + "0xb9d3760a5ea91dcc63b344acacb7321de784e727ef5967364bedd8b4ea61c50e220fa5bd17191a25d283f124da31e6ea", + "0x8fcf84b913f69a0fe1a0e1046c664c710803267619c7d24a45469b5b5c3547577e4d9035992beac24496b54122ddbb29", + "0x910c4a9419a0203830df4ea624e880fdda000a3f2d36fbe6c5d5fb3153df17de41d572d77cc3af296365449230bfa6d7", + "0x8f2d163dc7526aa070ccddbf767ddf0c696549e498279503c7dea64b08ce7c24ae5a7eb34af76f2764b10f03fe577c4b", + "0xb8d6dcb931908e148c0ed4ba66f5b94eefb0d22c202748ca4204d93e320269701c8da35ceae04ecf5833ffe249733776", + "0xa821030f4f4c4fcb8ba092fcd28fdf6e33778eea40fa1617edd7c14b0ab83986a93184a792db8dcb13c714ee9b55064a", + "0xaf295ccd0a125a3fede98976bc9c11f17618b466425cb6f8c329b146ef2f6809453fcb2a9cc75c3d15cf854cb9b22622", + "0xb301935e5a71ffa8c20db029805ca6eeacfb237abde4da92c8e0d830d488a174bf7c5c570271a791efc89563401ffa07", + "0x8a73dc79200dadf8e35650bddbd177448b6db8b202705f58c58dfd7eb18392cf184759bcec316444c5521dfcc52c81dd", + "0x8b863d8d093552e3dd1ee9c49a3bd7305494936733fc73c8d10c03931912701eadb1c8e3cc67d2dc213ad2536afe5a4d", + "0xb314747b63a41fd1b07fa307a5a4567075d0c53a92622df45f1eb9a21f57947df68d9875cf45d8aa25cdc24642fa113e", + "0x8e24b7590376ade065ce223fd4bf2e9f679b0950e394678e1588bbe91163f4e70786a0d5af6f62d86cbaf502e102f7d9", + "0xb55c9136132eb7b05dd27c70a39507de4712c9d7e96fb9cab4f09ae9c3d8d0f22ba2bece8ac7c976171bf7558496eb47", + "0x88836ab00c77915c6d3755e1169fdeb5c971dc318cfaad1104f1a7d0ffd9d0d9edc5845469663e81875ec83f627158e7", + "0x94ee999f538ebc361bf593a751ec6899ec74cc859a5cae504ad4301cf23594360c4ab3bff0cbf9eee14ee1d28058d630", + "0xa8fa800e9aef8888d29a28cd13c55764ae3e786460f09bc6d094f0df58333a9b86cc27b9db962bdd354a195347a40f18", + "0x98a055b235de4be8f26e122ccdb694825395115c2daa3d0744a5ac118a39ef2603c097e1457630d243513dbb48e23ed8", + "0x862ca0d3a7610d5548327a55f75b3a2d67350209bc6efd6b3e11c19f328deb9d2a6d3535ba687f2fcc6d3b00205388cc", + "0x96f9864a35ed4ad8da10b088ed2064ecb953ad7f2f97dde88da1fafa012e7b42e5a22e8ebea59ce3135c33e09cda11a0", + "0x8537eedfd338cffe319b4f58ae27cba4465526d8276de4fe28acf43846088bc2c68c2739cd694856fc0f0b48593af705", + "0xb6aed851d041e0f4c5773a141d41ea3e1f2bb19a464bf7bf0b9f1fea73e95e93b6b0ce38bb6ab9dddf6d5f4c84cbca75", + "0xada3e9893be2ae3fe7d746091951eb1ea989d0960b8aee429aff83698490fe28823d46616c12ef8035ebf3b70103dc18", + "0xb98ffd2647e50c082b6af66a5bb1a90bc0f101f8970d95a230724942f5d3037b0ba7af515392b439a4994fa627ade52a", + "0x982f535c3c64a1ed7b23ad08cf12d4522c68cb3b0aa42c516260ea1d466184ef488c7ab9b6449b6daceadc4455a85c96", + "0x839303928a684a161dc4b98d0e6f0f4338cf226ee97a3239b85364730a358bd72d9792648bd30a6da8787930cc9ede04", + "0xb1caa03a2a9daffcf6bd44e8219a78c550632fd90b1c7a6e65d8b6aa10f54af71a174613908f996d05cf964bdc5d1547", + "0xa4aea8120b84c0c709aa3a40d7a34e83f9bfd789a883e87e9e8aa058f32ab4704d6c3b4017a465f5c1406e3ea9ee39f5", + "0xa876ebae34a7c58d3c26736ff5905d7d13f12c9639bc7e0296981e44b115a1744f2303933229b33ba6d744eb8de593b3", + "0xa50c34a997777c60def5d23d33ed9f6dc5fa81255959361a541063ee20b1d71ecb0e16181a8e9d37a7c3fe20937daded", + "0xa2c0125b6362262dc394860ae8fad2517adf45fb6e65fe37a01c58bf2d7aa4579834af4c6176a3e9b9e34a6b08fa40cf", + "0xa4432a0fda898888b82b7cc308d0a7071247a88eee12287e8d9a047fa10ef4e8bae39604d2729124f3cbd8075bf813d6", + "0x97aedf113fc47dfab86a23786e0baca3c30a5b39cbb691f1cb8fa1d1b985c0b8229aebb4be4365936f568dd3cf2fa61a", + "0xa3f541882232892e6636ca3a89f4485ed6d226fec3597c071aa84def4f6701eace635e089797850f6ce2d80c0b51f78f", + "0xb36ac79ce90811aed2954800e686c1ea1e23d40124aee4c6a8c12aba1f74ef9077f253b5e5298935090dd866da10db7e", + "0x93c765906154b852e45f891203a02c8c66b5a4d69e4a719f43bcd67f678bf163275ac391805b079a820e1768192f32ee", + "0x947b8270c1d24f1c2d5c365f0a8969de1e8f41fed7a248ee252dc2a7dd31b0463eef44126ac2cf407610ea4f5b47396f", + "0xa6a00884fd273fd306eb31b132f507bfa6acca82329321ba35bb73ccf2d7ec1d85b88f0e0eaff5bcae180177d4d1f0ad", + "0x8adc89f38cce0116308ceb40501b469b66388e030ad0bc797686110b86865d0527b96f1d314b43557c5f8253e7949bac", + "0x80a4bd4c9f350bb6193454baec4410536d7bb36aec7476ed0dfbfc41bc459a23932442b02ed8e0c8cb8de8960d75444c", + "0xa4322d8a74e031a43fdbc5eb53db93b8e403fda4a6a157411af99bcb343c7e6f923a01eb1bbde41e955c8c4dbcbfb323", + "0x8b9ecd8210d115202ef63848cf6a317a412c2e1762b9afb4b16ed4fd12219c5faad8c2feb1cc50811d2e9db5149b2582", + "0x81c6f8337063428a463fffe3b8948661d6932324ff523ffedb31c544a64cee25e1cdc46baf43eddbab01018d2a3ba4a9", + "0xa718085adc0f22d763ab997bb4116bcddbedb59dfdcf5e7e7001fef48d6546b170fffd411a7570765c9a9573ce3d24a0", + "0x82d004982f5d0d6c43e8b6dacaaacee8d8b63b785884b245af9fc545abfcf1d8262ad868e8b07b2fc9d6f0b4e84a7288", + "0xad6098c8f2a56874d95750937c7adfc119e7ff211127fb2b57a0e6e7ac380e4087a7368689c231b670e4d035005d2645", + "0xaba84e24ad2dbeacc92c63127171cd5f7947dd3bd130d1139911533293c18a541b7a5af7d590be0983745e56d7dc1bfa", + "0xa02dfffbae59294c4fd3de4d9084c5fd9b0e1df93f06d0a43fddee8b3ac136e756352f4194b4115a3eaef06eb6ebf928", + "0x93101f9a44ecbc18dd3a509c698b6359b62bbffc0f1a30c181ba2228fe1e3bafcf42f34f3d8643da759a6af7d80492d0", + "0xb51137360234a4b9b53b4d90de4b369f5c97248a9b403b3e5305e9a88b3705446ac456becf4816c87fd952752c0d9998", + "0xaa9a248d5ccce52c83c94feb270bf3579f8d84c0113355fa63d127ec6d3a8914505fb921e4036aa1eaccd8ff73d6e286", + "0xa063cae166b07510e8937fdea5be9141b004d8cf2fa6fba811d8cd3160f4733682d5f3a67d0181ecf54664b2931f7608", + "0xaf148d446190f2012bbd8148dca340571d35e972435946efc276e159c23655c33692f77c49ef010a12d9e74120736b6b", + "0x8447b4128d30230c9890a9a573fbcae3b78ad784d6417e4bb2ded40260ea538950e4c7c95305217e008569eba5c94c7a", + "0xa3ff8b986ff54e6c9d9f45e9e5b4ac50cffc12ded52bc25597af5da3c7deb18c71c91c337e38738a52248fe2b13dfbca", + "0xa3b8c1e64ee9753f5dd0f67efaa03f781be3b777fdf040963398faa3e7aa95959affdf3245987fa993c4bff4920227f5", + "0xb76168782ba94dfa53e988b3ea6c05366f82a9f8781385441fb786d5dd8c767b31dfd91f16315a2b6cdda8723f7183fc", + "0xa551c235c2c282b653172a260fd3d06a4cd3f2ac9271a4c2d21b3f0739fe17bef1fc36a2207cb6a777ea0f7879b3ff5e", + "0xa8b34e99a1683c86018f7821f3a68166d9f2cd22e026bfee64e0c1edf7245f30b35eb8a579232afd1be9d361c92771bf", + "0x96b6a45a08f9d3213c9704581d215d52ebff46e10cb3dd23581cef2ff7d30df601b5e7c4566def3c15d4304a266c2e62", + "0xa95432cae9103a835db17b6314dbef274c413ed82125cd9beb6346e6f989513f3ce7f3ea175d280a1d61322357406e1f", + "0xb58ab36c6ea37c9ec8761486129f8ec9d31e2ad9cc9b9367281838c77c45131af1d8b3e9a9f3c0aa2df413b7041a9c6e", + "0xb0f0f57745855f35e0d7d12773150926e0b07ef4c6463cfeb6a5fe66a9131ad8da2753548a989ba9202d0c5031c16eb3", + "0xac92a18c1403be8b6249e6bc2376e3cf00d404c84d1b94e048d82a49bdb66734394baff02c50d9d5414df80775a6decf", + "0x8d3b00387f83fd3389b60392b4cbb77c438a42195d9b0a4e376a1d316e87e5be28acdfc2bb3eee5ec40c40fc628eae19", + "0xa4235aeed687a28e2e068eb8072bb03ff52ec7d239ca5ca177876792895d351b5a3b3e8c58547cc75ec2c6582bfbeaed", + "0xa83844c8da7dcc730dd3fc65843b63b1183a7321683ab92fd193f9b2d1308e8129e11be7d52d9279c1e5f8269bc1fc11", + "0xa3a405b3d6c55dc7d5e6b760c8101d1c454f744552e32dc62cb164362985a076471170209aacaef6ee015954c2fd15a5", + "0x841f60da0656cc613d6296fc2b878da09743b4bb4ad22f08e068462642f028daa85a02b85b460e1492a905ab23fa122d", + "0x8ebcb0b14c2049fe1254324a25842d4f1b5ba4465278cde765858814b97c79f69992fc0300319686f3f6240f63bdf87d", + "0x865391de7bf50b6458f33e00d53e4527c39ef29d4fd693d4f0c1b4831eab688aca3579c8739729915ff94b975e615c7a", + "0x8063b428f8ce96a5a7f85b69f2936c446b8b7e6dba27d9eabe72a1890230f803d9b915eae643cf2a2ae2e59efec60528", + "0xa7585968658d022bd4f33d95e92838b2c0145b3201540bd715f60ad8ece384a79f0ea103b09a05d82dc3eb942371fad0", + "0xb666fa67ac3001cc36d6cf50b6f1c6350afea5e323e514e9607f4b50500d1f801002c7206bcac845c87e21f460ee9caf", + "0xb431ecf6d836c9104404790b300ce650dc279776233b976fc601fd8b7bfc7ec97fc54cfe155995a95da67098a46e49db", + "0xabe538b6512d7ddaad780eb2e2b3899d9e6ea0551b2ed4ab9b65007cdca7e4f3e45ad5d1a9ce2b2ed684ce005ad92b23", + "0x800e0abdc1ee410e057426fa6c6540c0cf02f9e14a1c38af922ed089b7ce15a63ff5bef7ad4da381ddff72ce1dee4d8a", + "0xb10246cdaa159e3bc5c5b37673706e7a627ff7405c72e2f3406184ed172f007880f1fe1693a6fd1d80123059f953bbb3", + "0x8d9708fb69e2d64d160cfdeeaa45890e498c59d76a9a3adf2c7107ef62ee88bae786c8e4a43d5c79ca58e8c6cd52ec7b", + "0x8ce5d106ce2b40aa04f399339deb10dcca2b6d7a950a08f7b733704b8ac91796684d3887ef6beaedc32b7e2ad281e5d0", + "0xa4b1fd5b06aa24f9c072bbde3916bf5a57903afe4ea41498c0ff9b66b77a93c3030ef8c7d403bfb091f1730acf4e1aeb", + "0x8a453ed48aaf8460475474e588a1f6eb1706fb278b7f0366f3b7bc5f7ca1d6095b9ccf13a833832cfea2a2c6d17db8a4", + "0x8fb3304fc56a58c3cfd16831344c9cb05221fa1634116f7f6cc98c2fc94bbfd2707c476b8feeafbfe9f779f67ab3607a", + "0x8124c62fb715a864bd365f38fbcc7f57dd914236cab9218258dbb23d774c65de25c65c1f6da1bfe2216f099139e53175", + "0x8f84314b8a98454c16c7b8cf3aa7ba8b8a128008e8fb7f3eac4b0fb23b7cea08e3222996a24e3a0ef0bef1df67dbf24d", + "0xb0205241d45d7261721992e13c1ef6909315c07abb1da96471ff57ad155ed91b30e00be491425a00c906e829a3b4be6e", + "0xad6fc334150c0659d49266595ecff6b662a334f3b6448322d2d4886531aa767dc504e6b3a1e5e4edacc6ceedc573fecd", + "0xae7f3b73a7aec7814c7406a567cdb71b866fde216452cb6f72da0dc4d94cebfc62d78e58553fe6938197bcc52278f03d", + "0xac962b117645d0ccd03a1e93066f11b4bbe8c429235de0d64ad78adb24afbafd604624e91556a2d345ac119ec8163efa", + "0xb07b1370bc6a77dc78f07976209d248506946430e70025adad39121c91791821a13efbd6d262f74850ecbd0bc11d1d13", + "0x90b105ac138bc522ba79e6633bacc31e121b355baebef784f8f17a1cb8c9027c9174a0b87a07d1e01a7dbf3b82050c7d", + "0x94da80f5bc46ccb47f463399fcf7fe1ecfb32cc013092b435ce9b9e26ca171e23c40b4318c8d58b9a71c6fe7915ecd2a", + "0xaf606909b47fa48d83e7e8a02ee927acf1e51f221d6c3835a62feef4f3900732d5221755ecbfe50ce518f1f28fb4f6e5", + "0x8fbfd32d9bccc10deb0df271ff9b6ea55313ff09863d05188d8d5d757b346c5a9f95929cc64501b26a47f57fdf9fe9b4", + "0x92c9301cfdb8aaab03e72f2d58e1a78361e0e1c9ba1bfc95264ce8781a9865c80fb809f7781cda3e061649c2dcf5c9e1", + "0x8ef96f822cb4119b07314fa3282e5e68d2f917d161f38c37c6df0b4943e78a5110b96d6445ce897675f257e160bcb0cb", + "0xae5b18d9cdfdb88d75a287da065da489a35fcd95324317f21a68529ed22dbd08be07ff17cb5018c9e9baa3aa47ea1fce", + "0x85246b1150b7a72853d97938c557b3477fcaf23eddfd09dfbda63fceaf94e84e31175251f9f0fcd3d506ac1ce9d8bb2e", + "0x87cefb9b172d27b79fcd350198ef80d00e54e54a2c02e30e121744abc52db480fb1c99943ec35091889fd4150ba6d3c6", + "0xa97aa5173f3c1263b776f0aa11f342dea465d2ac366180de7b664f1ae45e0861188848709f37430db6ded2fa6e4e57e7", + "0xb3af02074486fc1a9ec3f5bb7c5053d44fbef762f8962954d4095366d7a46b6f566f41b22a5d02fd1eb14c19986b4df1", + "0xb4de669b811b8454fd1920b76cabf70fc2aa108b5751765ffb8043a429a223c32c56ad9a94d96b58e1ad5d8c83ffc26f", + "0x823fb6665f7ab3ed38af7be3f656071861f600ac063f7b1f8caa66eb1833c566f2ebcebc24742c6f93003d3342de2365", + "0x829fd6717d0b13b351d7ba58982ec43b956ae264f60e185e0d05f2f3079b13173d5c2454965559144999f7391260c926", + "0x87577c160e2642028b8ff1396edde6ca1364082990e5dce9c236892446c6dcf15d5215b81b78c1b8dcec55088270c54d", + "0x8dbdf313eb11fb321cfa75613251a44b3fc192882de86d7be45ce2cb0b2acc1862b8154e018e7c485d02d583d2a6c4a4", + "0xa5188ec1239c7a1658c368599bb999d2c685e3f42ed615c0b743c5fb261ed93d0806b12b3fdf153c97cc143ab96685b3", + "0x813e52787643b28a3aa4015011b81cfc035a5511dfbcb05412ea32e4bad47453c03163f710ebc4ccb32ddb19ccf1586c", + "0x9217cbec18727933a5d5588ef69c4658f4d9f8c99e814ba0edc8ed3f277476497bc058b1e98d1dfa6ae1d663c40fb626", + "0xb193f0badbdaf9835d48eadd4184a63c03d3696304b6910c856e321bc847b7f0c82c29c7522e23b28a70a60784e10a7b", + "0xab72c9a7c52e53def65f7bf4b5ed06f1ba57ea54c7b6ac7bcde025e0d829f741ef2d9dd5398b75208a6db83b53ca6d29", + "0x900ab429e5ee34aac354cc9ad73050d12e1afe9a2ee8fb8af2ca8dcb2ab4330afb6f3504431332ef368e397619c275ec", + "0xa71199d7174629295f8c7d6d3339ad461a3d6eeb8767fa613953930ed0638cffc399e65e3410f150d9f52e7937786085", + "0x87abf51a6fb535336ed92438cd269a404b5d7256f00a89ab8c4bfd1fcded5586ce92d7f47d8898e0a6fe3d2f3d04a11f", + "0x9941eddbbf1c458d61d1c62561440f8ed0f12ab9cffddbfe3e98e16185ea482dfaa90615e5c0f70f98ebcadaca286fa8", + "0xa30a6788992ce4de22f5c143ad8d04720c33f82c943053b24c031dc396999b581494c3fb90a234495a211fc73f928a4f", + "0xad9103aeb6b16316e04ab5a142259705f4994f4cc4f1d2f7fd9a731b5fa428866a452f514434e7cb8703f8fb9cdbcba3", + "0xb2472f84aef8d7b516602590463c5c5dd56bc5dd2ab26d3d387007189b04167087cf83a4d49eed426c0c334adaf66ab7", + "0xa005f153f8f4786d3ed18a1716498aa149a366e68d7b26308d1bdb7bcef0e096255c63f8b3f03b52e008c6400bb1393d", + "0xb411e9e9ff22226c7a1c0576555b600c53dc8a6f267788bd3a57d3e339ccc5323f05244695cc3c80882fbfdf805b7c01", + "0x8e900bb2211c8b88e720d435f82214697638399f73f2da6eae7a8e90cd67ef9e773600e0e01e5aaee16a27ed90a2fe66", + "0xaecc455b5581b69b18517d61645d7f50f9d7406f9570750bdcdf62e12f2f288d665e4a294da5e8306c8d68d2f77dad83", + "0x88b7c82658059cbea95434c3516f2db5810934881098a3b89bc9952c04a81a2b6cdda0ace9583cf55bd53585fb7d58be", + "0x8d83932cecaf2fb74bca5332cb11600481b0c86434fbc3eedb327a2c6dc9ef47eb162bb9c49cbf7a7146cc22133859c7", + "0x91ca01d15b0ddde00577e38b3ea5010d2d7a3440e47ffa50a7bb0aea01a0f36e224cb20558ae3dc4c5dd57ac80a0c895", + "0xa5bf146dfed3cef4cf1bd3d60ff28cfd3d2966742ab21fa96bf8d30dc24891ac95cd4408dca23b123ec7b937780e12db", + "0x889486bc1cef22199dc874cef25ac5cddedbef7c314c2bc3e335100e3c3d64543fa9b7f1e66cc324a2545f313eb2442c", + "0x96e82f48cba480539ecac1028a56570ce1979154789b5849c7d9988ea471173b2bc0e47848d3b8ce2f3a2f764da6bfea", + "0xb6a0438c7d7390b447a78beaeae313aedc904a9eb7b33982cfb66d53b9e64017bb38c1cfcb0bd76985a487836c80f662", + "0xb67f0a940482199c6ea25b4ae8ee611d9d09b43c0b664f7c51c025e65521546af942489810653c4f6d7104fc19beb44e", + "0x91aaa66035146f0f3a9c4a15e1704f89470fffcd8b8894e6ad71fd5e47b84bd5d32236de8d36dc0ebf6a1b059abe5145", + "0x81e65a88eb8c439e9bbc35efddea134080360c1ad1ca021ee3f5db77f34e6832b1898a3eb613d390b52c15c5e8f9a5c4", + "0xaa5ed2274dc721c70218652652558a63ac0b173095728e592e2e8cde837f0be4c1c417dd56c7279b87e17ef93d895b9d", + "0x92e28ac834c9ffbe7313d6ad54a0897a895e0da2cfebaf0e36317b4c80dee07d3a2f3e516307cab601769c6b8f0a0b89", + "0x86b53d20cc6ae6821b12cec65362e509fe1d8d03719557e100271bbd798693c8bb97deb37f2c38af29b5b1d3dfac5f31", + "0xa783e3910ec9f18a1dbeca90a0348e6b099338e16e0e2cb6813548768c4cc1b4b2d6ea6e77f4e5da9d7e38e9f356d80b", + "0x8ef12673fc3c30e61dc54412a33a4c575c3c6ef6cf7843ea335efc8289f4659fce695938fb7ce8f3e3087684ff5a8bb2", + "0xb72601e0379a87decc1e8df2af535fda35a7a112b9e0ab71405d3fc2f58ed90edfd541c04c600b08c6e16465336a95ba", + "0x8ff38f66b98189d2254d54bcfd5cf54a8b32d635a817af866ec1eb8ddde5728e198900f32cc45ae2e6f6b5ec8cffbada", + "0x9031489860e79f682cebc3928661eb14d93cee8a4e42f06ea1762010954d1a9d7ad65e910814fbcd5807c3ad1aa8a168", + "0xb37e54fa6af18e469a803c3e53eb6d3e39d36088a3a14f8147e53345400933e7310e1298fc73716bd51ee78021e7f2ce", + "0xaa409938bc038208db22b26d0d63fbf6ff3a37b88fafddb9272db02e0929f89648b1d47980017b77107468778273c061", + "0x8264c5f57b73acaa5b13dc181bd925d42d67b636ba01027ec175766faacee9367c2a3ce57717153b828b42c818487781", + "0x9884922c4e455f7cd37125b1426ebc64deed865b3fa6d6875e10a4b9a038f186ee26957f080dbc4c9bb8a5c8e05b80a7", + "0xb109e167288d3762626e6af1cefceb3e7083d4fcc30f1f5765138f59355762c0b08b34d6d9d39cee531384545e1e86e9", + "0xa6a53e732b85826f588b1d0c75219ca1cc92af82de209d59b30529dfbbfc3dd6ef9869ee44df84c62ca25d5d226955f7", + "0xb555b67f86f294359fe0315d97544e8f54d5396ef4dddc46871e7ee24a25db19fa73d6f3cbbc97872d7e68934e071989", + "0x8b7ebd372d29e0901163651f9509a7fabd48c469fd8177aeb5b1f8e57da89b8a96b41c9b7b8a986d7b6558eb1be1d995", + "0x8b17c5079b916a2c438ab3440a06ad27a815a4c558151b7aee8f701c1e99e599312900837dcc697d55dff48feb0cab30", + "0x8597cf58d0f3e818d78dbef1286c78e219df4da7169aa017093b628c21b32d78f4b47f0b8085b4490d7bbd5f491e9540", + "0xb69ca504a87089f7a358ac16a5a097513e5b7e1985936465ae62b73535a29115b86b82db0d855a8a1700ddbfb28b1fda", + "0x9041d37994bbdc199c8c0a26c12fa5dfb6443015f2722eb3954ad7c8aae135d23e3e8107640fe001604f4fef043bb853", + "0x86a9167012f6ee6e967d97f375e7c1c66cc8e8c8a235a3811325fd16ea4f7ea77f535bb89481d1a8ab9bc760832d2704", + "0x8aa37c477e50bc09e56efabf9223a76176661f81a1316338c1c322dae2a07f6e74759b1bebd59479feccdecf005b23a6", + "0xb9710e7ce7ba032ac430aad51ecdc2407aa411b41c0e9c53e46399728e5c1924bbf1541e0a6557d1f58850252a0b2ec4", + "0xa123b1081318ca4497d7f123a08b0e9e5ad552df1c5d4e8c4b774a8b1b549343c258dda8f84b673565829f3cb073f4fd", + "0x8a3bfe0db5b40aa6df27cf4a765ffd6922465bb05e1d657b4dbaa179671f3519d4cb94a53e16924c2643f0257d48838b", + "0xb9b7471c2a66cd9388369aa68f5ab2df3f12f4760083f6a20a909a3a9bb5d33553760047d45a1cae8c3229aedf52e9e6", + "0x8b89a1b12b2da244cf04d22745a9747c1ad59bfe57f3f2a2057a36c3042c212cd5ea87ac73c623e92e36b3a478649582", + "0xb23b48409c4d6ae8cca09ea0e3eafa06baf0505d828ea4977080d756d14635b9bd2988a85af2da5f4cd719359ff215d5", + "0xab68ae357a8b2fe88df4a23aec2134e35aadb6ea6725ec0fef93d609b240b1bd3b43ecec971d6617a7c355d0d317b362", + "0x87134e7a179637c6e17ea9613f0b3dbafa2ae31dba1908a09ca51bbf9eff1d08b4bd48686d6707bd79e4dd0d97e3d704", + "0xaf2cadebe7b1577c3f9c8c15ebb82edbb2866d3708ccdd4b4362e543428b3f2905ee93a89eddb92bcc30967faf26a8d0", + "0xb5eca9565e4577994f7e5017a7259f802a0c68d65171d34592263eac61a118bb0e2ebc96236c95c685de54e41076e35f", + "0x86527e94fe11850fbe31ad5a9c79b6f40d113dfdd4e25b1b6dc81d996a7a99a0ad78c2fb07b4c9e72dbf50943069253b", + "0x90f65cdb040411a9c9206b92f7bd2641c75052228b9c694816be35ea8086b75c3c9abde1ea301763e6a0a749bd2ded27", + "0xa33e42e2c3a06bcc1ab8cfc6d52c3f5a08e8656adb8e5b10e9c25d6bd0c7dd8e8fab18e6fb87d57c21c90008addbbbac", + "0xb34be5a7f6e5403ca1f966dd840b08cbc8b147d77f3b7d4a8f3c230ebde1c9c0c2f069c228e4760eee46a5211605b060", + "0xab94d75256360a7f7db252d6abfef3894143c9f3baa3fb61cebaed3c08d06cc382a7b366c8c15ff224028b9a7661a30e", + "0x8c1cc7ee871ddf44e99674ba577214860fc2bc3f0feccc0bc19bbc44f1749fe788dd8458af99857491c9eb60292971ba", + "0x8b7fa45a8bcd3db7d860a15bc6338a01e2af8e1543fe48c3a1458a61cec08f4881aca9c654c79b746885181098e08aea", + "0x87a82b024248f9613dc607aa30f4cbda1aebbdd9ac436b133687d4c6bccef56d611d5329ca19d6c2a554517c7fa39d55", + "0xb2f283ffdf2b86bf160ac7cf4f0b42b1e842da6873fcb1a85b411b742d9932018cb526c2e388ddc8d7d354cc1b969bd1", + "0x976bad2f2cad75c135d34a57612c778278682965e02d78061dca56dcab964d7cdca241611df9c0b1f4dd7d6703f55bde", + "0x97af8bc43f47589e3da83cb757b3b4a88ac5d980302889aea32b7229f844f88aecccc1d20dd279e49b4716b3ec8d80af", + "0xa8aacdf0caccf15aa3752d7c64e56b6a6667ac214fe2323610d6fbefedff4dbfc7e32bdb9d93133a44fd99fc6a82b9c9", + "0xb655ecca9ae8f661dfd6efa675eb7fbc85a1984f5a417eaa87d485b4d8e370c8f921f602cf49447d377e1db50848ef36", + "0xac4ecde11618be2746d03a61b2a55535aa63d85cdd5bc432b2cbf41cf9cb3e956bc53643302a505d3127e25ed5d849ef", + "0x8a8fa5fd88d94b345187fb003584c57130cd4266be426a1fa515990cf342b1e86d0139b4d4b1b0d444f88937b52a2414", + "0x876dfb0a348fb6d61873e098bb7aaf4a97dc92c0515d91a667a626076b91b6c88ab0404192fcb9a5ddcea9f2ff6428d3", + "0x970f2c1935ae0184790eac91b2f265d323e5be657100cf5e9da387872185d73e40855a41b0c337db9c14685a7c3ce898", + "0xb13e72760bc07032f434a12de9c8bd4d83cf1d00405c474cf165a557b332c9f625a7870ad199efd346f24585ae71303a", + "0xa729af84a67da301b477e36bad8f1276d07905fe933bdda33693172808624531baf52bef6da60fd01bd70a5a54a4e2c3", + "0xa111bfc7dac48f35ec194c2e4e0513ae05c084c21a8a7ccdb53abd39c1b230280cd3a5b6eb231cf788e8709bb9e7bd66", + "0x9353ad3cf723cb702c6f1bc97594c4e89a4a1c0b8e974bae0d0687ae5a31f1e62dc3e86e8699503633e74259acdc1d61", + "0xa454fd1f90a71fe3da2443741783004ee26ab8dd482f745fca09ef01413c2f942f45237c54c29048cff110344ee4bc5f", + "0x93e353fa25c5b238ac814ca6effbf0f38b463d501302703a9e06e2923ffa7d3affe3d22b31704f4690a08743c3c61141", + "0x80dded492aa7862f1d0f4cc2139ec035d954c2a5903c70051e6d1156845607ec3fdae48429adb317a0dbc168ba44c919", + "0x8baa57a90de15762363b7f975eec7b86eb24108e8b098f6435077dd97f5af03cf37e64ab181052b02077b7f729f10b17", + "0x9843fd5e8929080dbbef034d24cea921d471a4bfc36cfc339e838087664b2fd47899728931978782f935f7eedf2cc4d3", + "0xb78afcd1839cf765260a298fb71749b91fc5637bb5d5324d47fde2ae16d0b5973f5d9c4940a70514f33c85b4fea0d014", + "0xb4b30072d0704c8d3d142e0f204676ecaa20f69ced743bd92a57844ee83ecc3209200cc7fe16e69bcea62e86a9df8b91", + "0x94d182b2368f9dcb80d71f5f1abaa8e3ce3a20d8fe47ea46a5111fbf4ab84f51d42d0e5d035b6dd8dd6b93bb20cb58e0", + "0x935a8af4c3d5135686a9c6f8bcbe96457ba77011c9cb79973689e6734e3ec6291d705326ae3352199587181e6248c9ac", + "0xa69912595e6776c8abc160acb43abf8570839efbab95d1a54af741ffd74b0f1674924dd062c950cd90ad2255a56d1ed8", + "0xa1651ebbc37e9e30141b303684eceaa67bdcff39071ac63abb5d7886be1ce78f7459e0e69bac3c214a83ac750aba19ea", + "0xb919a2548bfe28b04aff4efe2b885801c8f1840f2d94810173fa7b46e7554388f751adf3a6e3576ff87ec73fcc8f3334", + "0x8315212d95842c15c708e5a55acd7757d4f7e8a457c6ba8d6cf8e13388a8999b57cba56cf993d84beac4280bb76e9f94", + "0xab504b47550d1dd5050786899e41ce92b3b3555e61cf5613245f313b37f303f1f806e7175943764958741f3df178157b", + "0x9991fd09588644c724e486727a8757c7f876fa1018f391ff8c0d4cd7c9178c3187d9634f865574e783d61dab17f3f6e6", + "0xade71463d2bde8c538ff15539a99c0d0ce39de3222c8f5503585928440db7bb2cba658a0c14a350a58ab2bf1268d7245", + "0xaa9ff42200e9b62da5569747dfcae88546dcbff55100c856e811531585a972af42d2a6c86cf296230a5666f6b5b7ea13", + "0xb5bf092578c8eb2a613ed7fdf0b09c1b94236b93cb1cc959f3244778ae5ed02cae0d36efdf4d1e4a15706f127322393c", + "0xb511b21e0a0e24f5784aaabe233de1333ff3f4a15142edcba55c740a8309ee7354e84783efd36c5f21da4bdbda27bfc8", + "0x8186dc106d9712d3b962c324313f310ec49a172f3330e46cd99dd2290551bcfbcb52d14349bb0e71d17eada6722df105", + "0x86b3bcadd520b1f010d9d1189fee6c180ede2b5d3f0b6f7c060f0b77b651e51ba83fe5f055ff32ceb5eeda20d7b50e04", + "0xb7ef4ec4055747e1c7b765fd3b15967ae4971959d2d732c4003f6b566c08ea16501115c90d6133de918b2abd61cec7b8", + "0xa0ae1175b167cccbf0fc5c6de5c64ab4e6496f18938c3afb0f16745df950bd702c6891b5e041c73d552e8bbad78e0b6d", + "0xb9c82b2c178ceff22ab580b8ccfab964148514deaf85325dc84e2ee455e4855b0567cb1569e6c29a85b9985b601f78f0", + "0x807cfb23763ae7e9367eaa2a829a51fcc4fd7786f4625085a78d79c1a360133a64ded147f771336427d8e58e8c66cc8e", + "0x89806fd11a46441e39e18b3dff5ed885474cedd5fcd2ac5790a83baadf8ffc8a8725f30b3b3f9af7af41bc0692f4e336", + "0x83856799ce1e800625241f62c36892841534b608064b717e4ecdf0477c505a8990c75c927bf597ac6f80a94b6f3a05b5", + "0xad835fc56939cdf12532e8888937cb51064b97a09d699b7a18236e0ec4cb312308cad27255a3015bc65a3c65f3846462", + "0xae664d2d7eb4c5ea1f2e5a4b1259132b9f07c68167b4c9284d3d36947493c36617f3eb576c83f0498f63fece5ce58c97", + "0x82e9fe7bb899afc9ddae3285af27642d6c567a53663e84c82c6a5e560d8d2ec7651a116b11cdbd3f7c9e7acaea59d0d9", + "0xafc5c26f4abc5002d334daa99d9708e6a7bee307ccaecd3a570d22bfedf6dec1beb6ce23cc4e4a1d966329e809112f98", + "0xa5be55450bf6ca8795d2a284110f975ed1e9776263d8fd36d82659b570b07cb28ee360f5e556b999f90bfde3784a6210", + "0xb32259b5f4a5da61b9936fc6e5de4a5922c34de1047fa57f9fa6451e1d65c561db063e8d94b9362e7fa392cda5427540", + "0x93d2ac2413c0a029b2c2863752791f18ffd201d6bb6d8e1b4256cf08d68869a29623053902b9db367a5b71339e810c6d", + "0x84ee7bd25798e5a5de21678d5da7ceb3703e25a6a68cc607c8fc4841b5ac14fa20f02d2cf65e1339651b59e429ec1074", + "0xa945cc43e3970e724bb3ffa5eab3b4809e08b2100075760a591fea7dc51daedbd10c98386f3fc2a24375023a676d7930", + "0x8dc00eb4a8f1d18f6d8f958f3cfd438124b7eb5d3d003199694ffba97845d8e62cac174ce82b832f8400748947fe7224", + "0xab538f4621997b8b08d34d4264d11c2b879ca8af1b4524af8df6bbe20de16e03310f34e621572a74fa55651177f6473b", + "0x8744defb8314926a4a851391d78a48bf5df4e69b8b0bae5028a10fedb1ddd5a1811681161794aecf38e78bb0e7199cf2", + "0xa8486b8e8e9701925773582bdbf106177e8a0dbddc8e3ebf0b14d09b9a3ee07ef935e83f948896670614432d3497414f", + "0xa50febe95ec6edf1ee958d3d3d2249725c0c4558219e9ddb7a7513a445905a710e38e7f56034e79dab0b0f8a5ef9e70e", + "0x8d6300788747832a681b919213adb44a2055afe5a8766204c7debd91f05b1166689fae834e42543d00757015e8d911e9", + "0xa5f8592240e3c253c7be1ef70fcc4c6d31f8eb7143cd93256dcf8f2960c3aa3ade00623204d51c72e6d5659958bb32b4", + "0xb0bc11d5e9506708d1b9e9cc3f74d7eb1a4d95a59b14f1394f04cc93d9a4ae34dc9ba9f92212e75b96c8c7f7319a1fe8", + "0xb67c045421be58ad656ee941acbe48b6d3467344126288dc1680529d9d89f8bd31e98fe941132550ed79c24e07c1c0a4", + "0xac5623b20b9d31532ce84a28343cbab4110ecd1192bcc4d6613b2ee238fe2b83290961d722ed7724a14f336791cee26b", + "0x80b24f8e92ce4a6cd6440cd312bbc0cff7554963e979503520175d1e1768c6d707a502c2f62be8c8ba9733fecccb49e3", + "0xafa2a03e6ccea4a1b61d0199ddc1c213495671d92f79eb650a2e3f037bfdcf20096dfe6956e8cd3ec4a290960a56e676", + "0xa257ab8ba18abe8963b5b047b602e53147c2a6738822f4c0cb4d58e7181a2254be7af024ec10d5d24fa8fb4089142a5d", + "0x91b4620f42dd4cd51012c586bf4cab57925880439837c97cc44c3afc8ee60abe4bf7321434b8cded8467b0d1b0d24052", + "0xa8dcde743d41f76e420769a89c3288018453891d84308519fe9f9698682604420c12892fdef7adaceb88b346734930b2", + "0xac1f70fee64863964576971c4b05271d3f44025c1145bde67376fe1c6670598589828f8b38b7eb4e859c7f9c8de7ae4e", + "0x953c8580870faff515f1fb3a5c1f64bd30cb20f91ef8de5c2cf034c9fc62f88c40a0ad9ea27663f9d556c909fe56b590", + "0xa91222cac4dcbda3ab71f52d9b72ca72720f84b57f7b28fd3a1e37790d5ecf0e00bc6ad59a011608c623cfe6dc12ed37", + "0xa11302489e82dda755f974327d158c208b7f016ffccd3a3fcecdc5e374fe5e7fc5cff242348da392a1f7ef4e4c3c6056", + "0xa65051205aa8904772c7da4efe408efb0d016134975d9de2099520d95f162943cbb1a067ab490180db548cd45ffa1854", + "0xb0c12e779a930f45e1127dfdd0f35b219320231dada7ab2747f9ab267cddb97e3df197736012f23d648410d08c14cd9f", + "0xb294c595982bdb94f3265bcc3da222c2f7bdeca939d3594cb0f38a454aa680e5dc31e6550f45e6b306ba21f9a540cf1d", + "0x8875c4f36c347c6be3821374d54ff36c9dc40e97f0498f358ce2844f23b4d19d4c311a3c4c889a56fea7cd7b03290aa9", + "0x95d9206e08181ade793f1c74f8c909cf19e20b71c4597b1192443bc5d32e50f008212308560d9c6309d5f58b52efc41a", + "0x947ce83b12aac9a86d5d7df5d24e3992aa8718efb93c5965d68b033763e3198050eeda9b16e79ef665f5e3d09055e457", + "0x871db19ddb8bf8b94b75b5d2a49b3109ff166a1e1e850e864a0b835826c7e48780f3d7eecfc703538be9aaae65a880bc", + "0xa082c249a380ca95da443ee133d1cfbe61b6b2bcb2f18b758898dc8c28547bd6782c5055db06d300e39139ce4326773d", + "0xb951cbb9ffba1e9184fdf909f6977801ab5abb099f32ae96fe9449946a4eddca114838b552514f8932887121331be1ff", + "0x8be845687d6717274da04e39cb0775662f8cac0fb76c1bedc6c7b3eb0e62243f8f1097ff8763cb4c3d2568eee799074f", + "0x8822406ade3242f64af3f492058d17dbb3afaaabec6307781136ce884d0c6cbaccb67c609c1ec9a80f47db56e795f613", + "0x99a55de756b3332caf0421edbecc207ff156b41eaa4454b979d8c15cf907382339a3607ef9fdd249ee6cf8a9bb398026", + "0x81806cc41ed6a95583422130b9426c970c1cbac242abf98aef2eeba5729ae79f62d0a3c550db95a1e60b5f69865c9e1a", + "0x880dc074e6703e6eacc59d604382cde7663ba002194f030922fae29a6ed30d7d8c2c41acd1ed2acdb4e24e0d923d5760", + "0xb6c274da1e55dd6d4ec66683261ffb9393a07f15a8ea729909f67b2be6ab56bf64a8498bb19e0a01e4cf43ff5ee41ad8", + "0x9907a42e750c4ec7e15b9b57e85c7bfc68aa6d0ba9910a03b9fad6bee2a4365d64c0a8183bc36af983748b25acb8b002", + "0x9602a6b0174169e8111b9bc9b86701495fff4ed576d67862c96eb459cd53f4ae0d8d5b60480815d846c8273e7c7f2a89", + "0x82075bc887d00a011a656e87237f826189438f63b63e31e597ca3dd0bd2552d43c85cb96de1840f6928b3a99d10380f2", + "0xa6d7c8fde556dee7cb6bff709a4611d37868c84db630367275fc3248dbc43a7f2c45f123f3a6d1d585df4b29017d4103", + "0xa2dd9060fb81ca89892ea8c89657e0634e1ff07de33eedb0ee2a65e2525ec47a5c550fc3219de4eacc4ede6be0919225", + "0xb26470ff3addccbc918ccb6976b18bc3544ff9db0a4fb1a134a8d6e487acdca599fa3204269267d5e9b0e52a2171de3d", + "0x97097d15868ee4bdde938f3f07c8e620a3eb24c2c0de05dc7c6eea953cfe9f0764c2ecae56dd2f4cd425cce211e0173b", + "0x814fa154ae69ebc08637ea6986c5b1ff762373bb79f89a3d3c988d46d789c7be77ad2c3a7293dc537df69ffcdb0ea40c", + "0xa828aa4ea70436af4a4eaff62220408eb36ab4210e5164b3bf12fef3961ea48511132f7c2bf968375d4e717ed070a2d4", + "0xb2ef53e51dff9a84055453edcebc38b969eba1e96dc7321ee37ebe28c79cb8955c7c9a87e06932610aa28657941f23dc", + "0x9760f5d9fea6fc5728705a4f24abbc123f4c43bccfbe3b3853605039b848149550ca7c9bf88a7d479cd7555d86a0e6dc", + "0x98a149c6ea5920b8d48927339cdeea2a5da7cde2032d02f76f34ccc7e39e064f6c0cf7fc4935b156de18f8223485e730", + "0xac83c934b6f2230844b854eee7d3e1892bed68b104f6a27da03587f00c80f39a1bc7cf0d2e903781c4fa93df0ecef2d6", + "0x8e30812b376cdd538a423226f92bce906db8bf8d7d5bdfd3fd791023609b83389acc4fb44bf8eddcade3ace76e5bc9e6", + "0x956e20deb25eaebd3db141322541444aceedac5630c48377a0e1605b54834912afedf69efe14bd5cea8d0c8a16ec39ec", + "0x8ca449b35b3e02ba3ba1d06acaf8374909e5e31aac1a704c963296b819e4127f2781a24fc3ab4130802ae9c3c71138a3", + "0x8b2cf1a9f075f4732e70af20be50c60ebbbaa12e45fe5dad33e270f68ed31e63911d45b5b2182930cd3599ad1177aec9", + "0x8a5c40d0f2a35f76b282ac2a57c946f29c6816162c350da34da965136c342be811f0470f00f0ddd2bcb71c7652544325", + "0xb9f7957ce4a71e6628fdde382f2fc918118ccc7214b4cfb564870d02e989a759ec38568b7af2972aa124a489bbe6cc75", + "0x8f934d40fd4e0038aa8d5a7b6d2a70d313f682292b1406215b530d7dab730938f8b4707ec47b1e99791b63d00ba6c6d2", + "0x8355cde0a3b0c004069afa04cafc77c78345e2616337228da85128e36e717011121a482c84327c011d4c9919b170f817", + "0xa868a9554efc93047605aa73418b1ea33e92759fa1adf107e756ae203821d5f0612b485633ef11ca02331e87e891b3a6", + "0xada077da7ce88318c17b660055125aba2ad6689c673cd78725cab954b2f05161b0b0c9b8a3d43054e73a0297dc8a0a12", + "0xb0bad49984debe1c546bdabeee83a14dd7b848726196feb9695b5052c24b78d001e1ec5782ebcfa7cd3979f38094ccc2", + "0xb4c200eb380d90ceb6db54eda8af1cfb602bec3a62eb357bcd100706c1a85fba5d8f0f196458db40f1cb782d2e97255a", + "0xb0e3aa045d0e85493c061355da2dbc520973d1efdb33f4aae7f88acacd562a9432d1fb2b2b1493b9cfb4427188d57477", + "0x82c4a4063759c7ca0ec1532b293344d9af74c3e03e40033d0f25220986cb78dd8d65e8b6c630655ba67cd5345206e8b8", + "0xb58bf4f0519cb6b075df76fbce31970e0232177b35bee581e7c4af3243f35516ffb3cfca1f80ab98f8c5bfc7cd9043ce", + "0x8e07cb448747c8ac6326ddd0ae1fcc7179015035d854bf9bb242e13a018ab533f807069091d6a219026aac2ed5abb4d3", + "0xb8caf0930923f408257c46ea586ae6107e48a00536dea1651867437a6bd3f2d7b4a222ab22d7831c797bfe8320260eb8", + "0x8fe7940a0bbe18f88f120783c435938222741478884f462e722bb2a56ac8764ee6e8305878e5223e43016eeda16452b9", + "0xa316935a98b4ef18a8259cfe3c733903b2fbf45f025ac4f5b159deb1c0e7d5c7885294486a62a81d616cb893cfab67ea", + "0x8455b919912f35110ac271784136a12273856686370d1eae8467e3016fa70e9e59b6f6cff0268fbf5c00a8a65610f233", + "0x8e238140a765bca1579d007b3d8d96279e462394ebbd1f53d44cbb31294d2939b834fce4da168e7f7c4f466feb79720f", + "0xafb9529129a38dfd9a36121051bffd6894cdd4e9d776e1d4fbd0d853a16e4e355c90248daf8ee1fdc424cdc2dec64f45", + "0xb9779f9178bf43ddbd9b8277a0e0a06a81b91fd16f19bedf201463cf3ba148e64670c4b86e36d3b227260aca0301e3d4", + "0xad7bd8d02047e068d78da2ae73589c353116a2dc8cd17ac3632b54e90621bd8ac8e663536015416c650a4b5116ffa090", + "0x83268f336ece3f481c9f4bd3b64323587f13554de4e0ff98c7db6c5d99ff85151f4d9c724e37b78b9caf1a7145091e8f", + "0x945c6d2dd50e844b4768f7c91fb9a5eb6b18efe1c0bddc47d73176c880cddcc4512c02ddb1259ba940d44fb4f1cca04b", + "0xaba23200af7a4bb64960f2370ea7142e96134bf7efe819f143d569342ba8d856466cba2ddedc52cac52eb51ee95f88d7", + "0xaf05deedc9a8e43e6b7f57f47109edf0c5b93b11d1caf86d29b7fe87d4ccb2116eeecf4fbd0f6ad734e93df712ee825b", + "0xb03bff40f70920416da408cf7ed43983ffdb95ef2c582566aa88cc10e88d901bfc0c06cfaecacc7418c0529db100fbf8", + "0x847fce7e82de7d7b8c5dceb1d68012fabcf3fbbe791070266c74ebf102c34cb59853108b806db29c3b1c8b81c078b7ab", + "0x9584fdc7c0af25fe844718198ead2ebd2d6e95a0fd4a1f3290c2d2d5ef4085cf43e026b06aa2c6ff7be9131dbf8dcff5", + "0xae64b820fd543ee83b897b32c60bcb33c23582be7209eec18241fd10335136bff84e0638633e7737cd3ff81276d2e3c7", + "0x8c43333352be119d3fd4471d6238bc5bde3e45dfaec29a78b8532529a88d7aaae991d16be599c73597099d1eb76e8145", + "0xa01ebffb8cdc696b93de9707eb309da8bf9ca5f510471a552628f7c7ac02eb2a4ca34bac608a4a72d63c4cbd5172c780", + "0xb61e4587f272911e8d0949edddd7b269408957bfd37967d1cc2288bdaf5accb25c64adbbc062f8ba8972f7c39054a404", + "0xb1a4f64cce740648021291a5d1ce4575d915a69ac34c2863e3fcff3c976af8f44a43c34c4d1c77302eab68af3a633fee", + "0xacf204d830012a3f79a5e340a98c55ccef657afc17cfb1135d82777c6c4674e492c283da34714d589b50113233f19f3d", + "0x96232ebc75f5e4218e13b3b9efdc4d43028c13c765243cb44b1ffaf3e0ffcf2c1cd8d82b799851ea1e7f74ba83f7eff0", + "0xb6b374910f2538570c4f9f77f02faf1e3a73b4f87c93524bf0d55a1feebabff9adf24356f1fa35bd5a8a01b44d8806b4", + "0x8aa7d2b492ad8e03c6a863023555a02a8487103bdb7c9e40e411ca10a525a3ca692bcb4e2fd7ab5c26e3ec8d2e307062", + "0x88db0a49a37c22b43ae50bf52872233e16169078474b0648738a3753745d9bbd5451fd2057b7df4a75d99884c3e74029", + "0xb1fdb6c4e91b686da75b5e1f537a09521bb622a4092e08b27ff32590a5d49b14f20776897eb516dc7eabb2eba7bf8f91", + "0x94f8705c4662b10f3b1b7d12f362fd1a34254d3ecc9f974480f55e83af0b6e5fe710a04668e14d21acbd15601107fa02", + "0x8f76bf512a7f088dd910d703504390b771de0d37a7084ee787e0f84d148dd226fa403b7999f24a76e8402233e1fe0dd6", + "0x9709c3b6146412aa6512b4bc31ab79cdf15ddfaf5a7e16211ecca6dff37bdb176b70bf8e196119ba66f5b998a755ca9b", + "0x8638af65a383dabdd2eb5de57d38a7ed919ef330d3cf70cb184be41c0add142ba502961abf7db6e0f089cd777efebc92", + "0xb3e7161a2bc82d9d49ea25a2555793e843a8344cbe96205948f9e3617d777f9f486b1dfe01f06823a91902ef606863f3", + "0x9462dddf297e6dd0d809a322001f0a8ddf689a5cab2de48787d24f83ca462171b6432739c088005faee50d4e5bf2ec8a", + "0xb0258ef6691a3cb1013968dcc83da5f333c48a8a0770f30f29cd166a2a7806c41bb475bb31ed78fe9e6001a127e1cc7b", + "0x86b950f39c1fd98a9bcd9850965532023654b47a1d0559f09399dc1e53de92c14d9daf532181b6eab62e068203cddd94", + "0xa84a3812387adf81e54fe4e90c53eaf60452eaca2330b93c5add98323724fff1c3154a0c6e22e6973faf470dcb2ab608", + "0xb38fd28c8fbda90c729d4b8e5138f8e649a573dbcd6ae1757330123fa9decb9e5ea9a2752e4b2599f740f6c9d0d6633b", + "0xb8f8bd623ed45c003e98fee0c9ab45375ee4e036cca12747c47d73b69f185eb3b19312aff720b35da8b56d658fd85920", + "0x969c320b1b2dbaee074379f14bef683604ad1a77cfae99e92ad930c081cdc1ed346f41c61d0cdf6636da11aef1e88ea3", + "0xa99cfb40be427ffcb8dbe8406963eba77c7a93cdec8ae6e7359850b2a7dd23d952ec6dd740b25b2c63652fa1bc0285e4", + "0x92d43413c0400f8602018cf1601c10c4bf63bbdf984dd5239a6c87642e5077771e0ebaa267d0d26b9a2b698529741ca5", + "0xb2b74b3890eb7f9b424fb6cf30980dad69802560847273e86eaabeed188ce1d24518e2d13b41d9c3676d75248dc424ca", + "0xab039acfa2bc192647b471101af8e95c032b21861d5329e7506f6b4ffcb873738b15517565323faba077a2c4658140f0", + "0xaf221e7e741210076956d9c4a8a248f3bc091607a22a27bf9ab5da3fc3c1722a2623dd7f51199d0b8952c414e716510d", + "0xa89afc158832a23de1605d97612a3d0fbe8c1cbfa7290c302c6d466730e5cf87a13def2fd1e87e42154d9627f9b8cfc6", + "0xb1eb68e9ab68a8c101237d5b369d18b0114cab0484f9ffbb5f199cda28d1d09b751cd176b04ece57b4f9f2996a33a8e6", + "0x954f5bb0596e78da5353be6333cd7becb38dd0060bdd7d9c466bfb4046255615ae38c7399b8a9990af595b21e7556af9", + "0xadf83cc9753053768fb329293a34924fc37c00e7918ec487238cbf5e5b50013dff2bc1349e065849a6a2d6f63d47fe43", + "0xadf9146706f5c308bb963089178579436217b56569d6f176dc44f8e26e84e93857d1b8a2a98589616b7a272fd04218db", + "0xa131ff9be642b918d9595bb52956cf338a8e2db3840b96da02d8a23ef990e9e1b3a32ff25a2fd5ccd29440f4c898a1c5", + "0x95fddc2dd5a8a22a3ec3973a5cd7a8dfe7bf88037261eac254626e64752e3936e995cbc97718e9f6188b5600a115f04b", + "0x8f3c65e58751e1b77beb95a1826510058121bbcd5ea3c33436218686f358547b1957c32d383d97c3762646f659d406dd", + "0xb345f1989b83cc72c9b0154e7a49c9c2de051b333a2bc7440be8d47376a7f9fc63749274ba8d1284f0cf5b414bf59694", + "0xb8157840c7c1be0be7f4f71734d6e62c944769d5086ac9b5a23f633832747ddd56bf500a5f4d14615762f8b6b7752af2", + "0xa7ef0af3453c3e3f42fdf105c5350934ae7819ee834c044e59d6e462b01416a15d6ba4d0160d59cfea7be62d8b296e8e", + "0x8df53eaea12db42755474c9ccad7c6b8dd521d0900158ea7738204a0a229ee37f4ca3a7855d03471602b2f9898bf3df3", + "0x869a47b295373d671b9dcd570e032fe1abe1d6224e386c0db77edae9073cf306a97f959f823f821c004e84ff4b2b4da0", + "0xb9334cc70ca45011690ef8b9b1283461a8ce46345cce28bf75286c4dfe163429efb58d659a33e24517be0cd0aca6a07e", + "0x97c7692e11080b5eb901579b6556d6a9074d5b9dbfd27dd7dba46174a895b53618708baab6d87f3b2649964da50b2f44", + "0xae40209a756f24558b3fab1ae7d59f992a7aa57cb074a63ecceefb8cf739d306f1c8897f9ff01cdec9d101937976282b", + "0x9644be5944ffbfb702738a615560d68ff7a026c4312c24a995a386f14fd191a5941a201f46c5c177aafeb89ffe718737", + "0xab9ed677c52b0d23bf88dd70f02731c9b5b8e9806aec4a29b10783d4e10ebe7340bac88ce1f36b5b4060316b45d170b9", + "0xa957961110f11f09e3b352119b4c4cceeb603dd713abaaabe830c1fbe23411bc9d6746f976444b7206dd190418e7d2c0", + "0xab68bce8c4c169b3f6fb739d6f4def03a0624b916d85cba3dc093cb73689722ee7f6e576341fd52e3ef907752a6543e3", + "0x83c65602e0560b02032afa0e67d884e57f65783f4c539d32037dff0fa336fc2e3789dfa566be69c8b4212c61f9d8b212", + "0xa83921c63b8a50cc669b469a1b294ce0be9e00cd7fe0d984ab3d3bf7952a994f87f8c4c54adc7343a53d8095c73bae44", + "0x93790ce547f696380ac11be76f212fc07a3836253ea1b33c831333e87109f2a3de33bece90bbd69138959dab862902a8", + "0xa9d092298393ab53ee1ddb9f74756e47cda0cfabe97e12a0d0e8c15b2e5a152b785de5f3ab179d972d2f1451f7978ae1", + "0x8713c58ef7324d8bec1766e28af4a9b66ba02b1244efd4eb26b3f8690283a07cef7d47632a97946a6439ba93fa36b1c7", + "0x953c2fc9770bc7180fd729a357f1e5f00ff641a47b0114119de47e3eecc30fcdaa8a4a052892883bbb8ad844a0647e5f", + "0xa285f7389ce4f73b04420fba8c01d3c1da0c3585654d757b7232c355a68ec26b0c82d6b650cb4513013024a2dcd17016", + "0xb9dc957ff99d8d92de7d3eacc76acc7108fcf2907eb64efa35ba3f099fbe2fb31b6005dae9c0a1a705c841f74a228856", + "0xadd50602eadf1b137f3ec5c07951649805b8307b8a0378918430981703645fc61b1c6988fe2d58f6dd737e23b5900bda", + "0xb61bcbf4c516697fd390b756275ca220c165b21bb030d1fcd297c6327a11cab04a4bf5d521cf64cf9a53b8ab70c53b95", + "0x836c5bede440d119d3501753783a0da2f0b325f379e9c90f911496371c79d0736f0e11a5557427d5566760ab47465ee7", + "0x8afe89ea335e62243052ee65479187199d3a9db501972bc0dc29dc05a830127e9a1449caa8efa5251b0004f0cf01b41b", + "0xa5fe631f6f87293558d1cdfb1105eb036aac4a0c745b205f355b2f2d785a39ce9588331108e7e6792d32342d44053ca3", + "0xa13fbef8ed8381464d0bad9b3d85339437828426098511e5be7c6cf4e5f92a886fbbaebfecc38d435dac49426ad7f537", + "0x8bd2ff66f9ea57b1dd2d3c6e955c0b977c6f05ecd68c09baeedcb4e9add8200b919109d8b1d64dadcd477cfaae2f9eda", + "0xa0f8524e20d56705b4a8e432025c1bba6f5516ba01b135ad99595c03b3c4358ec92161accaaebc1e86824cd53830dc41", + "0xb7bd697e14ad0987f553e11a8b8f3528211ddee10668befeeb5cccffec285ff0d1aee708672ca02727f82cf3f39620ef", + "0xa23530df9069165ef701e8fee9091bdcf794bb4884ec0dba6075719466bf1a791d37145a49bae9abbb77f411153c5b9f", + "0xb0a5879d15aacab516d5771910ec135f29b668ab3a826b1b6bddfbcde6a266d4d2336522932c1dd22d3845faeec1a4d8", + "0x89cc7adc1cc8eb0aabe1ff7872077db9cb8f3b641151d36cf93be851a79d65f8fbd971dd67ab3b06880cb94c58d40b32", + "0x92e791cef424ef849251e0f5e6abe5b7e88a7eaeee431727403711b9b5fa99c56de5294e4def17a641e1277099e73d0d", + "0xa51ad0c9e27960b91eb7cd025e07e8600c62a9d936a6d12a99abafdd17c06d87b4170403da1fc9e4004ee400058cd871", + "0x8b8e26d1777797baced0b36274c59a1739427eaf3f1c63599dc769a3bb110c1a603aa93d9cb708c204fc689f36b25ee7", + "0x8e7dcbb1511e623d0f658cd4da6505911efa68393c35af8f98191f3d75886399761a9073b563192ee0355133df9b6aab", + "0xad1eaf7b5e65419974741763f084a85b906da87358dcc5785453b7489dc4bc33c961b2323634f0f4bd6102134eed0e68", + "0x87a1650abf3e6cab7a4762d5b7f762cfac84a9d6d0dd1f8358c4e6b7c85abbe48851529a34b42852d60e6cfcf50f3edb", + "0x83164a4a7a589202e129551c8e0ac92a0ac3081bb9210efec836d0d547692bcaacba2ab0adace4b2133f9cda0ac09442", + "0x84083b62e0398cffde31565dbf3ef5e2c25707cb0432e9693915a254bacb01baeacdf66a3c8d25bee8fd5a46c5646c3e", + "0xa32e01be5304be092d3e1fa4df5869ce75092c4bde7e0eedf25bd85d435f175884a68e3381e0bf5b164d82cb8d25618e", + "0xab98fdb816d91ada3c291ca71e493d92e2960cedebd26824f9781669167d76b6d1c2640fd2933b7e9b055ef568fa15b4", + "0xb72df50027b81034e516c9ec5915ce661198884a82cae2e094629ec42609836287de7fef4050303df70b8780d378830d", + "0x93a3892815fd83f82f5bee4b050b40fbf6327cdf3ce25880e15ba16c8fa1d7afc7a40f5abd5d00b8e8f8ca166cbf000c", + "0xa6bca74672eca12ee57981585c23b247c0504b33e15c789495bfd9dce9844afe47f0e81182f093da301b97ce53efa2b9", + "0x964dbec646bc2ce02b2ab25d3fb59ffbfe2432dcd2870ea875e95ae40ea4c31b1ef1eaf2e003cb667d60062dd43ef9e3", + "0x8f0c78ccc8769554f53b87e094332445366beee09335c576bcc58d7f49ad6fb13f41d8461abf50161541527b00818d17", + "0x9840b54a55f8e8f7b59d1e08f4d8bed08c9b1329cf80522a58c52adfadd1884e22010130e1a6f42a6537cca8e980fa8c", + "0x896096aa5426d21e1f35cf48e826df00e21802ef0e396347e5e5139f004e1c41ba670a773ff9d30c7252832604753a82", + "0x8a0a67478fafc005762a429be397969a1cfbe7ecd368a6bd4444a6b3a81fb01b8ca7ce5ad7e07b7aab2cd5b1db398734", + "0xa0af2efbe371a0183158a3fbb4b20072e55498dd6fb4ee9431b850ee481a4fb0f4dfa9df3700d3b55121bf92fb97eb80", + "0xb1d398bfd868e16a80987b7eaaa4b642edce87f9744935273b3a27217f906cccfcc2ca52565ae421268976c64313c38c", + "0xb8bde5eb1348814a333c6a6803a6a4dd70212d60492059d8564b79445eab78cebaa06d69a165c47ba685c6fbe3c2bd05", + "0xa0c0b3fe98e2fe34abf04e0a08e1323183564e9602ecacf693053ed1c18c565c12bb0699cc38fc69936b70a9396b728e", + "0x84a55be33826814c5fee485420a5b1dd3740861d351ae34fdf081e7a9c8f4c4417e889c49b42daa8a9f90b6b9c557e26", + "0x98230781024cc7a3a215ab91e2a75c6dded7a7ed96045c879bed92ea34118ebfd16f1278b8d76b5d5fd9315d93822eb2", + "0x98d29dd1d240be4fd6a877bc366feccefa455f9cd2997119dee7f388405421adf5bf4f77f4c2d7fca6798a8bad3b2add", + "0xa49c8d870b7f519a858e8352fbe30582c8745b96d17bcc4a11912deaa04a5de84251c6b6b8c2f26486f1ff5aa908c4e1", + "0xa9d1dfee15ba99c6df03e61abf2cd4cbe72c796f8953f15f93cffe286fb54181020f3e90e888bc5beae6294258952371", + "0x819c785f696079f37ac63534e1f513004e95fc9b2f15678d3de8700a9339564d0d7ad47ab2c75520a73d4e48da5d8a78", + "0x8ec90689453849cb7446d21db7bb23bd6c5743f6f43e94113a7bb79c614cdb01b50a4322bbb28c1c6844cd9568145147", + "0x8a4489a137ff8ff0a30c8f82b2bb694c47549612464aeee511ed44cf0104deccd12f3e1732641bdf98b23ea4827b966f", + "0xb54fac6ae639fb5f97239e36a0d85b3079bc71ab213d6750ded8a49e2aad8f003a8e8cf243df3cd2cd2e5a362084c5eb", + "0xb674f2cb100d8ca0836f74d2c7938c8b2fdb4bdbcc2427b410e7fd16b698113eea32a004d987e3b96570210d11518449", + "0x8de7f49a8f2f96dedeb25dcdb0b671f94efd42b37622ed63da69911fb167d2158395aae74b0bc51d1347b7f6020ee8b3", + "0xb5258ce4a43631c1f380beb60608f78ac40221779feaff785248d2958fb227034657d225d5dd8faae717e56f936168eb", + "0x87402648a8d89c4e0c71f539ed2b0f872e6b8eac68132f419a43e6a557161d09c91debd17dc2589939350ffba1598620", + "0xa41bee644f68ece8028f73f5a552298dc7a38f3054748923e35822a7cd2ae588b3c333f5cf17e225c7fe381126fa3043", + "0xaa41060910a3400162451ae06ceafda475db4e51a9994507b34b6d7ed8e252b3c1dc67eb1b95364204ece3610296bc01", + "0x8c44023b9d69c82f11000f6693aeb0eec9ef6a401ee90f9d041c84bbb17f52bc3619a6cc12dc825db4275df901127d65", + "0xa870d4ed4cc43b5a2654a9922eae8e756cd2b12e1beb6facc74d34aa7aa18fb92f719941fd9a0f15c48533c37f16b3b9", + "0xab33b3f3585fd649159bcbe27c476d937e97c32edb01e3558207e245965e8efbd3f81ce9d169eb182319883cd14458ee", + "0x812b0512b8584b15f29051029eda1fab230341205f3088943fcfb8fad7805a59b257e717b0f9c1d31ddc13aafad4f959", + "0x8143a22411ed1a1dbcae0a568aae96afb7aaefa2ee60553cb52392f80b55be4127cffb38f511649c19cfd784914ab84f", + "0x8224fdbd6f6ebaca1f177b0cc8bf8b7857471e4ceeedd5b204f559560a880409eb8e36d2b113a7ebf8a5cf38dc3223d0", + "0xa2aedd2168e8107265fbbd5a61b5b115f7dc85c8cf6ca7f037354d79c081be9319c5900632335d0991f50daa29ff84e5", + "0x8d5e72eed45d67a2fd572e142efcfcb8b3224ba8ad3319ed5511c9691525480aee9fb21f695d7aba3bab1bb8ca57ce11", + "0xa51ac57748449457c299accea399fbd1054414baa5809abe078ef4a6b435f941514c0cd7a67430b920886c4828722d97", + "0x88f8bd01a0c4e76ae71d67b0a50d7b09b5df75eab0332f1b631bae5e07037fd0b495eb3f82d32550649ab55aea75279e", + "0xb55ea217cf9b5cea9b94241a1d57475b6fa8c1e75652297b2c48b7627993555b82838712a3a2883d99290551c58f6048", + "0xb76442993b2964111fee647df39fa6943cf63f14e32a44635e2f16453e46cdb61f5dcc4a12f765e2d3ee2a571a63fef7", + "0xa3fed5b562db687b73e5108ec03416e193afd3ea61b8ea35d974622ad4f04fc1ca1a213ca42615e7f844b8d1b178480a", + "0x95b7267cb41de69de2f715ea6e684b6191187ee4eb6236bf47be13a84309384955a48b9d45dc185882f1833d423d9972", + "0xb83b60a205428017f085b5db9de65839aab6e273b47b978486a8161875a09df27995060d2ea5a4c958657e9dd93aeba0", + "0x8d7644d8ca7f8a075f59202e60e9e1e2fda43352002310ff521f9bfc9ffff1676a040969631893392bc7a88386603789", + "0xad21332598d1b2f84ba897d8dab266b66a883cc7fb00f032cd224117414ad0d1e8daeb8a13d9c96c5da025cd111bf684", + "0x8decc47563c679ed7b36580d10ec85020b735f14b4d278d0441fed0bd61f27cae02bbcb77b8f24d4e50b84f5f671f86a", + "0xa2a8177d61417b09040570970609a762946e79f9c056a23464f03d4cd157b60a3bb169f56725b4e5beca434305d21267", + "0xa30f3ccf5da0607d03e04f517bee7ac687bf55d05b0c95ad15f2bd097c99e67384c7dfd9d84e7d5e66c8b60673c46ee0", + "0xb112a5f9679fe83188bb2f6d5e4b9ec025ebdc5fea15eede34f9b129cca66833ad638af831590bcb0b2ca01ef541c23b", + "0x8f493048e0e3866b18fd1d4ff595f445d024639fd23ce9dee02dd62f64039457686581b2f0654a09e3b5d9d1ed820248", + "0xae23181648cc4c10ff66a9f29c266984ecd2a794fe9d7ac0cc498f958f2b84c7562934863841bc4da02ab340318d4b18", + "0xa0d6a4dcb680177948681b11a498b6dd3c0ac89289ba145b48d1a222992e56e7822cc014d70c8866799f085fff0241e0", + "0xad9a88750a8fb9f3823e4638d7634fc284388a7246eb3c3f49efa2cfd0711e2cdd2391d8924f8a584652af3308fc6b94", + "0x8467c5c41ca539cef82d6bca7758b7fd947c2f6ca51abcc0847e58992f2f0ba18a30e4bd1464b954054e8a9d84360199", + "0xacbd2535ea3913d466148044940f374a4ee56500bd2ff175694c292ebdbb4ac9782a17727553428ad8f8287d4c00e58c", + "0xb09117044e4f484ead437ed0db7b9f7a503aaa5001604778e42feeae7960eebb063ce3233ce7a41f2dc5b8fc22684cd8", + "0xa540e08652436758e5c32af769d1cf9e04cd90744a94117660593212340afbf6e8fcf6358b092a0db42d45af13e0f85a", + "0x9588a6a6d59e0ff8f7bf13f7b76f0372fcc982abe08b28ca4c84719e06bc3a03bf4aa6e9c9392d8c140a51e0977774db", + "0x831477e96061527a2aa381a4ef4c9ec24e78c123e98028cfa61abb1a1ca2798562986a9ae43a25834fe12dd1ab5f016f", + "0xb31cfbefff8545622f3931ff527ea599eea41c2766d711f7fc6fe258a2c3344c2289c785fd36fee0f363a56fc560b10a", + "0x9273e54edc6114c3de63580c89e122efa99f135aca7de42c0684161916e69ee901101458ab64666b53bb3a0a1a0ff27d", + "0xacbe77506acdc98a37e8e2bbdd1e6d3083c96b2d6761e1659ec0ebf06c1cb1533b8420148732656c11ad46a66b924d20", + "0x819bde9c41c3d1e3efc350993737b341df482c778e921f3f1455ba8de62036844f0b6964ea6a66d769ccd64b9417abd0", + "0x991011f81e5cd7be2ea66279602e14d23ce49cd7e11aaa15b30edd578944793ede203f577990e50f2dbe4540f591590b", + "0xa57d90f2da80ed7d7db3d1828bc305b01f9c91da1edeedfd431e0e3a0da4e8b2b3fd87a5c1cc8806b1cff870a1eee62b", + "0x867f0761433333a96999bcba5ba33cb62c679089d9b34e233fbf03ae60df524917c9599b96d4a95d8a950b4974f8b03e", + "0x895f57c76180a8709f97f559407e2e056e7c1c98bb642ea7759581f078c2f8123189d4910e5e3a773fe96270aabba6f9", + "0xb408a95a7e75d4714243dd7dbc481ff447eca84bfbc2ef26cf5ce54e36c9dbff6a6c080e18bdf40144e267d6bb99be2b", + "0xb579feec90b9b3db37d2f30e6acebf31c8899ccaf3933ba0bf6cb89c75194b5f1f3d3893b9fd05dbd05aa00135fa2cf2", + "0xae24b0a3c600a38893a17707ceaf499167ccf8cac75cc3db5dd6bcb9fdd789a2f15e015802214ce95be8d37c9b756d5e", + "0x92683cc1dfa6f5f731e0b59bc92639822eb25f9805d43472c1249f3f53aaca3bc6efa605db106f40b78984979822ad20", + "0xa6d6f48b4aea397fef71aa292e645e6613fc13a799967845669645ee9021f73d37e0d5d5ce813befb501e2d0c6871795", + "0x8d1b9cd400310fb4abae241ba7f59628552ea4b29940240290a8c729cf7d4721bd0d35875bb52f63f9d5dc5c67cdb987", + "0xa5188984ae44d160ac60c654d611d68d68f40a331ac4166c37954a7af425f78ce60f973919d00ee9054974a91a942a8a", + "0xa9e7dec899152dcf16fdd3aaff5b8a2c11101e7815c7951d46685d61f9508b276f8321014a8c200f4c4d4ff8879f8bc5", + "0xb6f5a4aae5f9178aa2d41db18d2fac9a61be1e5fac2da77f7aebe719887d0f5cf7a53b00f4964cfb09ca00f5977bbcbe", + "0xa08fd53bce423807699932943018d488810cdbac134d7e35ebfbf989174484e83aa0c5bd987cfc342a7002980e05007d", + "0xb10f7a76ffe752c93da575818a5416900bd3725522f54dbc35761ba27c26625ba3a8a4abf3c3e63ce329bf569b3a6c4d", + "0xb1c6a53ff84011720e566a7dfc3786644816ace8b28636516df285687ed9373c7b6702f2c47d2ef675fea96629d73958", + "0x8b4dae3d427c996384d8019f3defcfe6d64dc36631059dba741faabbef1f045dfb4b616946b4c9b16302cf5b15cc0b2d", + "0xb28bcfc29858b38d6650278aa0287a66f44b3851266f1687719606785c8a4493df40d6fec6d50e71eabfa4eaaf90c837", + "0xaae6b97afef9ba5c1defba3d61ae340c1b550ed2d0a9c1080ff8a59e0dc499704e4fa3da178e87b3c51b17ded0ad8e0d", + "0x978a7cba2b6d825f3b0c6630454c6a1febdcc3aabc7ce58816abd1ee4c5d306837a20167e1998afa10ed68decbe5f83d", + "0xa39fef87424eb1e635a3c5d780f1375225b40e1a5a18dcc7dddde33eada74beddfcd7b57b46b22f97878ab165f57da89", + "0x90dce671b0938c1766774b6fd8f68325be0e3f00ee1845695ec6cbf6cab56fbb8e2768bd99e04bd66106b240d124a83c", + "0x99edaea575a39c6d827843a3dbbb125b913d8728d4ed873c16aed472db0b7cad1445351eddb9b663f2cad6c467ec28bd", + "0xacbb2d7e06aec71e84738a2f4d97138f772a74bd1207b2a1659d1da52494ddc33bced495ac0ef3b64c85d6d32f94d1cd", + "0xaa60f257ca677896cc7a1d9645f4889684b67e38bc553df24371c960dc6be63fd7cb82ca45d2093b1748d3ae82b0cfa4", + "0x8a2eca6df19a627d545663e9b7a9713b2b5f0abed567bace215b8b4a1952fb1473ce7c08b44aaa48ccee4f638f3e90ac", + "0x891b529238815625993ba455179302805baced0a794d4ce7b52dde93e0e35cbfa77228ed1ac404d46c688b9ced4a2d17", + "0x84943cdb4854cac741d765ffe716fa06ab1e3aca969e688da2fad8b0748f6afa8866dbb4556815a758278191da8975d0", + "0xb977405d6612031dd8547d27bab48d73bd000c013545abab6d0e53d89846a024a6a8ede6a3ac357372362052d3ac834f", + "0x851b6273fda8d8230cdcd8f5a7d0afb3cb5827a0735e7bc104c62111f208cc4e43ab237b186191d77f6217b0c74a5c2a", + "0x948f7ad0315463f49156f6352545120d17a39151edb1a2c67065fa38df7bbcd67402fad54681d3eb8091b2acedffcbfc", + "0xae48381d9c33c2cc9665e9d81c475e3ac7f211df7d3ae790f4acdd9c8fc4298d92772b29e132cffb0d5d92718cbb18fd", + "0x92a79b3098dd73e3b057bd09715287a7e0d539f154fb4fdb4707d20da390e85f424c93758ae2660351647b2973dc7a5e", + "0x90f616f46edcfc706c1c4b61fac3d51e1624846b077659ffea97343c8e38cad677642317ad90c4986e1739973d3e37e6", + "0xa99540ccfdf868db9cf4541fd07fbf0ea8e596b4f0ee4971b9462b4b647c8c684b29580e2d4b48a5c54ea74f41fe160f", + "0xb55d18f4ca33fe4087bc6b6bcf334ade46961eebf362c5072cd1037286ae8afe050c0485def1db7d52dd610a8e5e6bdc", + "0x8768a1b2f6a7325f5a72a9c78d77253765f1fa24574af0dbd3e128a652e2e6123c1e972c5da59156da18f1d6760f7ee4", + "0x95d6466a7cbf77f64a1178a1fd5ac5816f8d880a968be568d56e79c21d9ee1793ebb5b74f7e7343088194b60344bdec1", + "0x8955746f1d2db3b785332b1dec11141ac22c7e63bf63a27b2e12c36883402e668f345c01c6ccc355d472e875244a1a89", + "0xb441fe5c2fee669c77bd5581effe696cb7a7d1d9b90f2ef82a7bb37f6814ccf01587b117ce86e037931f5bf9570b83d2", + "0x979bf384443a310a06a23023d59633d32637364b6b2738a35e6c3d3b16af77de7c33db0e1ac8d40c65d44dd83109fe6c", + "0xa1395cc664c66e2abf9bbeb1d98beb6a93d0e65c6fd6db0b3f0a28ef540d8780d4c520d0f2fa9aad2e562f9e5249c617", + "0x8e776bf76c8698ec1b73cb03c0e2ab9f6d498adbeadb872cf18515a3ce254bbf7bf54b2150149ac1832501747fca68e3", + "0xad74b566c9a5f68d3a7e6f3da0478336e81f17935ebfe20be3a5fa2066f5bf7fa5a8ade8135614dc3f6e51d46fc6bb1b", + "0x93250f6451f1da62e8140b0c86f5010c10ea4d16de8257e3c766ba7505302728aa8e2e5c8c57dd0ae4d323bc8e43c871", + "0xaf055705da74a5d242be16e99d8ad07c447954c6aae0384cf3b0c988e9e0ac30ab100499d16bc2338e0066228dc07eb7", + "0xb3ce70f349adf558d8d60a1c11f44d7ef310fbdedef61ba6b75752da6ad43513bf2553f95e29e4f1476a97d6dabafba6", + "0xb5a8f052dd994bc87d0e562de026c44be1c0860a626615cd607a964830b363c79eb03ef696b9ec3df5788bd5accabced", + "0xa7571ed055a3ace905500a01352e7b232fc5eb907160786577d94bfd7358825b99d878d05bd7fe3348e2588d47d4da29", + "0xaa5411a09f9c8982712140b0f7d448d066a22369eb717b2fb137cf5abeeeb538a31851f909bf0b2cc1be9c71f10ddf10", + "0x9642b6b48b740678e7fbe2b43c1787a502f31e0f13b48265de8c3ebbb0efd430ff498d5dac511fc1a5611e7932520371", + "0x8981d501f849227593b02ab305c8c9f26c5c3af7a0055e05af03f9289d7f42d82366e2145cd7ca5f74c588fe861c3766", + "0x9848d40ee2b02a75416ed9edd0fe7bad502cc94058cdee1ed231125b01acf61875f96d43eda1f5df552a7656580110b8", + "0x8d22d1fe4ab2fe25bfd09b814d9166cc8cc4786e09c34fc8c31ef9069585466cdc2613a43138bc770f7a25d6692adaac", + "0xadfbc44822f59ff3459737128967ced055256fe06d7b7c82b2f0ee925972ae214459b9dd095d0fcbe6a4e1b469febf41", + "0x84b7c36f1e97c624ddb858a1ebaf47ec664453b4244c47d02ae77b9f8b47d3b36d4a79be23686fce59cfb6bd49d0bcb2", + "0x88a9c389810c6f1208df19a4b09729ef4486ffe81fd1b4a5c705a71180e7662f404eab49cf7b41e6a0a955db1dff68c1", + "0x92bc05ac2f452def9e0ba00432cc5b81ea2952a24abea9a78fadbe735ccd62b5511793c93b1869aa50780b0f00f8217a", + "0x982164882ae264778bb0649794982f1088eac0570a62c188cf77d2c54a87293d9ae88566a10fc5d03367176001c98080", + "0x8196c42694b27e4dca0be111c94da5a85822f34380682dea57e0016ff6902460409701755c432e48dfe1696b85838009", + "0xa7ec10d924f59df4452821899a85a3aa2e85906a6916ac2404059ff8762d0b63dca22b56c72e12d3bef8c27c1f1fbb7c", + "0x99339facf0ceb78aa7463d8636adaf3a09bba7fa51b2ed95c4486cb49ef07db3f0a3b6e7dab895fbe28cb22761e0eda4", + "0xacdf47ec75088942a4f3e9b22dec0974cc27b6c52a1e8d0e37267b59b1c8ca2b7cc1f7c5ddc1b2dc443ebe57f1895ece", + "0x8dc71ca50a3cb8d81e92682d323c31b3782edc0951e0a6ba11602d33acf451aedad47efa4a44eee97de20be3a6d35164", + "0x9926f99f6de7b973112dee2cf33c6d9d7d84f9a04c65dcf296b449b813bc609cf0d1914cb0dc55b9d7369b865c2ce9d7", + "0xa32c7ff29d935776fa91f0b4d2251af5f36e5e21ad151dee7c20aa2c90af077c5c4b7791c7c2b41ada3654a648acaddb", + "0x973912a2703199d6bd341acd1a19e118dbeab6c5392354b3c13751d8ba9de674b58aa222cc752a365665112399ca9039", + "0xb2d3947d07dc6223e03fb98be5e32b8d3095cc9487c91a3f227665db283b738ae79a9f68d7184a02a647c581bf0ef690", + "0x8d1a7d03fdc43be1d73176a1a67451b930cf60bfcbd8a60c51b53757763665fa33ada127780247761c3e09f930dbec4a", + "0x941311561393de8453e616de91216b1d52c8359a95f493d1e93f08a39d3afd900cbbc43adf4ce22c5d4a2b586e79a61e", + "0xadc095896c68d330902ad507a7feb866ac8af638d78316d9835e57da92c7adca47ebb632e7b5619eaf568b8aa411b479", + "0xaf9e4c44970caa6ae8e237e09d5116e6c19b584c722f393c5a61516a0f7949aa4fd1a2471f9565c7e8963e4c47fb45b6", + "0x84a8fca73501bf1d4d81fcf4e87a48aad4d8cf270e44b19b627fd1232cada0c62a5a872028ba987b63d20259e28c6943", + "0xb1a41541589f7beaeb74fbe4a587b7ac721f02a014cbe3a881733ce8d55191faa3ca89c2a8bed712efee16d4d61d85b7", + "0x94ccd65bf3ad8755324c5296dac3fa87e2a7158fd0e6efb7498e4fcfb7d380303a6fab01018aa34a45c44155d78be9bd", + "0x97034acecb3d47201f7447d285b4edd592b26122695b15037234e81fff55611f234ee9527f13eaa5817f8909eb49e42c", + "0xa94b945d4e6d32ee910659e00e03d0bd949444743f384fca156be8352818a2030f3c126cdc5dae1753420445c864a8a8", + "0x9243796da3732a94fdb4c334549268b577c25ce710a4859d089b669efaba957de62288ba5ba24da44e4000638fb5d4ca", + "0xb42aa88dae10159343d12b3d0a86c060ba4d7b426c3b2c6f5f165aaaf2a7c24287d4d407b4945d6dc5eceb0154ed4522", + "0x86061f7458eaddd85fb670ad55cd7a6572cc734c14cdc9da0e5528a51d0d46ce290a7b02cb090b1e1ecbdfe36f7a01e8", + "0x8858bd1871ecf4bfdae67135ce4e59d394fad65f94bf5c179f2405369810fe26bf157c66418c3142ef8ec578e1c83b0f", + "0x80ae0f57f99089fa177ae197e38d33f0fb81c22d572c2c448ab619ddbea1db58b0d53ac203909b3a79d9dcae43aeebc9", + "0x8f4a9a9b710ce5cd5fd48adf38fdf41bdb22f2aaf34f00f441bbfec29d63e4334e6adcbc024ea9ee327f4937b7b2259f", + "0x8b166db448c676e681af81d20e68a89cdd3da72277c73cf4e69377c48940f0454dab2187058874e864bc9cda6907cd1c", + "0xb0d0cce23e1d26239265741fd13d2dfe8a3704139aec344571521c393bbe61c91a4f5590785d844def7111052630232d", + "0x838c151220013527c401c9a072231bd520ab17dbeeb96c95f4429cedfd86b57181cff8a68c02620e74932dbf95bbfa98", + "0xb22a1a60414be0ef7cd77cdfaa703b881e734aaae165f83b9ffec4359b683fb50234b32e3fe5ff384bb7d2412ad63644", + "0x884fe13672c0cb6f63b06fcf89a2ff7fe6f90d7056767ccf26d28809594c0d727c96d039d8b68286866b57abcc37db2b", + "0xab57b933a2c8e7f45da8739a45c2f8f7fd4fae3287c5f9050b1b4cdb61c8af56b9b4afdecd8fde405ad175d14d89d79b", + "0xa922c6fc0991480dbd21bb2fa311fcc4705e94bd7f61a1bcf87bd28ce9ef5b2757ead235ee6cf77d6560a16638f5d9f2", + "0xb2bf05e5df9656b74896fb04389f9cbb226e6cb0ea62aeb5cc7ee5fa2f5a7dcbfa083619a9f076a88b6084f3820e1b47", + "0xa9d5da8e86181ccfc33d6b5601f0a3d1e1a9d0cc9c5e18a80a87c448126b592f9fd9e65cc6deb651c100226c2ddaf421", + "0x8babdc60c019367a1b4964916189b0fdd391792e08cd3dfc90239a7c5565705f4342b16422aa2fc818a12ebde46bc348", + "0x93dd1367eb11a4414e9b80dc0be03f4cdafc16acd386a4cafb3296b692394fa43f807a78ddb80098898532b982add0b5", + "0x9760abbcc0cf48140e529a6bb01dfbf439882f11717699fe14aa3e840e1af7069bb15675e88dc0344f6779d921630fcd", + "0xad8bcdc9ec3d6f2bb6a50b3dfab93cc3d7ab35d801a16bc9eb9ce4cf1340b715e14e086b112cc1b7404a9266ba6214b2", + "0xb623bea3479f9c97a23803f8daf2d184086384696ee6f678956add606488d5262544b40fa785c88588c71d3c5ad38cd8", + "0x853a13cb502ff998d0da4f78c1c395787a23c737744556ca94bdbe8b588a8c2c9e1007d58d27eb3e7b62619da1c2e14e", + "0xa38c59e7f23fbbf9f11eacf5b7907d033f07837782bb7eb624a6e4de8ad2279c3f8254df87ce0cf6a84d951605df3122", + "0x8b11be574ab840b1008ea39f806bfcc70e71add75b7531f5ad6cb8e77bda895b9e90e428559672c28032d60a7f0aee2a", + "0x8e4d1e68fc7a7956816cda465dc53106a102a3814d279c4d957ef967f16c9583cf258ea81370b36c5ef8e8796cd16928", + "0xad53a9e688223cdf6bc1fdc28210d7818b96aeab5bb2d1fe28fe7ce479770955c11e837a0a3858f3141f60757020efd3", + "0xa6bc4eb40fa9ea152e38c00ccce94f8d33b6b1dae7e81903f1eca0563b9a1c308add713acc16d640ab98299f18c46ae8", + "0xa5d70f0025cbd9ffb777280fca503edbdf0d4d965a0e4865dced4c783fa2a55fac41dfc0c2d02774a416c5bfb5db916b", + "0x9904042dea5f5d5262a7062b6dd7dc6867fbe26f3f8ac64104c475684c4018e8c77518a50bf6c1d1378c407ead4ea001", + "0x8235db2429756dabbec36a3cd60978044d0995bd477d0369e87655a42dd8c94ceed66cda7e6a04eea0fd4d83a14b089c", + "0xb7212a00f822833c4891e189a1bbb8e2902bf556a721cd968e307726dcc57fc592c4c5b7e1ef6c9fb116289ec570509d", + "0xa326d1920177d8597deecfc597ee27260802e100e25a636308499d307aaf9b36bdad3c3841ca4947b590b3486f0fdab9", + "0xa13ccd7af24792b31e3f01e75f0cf8fd3516ea191211832583172c6a614d66e0a07001489f31473f95a7fd6372c5909c", + "0x979f9ebfc1b58d8b9e11abad60927b935bfac7f318759d8955b0fe3c92ebbde05584d1afe4eadcdc5beeca64bd1f8226", + "0x99a2ced26a56ff6c3da086d42d7830abd09e3c8449418e27e52d183af24317be2a59855f94ce7501b6a91a8957eae43a", + "0xa915ca7d67686b3d9c734c6cd8cb15e6b716ae2fcece6ef5e32c95368d6955553d13626998aaa988eddd421ff21ae3b1", + "0xb65c604d4900df54e6a60fd388cca78a870933b4aeb4575ba68957f8cc42b250f7db6a14e55cef2dd3e3fb98f26f2c89", + "0xb0647de176f322668370284f2ccedb54ad56501d35f28eaabca9ca5b9b6e37fa40d069a5bab308488e2405b10c844e2a", + "0x8771766da0fbf691f65a4ab71ab13b869f94be9bb4a99927da803249ceb37d83dd7810883786c6e9705651a8ccbaa9b4", + "0xa6553fe5ca27b27bcdccecfea0aa6f3c3b34ab9a5daa571b67e2afaff6dc421bc48df4cd81fa0c3c3f60e90da0791658", + "0xb480de89d4bd4d967139664d4c2bb06844b03ae2a6c5b9a9cc550a61101a696c87196b2f133a1af0378b0bf288ba4412", + "0xa1c89d151543a70a24982354260c384346773257065547e580a4c8de55c040178d618db95b2e3a78867f93e8e496a0c6", + "0x95816aca84d8c36aa5227e4c3c630c4489b0c61a9052afd1b1600be79c3bcdf390d3174fb60040dc75fd04d459c742a4", + "0x99f19683c70707f84dc1995139944910c79aa2d985d99287212abb2545e9e7c3d1679012d7c01a1cead232e3257abece", + "0xaea619bb8623ab72cd28514480de9e6e32e4dd1e977480946b07a71bba6d1f5694b2d305e341117243eda794d2b82bad", + "0xac60b0dc7667bad9da96e8133bf01e37edef63d963bc5747cd666f764329818c6288e793dabdd8a3a12e08103b430707", + "0xa6f39aac9a3de59d46e12760a5ddf582a9de9b197eb3b8dcc5bae06b6d5e38bf7dfbb0f3198674415fac4ab81a522c2e", + "0x953990f81bded494cbd2b8378d702765ee7901c01c4ff55bb2f65aed9952125478a8ebd9017d2a070c81c1f90004871d", + "0xa7a1013f7058b8dd45df27c9ab0617e583cbb88e6e290559c7453c25eaf4f932fb1ce35aac693529f6b8dc348bca34ca", + "0x91c2c768a6e4a73f87b724919da52d9ba2d0460d32adf3f95518a93f93f70d088ae73cfc72b51777af6f745d9f1c3b07", + "0x8d0a71e3275317ad392c680acf5d83127c016c7e8a040cb8f3e23e523f831b501f5474f4bb0787bebde9612ca17ced42", + "0x94529370a20526cf09579a33e6f4807dbd680293298eaf6708ce287bf8eea31c8b2ff9d87a68c63a906d3360d8010ffb", + "0x8d22db5bc883023cd2e65b59ab2464d7b2d658eb154add67d3b0a606ad73e0ad407e11a745ad889ac885f69580a0710c", + "0x91f7b17f17deb4c576fbc4a325bf2240273b1d5ddb5f41e7446a6542e9e217ab3d70b11894cf0bd766df1aa8144d732d", + "0x886f34229774e7faeb4c00389a737e73027297b159ca9933afd9506734d3589cf8fb551e70848fa40f73af12d39d94dc", + "0x82814aec3a5ded1c6cadf2d2a3a755c49c30683e1ba3cc1bb4b44b699c4fa296f190922a4e7c20229ff9e9e6f542f1f2", + "0xa8efb39ecd6bab71090a64f124dd7adfd3e5c233758821a38541d34445a1093dbc196b0a0804c70fad8e30cda6b6f749", + "0x815c34df5083a386ed1bdea28366d565d9ca9bba78bbf53c35f0b99cc897fa7150c44295be51836ea8f461bc9f083b5f", + "0x95350dbd91e7fb45e668c4b9c261f62acc085512440ec23ecfab5c0ab76a800293afa1aa3fa4ee35c25b5b8b436832ed", + "0x81e679bf9aba11ff03d4cc546dec36014d2737e3b39b2f899c58fdb34d03c50fb0d6b399f1201f8011f09b630caeb164", + "0x8861900bde9519c2fbdbb68f427af696f7a4c4eb40c3c538b7f24d6b2dc78493635754c5375d259aacf83672bb8456f7", + "0x8f63e314c71e6b122dba299ca8e51899d57ee0711e4b16f1d1f210ddd1a8b9773b7e00e3d8298a0b8ecb09d3a7936967", + "0x89940a6ad215ebd2518d5d1635745bff490a224482f523b9459c4c725fb7705d7a1561c2ae309cd62fbeae8e17b104fa", + "0x914868f5b288a25973b2bcb70592357742c813cb6b45cefea2deb4be97799f0fc9ae114939365ebe88830fc434d96bd8", + "0x806f62fb5c936d13f51b479c504ccd5864d8b38abed17fa4b8e08fe637e705021396fe404d8365ff6fc79359cf356f53", + "0xa6d8793d61fa2965b6416ec397fd0b827acb3ea2394d7240977677cd883aa52e0e15522b3e96e2dcc318e65dd133eaed", + "0xa38739564039b72e6e25c4c0ad62dc28b882ea5cf1b13d86642ea28e1afd0aaa4284f2d64132bc1b23eb9fa009984483", + "0xa676f6683241bc8baf2e48d00d244f43398417f1bccb027f271f809651886b817bd814fe10ed9319305ec47c8ce6fa6e", + "0x903835e6506ee699d38f6114a377cdee64c9cb22678a646d543637a3bf9d9c1714852f58e6d7f6fcd82c34dd67b24283", + "0xa8fb4ae48addb0292a32da31583e07fcbe9dc563feb9b2a9ae350c7762fe58ef66dea1f7c99ac6575199c4510e17073f", + "0xb1d7dafb81daccb43cf749edaf189a7bb372501e23b01e17e7171eeb44efb80bef76cb523e365a5c9d129482ed369542", + "0x8e59d7e57177efdb610c0544339f85c98e53cd2423e4a3e65e092fb77a8c86802168bfad9702500e12f01a414bef85f9", + "0xa5469e67bca647090d006f23fb552e45474dd5d0ad136950d941b1f2b365b7f2e30546412b43aaed72451d3caf68a103", + "0x85ffa640066d219254ed56ff8e95fccb5e71b7587eca6978786cc325a9d988dc7a8558c40f75543276a6fecfeb063733", + "0x8fbe3c4321cbb8b9ed664ab528b96b6041a1eecc431caa5ed4194ae832f69837d7ec40aa0d62e9f3f2f299492ea24658", + "0x8ecbc55983e8c4da76ea05d5df42e481fe54c782bfad62154750a3648b317326f7b5df5cd036cf08a27c471699c8abc1", + "0x86cbafb29da28c7d63ece96560301eda2ea5220aedb13cbc563198696319f6c5cdf6a2368a69006e4dbcbb0b2f963089", + "0xb3e42918384dc386311a3278cc5561b81318c45ee71dbae3b6d289721ae884891454c08fc92ef36b662ab9539880fa27", + "0xab81d5c590baf806eb4b6d31fb702b7c3dd1dd0c8f7463ed5ac9e5950c2ff8ffe1b74a9240555dc001990f7eac3e0470", + "0xb2b19d3f5f38d23910f14b42d0e22a97852d2e06e3172eb86ee7fa54ee46aa7b395ed8a69c02f72ae22a9e038b84bf1c", + "0xb022be92f37e160eea666f4ae749445af81e4a8378bd1171ff3b12815914766ec0ec4b42c340690a75e19efa150b0972", + "0xaa4b70378512d3f8dd151250d45d005478570cab5e9ab9e1bdee88d71dfcc0698a48a85b7faec2e99f203284615ca369", + "0x88abb826b7ed015a716b00909e0f8efeefaae0e2b6796b8c268b5344b844e87eab92181543930160feab933caf61d65a", + "0x8115dbc8183a179fafad79aa73571f4cc5643c59bd135079f2b57ef22d4cd2163ace8867ae0d6f437fe557dfc7d5527e", + "0x93458586eebf129412195f8e3ef9cc16df89405605cc2ed16fe7afd9ca612c00f3d4db0eff1e3c28a1fe54b57fd519c6", + "0x85849ed1464120c2c33780c8fa4fad6be6c2760d645f0b0e664200dc7e88e6cd4a9e5fdc119727e21bd42e19a3c03eff", + "0x851e0ed822f84d70e2b93c58a2d300d232b4d054ab5b0909f95220cacde4d2cfa4890c9ea8c77fbbe12666f49ec5c7b3", + "0x811d998737f22d451317a72ef6a52410656906f150ca29f238bb385cc7e1e69b99b1bff9f8c5a9ab4906c8d9568e9dac", + "0x920c099fa4fb28ed07384b877155d9f7fe5b101c6d5fe97c9635ab5c266c5761fdbbaaff626964fdb22ceffbfd3aef08", + "0x8c91794ac5c21e7d555b4fdddeb29dd027e91ad17911ad626b3bdc5f095d679c12241790d6ea8c73d31a7725c0d4f070", + "0x83351b7bf086d1391486385462ad69686c0d619418d69034a80ae6a630e8021d3ff9ba183efbdba3d05b7c56ae77bc17", + "0x836451237f642ee23a0b566fc5284d45884d8cc8d9970940ed3467b74d092f28d0fbb223d2c0c0e305e446ab2f36f1ef", + "0x99ce2b44df2464d35aaca57fa3d8f556a1d8b297f66e1b7ae376397e164d06a4ba5acbb6b9c649b3375fb109f5ff0b6f", + "0xa5681f53a59bd142a7d627d999a8836bcb24475be1dc2ee42e66c5e877fafb7fa188de4c7a7e75e1343ec8b1111ada1b", + "0xa15e6f9c16f8d52435e47acf1e2e957aa23ea9690b0d4b2ba2a597756b32ac8f3ed3998a8428430f9c86e70e158faac0", + "0x868136f89a218ae59de4a15783a96b8b61541ba0bbc3b13699ee19c4d2951053aacd4a5ee1f54e004098ed269d52aa01", + "0xa3fce644f2ed97f7e1de25ab816ea55162cffc4b66de64166183ff435ebe4f60b505c6d9ec25deebdfdf98ccc3955e97", + "0xb201ff1a8bd650dcda3f44a7ee9a91543696e5e0b3006e7175dc43f6cc2df29b1ae835f737f72a321f74cc72665ff398", + "0x88ef984c65006247555d28c40e022894f4314c5a90216e6f2300bc440081634a8d214108961c4e21e321548a42a0b8d3", + "0x97834f6f2f7a9adeecd5e3d6fe9740f233b5baf87e5e661e4b3c45d51f78afec8711a2c3b820f39b9a1cebe85edddce1", + "0xaff3fd89d043dd7accfc6839ccf99bbd8adf89c5fe5eef8ce9be7b4d8b1c21833054d058e3e6de5272539de3188e6dad", + "0x8af33d2461d94f0fac18d4e38d94ca3c332d796110a8a044755151723fa6c97e8669ee797a1ca900c6d4f8a3a18af052", + "0xa30debab7b33ba28d74011d203ddaa8ce796c3d8a95cb22ed9bb21866c8d730b2137ea1df073e174e20d24ed913e1e01", + "0x8322125dadf69f547902c767d350293b9458d73cf82a1f8dc436713dec272b2aa2dcd6664a17b336570f0f4cc7b91ea1", + "0x8d23111f849518d19b2a727fbe7ad1daf86332be4494ae909eb4d598095737358928befdb536b07a442b7489a8febf6a", + "0xa9d3f3e084a212c38f9ab6bb92c715c6d5d1274d3d75aad40d5a5ed154fc6b38d834c69e8c0014a2e40a9ea849a2d8ba", + "0xaa4f898576e52ef770001e02432da6018a4f5fb89ba1b57871438deaa1b8dd00728b4be9535c7263c8bd5094b27b06fd", + "0xaf7cc54d1c0b5cadbb92a8a0c091ce6fdc6de2ec88856460b8ef91b1277a40b9eca631535caf0f79d069262ea13f54d6", + "0xaa102ac105fa594ebae2ec80affe24ee022d2970db6b78240bde347e8b2a29dbf04d2b102b8e410163a746ba415f60c3", + "0x823589f0b69e7b54df05695ba6ae0fd083b5c1d4419d14d9ca03c45c53c518fb5cd1c4505ac430662cfed44e057907a7", + "0xb3d7ef759bcb74345383e746ab7cfff40301e10f65d69cd1b2632af4266420c2b4de741111d36685f5e17d800ad43910", + "0xa82d3d7ef4a8f696aa757618b6d0f98a0390713591bcd14c4b3e9a1764c5a6da0b90e2652046cdb54f7e1ec25d38cc9b", + "0x8250cf334fe75b3b8f644a251833f8161003d623d1eb9a68e43a4ebd52842877dcd39c885b7dc93acd1ba91fd4c0f321", + "0x841359d98c3a73393d7217b91d4672ba7539d1a2c73c8266c9c1329007e0a7bdbdcb014fd073814839dc439a42cb657f", + "0xb88b9f26f21d0c505966864e5fe0f117d8f10144dec18ea055ba4060849c9a946120a7022ffb9b75030ed4b6b1d824b6", + "0xb4db59cd04c144ed3742db8197d7ab3cbd55cfae893e13c498c626e0f7d75834fc5d7e63ca9faf3ebea571aa3a4dbf95", + "0x849a4eeac7e24cce3e49ea00d53bf63918458d9b6512d7fb1b337bb2ff6a3e9e1b62881c968efa3d98a41e39e7ccb9f8", + "0x88aa112b624efa1ed15cd7caa20b714d004b59e688e962b2b32711d82c13986d41df23866153fb519892eded3853079e", + "0x870041f269878bd90fd0a3d299ab98730da32d6886b070981363cb195a655a43b4d4ebe0cfe45be3242b28fd2b310cde", + "0x81721bbda8dbd86d0ef73c488024e2a96dfe3a310c0c4730577eac553f44fb656756b9458a7a8f8df8b674a63a86ddc5", + "0x99863da211673f2790497cfce202191d9aafe6ddde4cd3bbec018ad576dfa2e3d71c913054a66c787b148082cd5a18f1", + "0xb8f0bcb442fd2934a837f7b5843fa9c39ab94d9010557fbd950203e64ae348a7eeef2a098cdd1e30aa31ae86685e97e7", + "0x89566fca1875cd376950c8e8069cc65160352715c892cba27f242c8bfcedd8963696d87136aa51576625ee0f86e2cfdf", + "0x8ccb0adf55e78be06ea45d2c45a9fd511af18b5265728d9807f3c79477abc6a187d9bcae6db8bab6b1eb500fc0be5e8d", + "0x83c77fea9ff37ef8ca8be39f518d68966a14b217b0b4176fa4a0cff9ccfe5e1d855189b2dd0ca2305611d3fc707aa31f", + "0x88ad940f95d5b0c6233f6297e1c7b9b1d862c4bbfdaefbc330ac6a3ca747117a4776134b0bb85804ea9b77429b6f111b", + "0xa3893d1113d19accbcdeddc50c543cfc0ce6ae6617d1ee491b9694bd9827c48d587c8b6405dce8fb219266cc584918a0", + "0x814e6b95031b363098c6e079bc5d8d44f68a07b002eada46b0e2c7e9489cefd51dadecf28a8f913d6156d28bc888bee8", + "0x8220ebc839a5ae92453efb3e7d0a393de840797d24eea4de6ba61d17160e88951a7f1690b3b190b638d506e2a941a9a4", + "0x83d00934991d9529e8944f2c0dbbd558258fe8f546cc0f81a400cf7ad89de79114b9e6008190db5f7bf09e7336691a2f", + "0x94fcc56f8d1abb4d45efb77805c2e0d088ce4ab3279b4269c14281e2fa41cb408b30fc2943d886543323422af8dcb22c", + "0x9593c7cb07e85adeb5b42a472b5545dc0a43a3f099e93aa0da8142acb2ba9daf53f0868c2dd2b4722bd862061ff05a3e", + "0xa0e686b9211aaca0dae48163d3bb1cf14ec74e1bd5230ce29590df1316b3649efede975cffcebc9bf1c1c443fdc455d3", + "0x8fafe0db707ccd52be9133efbf1cd1d0a997484de762b98f55e3fbe9e29c15c34177579117d8d13c0bb2c01b0c62f203", + "0xa552f96d851267b3a100861b6e45cfc22913e9ec4b89b75df7147883cee8f4905176481f4adefbb19a7ed997d736905f", + "0xa00cc6cc413254633e8992a7f509ad67322bb4a000cfbd61eb4ee72b2a527cde033db037713027b940c70c3585931281", + "0xb020db25ab43d61307e64c84eedb4a0c70077beaccb452e6788af885f1661117c09129e1da01ecd00b4cd219d206ab3d", + "0xb046601f529d04a7593cb7db5824ee7a284505d5dfe423d5b1513ee501f90e2bb51b32ef3f0cb136b0b807e613d1372f", + "0x87dc34b5ce33d0d412d52d76abf22784f954857fd71a96e3bf1fa464de0992081367706c21f5af1700cdca687d0e715f", + "0x93d61675584a373e8dedbde8f5bb9ca97e47d71c61db50442ad43003b16e097eebd9dff1b6aaa9697eb870b01761e4c9", + "0x8ea5afb276b6bdd48018960b262d6171265981140e1596340dabf5ca0914f9300ba07523025bc1b1ac7b5a33cf8cbc41", + "0x8ebb3ae9019229849496a4be3c833dbfe3c03f189ef19b13ce1cec74099ad9648760f18ad00cec5c9467e31c011d878f", + "0xb707133f739428787877dd2b504f3de2b821eda0a8bbf85e617943e22e312861c87d8c277f34926b368d2143431884e9", + "0xb6519d1cdfca21d75a1782b97f1f0edaa75565a346e33c0e61c06a2f504c7324a33c0afbcf6833b4def69dc71f5edd29", + "0x862ee2fd4e255ab3ab85557aa4c57e940187b024cd60266e19082fdfbb045c52bcb20cf4aba98868c51205df325e486f", + "0x8abbf5c71c444043ce103eb26cb48ec25e2aff2fe53a5b6f6d01c92a1f9bc5d4579942f884a559fa0d98a395ee8ae1fc", + "0x8d4def9588877739df1f53aa71435491b1dea496b289780bff643499fbe6b9267f23ce7a2867e2b93031792282d25af5", + "0x841f315e4cf307b68e5c0d96398455e9961bd81095c735d3c6e9dab985b9619334f4bd9422c417bb64294d02641f7505", + "0xa9b5ad20b8ddf121499422e7881d130a4deb7178b9c9181b04b4dfac9a046982b5ab4639769eb042690731f5380bd50a", + "0x991b4bfdafdabb813248643b717e550a866dbe67e42b4c92a3a9986efcaffa588bc64ee95e155b96680e079302c42f84", + "0xb4517fcb17f1be78d8a9909fe646c106f4e08acd2765efb3647f36738434be615150a82faeeed39144b4834da55574ca", + "0xb33cd7671c418c9834e2b166374b4a3befe21b21d44c173b66a00f5fd855d238e2db7c4bfd490fb0755addf949dcd681", + "0x98406d0eec461cf6fadc2b9c7fe3fb50dd1673662612bb5912e33db4748b7c522fe37f12ac8e8648835c86457dda309a", + "0xb91d4e1af08ca61d7855c6403f5b39015fea6b6951b05b1740a43b93c78a18c214599ad98a7214f42b2ed989e2593c64", + "0xa4537713b4f695fd153070ff92591f161641a5c901eab25bc3683810120d163191f3623b6ad43f891f5f8729b4db5608", + "0xa890668abd8e04320a70dec34a2e4dfaa92b9b7ff21cfb47d3b6edeadcbf2d24c1b4f36ddefcf26671ed97e6bcd5b4db", + "0x8a701937d9f615d050bf4be23c5af7f658954945b9d3821500f794b78b527202f6a4317154f467466afe2de2a5118cad", + "0xad3a3ef6441a5cec8c12af132e03e8f8f193237e68bc1d9e79590d8149b635cd1b308ff7c34a60685c609993672b7d52", + "0x83f7b523fd886b6fd473c5a5ed0b07baea94f75970d1457ba64964e85f9ca39d64fd56450e22426882ef3696eaefdd98", + "0x87563b9bb20215356f5a0a8d8a93c7ebc4e82dc6839e88067488e12750e8ad0a6d073db243f5f32365d2b3dd41c4e439", + "0xa12bf0ed630d78fb8f432e33c526f61849fde15238da3c3a2faa08d392d9be0882b166d7762ecc69954f0adde915c6bd", + "0x8ea52ba6abf09f65ee96d9502ea3dbf6f3e14995c077c924ee6fccf703448ce92de595f8c753522a14f18134b4f41cd7", + "0x852652a7d2b03466727ada528d0ae650c0a7c0d5a974fc2f840d5290b0eb0506e102f61b6dbe94a83cd472081d1bbcf9", + "0x880c90cc00571e3b229f5a85607a852b5641d0976c588bdfa0c3b724c019cd20abf71e2e5f32bf8dde7b34d63dd4889c", + "0x99469085163bb3e7f78b3dac76be0fae598190e512b49c90b4d1741536f397d9c30930dc0bc6c07cfb248b38511c280b", + "0x8b4073b4d986e6b681525925ace8813615cd1ce4b39c2bbbedc9d6ac610936e0c18c20b8ec4bd76ef6947f96f61307b6", + "0x84a9a9562237015a93db97b482f25b9117ef1efbdf0a0379f073216acdbf3ff52a31dce5484aa07f8f7ce3614db4d2ac", + "0xa2e6c2cb376de59dc773526bbd82b66711b6cf2961d26b6434d0211f134ae9bb99d89328add36983bc0b85c1761d4ed5", + "0x969fafc42ab8df5479f6f02291a4cfc3bbb604515af761a4e4b130867e5dc0959c60b53dede078486f756a9d611860fd", + "0xab4e712a92ac9c2b5de061d0a2dfa99ced884fc7a72c66a74c3c5ba1b7acb174af602705a64881487c72608329cebba7", + "0xb91f7f3011efcec9b688e9291ce1eff13ce43b14b053e07519a54f6625f67ff5c7a325441e77b6a5bf0a47a361345ef9", + "0x95ac2d635cf6aa99fc1d8f288b283a5c3b7791dafd81953df2ccbb1dab580b0bf0863ad2759ba20da9b675a120864a4b", + "0x87c475405c98fbcc72d6787531699a82e2cfdff973cd32a844acdefd0329d455314e19b8cac19887c6bfc95b3454cf69", + "0x8cea1863f4012f99a49e1318a64ba7ad2d3854b2500c0ed001bd37b0fe97bd6802f718a19a179b1fe68ac813f7886001", + "0xad3e38d8ece8956bc9a724077e74caf325c185ab38d24f3722cd8890188efe23f241b3d0c513a47935c33422dc33f2cb", + "0x8bdde76de4b4287eb6f82c0740d96569ea5608eadc0e43d69b97dd34674a0cc0757454af89e74e7b805bc60e99a7011d", + "0x935bd998956a7b8252e6b64cde93c5d0f37d91f00c685d856f86592ff63ce77f1767bfd06780149ba5364a3321d6f75c", + "0x947ff47251a434b884206b734d34da0d6e38768e766a75a3be5054fc5ba085ca8a7f3540d030f0a5986bc94688c24632", + "0x8406913c26d9418ee4ffbd7fe895750a1ef76e4d1716b8cc7a71379af80df0f64b4d3e588e0d5f9e3d8e85820a86f3a1", + "0x90c533a3f4b7e8767eb8f5b61c250a2f2b611c4295b3c4ae87a1b053ddf8711014a4c871b49480f675a16db259055693", + "0x84678846c7e045245b6bbd2bb06b5f8d59ecbbdce18357d3b18ed6a4b3eac5407b55153d1b3215d2571cdb8e84f50e7a", + "0xac7feeb349e5419923c91005855d5104860eebc08a83ea2470980838b6e12bb7890879378515da49eb5207fc987e100c", + "0xab0c2e4f0c08a005e9e8e680698b36113899ce420bbad5e87c5b115ed358ada39e489dc334bde0a8d5a5be741449d9ec", + "0x8e8cec5b77563c8cd9e4ba5f3e53db54e0d6be49d68b25dac09e6da011d190ef3cb079b2869409dc769b5ad911b0ea9e", + "0xaa648c3258d8a3411c23f996f0bc3830eb4a3be9b69e4834bfb63ac5ccb0ff3f20b6a2c86e6b892b21704ec1c3c21ebe", + "0xaa67e6fa49554f6136b3713de5485791b4ef394a86c5aaf45b7189a58d3a8a93f57366be5e3fd2bb0c5d8ae61b203ae0", + "0x87d86789b03baab0f0549dd7f737d6e1d02279da6bdd9ff04128a106d5cb77a9583e9b467aa227a2949bfd6ca4a66b7d", + "0x95dc8ea0af958c7f6baf29562040b56e1ba8c96c1324034b2fe4a1d2414380fc1d4f0527417614477ad39edef273ebef", + "0xa0399ba52280ed97f06ae61f9582a5ce359e116be6175efba0889d0d974d95753692436b615dd3cb18626411968f369d", + "0x8c680bce0a5929f01c5d05d276b63ee5a326d2ef4ae18d44514944cae5961b81ba8584ceb2ef9bc74d31ff7ea27cab52", + "0x95f80b6c088a2da0a3355b1e579b4045458950b9b27de92eeef0dcc63b874a89ad372add3e0ad02083a94dd12e8794ae", + "0xa50251fd2c849c477dda1105b382dce6588b3727d555fe06764d338de44bfb1945457b468ac9e5438880c381895ac798", + "0x8b435dd4bd86b8dbf60b5afb7b9ccc45696b42c0c4091c563ff54ac080ef50c56258a6badb446a8c6cf70e66d69582ed", + "0xb82eefd69cc9893a2b1b0898208c29c27f344b4a0994a133a28039ba4d6c341adfe637201b89ec34e179d94c36e88044", + "0x90cb754bb1334f620fe74b28d7f8b0396b706fc6500920a6b50fa8d05f15e08d13ea0cedf964d44fe946b324487a5b63", + "0xa563cae8b272de444a806a1bb8ba0d915c6b5b6522e908dab4e16d43a0cdee06d8ff63914d8b1ae54ecb2d67d648bba3", + "0x8ae79eba5a0d8646f382b1f80dba6490b0579a7866b6eae4eb09a79d17c855abcb6b4214a7f6c670733326bc637bacc9", + "0xaa02f14ff51cc1aed11c2109e9c4f8e058b8083714969fb31fa4929a6b1d7f7ac1d4414529fc5cf3066e7c7b84278eb2", + "0xa1111c7a54d1b61cdd607ca738be8c1c518d8713cad64ddd91b11f82900aee2a97f7e485f1f63fea18ac86805f0ad193", + "0xb8cfb369d029a7b89b4da77c1e7acc6d1082c33f899078e9b6f5b253fc1cb9f6a5a7a74971f79681379b056f8a8d6ce7", + "0x84ea06ca5e8f549ad7b84cd91dde4b7508d34122684e0fc28042e944a47fa6647f7918be861a4a1bc176ce577b18fd32", + "0xb8931a5fb7d218ecdc270cd6095170fb3559cffd52e1b76217c52af1101570e680768178020fee607313215dfb3faabc", + "0x84dcf4e4c210ff05f684643a64614dde138c695eab967b0a233114360e656e985a134e192cd5412edb2c60ed25c55b78", + "0xa44567767da132c3b94b501685b16bf234e3f2fbc80ac4ff50e5753fc87a66bb582f9bc5beddc35b2ebb96c4f8dce4ea", + "0xa42e3a066ecea6f2a418edce43303a16ec43a891b12a46b671bb12c6286ef4b79a2a388f26f36ad026d710ff2e794175", + "0xa11d2a4ab56332ecad4ad650cc761458a85d115b902ad48082e7661254a0e10317c72e5526dd9e5067a84289f0b35c7c", + "0xb97eb288aeb4d95cceccd5a2af907b57e58057fd8e1f6d81ecad7a267880b6e5af3ed22c59a54a447f98a5159f3dc115", + "0x84b4f6fe24aa4cf5c52005766f0d07e5386a82bc2cb4d4431094411f6abb386be224326e5bf992cb1cb432b2278a070e", + "0x88aa3d22c3d5f3b4410471ff6b76c2b4f24869c49af97ad139dd4532afe7726825455c80b570cd7800fa9e8001b72d23", + "0xaa2742e1c628f2afa840e81a59d87faa61b738853c2a5fd935dcaa594d5705e4f64ec6553b4c2191a3cd1fdb3643a324", + "0x81c389b6776a9221197eb9234c63ae57423d6f3693df0fc2859951fa766586a176f7e03f43ce69a5a5c1b24667e9f6a3", + "0xb3aca648b4d0a7c418dd3114d181e9da38f8b7a69f997af2dfc4e70738041d0820afe6b3a300fc57e0c6861c5179afe9", + "0xb1889232f928513969e16ed55b7842e3debd832f9f48cce11f8b352ea7241270fed9a40f6a1ab7dd4566305bd987c396", + "0xa8097a1432d82d6a98a1f464cd168ea3d4fc2f1e1557c84140b7890ce25df02634f8a598cf5ebb2a150b5b6b927db180", + "0x8aa6a6a4a6e69be6108547653ad1be0134e31fc0f9b67df0ce259174a9a84655aea211903dc14209864e97b549388f13", + "0xaff5e99bc46f53697eb9e48640ca02626f37050ce499b3fc410b273cd9d64ddee9f5d48a276bcdbc284c37c5e9f5a64f", + "0x832418e9c309d573c9761d407e336b72de176d1434ecfbc928eb9e469bf5521cecbe2285a1b01762f6bfbc4544f6227d", + "0xb63febf83647b37d04c2cd3e6663595f988f73639f6f16c3c5515f8cd83a2d5e1a2ec24b3797f5ef32b09f87641f4391", + "0x88b8b471c4ded3d0134a32c25dffd7d6b6004e7e779a57928d3692333d1ad73137bc76f50101d879e68c14d6e0b71c4d", + "0xa18179c8fef87f4325434e595685043b1d9e87ee08bb13805d4aa238c2d0a7e44da16f938622e15f2b8cea549ab9a3f5", + "0x98c96c2913a389f8185260e7e48808059befddf21234b6627138efcd1859710697886421bff8f980493a3abd28e900ff", + "0xb498cb9576d8f51dd81d0f3f66b9fec47f539afa86f0c34be2c76bc814047a7fc01a85f0eb284d72ae67321f829505de", + "0x911e8a595d64fa10c83ad2dac191b07593975be333b98d38205c660c3b40d1260cb495433b843e0a91b672908e582044", + "0x8059a17e2e8fda1553ff087796e7b123ffaa7f9a52e1ae825e501258dbe79b0bfc8d04523e5f48fd8ac93c9acc84a5ab", + "0xa0a36aa52d1962da0a0016e5c277a00e1069b1edd77c50f1f11f2885435a8465dc0898427add1561552e61be5cf269b7", + "0xa626072d3f548dcf7af01e54892a412c147c0f71ffd02c09083831799eb553ff8c28a2515ee166d18d6c39c99ef4297a", + "0x9285e38c1a7ea3d4a06343efd6357bc703bc924888ac9f6080a00c5a466467baf056d82a458cd1c37ae3facc944cf2e7", + "0x84b103b8cacbb8f82c1bcc6010cf3e5ed55d4ab19a99a20cf45bfcc0823891d760afabe81554d24f8fe0869a18aedf25", + "0xb8044cafd1833d5c31e2b65e90fd2d9789d3c375a18ba74d759e250eb62729a23b6bb004531e9d49b8b94c69f8b9e4bf", + "0x9846b9dea2f0b5f5e78e7c783b9c4be45c9ea43f6c33e1eb0a0df9574c35af3e9cc450c4d7b2addb8f02a8144a30385d", + "0x879fdf00ead61f7225f8f30a10035aa9fe0d1734cce4f5f089a999702c42b20d4cf1ae72bdcec8b6ad32b4f60bad327e", + "0xac1b3378e314d25496f71cd777e3cc6aab90158371e57b3e4fb1642fa746309d172d8d149f1213506b2fa3472e921a48", + "0x85cf16101661a672cd42dd0903c9a77c88d9ff1b1e65f77566a2f9235e72b384f746f128d7b75663a27420e0b2dbd86a", + "0xae9ab26f936937ada3914f6c144b56116451d6089f3c60aa67bf0d19ddee592443a0b629102b9aea3b9a991bccc08f6e", + "0xb745e8dbbc778e375cc4880765edb9a6124406f09d77e9f1f7acf3ceb97d95ed7175c9cf27880c9e31feac79aac8877f", + "0x90e79e64e32d0351f23dceca799be758931264a9324bbe09b19b0f3709f63b88c29e72b95b3ed58e3aed670c4f05a3c0", + "0x8beae4858cba39bc8b5da76de73808e1b24e4aef09bd2d02ddc116388c15417a16880620e577911c6905ff29910ef159", + "0x84cbd65ea441a5a485c16b707192fe14a06d824f2dc1e02fb3504c3462c88c435e724f74b9023251f080adb7bfbcfba7", + "0x8b82f034608554506f84eb5582154cbfcd49e67ba6cf589b13996bfeca52f5f4bc98d22c249a6d558e1788a0067fda48", + "0x840177c1f1bcd70b1fc0ef1d59d3b14a07776b260db9e0af2b4f495b6686bf4a7209c708c096ba8e88b413bdcbd6af59", + "0xaf6c41f1d44a8c934754de4f0b4ede2f0334f1f9a17744b37df63bf694a6c19cdf794a2fe0414b88e4abe0e30d6279a5", + "0x81f97392b2b9b73186dd1cede2ed6d34aa61fed0f6d031ff268bb465a2356d056f9b0fc248b0302cb0329296addebc82", + "0xa154ad85d8211cc3dfe95f7901b2e5cf69b4481d166c1de08d6b725420f8657ce3f0bd3cebadda671e384a8504162c1c", + "0x820c1abc3b29c974bf204c150ffdba51d271c7bb1db74f552e98c82d0fb7a87429ec34e424f55623967adf364dd7e905", + "0xa7c9713a57e3feec1784ba8558046674bc7f4d1f4b6cf60e64233a417925a1412ad1b06f23fb09d57b3c2a995ac39208", + "0xa0aea094e47836a74a126781e597d6885edafe8a749e951353bb07914f66386796925123a066dd108f78a03fe7bc1863", + "0xb96fdb1f602ba403a2e6f1c889746c22b57f96ab5fd0ab549365be856a2e6c2db1ab8924c1bb287f9738340317fb058c", + "0xb5969001f250053630f3ed7795d5a30d1e6627ce881a86c6f02a659dbb9f6dbdefda3a07189e66cbea6f9ef3a633b127", + "0x81c518463b0208fb95c670d05b8fd3212289b985b1f458d05ba35e059fc04fc825ca2a373a877865484b5f2e05451914", + "0xa541bcf442fcf4d5ce5e4e984108ae8ea2d9006f0720c2137b2d6c852c412a57ddfc3a6a6f8ff975509abc3b035f1c69", + "0x89c2d0f46c44a8a2ed4bd89bca7bc11a2055d3f8ec0ef8036a09de0808efa7d82ce7d0a7892f808eebcdc1650d2f7434", + "0xa1abea925ee4e4107049c95582361a8c9b411072f485db54010b960581645541fcc344e6bc19c07b3057e4c2b5a795d2", + "0x95d70746971c823ba5497bca39caf0f2dd1e3746e516c7703dd9bd45eb77fe3fd306076170f8eeaed04d655817da0eaf", + "0x8e0eb0ccdc5b12cd854ee82c791573990409437794664ac782aaa8db37937a277e1e177eef58c72933d946568648668b", + "0x9059ec62b9f448c7c60b592c9a6d00cc39e2819587bb34ab35680477f3c1748548748b31df0642055aac2aa466d8dcde", + "0x9963450c16e6deb3e8bc6a5794dbde361e86c9e6c537b2aed9aaf4058390038a725c7fb90f2d848fb53706acd32779da", + "0xb20393057805ff8b65c249903e38ccd20206c6354ecd3f16ea4eb111f1de33d9e5e3e9d5c68e5280510d81a3b7e94d52", + "0xb81ce560c748ce49b9f6417cd670b16a3949c63d6b4ae8848764bc1fdf794ac3f7dd8e3ac9872eaedbe6cc78eeb5a22d", + "0x876fc630bae8883ca08be9123b3a030ae5a2d47fa17223cf141832dd1c8de57dad887083323cb1372b30d4b7f59f6f2e", + "0x9165685359e8bfad5e6e40bf2f3bc620396e34a2dcc02b259776f9e4c0062aa4199c2d793a7f258006f78147cb176129", + "0xb96a6a400d5bf45cd05fe1082322bf89de01f66c775822117cb59ba6cd7b4641df327c6970a829ab89a3b1b0eec206e9", + "0xac8e07ecbd88cdc826a9e72fb803f657b1a72d6affe88135b30864bbde5cf4454a81c7a75240960dc0fd275f2a8719d7", + "0x8ca4b19ce3bcf662efdbffba59f55bc770cf88dd066cafe5a0d8c4d1d2ded16120d9c24594d5e3210ae3505aff2ff830", + "0xa7d90fad6378055d520102f1490c6c8f1932ee0738321b2896977e9617f8fb962c2e3e3b5d4ec5b51331f0293f305dfb", + "0xb3a9b3a2aa4613abb069e04ac2b4f60772ffd7f1f42c2498641f6510462e9e5ae70f70bfc92d545e81d8b29fdcd0c2c2", + "0xb1b2a4db36528ee35c93955c58b47665f8e81ac94266c1dfadcb6398e02bd0963854fd210169571cf284a9bc088211c6", + "0xa4a57c89fd14f01a2cf8cfebc4823a53755048807171e7c4bd6ce1c10fadc267b8c9f68b5a6a66fd33fab996c7251a29", + "0x8f2e78901c6be10c78a8aa840662bd21a5619050688d714e2500c1be0b9c662a8122f13089166681a7450303a0ae46ab", + "0xa96c039a3398504af7f56726a36d30ff04d8fa3ee7922c98946829477b357b2146724b7774dbf2b9765b186fb704d96c", + "0x937581ebdd4a61aa12d977f80ec374cf2d6104cbb5a9ea56d4a02bdf81b11858b4c78106c5a5bc9300bf83df774eae49", + "0xab796224af668fe9b12411578b29246ad043bdebb03f0870538294785c857fe51294b8dc65b4bf867eb3a1613d0e499d", + "0x92a12201689426e46073f9c9b1204e5fda47821481628ee1870f22bfac51348aaa42f95fad80d5cff80a9665c725f4c4", + "0xadd692527fdc009f4e2de543d61e10c602fb372316b96da3558d7820db78429a6f484351a0ec72c2d9b48a781745be79", + "0x99b62c74c4793794a09d6b06b4d3e1aa12484883d9c2f51b5bbfc7cdcbf2311bc255b9349922b92be1402371e56128eb", + "0xb7516f1d72a7ec42b1709ba220e9d786ffd72254cd93da5e9f514fc34072c06320e6d3b0ecf3e23a88eaab66a0f257fd", + "0xa2011478cb66296ed40e066e439fb07db37f5a56eb72ec40441ba35a1ea37271aeb307c3eba293638287057e278b5926", + "0xb753cf3be1d62455b880aaece271e3e4141971959ae08ded6fe8126565d58a50735233a25e811112cd323c88c0e2e065", + "0xaefb84a80a7c4d56183e1cff3ad1869e341e3e4c4380adf9465ee584a39ff2e8b214ba7c8ec867494c19955554fab84c", + "0x804e30a291a5ecf856f914dfb9b5fb00c7cb2c024b6145d5e3fa7cadf017452764ba9bb4da6efdf8fc446f8ec0072d46", + "0xb71b0799231f21f2d58b0eaf29f75eb7f22d9f812ade59c9da03ced9b9c56a48dbee6a07b26f29ed425b29da72a4edbd", + "0x8d707854adfe5b2f93a2c44a74678da5b043f0d4457570806c776dadad3d94033f5e5e487ae284061eaa691e95f050f2", + "0x87b01f8f98519815eda37f6bb4c225284805ac8352cff2361d7b5da4538ed23650101c8485c365b0d9c53ba23a717523", + "0x92c08d20120488d200235782b799d6c344074c147f579ac08ebd86d055751ee6789fc661e79117db7c213bf9ddfdf40a", + "0xad534969ed1026b0e5d1f1aed60511a46f9cb5969faa9803f2a13623e5f877dae02b16c2c177ecb7eae8de42e8950213", + "0xb729e82482519d5db5561c4b67167feb1a7a3499db8fe71a3a739e4c2c48827db870c6e3f5cbfce77885a31e23b28781", + "0x8f017468ea3d8cab5e0063d2cd40f476f8de456ea1f5d2b228131f493b3ab7e74798fa9f6e1559afaea4fc2501eaecdc", + "0x9797c616fe91ab1d877ae67367f431d10f07b660fa3615fd528abf712477310e643a5215dfd1e6e0ba6762adc065e50d", + "0xa9a77bd8a1fd5490c5e0e68d7d2338e9bfa49d672513e88cadbfb645251a81858a2fb9b26aeb653aa024d822bbb7dd48", + "0x8be2725fe87a1689c92fa4143159bec4b021e3c345910ac14bac272d8ae5aca8f49ee95d67ab43e30d5f4d89a60218d4", + "0x95f7b6b30804a41ac94d4452a7f37bc90320185e37ecb2382ffdadf7054d464ee05cba00c26c4b49c6809ea438e321a3", + "0x83fc180131c52b2afa21db86ab23d97b5b7810de2ec0e057e4e2bfd508168b5421d906c4564f8dc27e3783a3f7f3bac2", + "0x819abd4ac6b44d53252ed156c675ae2040d20a1ed0d0501b914679de75b7095ef57f272e674951c01151c513d1b1faa4", + "0x9569907bc9891dd4fd37db86cac12cb857871392fed50ba63e00bed0ae34339ee81795e39382da6446f9d063a2bf118b", + "0xa04347bd9b0972497ff4eb8c31255f85718ac3720ecd06f49e7756c637303cba624319840883d3e38197ef5f9f2b5b8e", + "0xa22cf690aeb83655f885ba709965f29b646f7a60afeb3d9b7c4a69740a927ff2a70234b5d74f8e70a04fa0e03e785e33", + "0xa71411f587e96162370ce3e0fd7668c28065b592102bec8cd2ba077c367b1a62b98b433f38f5cdfb7aef5c7a9c08c96e", + "0x8192528d8d83f9848f15e08e59ce943009e141fb6cdf4b68b8d1e229b81ddb6307b00ce92c1a65c2de4cab4f0f87814a", + "0x8fd074b4cba20008d5624536ecc128891109acccf70722433c80cb1d880a9a37353daa2fbb35d6ad9a8351a757e445e6", + "0x8676c4d4fb19e47e37b96f51e31e6926c95de77d089bba01fb2e86554b88bc4fdfd5689460a9b5cffac626b6bf161e52", + "0xb7e5bd5d0dba84574ec1802a312bec463796af77e7eefc65ca3149dd24fe30687b70c91d16db7aebddcf56943da78536", + "0xb724f7e8d34b3f8e607068f9ae5e08c0c0a86d96da4b2c42e424655bc4b3cc3e3fa56d92c298fe87385bd357ac1b874e", + "0x89507c486021928e83b3bfbc59b3d1997c8e8c7364a2a84f578b5ff20f385edc0b923b11c9a2954181ba7a60716ec2a2", + "0x89905201bf6fc8f2e90fd8980fae86c3f5b3748a3a982c4179944fd4981019881bcff164325873ab02bbb3c5bba747f3", + "0xa5c9181515b5a7200dbf39e997c013a508c802e61f3a22ebf1d0838c91cf9442a636951a9c3abb78a0a54c0e01628ce0", + "0xa47e57a28faa5fb358222b96082758d49824dd84e884fde595c4818e6f79f6d4608ecbd7bf475aa180fcfdfc2f18c9e7", + "0xa6b5ba73db505b87038a30d8dad3c9f105a5053b0101d01e58b230339878aff93a426603ea9c3057efc96754be4fb6f8", + "0xaf96c80e23ce51e78bc30508a2ce9da86c01910b65f0686c7c0e27dd36764fef597a60808436d0065daf7deaffa0dc46", + "0xa300c1b8628119ece62f49439314e71fff2078d08b6e4279d2412fc74b3a200613d714ea59d7ee21e8dc00b17dfc2b52", + "0xaaed871cf7c1e2a84e6d5c88206583b054cdee8eae5272bc85485d384f1d3bbc6cff0330a511fe81bae8b3a17ecefad2", + "0x972c3b3db2221fba2db6f1af0067646d7c0cfa88757ff6c14d6819412e17fc08cc8caec4e8cb8bfcccc21f1f0b00117b", + "0xa40096034552b4722d4a702cb50eb4111f4884ca4d7a73786f391932c27c5e0b449373ad3cdcabde9f0813deaf01f202", + "0x844977632b0e5958c115d5584b8c28ef39fac5516098a42b9e922f31bf14755db1f04cb820207d081531f19b50157224", + "0xa7399d34a3a515fa4d7746479c3f808495545ffe2c35de8f08aa21fed6efef2bf0f4d678a324fe603c1d7017c308bdc8", + "0xaaaac109a4eec92ddc4ef53aa4269c557a5301c8f7c9c55380ecaa51bc9891486eda0845624499f1f2b47231225788df", + "0xab89a3c1c94bcb2a7b1551dfac6eb790ec8645f464d1a1d020b8dbde208a877ddb95efc35e04e022ce7e41b2158826f8", + "0xb4060010a064cf0bbc3bc1e7f703ce9523722c537c0592bc35249c7cfcedc0bd3ec9570444bbe09dc7a3b1b8724eb7ac", + "0xb8e4b4fbb1ae4620bb8ace077bcb20872c04b1dae9100f71ca0a6761f9445d7f414c59e55f2a299b0227615a35b02b9f", + "0x89443830ff289777bb568292d0f15ba777ea5a72734e57c1df7032a02264b3c15659be1235805957536dbc6326b9fe34", + "0xac58da1392029f0efdac651b97f19ddd9be8146c06ae44ff4d87b13c14a9012bc13ea1deec33fbb67f7ad4cd9e9abee2", + "0x8c5657f57516242fb4ede5f6d3dd3e98a592f63aacb830349ff19c9c80061bd6a537fd5fed43da43dfa9f1949f1ead22", + "0x90e2a0844f7630fe20c88244139942d2744c0255e0577de7ecad8c9ac8d18ff6259f074c0f4f7d90492a62ad07d60452", + "0x87bba03333fad326c39da4899e858fe616c2b36769b3c72d9266f8da1e6ce10c98b6860b41474bee3eb882ae9e5552dc", + "0xa02fe03d509ad394a51cbae3d95133e4b7b39377b87a1f6901e8653d19a2bad77ad1f0bace8d761a0e779768d60c4ac1", + "0x98e93c7a6c824b6d67537c9b009019f43f0917f5e83698e6a195982958d1a562004553546ffd5226048ab702da04c310", + "0xb7fa8aa5534ec384c372068c4325b23f07406e43dcce3b6c6ca741129a0b0d94d44fadb81d56208a22dc5317a7b04be7", + "0x880d3d67f0c39abeda1c94e980b81a01536c70a48aee74b3b230f5f2187527b0ead54584efd750af11570ee4512a65e1", + "0x922bc9a713f18ba16d30938008e32257973084b85fea2332d99ef799411ecdbccc1f6f5323f5fd905fd5463748688aa6", + "0xaeffcbb5966afbff68c5ba190468d3765edf800216ff8b94f1f0c8b8686621b57f4f73d0f52b2e48ad72e6cd6afcdaf3", + "0xaaebf9085ed77d1e4628d812d570a9e193b2fa1fd04efb27c6392f292619a9dc936d38cd89ca627b786154f02c06f8e5", + "0xb12918bc1cb8151bb9ce34dfa97e9c5ae00fb5a438bf83695a39172b2575ae93040e23eb84730c809b4ef9af099e6860", + "0xab8bf70c98989f4dfccfdd7b2da2bfe7a4ca510b86fd7dca797a8b9d4ba2f31d06c8e69f1331ec36fe1d1cb793862afb", + "0xb374a716a34eae2d018d808d34e993050885a69b30deb417778f83dc2ad72f6469112e84df719216b6b0a94300883ba1", + "0xb4acebc76c75b852a3cdb0505125d5c80b3779c0b3805c06026a9b2837718ed7b411fbc85f8b699364c731a2dc735aae", + "0x873c15baba5ac29f2adf984cf3cb557ab70bdc5135d319a69a496e552c37ca8bbfb6fa88887a9a30a17acf5228ce1a2b", + "0x86fa1a51b569ea530088d2f5c0a4b69cb6ec2b17508c68fffc65cae7dc18e6fec1c9fedb14f4c740c2ced0b286b0612b", + "0x952e68fbc4d47ba0e62c6c91e1e03d105ddd5977a73b2cad945f311bbdc54f1c2063c257899ed9be1458c13521076dd3", + "0xac85b12939ea94e775c30960b06c69ce003162d5dd1aaffbec7974069d909a66ab99b804d92c34a87de839b61a738e6a", + "0xa929a903af7537af6da1c4e373e7096fb053a804e2087c16cb945e865391532e1a0e3ae02116e5ddd56e149bab1c16f9", + "0xafa967cc5ac598d2626864547b9d3862c89d164ec52512241e78e900fb74344ec507cd2e9d154c669c01d217479f4f60", + "0x81df8c976ff3f83c1fa5ed1556c466fbbd6025e9af8444dfc3dc1e21fd8a6e2ee26a197b9acf5b96ae75129f9a8502b8", + "0x8637e9554176f2e8c6d3c8382d09ceebb3192e6c94ec9922a1ce4e76b4a96596158255139b3c73ac486f2f46292d7ddc", + "0x911b59239c35cd2f7d2d80df4578f2ab977c670236f991e094319eb69741cc15a6d05571228ed218b9e844eed6b5e12b", + "0xa60fd0581b4c5b052e5cbf9163380d2f5736c7747659665d9bd8360d136395286435aeb540303c59b139741854167620", + "0xaf2e0166701c3f8ed5b082bfddc3fed0eaf3fcc397cdf4ae4126243a9ac8c5c823d9aa8d74f26ec8244d8d0c5ae0320a", + "0xa7b71e358b6e5914cb2389d89eb5ac58d08030f926ca3afbda0a60ed02a2e82c53b8c21178c1d8e1fdeaaa14551ea8dd", + "0x8048e56d6e9e7a4dd09d00d7c22333723cbb9c29089d98cb11788ed5b170df737df7c8abe31aab49a77be92bf4520140", + "0x847e79efdaad713cdfe39638b7df4514ce4f8bad54c99ffed48d93027b3e264abfb3edeab2d3704e41bb5b9454f7c9ef", + "0x886d01b3dca6c60121476b6da0eb273d231fdd24393af5437c934ce2711cba1904f60aeb6fd031947cabf7e9a5c2ce0f", + "0xb5d37d466144a054d8fcf97c0221cf7cd4ca4f9f2f0c2be8f553e1169cebe286b566c7bb47e674381403f9373945c33b", + "0x928ee5b78cf9ef4f5dd7ce7830d761e87e2de5fabf0419e4160d6ffc59dd797e9e04ddc68db57fe729765fcc851256fb", + "0xb6df1e8e22db90fe94463067e578fd045132ee2a50678a339fbec5814bfbefff5d4f22147718767dd8d9bcb834e6c2e3", + "0xaa8910274e2f4079fb14b954062d8f8272f45d14ece3712e23bb3e5ebc60a65013946c74814f07eb10137f96f1fca3aa", + "0x8c8685436c84e73775152a16173759a7bad8ec78b55ea483600710c603aa8e8a27806e189d095925976ce28013c68719", + "0x8f8f5f715d8b85286e05f3f9d12b57fca87dfc65a58dece32f69da1d74dc80d7650ce688c745a98e4ba80c8bc7362083", + "0x98043f4271594a975a4b61d2cfcf663d372debb289719b353ebb8b946a0d55348522b088ba4308557c112101c408ea9a", + "0xb8320fb9cad7425fb6ce65f06be76a0629b71dc98cac426515adee5b38b9a574d637433fe268a9db2c993145c2a8e533", + "0xa9157484e7d1c45c91c1d863c430e92315a93eca8d72abd2bcbae5ced88e1936d20d87696540ef8f59a2cc357e02c6f0", + "0xac17ddb7f1e1ca96527d8737e749e9207bd5df4b65d54f6dd78a3bb2c3d5a131173fadc8b8da0cf32c3b16b265c8111d", + "0x8cb62d0639455033184a745d6a572a868e31dc2737d38ad548d8c31995d68624cd94ff8825a61059ea506eec7cb21644", + "0xab953b9c1dbc0b81d96dd8f9e6574ae89b0968064e922664747521749bfa7a3895f9d06e89599f4dd59f52ecf190d56d", + "0x868fa16a20c35eeb3f48605c06527f6b86ac06b8d8fefb34d6a03ef162c646ba49a8f1be881cb2e6fbccba280780d370", + "0x8d36816a0f1a2f205ca6459f20603cef195cd1d0e63acc47e3e30888970d56d77ad4806c3bab9717f1b85861bf114be7", + "0x8fa55764c0224e2d61199ba902b50a6b5eca74427d3a7d09821f25a2678b9bf3179e35ddca314a892948a950a2f0f007", + "0x80e66689f2fd72248c77f883c62d5f343a93294a6ddc1b2fed037ed258ce7970256ce5db826d6dab7675180282005715", + "0xb7cf6c3edf555966db1ff85128896ea8b91b0ce66d30793ff887307698064c145c681001a8cd203ec209625226fa7edf", + "0x991f6aff66c6fcfef3bf9f8374885874479720b79225666fec9571b9463fe1abd41947bab31a38fac577d24389b92f22", + "0x867980f4791880ec0c9a6014bcbbc00ef9f0968ca8872bd57296ea4c514f47b08ef51f34d00df94b081729409a4837d5", + "0xb3f82240e012a852986012d2fef2c1a15c6103ac7701ea42eeb916205034f8c2ae2935375c96753410ac9807084af7a6", + "0xa8518ed7e904582d6fcdc1293c302dbe0dc39af957c133eb0ee11a7b6838b849fbc52eab9695cc6abbc907ca8ac8842f", + "0x83decdd477bf405a1d3476956b758c7e1904834f8d75567ea836d2c0c6ff6e02e79b873af88648cb5eaacbf481ee6add", + "0xb126c2af681aa6b82424a64204cbaa1136f9d4c7309b0d9734b2753f30ea25f2a433e4f7d1772078d1bc2b03f7f8d1f5", + "0xb83d4b0859021b8a1d626df2d3df95a1dbad04164c593f01375b639b104c0a0185e6ca70288a03956c03aca9c0bb32d2", + "0xaf4cc6523aeed2c6518ef32566de3dba7ad72221b76be6522b8aa69dd3a4ce8528fc0e5f48658010dd3b307825625093", + "0xb257902555d0d9c277a1ae585dfddbc7654ee7311bfdffc283823fae26f0950b094f84ba74c5c501b8443d21a87e25cd", + "0xa81a233d481b0daab31e1f44dcbabce8853443a672d14bd4ce647ba6848acb33996bf893503b1f0798e7fa6812169593", + "0xb2ae8fc98cd12f1fd439b3bb507de47783a73f715055f5e6f76d653e5304c6fefebff77b98aa9e8bff8d849d11dc083b", + "0x98453c111a39ce183dbd6fb606eaf606be5c9f2b6ef8d4590294ed1d66be085b6d020833496a11f10083362e203a73ed", + "0xaf79f45e8f95e7cbe6c2377daf1de7a438706c11d92758a92d13d0cc0ed0e57a492055c7d2d0ae2a584186a71ccba5eb", + "0xad7627321b3aadd60f53e25eea10a329c6b5df0c9a329ce62964010805e3d75718057627daf0a6cce5e7e32e61730819", + "0x81cc65fcd766a5c988a29120e2cc6e17c58bc87f24942d7132b1ea1e700d32e11b910543ccfe9430ade2733a1ac58802", + "0xb7873379bd5e3bf6699dc9e381d17894ebc0a140d3480b5bedfb0cb4b9c58bf8d1f2cc83824cda26b4174310e6b4297f", + "0x90dd749144f9fe46282b86ae62a6f8ee770298f80d838829d157824dc4d84458052cdcd2e03974a7ab5f0922555f0279", + "0xadc50e26a8604c4a2d63795ac29896056b2bebf2c2db100c3a552260b951132511ea47e51ddd894c0f229fc02ef6517e", + "0xa2ede40f63bcffd417a7e7df75e564b58b38f8764e46e4ea0f48f07b70ddbb540bd533659e40b50ec9756a5537e851c1", + "0xacb1f8519d483d8bedf2780249a2faf5e619677bef8cc9a1358573199326fc8be67c933f68e06d1b4ff7723d21290264", + "0x857277a59bc10c50ed87b8a8ab78afcacec771972a2391869b101c0ce6aeda8a96602044134eaf694a8097ef9e4f7618", + "0xb1f8b44981630c31055092cc65178686aa68d30e97bf08a03c35e6dcbb6b3a88d8c748ecffa6cebf41b215540bd9000d", + "0xa92840fa3f3c7822dc8614dc9886184e7e5509657f7aa1667cd847ddc06598e6af7d3eee252ef46021ce237f9a0e2b93", + "0x92a72f190480668f754d0d0a575d01a33c0ef9d736ca641a8e474b71d1d0516191865922a271be773cd5a8de39af0fc7", + "0x8f967ac8717d941fb3de784c5e2cbee89a01ae31d6c75e5b53da41d271ba40031f9503982e07aa6771c29a40f4e42448", + "0xb206e346ba237fb306f2633c580b41a1c20ed6cc68f3963935c381241d927980a53197fdefddd0d2a131dd016c007c72", + "0xa15b850917980eec2456ae0af38981773c35e42a3bc8d4a43f965970455c6264318270dddc82d5ab66f80655476616f7", + "0x96d7f2c8229cc54426f63a48d898f8515ff8bf21ac5a2e81af9be9418c9af07d355db44defb34e42e4ce39f5568490a9", + "0x81b47895b4a249d997c67ee73c15721b9f1bc629d6420c3ba0157f73d2a55ecfcf046d1ffbfa532d035c55634aa87b2f", + "0xa58dd50badc62241433b9793273d13255651524e8ee7594ce8cbd2e2ef08bf386147f232c1723f69f8e850b62801bc92", + "0x97b06aefdc54acd539ce0dab8240d37c0e74ea9f12f73c59949aa7e3f2fb91611d0e92c53c11193511080dab70a0a6bd", + "0x8e6002d7213948bfb6e865295de1e22fee217d7d8cab49a9a3957f8a5b681782b2ec5c345044d4211c86ca589527f3eb", + "0xb75aacb41b4155d27491ffdc403aee54ccd3ba0ef7989a637d7d1303d3e3f6e85639daee07e4fe113f45533b74a88c92", + "0x89c3d673edb5e557c2c4834747becac1eef6f871eb9b1d9b0c94e8f6ae7a27bb50e69ef379085b9b60cf2db767e95699", + "0x89b0e199db69b1e02f84fdeec519db07d1cdf16a6321d8cd713764dfda6b999f1b9344f349f772c66c3468cbe861509a", + "0x8b8051408edb1c71fb615b911f1464cd3c7b5a2c37e04e64f5518562db209c65d98403fe9f3524ec8f0a6ae7db108c28", + "0x99d0d877331bbfd1877d138f4e0952a92c68996dce02b5cba15402c23e10ca304856fe633ef8669b6e85f5f60d4d54f5", + "0x9701e07f9c76a407e22cfe58a87c9c155d229a506732728d7c6ccb759e79de5c667e007fb5a1672a9e2539b7ae4f1b9e", + "0x8f7046a7c6cb7634cb2611644748ae5da2cf3e5f00b55e2f132ee4147ffd1a75012f04bfd49a7adef9117a948369414c", + "0x919884794fb151215763110c6595c86a1a79d0404ee5eca20344faa787cb489d0cde30dd9ee4bcdc42408ab6c67f13a4", + "0x9480e52c34bdcb7f9d9698c166fb087b4775cba38dc7f523a11cf47aeac2de65c884353c32f9e78316acc016bc66cecb", + "0x9787665c598938656f594aa6a678baf1ac3228ccb518265efe6ba30408bba8daef72bf3dad050445fb5d6723b79ef23e", + "0xb8a590a84843ac5f7b28f507026d5548940adfce4b4d849ddaabe2e00f278ecf982d27883a7a62efe19c9f4bdf67e20d", + "0x97f3cc546b575b9cfb15c91acf1c1cd44f60b6e52cbfd37c5f21e68a4422faa02f49fe944b96a99c27138abdbd47af89", + "0x8a2ff5a4d642b37063c96023a675a3a12cd4c28d653d0b348e1a787cbaa6abc078c9508c19aac5cf10404a560037096e", + "0x92a5fd98bc880773a1028ada6dfa2bbb4c15b112902d4e2c54a940089ee48211c7302f2b6d94fa7a4c846b754b44ab02", + "0xaf7dc48e4e20f1b00011b11b1f93bcc1d9d703e05ab9d1cb5a5167b33448fb4e969000640e66bc9ef336cb70f9e399c8", + "0x98ec16bdb8d8c11a39d540144d336bb471926cbd3e0baca64e7006278645e0db7145b7faf5efdb31096934497995c6f7", + "0xb0fa84e3068cb7175432dc4b33c733ceca8c578b41d80bdb30dd6e3670246b27db5cf4791bf909e35dbf21b567abb9a8", + "0xa7a3ed1e51d22261a83c1249cfbc92daaa12cc196848eadde47cee2cf21b82a8a2cf0c02590ad1f663a3d595c6c7f366", + "0x932e159a04c51cc65334505f433c56a271f621dfeb9033fba8ba45c6fc7d089b629bcae1805770c0d44959c5482140ca", + "0xb62697674fdf7f665549918b63c6c165d396a08e3bcdb8c6f7da0f4fd47e86566614fe265e45a66961aab2e784c0a1ff", + "0xa714359bc528eb9a4cd8e42ed4b059a3d4126305df3f2c55d2b8a4249d77fad1260765e7604a13e33a191b179061c1da", + "0x96e57ce3787bcc23c0b5da5ce9b125a865f49cc8d1465c71b6c788bc48d62e0cce3cc4642dc0f4f9011597409a48cd9f", + "0x895e1748bee408cb1592c027f97d67ea1e5cb967eaf4191b485c9a53649cb59dab9b16d1110c939b9baf8dbf2d96c194", + "0xb6bd0f9980300be507451fd85549a70a6f875545932b90df64027b33c48bb37e6723a11071dbc4a1cee280574a554ab1", + "0xb51edb24e7a12c47d9a4eca6acf2141832ab213d79ecb45e08ed623bceb3b9f45e29e49fb8d293c35ba829baabdc3c86", + "0xb656995a0be1d8ac81e815641c81454503c21dfcadab615262b2d642768d4bb036a952f69e03d6232fec92907ea67462", + "0xaa8d70e310f04368483e56d36b1e9836c3cc090b926f40a30873d044fe0c238ae871964bcb562e017d6d81c791121e67", + "0x8e19b72a06e8b6b2df8724a2e9786854a2ae144f8d1460526d4d27f8d9f8de3f6b20e6c6fd86a5dad74ec7f029780595", + "0xa1184a73d21d3a19983eac0029cb05b6f43651815d486bbfb962d2b54aff69e6a7cc98bab87310e5673f137c20c98972", + "0x8fa6ddfc8a478cd65d472b067d8706e2d18210466ae551ca82688b35907b4a20a8ef98a4fe0ccbfdd8bc5f3cb74d1a7f", + "0x8d9ae18df6a7b463c48e0fe235a17224fc8e49d9e517d78de317942a6b536cd43ed2b1a78f9f24e3db36876329a7c87b", + "0xb2b2aff6e6950a499aeadf0cf524d2c2550c2295cde2abf322b82214a54f2cb365d9a3d765a45cc3edeac8fa06a72804", + "0xa64a3b5dbf7b7ad9f6a81119324f5a93e5c2622cb145dbaa25b21f45e20275100262f16649386a32f1b9cf2528372ae7", + "0xb1b8746cbcde456b87474cb91cec19aea3d6c11040eb2a0b573e2c4cc006cf752d2de81d31d38f8c44ac666a08eb5ac6", + "0x8d3cacb79f8e65b9e1d37f86303ad4d5fb90bcc0e5156ea5d9e73f330e70be2d90447b5ed55f66b564128a6afe057302", + "0xaeeccd874a29bbd2775d36dc9661ee28294498fe79ed6e1967cac02ccb21ab8ef3496eecd43fcab0dc8f9fe0e9d8314e", + "0xaa739310d74c4bcfaf7ee888846694ae30edca793473e3a359bd1421708c840ec652ef0218469966d088ccf03377c9bd", + "0xafda3dbb1b38f21e2e41d8087ff6a657d060445ded26abf4b46b3c53a3bef360f808de204b13dd0cac90eb05bc4cc1a2", + "0x8e65635a1e022e45a206330def85ba13623e97584439499ef3e9e443067155210cef08eb83e3be98ed85fe07261b3a4b", + "0x8ca0fe186f7bd617826ad254673ccbd02e156bbcee046bed70f7b70b946a4500ea528952b30d5ad77d0ca7920aa82231", + "0x89f0e2a935ad381d7c172e66af148ba32044eb5ff60d83864cf62e0d3b759cc4c5e2f2c6e6fc937d9e709885689dba11", + "0x91d037e052e1ab7b656960fb5c82948c7e67d06d2f346711e8f4331019ee5c8b5fe2810a757049f2a0baf02986da2b5f", + "0x8b8a3306af126b48d70752fe6051ca1cf6ac29ff7eb6cb6a53f8290efb380f2f11a4fb1cc4961e0232130e0cc9bb71a1", + "0xb2719e20599dc0ef0b78f9bbfeb83d9df5c1678959cd640525013f7a54add62a055c86617475108e8503691f916730ab", + "0xa9b393db7675f637ba8ab485824148a4371a193dddccbdb4701b0a789fe9a977beb1add560917fb7af7d37990c36e6de", + "0xb49b961f4d9aacec4290047e4938578ceababd564fc9db222c9d7ed78ab27c7600d1ddc62290c81c25bf39c990f29760", + "0x86fd83a6ace6c961b86c794819c7feb0a4260a2b5a26323124bd8ff42fa36e595a01ddf848e21ec76092e8f7a9e325c0", + "0x88dff5f01073e89f8e2469006a751dfc0f488955a5ddc4534f89a10dcbd85130427092ee4ad9cc0d8aaa10034b16ca7f", + "0xb58b68ad4ef43421afe8f1ecb9fca828e7a79f01567c2230b6be556d143bd51b21a6a24836ef0dc0a2a4d757d0f7e93f", + "0xb5d75f9741aa3fabebc1a105df8415e2b81230b399fb224a6497989404bb50fdf2d24a9d0789834e5a1b274ffd56b73d", + "0xab0716d964094fa1f83b80b28a41e6fcdca2dd84151bb2f211e51d49bbc70e496e44643b64dbd3805b924b0c079f2f89", + "0xa91fed4e95695fd22830307073081298061da9a1ee0a6dc1c4ede48f70c2c34bd418d2492d559c701228225b7ff6146e", + "0x8a0fd3d22c62e115856b48713c2e3a23fa108a970aef45e649fbd291c19fe3d0c1c25dced519e1b10945aace7608bf0c", + "0xb0595a1801579727dbb553e6a39bda291af33b65fc2b487455b2aef0b4607ace1ef9e3079fc003cfe1d133b1f49c9a9f", + "0xad1d544ed9022ce7f7cd24d4e5025e918fb61e286f7ecf6ceaf234b9529f9be0ea4692be5c42479a32d9b793a7b17373", + "0xaf80e0d7d705564c7af4fc31c7d3f4c37ca1f19c4f08d04af1b54c5a09bb20471b105d113924270815f4b70ef3dd0d7f", + "0xa24b3b82f8456003a96b229b44a55dee863d35f2d2a71897a6f0e00ac643e25ca92ba6efd3ca3af50f1121be7e9b6149", + "0x802f6a2cf92f1b9d93cd008d11d907c69c90941da720fab3541fff23ddb3791cc813cd83ba4781c43b0ad9c57c358ba6", + "0x99ca440ade2d2a886767b4367ed3a7b2d2809e4e21c7ff1d4f832b347175f9ebd416cd1276190553a502deb2b49e7ca0", + "0x8b36dbca5d440c3dbe8defcabd735ee2682a414329985704b7a218a49785f70f8753a7c62fbcef2cbfd441ec198a3738", + "0x8b649c4af6a535af7723eae03626f709bb66cf5928303c52aa747d6a9d00f856d43f77e4b32e53c022ff78c76785724a", + "0x84d9d9f927931dc27ceb06657e3fe90dcdf18eaa68f8926bbb1c4b45c710c7bb372cbb9c6aa5300ecc298e7837ba6597", + "0x90283ce593c74ab4a3d40ebb03cdbaf662d20d9b0c23eb22836de07d92ae085583ff26bc457579b55bd5dddb6710ffb2", + "0x9200ef267c121ee5e58c6a449881f5b28d72243203da61889ec05fabf60ca43508ee29fd996703607b49296f8758e7f5", + "0x98902a1e204b32dce1951421efe3839cd98fa032a43c11402f6ab13f1fe295e450d225ef40094a8c1c53fbf66783f9a8", + "0xa136e59795128af0099e4184ee79456c667766a8b7d5bcfe4d2561165f5f28cfa678add358fca79434654616025bba23", + "0xa6b89ec95eeb900d0eec013ba70b65b9b7c29a8a42c43b2da19afcd846a184d04c0fbb241287f69491456c1405edddbb", + "0xad2e37fec3f21d3896c79aac9ee5bf19c2dcf37df02845a48b19833f895112e96ea3f8e64e85c0ae5d890bd5993c406d", + "0xb94fc394590477009f453d0f3b83b678ffa9b569b57aa447a52f0f914361fda3f0bbbd1f3a9420786c06df79d0650828", + "0x88508006226b70ef6c6158e750da8a349148e00778e2edc29942266f7444d9d8d7429156645605eadf1fbe3ce3306f82", + "0x9650434dc92ae457daaf07af5b7852e21e0f3ab950c8e27ed19b2f836f625ceafe0c2ec88f85e8c2c4a1f0f7944a786e", + "0x8c3ce4638ccbcad787d3b132088b1756b85f1811c5347225b671c3835fc63b5e60e237e5541bc551d92fdbe37a711ccd", + "0xb93e63245d735922d85998dcc03de71c9e4d4585646ad388871f4e2101e22db3e4bc2258be21bc4f804deefcf772438e", + "0xa6e84e9d02b6af82a6af989c5d90b0a3a7d410b77a59a43216e787ff4b49cdc9f86a344b94e1be45386152b70081a3f5", + "0x8a266208c5494719514334505c2531c6c6ac9cc2e58ef8d8569b84bc2c035ce568d28dd0a4a855ec5c2fdae8fd64a8f2", + "0xb38872cd236c815f6142914e3b1aa85c8cebb6e7fe7aebcc87b260eb1390ff9f3e49118dbb501bb3c53706cb76a1a098", + "0xab1a6b4e9bb70b3c633b83a1102eb38fba3195076da098bc10ec1b902e420b81e42e337f1988d58a8933802e8f9c5965", + "0x959a82675da127f01364727e00f4bc835b9adfdd64a9acd2e05ad3a8f180ae6ef7f3085354fa421f03435b6a8b5ccd2c", + "0xb667c1ca470e20a646bf15b2f3d97cb026add591a5e32a0872e6fd1f9d4e3e1f9e4667482a50f20e714fee0a1baf0f0e", + "0xacb4b73a2552157b5ec27f403127e825562e170e9ea99ff70fca6c8f52d1404ce193717b7447e23255687c2a6934b278", + "0x8b14fa41d82ea3f1ac9b4a833b780e53c1936bf36d4aa9c82b012d4de7e2b3ea461854d0b2d295bbc1ac1c1c1f681f39", + "0x8c0137f57ce345b83bb34c9645af6bb87a2fa8f8811357d5d38fe77a6d6dc89080a6b97cd354304fdc8e597c28272bb0", + "0xa7ef1e703c5de619e11334c9147e3b800951568d06a5d3b323c490a00cb42a1199b06f4a20d40797feb0f7dac84db87d", + "0x96cf0f795c4fe00fe5300e2339dabf8ce97c7a6b95230510d295bce59a5ebb82a281a3b96741809847a37b128e642074", + "0xabbfdbf401055c5cf88012b8c4b304f724cea267246136351562b1effb3a46f5c3c784aca86f9927b445574a86ae4f27", + "0xb809c515dc7471f09176aea6046d6d4f272857bcbb85bef9bbc0ffbce59d2b60a6dfa96936e3162133ffc0e22de82528", + "0xa7cb56bde35034ad18577a54def2ce615c0f56e5d7a104e3b46f03cb851d894d005bd31abf49f47cfba615ae29a05ed0", + "0x8e0fb940144664d8ca85d98788da1b9e8f3ed941fd005ad43ce52fbe55372a380e978583f013243b63c4b69ae713d6ae", + "0xa92354270042fd73e2e5723608d57cabeb7f2849db96723a73d76a58be3a44ee66ea2cc5bbdead7bc32aa15020f314eb", + "0x992ba2a05a8b04e33fe1535b53c7bec5fae6db4d49c87d69aa35409ae5840ff6d53057ba79cbc9c0a6cae5dbcfc9b016", + "0x98cf48f785fe47f9fe68e28ad1773f5e1b3f92286d497826cfc2619d409b5c38d508f77c821f21631aebc62a1ff1481d", + "0xa233fcea8ed914f4d2222ed6968e9de3da1677e2f21908652f3d6e410bcebb3df77f7068b05fb9c0658f9fa418009ee5", + "0x90bf9c5e174dd56058385c6fe1a873c6d0370cb395f84b91fcba6541481c762ce0b5a8677ffe655b601621577a192072", + "0x91b23a3234f9cd85023de682bef5057096f9261d3e3e262ce9fc38357a4f6bc9573f3d362a24c7236838293f1e53e205", + "0x81b9dbd5f058c9a4674169868802dac9de64647a2df232ef3169a3a4efa7442d133a3d16c64f46f9a16e8722da7480f8", + "0x8be21e1378ab6a9f335309e424cffa55b598aaf9cc972370962877166381b200d8d72f4cd633e757848b3f91362903b2", + "0x9662a114e4f2bb47855c9ad884831f89ebaccbc711320b539e6a5e32fdd3408fe8eae8943952383285446fa67a1c1543", + "0xb0bc8b7ee8f8fc5355de4590c0feee3c1668875e5174e0b217fce6e1834c821be2af27382aa3524c93a20f6b388cd997", + "0x82e430c2209e73d0d9889ba476adff143ad62dbb7aa4e444d1e9374c31395ebf060058b69715f0c5d5d87efc2318063b", + "0x8d7a3d5917adbf63f1f7fa7ed57d40abbcfa79c2562619d1330a70a1623a4287a5af30a5b7b55b53a35fd6def2beb928", + "0x84497371e64d0de538d1aad42792bde6a9c56289eb0905fd4b853843baa5a7c0ba3d9746a247c3324512e7348398c543", + "0xb9c2c68f63af0c796dc4b50add65d4c68c8f227f27ef1a68061f5c13aeed5670b1705cac0cccc24c32ea6c32f8c9322c", + "0x98da89bab57c34539f6aa986845ccbb059da40b9d09d22d729bb83cdc0531745e95928edbd5b9156ae0b1e527f070491", + "0x95309ad28dbe109036fe9879211fe35435afc38cb6f3581f48329790af2ea11dfedc9c1bcab93eed7d63a3c5491d3c62", + "0x8de5df6ab0e0f1c790ca00b244351d259216695afaa64d0883af45cdc11654b5285b52da147d0d1334ae30c4ad016988", + "0x955831a6a2305e271c75851b789af54c3a43c0ecf3eaa44bf1a0901439a07cabf6d0cfa00530e8081772f593a982b0c5", + "0xa5858ce6036b707aa2b147c64124a97af346ceac549a5dd218f4068ae95a2a3f6a15a25be04bcc5e2d7d8d3ae23414da", + "0x95d97450e39ace88e4ae8956e8e8624188bc3d7bf90cdd9b99a291f7c6d5abaa6d18bbdbc8d19256b98c5d0a51d2a790", + "0xa2be726cbb6edbdc2d4b231b973b5088e77964ee5c10be040ef47b825f7428413d2d89749471e423ee672bd0835c74a2", + "0xab92cd63d34a38fefa8efbcaf382b8dac023e8cdaa869fc12afbf1ef8c8dd2243381c611a2496e68f210d07f7dcd15e3", + "0x828732b4d86a7c42b8d1b26f45abf04eb21d08bbabcacf490db05a372e7dcad494671b0208189e173908443598447094", + "0xb0b5cd020b63ca389900771124a82e03bfcd91f97c2383b40a77cc09f4b5a6aa6a3b771ab4d51297101aa7f950b7fe6b", + "0xa57470527ee6e8b3f77388f468c33a9fe4b5e5ee676715a1c9651ff3aad44274318c73880300c28d34ec0b0adbfbf552", + "0xb614eed509e6a926476ef0f3d0c2b8e1fd7552e7ad1966788defdc3be7a71b59a7e815dff8a547fd720088206bca2125", + "0xb67d5f9160f8a63d472c86adc8e35e424ab9f63e33f214d35adf0f08dbb67974624101e9c632e68817e95f5b2031e27d", + "0x843915ff5120248b22956852d71d9c4ada0de12659bfcf9b233c52f2f83b40ab50bbd0499aed06ad35b3dfab654df528", + "0x90a53bcb84b94c910c16fc7dd42dbf9a541499cd280edb2caca291861264c64ca9daeb87b4822658d9e0f3748dd13090", + "0x87cc09a9547cc87dff84996c77a4323eb82f77a0a7d9baa69add071a8ba99d96dacc5d7e13c8e7af7358695762741761", + "0xaeb3a6c0e70c8864dcd51c6e0a995780be0c7fe91453835b9e88ce4a52302dde25341977ab35ffa48768b84769719a74", + "0xaa5ee1607540f3c8d3f147821e39c674645da80c058459d9224fcf979ae0d500dd6fafc2865d111de7bca34b7a4805f7", + "0xb354b60db45c9f53ec9634089c929b017658c2739883604e4d098cdc88eb08bc441a8f8c913766b5e727da6f2711ade2", + "0x9166fbb4210b870750bfb0c0bbeb24573aa50e6f95d8d01254db31d5c28de6b356a5b2a84193fbc37914f258c5d078cc", + "0x93b8178a20272827b6593cb513356db77db5512c3f21fb92a4d39dd5ce9472810badd562b1ab7a9a81a40e4a0be5aec8", + "0x81b6ecae7a835fbeda64cd70c372d72eeb8de1621d3a4208d3b2fae05cabdc0525fa195794f6cbdbf46da6ecb529170f", + "0x8e3afef4d9b55f3cc15e59b8581607b50657574d732747c4b18b5879222bf17cdb239e57023542ba5ffbe6e148ab9d74", + "0x83acb7efd1a67df2113fe1d14168beede308cb6f0183ec8f1eddf9a06fcaa40d02b29737fc6bdc2543802a08842a0049", + "0xa8dd92cf05ba4359edd6a9be51be1b9bb370075aa1ab7731f45eeb1b859f4032642f997d725c35c1771dd8aec546f148", + "0x81055f31fcd57f9fe1d81b5af056df9e640401ea972ecdeb5d66fdf2ff8feb607608bb9106ea38a7908730d67d6fe283", + "0x802466d0160fcc73c29a2f6eccb5ac0ceaa0b10b4181fa5c5a9c256cf07b0caba94f87b57adb360d3df479813e7cc35c", + "0xa8320d98394a5b10cc457f876b1de06c979e9ec6509a3b08795bf89d92ed5068799d959cc3009fb35191c41e8f5bbeb2", + "0x93d4869206835599c48cb426b369242652b1978b474c73c28f1232869eaf4f3f9f2fb7b37cb7e263cb7664fe57122d94", + "0xb63527a48d4cdccfc4d61cb646d593c145be9c8bd779ca64928526db954c7d6d4f323623deec5102ade58469aa8da59f", + "0x9252500589d51719b0dabca47a45acd17ec1650fb63bec17228de7e735d7a6dfd440fe7a95281aa3e15f7e760456eedb", + "0x886b79827b78354012a3455e62b4bdb1c2089847b3418a2256da7e67034dc8472e95bd7dcb5473405868764a7949b7a6", + "0x89e5cb182c878b13658234f06f18757a3c53f582099f6fd44a9e2d32bbc5e554c425420521323c046780559069a714d3", + "0x9061dc2c34bbf7f145bfeecc2da3d4b5047afafd263d473a36dea2efde84a4d34c5b32e8608b161567dddbf57c2d5b2e", + "0x8318fb49f9a0f6f7bf5c209f8e91366d61a064c70de668cb33477e0d9bf601a8b00369a1ecf0ce7e6690f78c267a6778", + "0xaa924223a47ea00ee9d8223e08ad32f9a2c55ac6d0dd96c9d8e629738502f15f494befd8868ab59512da2825fd3be192", + "0xa34621ed1daf698b359262a2af548826a638a32888685937fc60686555f969dac4e5e3ef988b49a6510cc62d3e246b75", + "0x93e99ae12c365508987dc10a3ef4f7a3694cc71e03e1f22f64228849a3d0a030d755bf1369dc61d7a224367808a97570", + "0xb575599e8cae3bb3d1b5c67f5d89635283e10925f6c8abd37bcc9e8783cfdecc91f8e925ccdd4ff5001eaae2a3ebeb36", + "0x94856ebf6b60e5d3367802a4104049ec759ca1af8b07b006cd4ae01efd453ad785dc74658d5b18a6dd29fc513bbdb7ad", + "0xa8ceed51e64c8f1453ef5ec8b8ccbb64ca1881414a885e051e539461cfe1155c677b05b27aab2c1ffefbdb63a7792c7c", + "0x8b0b6d06d86ee2972b69fbfabdb5e6dda197ae2e3112e97814281323846542a0235fdb5b2705c6c2f6084db40f28927a", + "0x8b76da9a3d8b9843a127ee112689c0bae613d4a31f9539c0317c75039f4454fc9e4cd7b42202b7efbf8e047528bc3b2b", + "0xa40de1dbabdc6a91c740d4a24af62deb398a0d5e27df703761958f9d0244c738f10c8f77063193365ad68a34b6f08d98", + "0x93e65156263f53d348a9c932856065a1ec6121d845ca18823269d5b2a530b96a193ba8005a238a04ff9fdd8dd95cc891", + "0x809b15057d93c21f7ea65596201aec3abb4f4e4335871996b98c7607b6d0d66ffd2ff14b6754318726fa8f45062b4fa7", + "0xb280eb4a1ee49732eebe5ece7a4b1296ec0424afccbc7d80d2307e3bd815433d10215dab30bdaa2520ae85daaad4ba94", + "0x907ed4b9d13164d946fadf2236a4dabef61d75c3069f8133ee42134a454984c707e6a69efb04cda70beab8469d10d582", + "0x9949674bdbada9da643aeaca36aee38a0d9ee4bfac649ae7f8092f27de9fb268f70d2cc56d0a7efe9b9a328928e9d329", + "0x901c754491e1bcb8bc37ebebc92f9d4463e3233eb2e57b984d96d775545c7c30c4dba1ad4b57cbc73df5cd2695588138", + "0x8dff88cf89fc478d565687cce6620fa735e3f53f7fe4f255d3a3b824e440b2d32706e433da1b745b217f70b106042b72", + "0xabc85ff195f75b0c378df131fab8162bdef1ff9a17434cdf11b15d5fb61e94c6087f559724c98ce6e5b2811dfee8c8c7", + "0xa42157623da19a4b8a369edf402bfcb5977aaabe882dd753d293e646a4437186c2a4b8f21745e8a1e13f0b02abbf27fb", + "0xb80d79f78f7461885518e310c65e52e08d906ca9cd7a042a108fc54411eecc6434bec775519a258789e3a9f4db521907", + "0xa960ff5db2330cf57b15c1a7bc58ce83b393019ff107ef11993539abb34ad2316cb059c5f3c1af81cb9e82b921ebd45f", + "0x8d234f7d3d9269a5accbae88d4269a5a7eec7e2782a37f8ace71e8c0769f3feb520c0b6852d907eb5d7221df1bba1e34", + "0xb7de4350f2b88f84cabc8ad366fa642bec4c27981aa08db0dd9086d1d87d50ab506e7c3846d819a21426111619c56209", + "0xaf20b04b1c1779d13e620f1b2abd76877e8d4bded302cb1e88be87bd4caa37f1e98620eff37c72875ef3f8cc45de4745", + "0xa837297fe2727502d86dcb66f4e75ee5efeff66176aea964b7cb42785ac12114891ec48d8eb75a510d1c098bec029e42", + "0x84858e7a94ca7ed5b2ee2aab20c6fab3d2798882294df4cd6de1cbefd2dd1c2aba522fe9232caa4305c875ddc16b8f46", + "0xa1a1fa00e2fdcea1a578eb0341ef8503b86f456f2df056a3b7fc48b3f44cdd630029cdb3ca5e2fdd331ae2572f44c473", + "0x95f3334cd2dcbb7d80239f56dcefb140501b5b27c011b4da460d518a016c2622cce32c2b6c1426d846ff23dc64851c15", + "0x9844ca4a2d30bc4015f34852573d8fefbf4c3ca566990829b616c781ca57065cf88e2e3f0137fe56ded0d7ed8c263096", + "0xaac60f35f75e923c9f0e14c0aa3d55d356a57a67ed15ec33f56566c3c6ef31aab5660bf6e80a6780b3d059592e8b5768", + "0xb8a2dded3a972476aa91c13fc9c393d04a7e1eedddf40e1ebd241e649685af79bae7628a53bb20f340c56e3411157f47", + "0xa0d7ad7229542972e4e89b357d6469aab17c6036674b7e122d9f7284713f689ffd2356031170c4ac6fcd72669fa4e2be", + "0x8c1af7d5a793b7db92cccf968f2a954486e6dc90e10b4771fc3ae047baf7c3c0d2d7226cf4113b66a264124f7691a836", + "0x818f9dedfaf1d8ee062731b02cf682f1d67d72f80eb264c27b7c162323790258849894fefe95ba14f594122e3621b25e", + "0xb33035cf25a00556354c6e1089de0eaee147c209aeaaa7e8afe6c00e8ecf8a87a52392e81849dcf5c93b3e28417e4a23", + "0x88fd49a86fa28be94b70473faffa4663fd7ec9f292405ac3e2a6ac2db6289254141be4a970bb54bf3fe1ed5550a6d79d", + "0x90bd61298c7fe34ee43abe4511c9c53ab61211ed0545a5dc8e276faab3cc783cdfd19b644616d301d1e4d37e9a9914b9", + "0xaab03308ddd101528cd8358f4be126949a11902c40aa1741a7d6e541e7e59dbb77b854d9e527f2170a1d465a4750fef4", + "0xabcde503c374f1ae083b139889b2ad98b61a5b189d2d866bfdae011d75b373d1920ea9d13f9b54b7923257cd69b4131a", + "0xb76eea51da61f502a178088752a2f2b408b0747d8f1d5b835cf39a94abb30dd3589f50381edad0c4eefa436f9507d37b", + "0xb907adf0f2863bc27b278b4428f1a825d8c3b7b79fd10fb29ecd50d5b4a395e8cc5daa9435d9eebe927300baa4a7dc49", + "0x87a921390f21f8ebf4ab6abdc012e1ccfed5441d31587ed7fe1dc2a121aa90479383b49cbcdf0921c76432a308e74614", + "0x805d4ef3051f0a06b4b4a542970562e4765bd74809517e7cca9a6be79dadb54a416f55e95911eee5bfbd90350de65184", + "0x87cb6064c0dfbc31bf21281a1f478538a397c91b3178edb7f4c1d744f61970aa43657a54720bcb118e5a793c5e3ef0be", + "0xb4679ef9c8f2f803727a1222b407daf2b27e445d5975e87bb3fc4baac6c8949ce7b493000586aca73a7ab3c65b5ba2af", + "0x82b8da02bb73573844334cb90006fe904729ba2b9c99261e6e433e53fa4c8f528a4446d5b21af02816bff7fabadf8173", + "0x8146368ac715ab6a22d7ea9ddbb239f20fa7805eb914d43df5339f111e0b680b05e86ebd19472b1f9d3d0b70166d8c3f", + "0xaafe9ae973c05ef78cc7b892c8d2aa5c98de3c712e6db230b31647f21a15b3c23d2087385a4eea98b5daab60cf28b435", + "0xacf58bebf1a34acbd870b4f70b4286f0285ab9fdc7e44fdcc293d6b95a71d7fccb7d51f00f8dd2144a8e78b5fd91937d", + "0xb79cb8d11f793126c4165dee7da334d02a4e8f84411f890afb5bd0c69d5f4f4db495ddd737e47f498514d9b2523a221b", + "0x908b79100473383864ce5c01ad421cb77e8e63f382de35a9e56ecd77ed14375e96b7e28c7f89eaf89b8ff3402a07d2e3", + "0x862864557f0af1c9b49d238ad11e409a39eb9de944a5436fe897f3099ff179b87147174589efac35ed154bef0856c281", + "0xb08176c9d805cf8eb8214794269ef6079db3e16505c76d503210e1119c02966dff82aca6b09aec24580edbcc278f2069", + "0xaf83e0bd35526d3a497a9aaab0a1fbbae8de642c864b5725298d5c27cc3649dc7a0278213668b8e10097572cb2e54a8b", + "0xa6094171cbb582f3602c0a59328ce7db2e0f7eebce44e7bfd997728d5a05e6bedf25fb38f5089c2b8de008e456ceb055", + "0xb54dea82532b6583f62f648feb717674cc4b9f940ef9ff524efef36b6fd8f83266a6134fa846db20c1998710c884a031", + "0xb1c7890c65eb70c22859e693017c7840cd422c58e890da81b5df0c1e8e7f980e738a05b5c94b6e876f94d0a5c79bb948", + "0xb0f0bdc0a1a1bbaf7a8c8385c860e9eea8ff686fe121dd1682c0df869bd8a6bcee2bdb4d0f68bb96bddce3c0e9e1b30f", + "0x87970dee1e2295b9f29165c163535be813822995260c143969316c5ce37110584e2ecb7073dedbbeb66706c184027a48", + "0x9334ec9b2a55988d0475bc015a42f3bd661d4116209630277b06163480f3d8814f7aa4af1071183dab05645d2637dc88", + "0xadce649232787f9b3d10b5ea6f59b5010a0437b19e905d12e96fbf1227331784a7ef425a8452228db05523a586a0b302", + "0x8206c1c73e33f65f0b819a9cd3366117c76e8706177533a99c0d23fefc72b782139abc38216e315164e18f4c4934dbb9", + "0xa1d1f048b283aee13f56ad2a6dc6bb65fa5c66b44e9baf95607a98e39d7857e03cc9d083234e245a94052850ecfbd35d", + "0xb625ec77df657e671044c052b6aca96979e8695dd74427f21755ededd4a4d9b778fa92522a7774a38b6ac8d4eea15f6e", + "0xa4492188f679fc10c93ac969e3a4c2baa62e4a0f84ab918ff14b0e767a1b68d80bf896aa9c68fd5b0611f743af4009af", + "0xb90b4505106bb841db4234bcb9612df88b280bdea8fe7f75764e537270835b635eb0796212401ce5a3679c609cfe1a79", + "0xa621b3cd62ed9e23b9468d2493cb378e1d5c74119a339a20bb28cdb4416ddfb1f308eeded5ea3c73785249269d85a887", + "0x802f3d737011f660b7602e737d72ba2e751225b7c56dfb2a156b12cb47a9d878ece0a44496053487fd583b0ff756b843", + "0xa47cc7f310fdf75a80eec0f5187e080bc78360c508680135ca69f65ba38d117f81a106ec3edc14a478ed87d59324c0e2", + "0xa75ef0df038fef1efc94be75441491ae8c7b076d2c692f688f455174539364c6d79f2fb0a6525bac7dd60930ece0e6ee", + "0x82f5ff0e736c7a922ae83908e2dcebf93432a61702b50f432185daee774ccac8c12817f7aa344b41cad90d7ea17da7d5", + "0xa077f4a0e5a35bfb0acce828751961fbc2d1dc6bfbb5cf3f5a5a5004d3273ff64d49d62c63c28aa3c1b01e57ce70b3f1", + "0xa477bfdeeede17f52db725e7927c2d95d52c4dba3d857337b45fef776cc2ef2212da9fec08a6958ec3c2f0d8b598889d", + "0x96ee0ce930aae64b5b42a841eeb19d05c4ba0721646d3b59db86067edbbbcdbad75048382a7099ddacf08b13d290296d", + "0xad1b12139c353d8bfb2ff3b048cfa044fed7d388f5a85a7fdf529730fa767649e713a97c6a372e7bf4102898195f7f7c", + "0xb78528a1b61480b7eb7390ad76b387324b5c51a76a2053654665ad0f3346835dfe32ef3c0c8c6de1cb5d670acc027855", + "0xb5b382cb086e4cd012428cfefac9b7cd73abbceefbb368c0ec5d0277eca32d4d8b0e529afe3c78fae37f221d05e9c5e8", + "0x96f55b368ccffffa1777da074c1baa869482e6ef45a95c7f8aec41502e0830d3bb208d252d2dc96e74d29a3d038fd80d", + "0xac5c2e75b48d0c4df234f4eb4b9170e26b1196db2a18ff6d72ae9ac7a6c87fc9ffc4beba4a039288688e21efb5727fb5", + "0xb9ea44258b7178895e9203993fa96528b24f8847662ac96111c5851ff3bad953fcbce23419196891161042dbd2919ac1", + "0x8b256dac7cac21287a665ebc13da95e23f6852e4398ca88216637707e47bb476b02640544eab789a7abd0422bbe45991", + "0xaf2362560961194b1c1617886526f142f1207b4a6f82eec096c952237b0fe6ba5d815de862272c8c67e6f32b8149efe5", + "0x82e56d26edf0f779a9512ae8837684a8d55e7ec9dec0cd7489321cff52084fd0827e1ea8cb0c410fdc3988dc8f27b7ec", + "0x84db239831741eee5784f1fc8c947b20617313df07cd0999f5500b286471331e2d5b4cff06f0f335b9103ab3916dc85c", + "0xa391fe3642eda40c19f0b08ff5ef59b3909abafd45c50a86b20572f70a852b11d637f0e868fbe50ec540f7103a841158", + "0xa660b772c6554727eabac8a2fdd8698017b8d0fc180b996900fcc1aa9645ef13376632fbc31b44fa11aaa95d5b6a2064", + "0xa9a35ba4049969d9fd4dbc3af97f82909a019df232bd3345d6a4b8f1fb5d62e4f6a8bba1825b5c7a06f23120bf30ce9b", + "0x928e68b572eca7f0963e536ab3c5b3e049daaf10cd59f310418477e38b84036803bfba21636025307bdbf04d92b36911", + "0x98cc88a791599921b336ee679e7d77fb3df77457901d27cba11c3eb1d4e41a23a89348bca4ee755d696d69b94a2a6159", + "0x98b8ef408fcebad7ee711fa26a0fbf56dd75aa4aeeeb684552ffad11d561682197baf04a1017746078adebb7122dcc5e", + "0xaea3dc74377df07c577c247a8ad520c516b7d33be802e7e16b908c1ed08af4a5eabd22bf8c134abc328ea89f7a8ff2ed", + "0xaa139e29209f618b9e3b32456c45c214f54521d5291001d4f74bbf50bd0e2de9e20cc988a5aa36601f8fb3022681d1b8", + "0xa2bab2eaf75cf4e192428cdb47df4b12146c045db495d33a187fa69f01ea4364fb987b6d05e0f6c7968f4f1d1d0051be", + "0xa89177e5a659d60dbe08f772b751839e7241416eb5042fd78e712477df3647ae77cf58606b751cf6fd8f200513bae79f", + "0x8942df33e831aa66668db8dc09357da66744e4d6f3d30164e2a5607ba3a0d12f0190a4223ed182e8e21e0b893f1a20a7", + "0x995fecccc3990fcf26d5b632bfa3e1ca86a28326f917ef5427168271c80059e5f9e2b893a4a5de3204a4c45777c562bb", + "0x92e5b9091668a2d480496a15418dca6e7691ceaf526d41cfc9e4b9f7687fe656d2f2a759747cd2438f5228fe02a9f057", + "0xa8446281c9600375ddb5119ee785c13747a420bc073c9e53c979cf7af11f9c504da208129a7d0d3dba982405450c2d98", + "0xa1f1bbd132d921ed51126d726e02f055fad5c45f205b697d07b841ea76691aacb7e3c6e808c15245b1f2aec77bc85853", + "0xb9f725f2615179720354bab61a1fe5ddc17aa8f7972a9743e7b4b296f2f2fc4f5219f885a41e723f8367db185b82422c", + "0x8acbfac1b8318dd51a99b5c390c5508cd386f17ca3a9ee72e26729f6fa981cc8ad5a069ab910fa61ef00db56c19fd1a2", + "0x82014d0dc25a7c0a63a11dd9233ec80f6ef010c0fa6befcded62e7da7e7ea8e7ce028aebc7714368a2963bd5452ebb89", + "0xb8b986e8d374d199653de8b8518723f287c0dee83e5279a67879e6d080a66186438094eb11e5f842ac58220860bf1cd0", + "0xb56a4f43f43d42119126e279df231f9de70eaddd23e96d8b1cb6465654948643a2be644204aeacad8787fe55b2f23186", + "0xb92a28e2f85c429773cb699a880e3cd23aaa061b57154b9c85e5e0c2551c2cfb0736c72894c277bfb1a323c3f135f4e1", + "0xad699c3d8fd3d4dae0542e873fb6128685b504aa0f3d4ecfcf922d9e79783d43b2b48c5191884e0c4371bb710fdfce31", + "0xaa1605ef192059b8bcfc379ab90f2332f4b131b2912b789b756f81bf2055ca934bccf1257e752c2252ce08b2a62bcc11", + "0xa9f932b5bfb1bd1e0a7bbae7ce7be5f44d0792067d778545ff91f863867b611d56cbdcc65554b54ba49b64d70ea37681", + "0xaf05434eab76e6697dbccf81d3b457a7e0fea8c5efc854e03a3a11eaabcd42e6518dd78ce076e6aca788c6e1b8f66f36", + "0xa25e5f7a3c904a233ae62a968a8570e72e5210018cc50760acdcf92349cb61e3c77d9e69bf0cb0b9097e954919903b62", + "0x991fa13e0d59fca2fb1bc5f837b71097d3ce5b6d04545849aacdd52562224669bdea1e0b9ea7e89c775fac6393121f99", + "0xb150d8bfb6e2927d62a5f46cf1a7567474ed3e7cf12a81e90ebc10a9525a57318e3811f3fd92ea4e7e8c1c9790bbcb8b", + "0x8282eacbc5e035d54d14f3d2d125dc11b2d152a66c92e17eb8fae9eb0cf99571831ae49ea5b22eba735e3b2a63a9cf28", + "0xb451de4610e15faf45e67419f5448f2f93203cb8121b868156bd1bcf9ea368c7ba97df9f6578155770dc970bab96caea", + "0x96c23eef0f8a8b0bb5737f9cdc41f092683bdc1a16959eecf57b15eb984e623921e57581b91593f3284fad3f876b6ceb", + "0xaee6fe1b597d273d9f0a7a23315fc00b49a241f90c9286fc500b2e252756dcc921661e0793f79d7238761b754bee3848", + "0x96636070af3287ab31247a1efffcf23aee3bc7599f66b0c9009c4768e0287d80e7db0b78dac0b026c2bae2b91a8ab0a3", + "0xaafe3a889d774bdc95d26aaf2de04467fcd8988b09b0406a5d49c148dcff33fd4200f73dfc138c80503d8249790d5282", + "0xa29eb7a0ce2603a89f772185bd64e313bc31d58d0fbfc0774b08da13b992da82895ad2add8cf2a9ebbaf2bb63b3b8002", + "0xa90eec2e5e3b838f3cc24d027a0622c52a836099ecd68ce00bad85d500e958cb89a9fc1990e819d4f0d68843f9297b81", + "0x8aa70e919c955f52e89142d8def10f52a46044c94c5fdab0cf9bbeac61456095cbaa67970a47941ce07aa27f151c5d30", + "0x9284c6ecf259f4aec6a63fb84a8db96eee68fa5fced5408be15f3127b8d397b341bf4c84db0f72f5f9220fae84152b77", + "0xa0bef5a2bbfe7b15d4f639e3994fcea9ff100708e45de0ca87f9d0aaa432e72ed30f6ca7a5d4fa909d30c72ad0344c10", + "0xb176fef93a5fe45bd3dc084ad7cef98195957afde16bac46ab76b3d662e2a873317f3d0c0969026cfdcdc0e25bb5fd5b", + "0x8eb91f92da74fc39cf5c0f8b73af079d59b44353182c08b07a3ca980386f87d0ba06d6f8b1a163e654953027780ca47e", + "0xa34b5991a5dcf62a9c107f9c98cc51dd2d115da380062b152c1f495bcb467d8bf2f3d67400c9dd608c6929f2373fea95", + "0x851dd9fc4aaaaf630ffa9ddca78966d6661d699280ccc384aa3ccdb1a8135ee74141da5f007805aa374702493f4e7545", + "0x8bcf8f4ef2bb5ce816b8c4553e909aa08246af9a4916576332fc9980b7f57514dcf77950a8352092039afbd64829da9a", + "0xa4bce4ea541f151e1914dc1493f595ded7e63ee13a84d472342b72c3e80e3d90ce3dc4ff8a541c5d23bb2b871e1c86d3", + "0x98495944859de94b574c7b3d77611802c4f8b422606181fa230ec0ba956df323710b88de6380fee4c52db7f24ce638c0", + "0x88d51dc35be87bfbbdc5aa0875ff9f0084abc5671f98f2e50d16f026ca4706f01a9357d3445a2abed91d70a028aa25d4", + "0x98747c59e31427f6c8b8be44c42e5ed3e7ec483da8381ff9f9d70f61200631bc8eef54c3e718c15681717decf5ba2630", + "0x95d953591abc90bcfb028a41c6bb7163f6c538e01eaef9186cd4ecf6f6ecde6c679ed03e1dc9e6789a55e97b78ddad97", + "0x8946502f19d818f43a4fccd7f8f1f87c74fd6bb47d3556d2ef699331beebd1438f676a44cd2177590c8c6771490a2ad6", + "0xb6a7aafada6624181b6adb99541b7f239d6e598f7311a790447d0977da7e6c33407d3e5b7106e7016d77a0a930e7c94b", + "0x838ca058fb91029d60c6f0a583284858f58c26b3a89ed05324a01f73b49f38e3980bdf486499986e9e3efcb03091cdca", + "0x916f96166174cd88b5fc70a5c5fbc32880729e4f11d07f21b97d94c2900bf56fe56d9b564d5d95f64f844432bbad5b74", + "0x80ea3c4bc5eb0de091551a191f21244acd57e00f0e0953c3dcf402baefc13fe5d772453cfa84bd49563e2c17bf8ef5be", + "0xb92d2d3bfb789f75346d0a1beb3eb58be03d0c96823a98ba0475328a295bd094405e15350e62f5c2f2024a17a3ef8c1e", + "0x821a3fcbe4f20267339cf8ea08299328e8e186813abf9de9658944e555662b009db9fca02527759382c9111833af5587", + "0x8b719061a1e71ff9e2b47592472f4e44cd5bb6a04fecc4838eacd1d7b46d90aa45fcb0aa7219ede6dba1b6fae658a46a", + "0xb863c151c8be20324f20f0996020993fe5e7e7e276d951e5170dad827a8006dbacc793f50a3138f7b30d559e8b640a42", + "0x97d4dc02329b8730658aa63bd9bdf0175d64abaca796dee9714e8338be2d20eb186a88b18b519b0582492b8689e52953", + "0xb56a4057e615517f4cc5768a527a4db2a6d027982bb929affb412b17f23a442f671265494ede290f9a6da342d2c0c5d1", + "0x8c6e6647c660875e867439adb2cd537ee619934f48fbfa731b3a98e4512ca67d895a93c315a61421d059a951a322229b", + "0x8f5deb8e22a28b1647c372fc1cea974d11a928dca6a43394bf39ca4a0ca83dadf7876823553d054353f159de2be04ccd", + "0x8a13a8e9972c14f7f1d9a10dc997a9df420faaa915c6af254a6e5867b0dcea449a0ccbd9bdaee5de1441b187f65ad4df", + "0xae4e544f97a72fc95a20b9c9e8ca51f2675ea3aa123e9d546fa0f71c7d0400e26685da4826c38dabf69b1deb82c15b1f", + "0x8d66d31420abc74bbef1d6554dcecfcd53c822e741b69e7fb5850b4a49371b663c5daaa33f6693000e7e52052f867a60", + "0xaae4d0c8d43fad6fce5ca84917d9e86d5a7ae35491f9428211861a69c82a8c94a64f9b23a9cfe089f2540924ce118e9c", + "0xaedcc14f348d39e3b22bc23ef3dd6851c0e0915fe2d058e67090782ddf989f109f6c370fe904ee104187c8a8763dc097", + "0x8c543edf872a38d5bae420a602e44d609819b0ecf8e62395c7b54966a8ccc657d5985e799dc33827d785dacdae80c553", + "0x89d96af011fb7221da0c215dece9251d32b979bf253a315178aefd3347d4fc150fb98ea11377c44f7a09de9d40ae3fdc", + "0xadc1338c3e7c9b7e5cef774dbc97653e37fba0496fa823f76bce39bc5f98827491edaa9a36acc8d676c7295448c66f97", + "0x8614ad32c6f741e2565b023ddf1d76a5c8efc2d90b9a4dfc9c3360a8eeca0eacab38cef28bca85e567fccc379916cec1", + "0xb2c01c02eaabc1d78251e495722d0250e94344ee9be5651d07d5dceff75a8e41cf4c6fdde34af53bf913e26d4cd7cb0d", + "0x80150e299cb04cc20e4e55f96eebb3323e63033783d2e1c6fa38692947f1ced318e4c53f3fb29fb0ed44c8d1e5608db2", + "0xa340951571440035b4d0de95b2212f0afbc8e0cba92aebb23f73b65a9531a3e2884f3258ec57f7fbd9bb8a6da5d0c02b", + "0xab6ddbf7aed3a253133e74895781e2436bcf793b48bf991bb102db4ce6f5b2e6ceabb6ecab6011f256ddb5173e183be8", + "0x8f8835054bbfb49d0cca0cd989898316d3d9589172bb9d72b623bfd0490e5eaf32c7a8e581cca5e9b81fd667cffc11b6", + "0xa4656af1c2679c01daee8c132ffb14986d27242df4ea9b2ad3e2e75a4ae1fdfa8a5d39c5bad914c5307e47f5e85bb350", + "0xace905932b85fbaefcf71a49faaf51ba479636dd86db839689cca7a33602cc23785a0180dd5871d09e6a1d61f61eee49", + "0x984d4f7463e5c60dd3ec35a87106962fb1ceddf6406e2c4874d291f33da924098ff949902f1d502a5a63d508fb813e71", + "0xa9899e0506d7f0869ce84465c65f515b6791f77e191dc0985126c20f161796a4ba9929683f71e997385f69d38ccb5aa8", + "0x8294baabe6d6e643742c6ab85902762e204f8097787ea92c23927eafedd31e269a10cf8f802dd3b6419eadb4a3e47807", + "0x82f22df6de842c4faeaf34c4f5e72c0b4c220ca963e6a1830de64f9a4363746f615af962ff1a7827f8b0cfab29ec0fa4", + "0x9933346dca4f14c59937cc20c6b66485b3ab494e620d9e2372fb7044e9695b526e6db47ccda1305ee01e3695bc5748b2", + "0xb0d59d458aed41542c1816f82d04b9ec577681fd296ed44c936f6b2a5c1e665b7b910b9a083b2aefcd0688583177d2a3", + "0x97c61e043bf50e6c6248d919ae2669473ec5d39d89f506d100291692177e29a5deb7c7659a9a9909b3c83078bae334c9", + "0x8658ebf6defb98586299222ce227510988d9089b92684f36d3a5479308c7f67535442ae81cb5831f5ea9fb67bbedb478", + "0x99c24bfeb47b9d6611e2117be01e5240eda96e07301cd7dbeb7cf96b170da1469a9792b5f9c34e8049281fde5fed0803", + "0xaf6edd8a98b0a3f09457b126a2a886621fe8d061fb1e2c9105c447cb5b60ce9cba483175ec8000e66100b4dfea67736f", + "0xb0b3e6beeb96a7d8058c9110efbc6f6b4044fdf47084bbff507011a03e5a647fdbd39f35382faf4d5c24ee089eb94200", + "0xa032327701be310e95e7c78ceab3cafbd3457ea520ba720d926313e08b8b7f6cd77e4acf63c16203f07b667641c4dcd8", + "0xb0c467e71a67f4dc235b2afee3fc1f953da32e7de1226caa5e258e91e67d24609db378db13346f0b243b1ef13dfeaba0", + "0x803d9e4fd7c9fb56967471336a29ba5dcc189810b47483dc9c0bd0564d27f30d3170c072ed4c985a60e9d27f0ec02ee9", + "0x8d97a77c45c3f4c4ec8182e959798c9a6293cdf6b9e3407be1d5297885f5a7f12cb5e6299e2355cd50b43772168715a4", + "0xb1b31fde4b557a4b727b89e713f121cb7f7190c41e5843959260c4b7b74e99020e4d44667a329da9038f1f7ce392ae52", + "0xb8f56f11b4a5137f194930fca67b159401f6dbce368316164394dfef5aa18a27c1c144d5bd19a801c5f097569021678d", + "0xa6862da9c656aa59d39554d75407ae2b50a4bea895b6b2092d9e87e4e8737d2b3af9b88fa6deb06b4d48c46f6c51bbd0", + "0xa9f16c74b44f25a0c09cd6d81ffb3491c812c5eec1a6ab136dda13bfdfbd76005f4a34c2f9cb589010dcfc12fceaa501", + "0xb3fb44f24822fc7860fc36ec295f0dad66614855582acfbb39f242b4e5f8114ee216eba0c01a666ebadc2918c8745d07", + "0xb6eb79c28a8b1d863632c4eacf53f3477901c1822bd8ce09d095acdab9baefaa7c094ca0c27c33deb37303540e934d7d", + "0x9523ebf8b1dd5b785707afd9b04517f5101634783b57dc9aa2196c37adc8d0b111e6f4d3f6da503b8e882fbabdfbceed", + "0xa00b0348b83fdfd3158f79bd617ecf384fd3b3ce503a5b4598e48df9b8cbcc28b1b7b40975a0497198445dbbff41a11e", + "0x8ff2e0e48dcf51956214844dee49d343c26a6927aa4c1e6cf48c2bf61d148192b27c05ae6df62c91f3930a9cf1787022", + "0x96b87e8c9d7d03e0fedc1ec6e04f9887376782a9805f90899a6f27f55059a3d84ced09509272fd3cbacb9846d2c66053", + "0x86464f12aa071b569e12853e597292b5d91a8f230bd0e858c7e9f15d01b3f22c887b2670a9058f3050126d5c409c448b", + "0xaac9e3aa1d5fce1637b681a7b43c19a3d90d15907cf5c589a32bc9bd09bd13e34d9478915b3a9e5c3560ecef4c417701", + "0x9563996153bcd8122c6f94d9cb076a3bbf5949a34ce1364c7d40fa64e41229377f7adffbbdabb5894f27d593353ae612", + "0x819c84c3ff5087f086af62c3e8682cf436babbed8c618e9b4522a6c0ed897683833457c22e29485129a75ba3de31fb86", + "0xa620f1ef78b01e7675dc230e45283d214afd836b7f46f2a46567219e3845f23a59cc62c56d7cc51ca236dcc8346ada05", + "0xb6be17940d4bc6778d4bc07c7a2e07b3707ef263b309703be59eaa2cadb46d3758568ea6f0f48c5376f2b5c72595472b", + "0xad71f552c356d0726b52b74406cd2288e3df473d9c654a2fa7e6e1a08cc960753885b9c4fac6909be14e4d96f88eb780", + "0xb15a9add1728f71728f590232ff1115b68fe48a96663cab94494c3a23a2810a2f160ce9a5463fb9587fc8aff658df7ad", + "0x9687fd6e44a86b6659e94e7df531bca3cef9e1208a5c75b1543756e1257518dc37f55083c9395d7743a05a4e16ac419e", + "0x9358b915634ee72bbef24982d5fd4efe0a0feee595c20f5271b8be263dc761e3379f3477d55a60e730d84479d2336850", + "0xb61894ca28603b2b8632f7c955b91864c3bf1184caefd185039fa21a635989b1f717b74cbe1cb19098354da7403aa20c", + "0xaa1881f6c2af1ae906fcfcb687ba2748661839f13187462290acc39169f7bf783d87e454e4b55802da13f5e89c066951", + "0xb92ce145d9832071e1216aa998282e14d652fc8dfb6da386ad73480d535e3bc043614773b988287d0f6f88f53e8889c9", + "0x8411ac97590e7df2a56f8934fdb1e550b9e44bd785dbab4badd0dcbd6910ae24c2c7fbdd664d4ad96f5cd08869fce449", + "0x97abde3fd22bfe38e8b670934aca3af31944d33e976f5f397f6ebdef02c2996d34aa66d12787024c15e401f2f139189b", + "0x808e9df5440ab1029410875c78863b26bca29ba466ff3b73b184afbe2e83069bdd7d51c0766490b6ca1d0eb097ac8582", + "0x83e470666637ca256c93d98f46f03491632604959197f6adee97a345b98fa826064504eecce6a3c77e575fc5eea27b28", + "0xb6202c9ca2520258ee9f39afe292a60aacec55b9e0183d77e1cd4f49e9e07c9dba401f3505785105b54b65b7e85d75be", + "0xb28ab7c62d3ef757080031c7e21eb89f240f333d12903c74e22c6faa4a2148bd4fcd1943d8051ac4b80d550650ffb870", + "0x8cc1f14ae88fb4cd3c4a5c82555bcc6742a8cdb01ee2787ac587e25380165b9bdba54f8e40f0611608b1d4e959c864cd", + "0xb2e2c8d5617cc2e5ace78debea3441bdc7ef04fdd3568f9275faa653240a70e87685f842ee964e628b7fd2a9425a3923", + "0x810a5443eef3a5135c0a673d017254a9eda304e57d6ab6a613c151e394665c64b224385433ae20efd25a316657ca9f55", + "0xb95f6bbb64a1ef1b1b8945b4b41ac7bfccad96c2b0eeaecb553cf1bf8fd4fdd0716477bd6ceaecda64ea1bac866b8806", + "0xace6927c085d8f4b73b78cdf4e40ca503f5d59bc94d718be2c8c1acefecfbe621057febfa16aedc508e9cb170daae4b3", + "0x97912e0fe88dbab21a7495df752465bf52e63da6848b65d52d6e7c593f559b7485a53ad85aaf7b8e83ae9df7fecaacdb", + "0xa1c796311555eb3316bd81fa4cc83fd6bf560636967e38752648c4d9a9cf169b393d1e315da62a3961a35a82a157b388", + "0x8f4c37a454415af35a94d63527c397794ee3e4fb116e2d9698bb6e5e23f1b8e9320f7f5d4b98e04eedeabe2516867a4c", + "0xb14ca42fe06369c5638410f4082aa19dd23219ba69bc3f03fde49dd58625d0e9210968766f798e9d826ab3f0e137032d", + "0xb69e44d8971a95c763ca9fe44388b9deeab9b298ff8ea7f40ea188f7944904a9b09eeaeaf3335bc73b3ba18066c255f8", + "0xa06ada044ef6916fffde95120fd278cf028e9f93c9ca91cb512c6a5129e34bed027d67574b5cb589d2c972cc40d212b8", + "0xb8021e363f49979dc06b2902487a08355a1acd0995156aa406cf382fcb7c0fd1db40ade1e311075e79ba01b4f7d32c5c", + "0xa69bb787eb6c161f896fdccea799de12be4ab513c1f2adada65aa3fb60bad2ba9225015355b00e1886e4414d6a279335", + "0x83e9213874e1ce5e7cfec3d40371da34194b789efad8249119955eec2298ca18dc27537e05321e4a3eee6aa0f540e2af", + "0xa1c07ec5ccab3b04e471a9b5798e00221e8ae62c1c3a177ca2c7525ae63e41f792a972b9f619b1223fa0179e46bfee45", + "0x96a5751a3d7a21e72dc4fb3d39679fa2432475a068f8f7e37c84b6784b3b6eba95bf1677a214ee52d48d27a895118b67", + "0x82193fb0e0a0f2d310f5cfd14884fe140ef67407b47aa066f5bfddaeff88b2746eea73340a88d6568433f56da8de6493", + "0x80cb0eaaf3af023d70688414d8a016fbabcba0876e211650fb4a27f438050d6c9cb0f1433581a10163b77fa27b80e5e3", + "0x8467e48574f7193fb85001579dda04539475676ea7ce5bb1f26236acb3b701d5a2af717f1cfd9fc47ab3694f0ce3b799", + "0x893f5fb51c2309306a6db63eca0b8aec11f59fa8ad101b79c941e41ee8613131b27f4f0ee1d100de3b10d6dd6458e4d0", + "0x8a1833dad09b59bf42a1e6376b9dc1c789c02dc59a2e71a28cb779050e8bcd8d5d09273bc66a28606a4b3b151c9551f2", + "0xa9583fb260f97fdc93926f96a22fd1259db5955e67a83a89bdfd5d66b5b8c5a2caf9f076da218db2c06346b165a596e0", + "0x802a8edf68584fbda9e987497e2ebf1ae4bd9446fbfd4afcf65d8070e602247e2f7bc4542df1a107a42dc2da6727dc40", + "0x80a95b93428416f0cff820d58cda030d1aa241e310b10da956c70a5dced6cacb549ac0aaf05941304e4ea43ccd8bfeca", + "0xabc0da3a71ae8c4e1397b1e91b96b16b0d8102c54eb63f3ad106b21cdf5f220a18071de195a11d2e05317f0cc6b3616d", + "0xaf50a0662110bc6489be2f062b387eb5333109a99ab98809321690f4160209ff37723ab3537b6ab91dd00595eda16b8a", + "0xb21a09e63efb2523e066ec2695cac0de1775bd5cc5df5b7fb384a9714fe11700baf576ce6268753746bd29e122c88f79", + "0xa4e4da6076ed875de26d70b32f72c9328def12578b9de3a66296978fe5120e9b92fb18e4e9c50b2137d1d0f8955ac220", + "0xa56c461d610c5a626d4d603b51bbb13368be4147af865aaadac5aa2af9fdab9f35315a8162ba2472fe402d37042ac918", + "0x934bdb444caf5143d9348134feb532896ccb97c15a454378ab960892b0f4eb7713abb32b4a92459396e76acd2d2bcb14", + "0x96c10bfa69fa8a16ae959c78db2524eaf5c82e3d107c6bcc758e152c9c598a30880e3d6089d0040311dbce53c6e14d89", + "0xa594e8ee1ac6f7e62da72bb442bb835256746b37399d98493c49ad0b63b07775900171e5e28505bfea093b7918041e57", + "0xa68288119cd0f19cc3959a13576e45f9fb97c4b12d337714135642e6169eeceecfb9f3d3e994b56e7c1b51b01d3ab305", + "0xb2f6bbba6e95c6b96cefc31b5ca23fd857c7fd5b5bb7781efd1e42d489548b385d6490aaaef480f4bdf154bcc5d3f444", + "0xa562055bd5d8da0c09b269b7d996ac7947f4c1c6cce75854ee05c817d3a849a70db109629d830f33ca65cc39a0f355da", + "0xae716ac4b50d897dbec77d546a516f96ddcadde0eff7b7cee932cb5a005849986756582b65a3b7fe73c4afad89453de4", + "0x95f5db74f66ddd43dae7bc2fd4a2fa03cf41c7a6253369888aa1224c7a9219ed262992956dca385acd2b985fa7c2dadf", + "0x84599a6fd2dea30f065c81db38a5010739599abd59ad9d2447a6b29c59dbc396843037a17e084ec2c5ed30163303cd06", + "0x8e2d807d682c73781ab08511be42a0ac211423853192aac94174591153fdf45ede8b037c6c00596b5d92628af971a509", + "0xa1f14301c77530462b94aaf24003f3d7fded012d2bbf290cab4e70c5d2fd6052e65ac059f263a49f31e7edb0236ee4b7", + "0x9465fc07e4863eee6630685a720119ce2e65f70f7a497d1262bba6c8ca746fb828bc523d24b104927b6d3f914dd31005", + "0xb5c9759dc560f16263537dd6cd557b1ed1363dcb5cfa67b4e98fb245ffbbdbb24d8a6ad69b8b75945aa945d93625d451", + "0xa2579aa084932db62ee72102fef8f923dc3e8bbfb752134a12995056aec72e655a19bb7960d7ca1932d0dfa50a1307b6", + "0xa90ad5a3e87af0b0681c107745eef217c37f3491526ec5d8187dcf70f1ecdc8c48abcdfb5d60fbeba58a97fa561a5fc2", + "0xb54a375c7a99d11b1543f2e64754efaf49f0d6d11b3e7c57163b5d7fbbc4006d895489e3fe75a2518992679729b738f8", + "0x8ab2cecacdde061b093246fc931869e1dabab24d1017464fdcad6c9cc5f9587607b79e5af52b6d80446804b1a1790bf1", + "0xa30232edf62a24c089a55569496dbb41f576dda7ca0ad228ff72c2ef31584f479b356cb6277e15e8123b829ecbb60fab", + "0x80aab825a28de3b1a8c0d26ad00bfd1c7e55476f188dcc47199d243ded3296950de2bf2c084c6cbf588091b2c8e9f80f", + "0xb0c8be53b9ec938d59adee16ce3e6f4325f3932c1f0f2d040667b70a6ebf38a010b39e2c5f2102574e3bcc9c3660e613", + "0xb63bd915a3380fbf3eddb87e46a1193526894a44da99047d7d3647085e61b6bd5642de59666e0750fcdade1b1463fb90", + "0xa6f7dfb700de151c94ca1f017e15d726a6aa54d00d0f3867ccee69a6f8e7898996bb710a26be425701ec85f0d685c83e", + "0x9831cb62560c306d59c15bb210da761cbf679b9c684f27fbd06c48fb4174e39410b8872b952fb4542008a4f90b2d2739", + "0x92aaf201e6d5570a31d6765602042c3f4e5f340cdea72d60021cca2bd526dfc2db60e0582033fa4b203acf4b1a93e399", + "0x96daaad9ab48d09bfbd00e938995a55182d0f8ac6377069565ae829bf06490de66b8d76aade13fca839aef17397bd703", + "0x942775dcd1fb2b3232f63c6325ead96e588e49e07dd02e9cd22ca4d17f15c8e0662579d607efdc3ab3b905bde33a3fe2", + "0x863db898227b30a6eb0c4bfbf649fa4f5279b617171cfbdb755d368c588798884b4ba063f3584e2f61939656e3883245", + "0x87ba26a9a9a3a0d3cf7e921c396875340f6b9df005b65cc775d1c4b783d185e197565af1abad2ce10f5a7d441d573547", + "0x90371e781c1c82c8e87b70b76f5f1fbd0da5d5b153375b1717e3b36720f298d64206679ade938e41f53a74d3adf939f6", + "0xadb1d92ebe4d764863fd33d52447db86f2d196e1189eb93a7e750d07031bba1f0425b2941c6557d62e2ea853dbf4213e", + "0x910ba9e90544acab343525bf2ddf72e73c443ef7f04dbc767409eed4f203ca5d08c3081abfb0b150f6cab5bb828ed623", + "0xb5e83473c6410aa7a015e08d37799b4e3bcda28699482210ce6b5c8ad0546a2fa3891b84d4f7839cab56ace06e47851f", + "0x8f0b2a92a795eeda79f8df5c24aa5e05b6940bc56e66e0cd63d7c820bbf43f49d8427572c9971d95ea8fc50cbf2e14c0", + "0x975e19d57379f6f5d21369fa59a07b00fcaf31f40d6e9ed65c7a53ac43e82677b31faec4f8a8f6e1f3767c73b68610b9", + "0x87db112229054d213898e0ab83bc0972210b4218e15f7cbe578d682160d09542fa1ae4aab8121cce04ef0c1948ebd563", + "0xaf78046a1216a62aca5084ecd8fb596c90cc2a90a198683a2aaac2efea2ba61830898c610e07e0f6d799f1a70e2eab94", + "0x8bd58ce94de14441cf3c17b61bff83146de7cc1fdf6bd19f2af92a5d6e115009e1a63152849691c36c3d05d1930c41a6", + "0xa69ee7520d360879f55f70cfe7de3a053a0581bc958c28f4434b425bbc0c7dda3f7c82942c86d0a4c87a464854e5f727", + "0x9507b3ada5d842843ce0650fb4d681c0364498b24f64976d464a8192a56c716f8fcb6846d291f5f26b2eeacd136463b3", + "0xa25d97d58a0b3502eecc0fd9422c038dec7b9582ecef226ee22e53c15ea3fcd03b65fa987e9707a0779d2627927b5cd5", + "0xb31a08234567ea9ccf98b420fcac0238e59f05a2b071069d06d427826ed7a2d535cc280eb31072033ee2e2f5ce7e4c5e", + "0x899cf1e20b6abd3003e5ccd488c63b2db434bff76feb144b0515719a8ab76ade3422116306eed0e325fa0688f006c0a7", + "0xa936ad9ac3d189b43d0f4b15682c389c0826b479d09ada2ae2577df61780f11712982648fb68de50de9e15f30cbefebb", + "0x8c8a69623d2267d6c4399dd8968dd62a406201ca9865330195546441c1d828e8cccb50e680820d1a749df35cd16f9fb8", + "0xa71dbb29be269dae442e54ee52849feee8a1a500e361a8d3c9eb79ec46d5b72624ae832306fd9d42954ce83e43ad2ca6", + "0x853c182d8a2d990cf67e5cc77a5a15143985e8e170bb4bb1fa190441ce1c7a79ea51df363652f7feea6ba2a1b720cf0f", + "0xae039beaeca0b2fb277cd50e2c797da5f2f59a50414f6f4bffeb129285b96ced0528986a12365d9ff95ee9a63b418623", + "0xadf6b86d58812f87bbb92177a4110c638cd99807b87478b738c8903debaeee5d4d4ad829c3625f4c98760423d6eda459", + "0xb7620299330cb35ec9251b300ae009b8403787a68d9f4ec30fbf39f2fe783e11bb6f04d0771f87a2644bc5312fd19fe6", + "0x86bf508b72b48e7d44ca99fb1d7c858ed41ee6a7a8ccfb79b42acc694283ac585420f8187525ba1eb13b6a6800324c6a", + "0x842f23b19c79aac0759e8f06ea70f49a2843a3b07b860b99a9baea6a6d286c900c4478681d05f219463ceee5f9262265", + "0x89b194caa774388f4f6be1e45ba4116f93aa177d6cc823420ce00944331befa7169ca4ffad84deb99ac31184fc5a3c3e", + "0x85e0773307c191a2f7cb99cafb2f9371de8a98ad4ec2f532b4b82c5b58ecfb96bd9925c6b3540c40d8adc10487c2210a", + "0xa30803b5f7ad7ff8488e14c746bc940fa15cf88cb443dfa278e0a253d1703f7842c1828ef00a137e884a8763d2b3c3bb", + "0x861955b282456ed4b03f4d950435d27ee9de86afdd89035b932efa017b97f66839a290f2b945ae030fa7d8c3de38dccc", + "0x97abb22876fc7c117d19d8d37edd59b80de644c0f8615826819d10f5b3843fb3502e64eaf7e59c44b5ab8db96233ef44", + "0xb75f9f8d2707992a4f52665db1ea5d29cf5557f30ee7d4240d7fb1cb8fd7f3a16bb0705274d71d47d93d0abec618eb43", + "0x862a6752a6e613fa0f5fd92140dfeae6cb8981610a9472507b495618f3b97d43415f0e6fb09556f33e8c164c4a5ae6ad", + "0x8e18d224a5530321177c15eab8f572d0323c2f7b1bc1bd7ce0ef708eb3c6e0125986945f68d7ec4f3b96fbd92d7a42ec", + "0x8655484525ea6cacc801fd4288f134d77fb13c781ff895b4ff3982e23e0cb93165b9d55412d38c0470cb551239329f14", + "0xb13b3fb85b31a8bfd41c066f3c0631fda3c85c9f1bacce0fada7c5086ca90eff47c2f40b695cf8b4f48372469383c553", + "0xb91508920457f56e10a52fde5588be1a71ccc5ce1dfdfb40cd1c3eb9387022ccbc3a27703b61d716ffe7f2fda498e6e7", + "0x827cb29208c55d80e79689ae912890b1e6c50b270f83d9014dd969f547bd8381131541fa04d239de4844b35982b3fc49", + "0xaf6115768d9ee7cf31b7617ebeb8a06dba8cccdcebf6f4137cc6614c13c643e1d4f2d915d9ecb6b297a96b413863d29d", + "0xb24ce41e19d2d4873018d19632916f25ebb3b8b67924f26dec116bfb70b28fb47f4c318471b1fa3ca80cc9256d73b9a9", + "0xadb41c40c41dac4ff00594b04bb938f41b80ad17418e08b164c0408ad8b589f450a4a18a5b123cc74efbd3db889f5161", + "0xa5ecf94c41ce25dd2f92d9b8adebf8e069721143e40b411d2ff8c268dad4ded03bfa7ac94cde0ece31e698f32ef4679b", + "0x80498e6567400b417d3a6071cd1125382defd1c4f9f444f2b9b9972c98c3cd868117a50051ec0a1e968998a9a8f34ea8", + "0xb390528198706f3afc9050e09a29aaa7cf600bf0dca784a32277c30664df4fd0ea96ac68045196345d42bc340da4d496", + "0xb62c817d567b6b443f724e86d774d943be0e5e9c5b85b111c0f54f49a756e8e3127c3b85e989db67277c1c2fa8b23f0c", + "0xa585bcc1e54c44a2845a0dbbd6c5d4c51a5c5e6788f5be359e5f60c6e74f4170188f579b5f354848ce95619fcb9ce5eb", + "0x8c8acacc96a38c7e334b2e5ff9b80c1065169faee72dd019ddd2e7a74a3d39b20872e08377187c377c663b1557953360", + "0x954ee7799428c304a8f8311890604d1f1082a80396bfe1b1526de7419e10589373ed34a50d418a42135784c98b9e1248", + "0x86c06ca9011caaee34d91af3f160263f97bae8b1e6a847d6e36b92150841e2f18ebbd430163290eb32b6d595515a9d38", + "0x96528c048607f844267972376bbb2b2b0558f8b19122218f90e4645ceed42a41daba79df55e3c49b7f1ced4354411144", + "0x903ae133bc9f8da4d1ea9adac9cb0d945b6db533bb288e57c0b88348aa14e781d5c34a5aae94fa8d8019efdddc93332e", + "0xaac10d30df44f7becfaa051c72e1f39f3d29fd6c09c6f4f7abb312fc0473b27032db5de7b8cc987ee6a9ea895e087cfa", + "0xa7e0c9533f732deb31c1aeb4e5d926a350e64df6d99627bfd54d56597045115b756f96f3cbd78835b4794aa62ae05ffa", + "0x93b18473361883a4d6556d72d3ecbc25f9c4b4cb532a744e95ab8663b61f72bbbae05ee212d9a3d673c2973aeb96c031", + "0x93ca9b978a1be91534b93102c137bd7c1d917b57e0781334e14d6baf8e10b9cfd582ff00774f03da4610f98e9fcd4fe9", + "0xb26cd2fae78154c195555981c1f86f364a3e031477248d0cfc370a11448411d7b47bdbf2a6a8c266aa22a0b491c210cb", + "0x810c4c07292236f1235de0fa4c7575860b796f2217ec8148cd8c13f87ec1bb336d76f3fc5f79720ee84b0bb05ec28d4c", + "0xb6fdc833fd43dca370603101816b242f641b4ae69fe82a294a8a1ea6b0efd5b5810de98ac447f580f43d964ead5382e4", + "0x868af6359fd81fa91c92f6eca3a4749b8b45090b33e4c47fcfbbbde160c8000a182ad8d9b1fce99410a8461810cd91ef", + "0x92fe5d7dc648374c0c3e477f212f43603bbf3d89e263075e01dddff2564c9523bfae8a8b5cda79581661c0b760d90c42", + "0xac318e0db4773ed6fc02f5b58a48bd29e8b3ae74a56c3c914d1c885cacd1f5ccd4ffee2bc94c4fdc8482cffc4eb7a4ca", + "0x8c7f79cf0308ec38ce43ef93b57899d52132997fb19f2f945013646295446862b5e3a53d648f76f23e3e3de094fe66b3", + "0xb2a5becf7fb88c84d2cbf14cb85d6d11255ab0440103fb4c0a79b5db13f584d68cc5a8d379043f10280d70b08055d736", + "0x88b2cc993534fe730d02b16de74004960236be126d35867d4bd88c869895a385ed800e4e431d941b675c809064a47094", + "0xa33be306b94e06ec4c13c9a938ca802422e1186a5e1640ef156cd934a752d4e26a1f3641019066553cbb2db06eb5f402", + "0xa125b09d9d0ae7ae170171cbd1478d92eca60fbcaeea8d295f7709a67219c0cdb177c228f77a329220f48f6695764e77", + "0xb4bf8d36cd5ddac3aa9a10becb1c60122c73f1bc93ecbda81c66e38ca589b50d972e000a3e7b34d8b0db39b5c7dcb536", + "0xacf1d93a3773cf539c42c9b71f393964e31865cdba489d6254cf303df43142400b3f1041d9c5059ca9c0774cab6d8fa2", + "0xa1f86fc289efe7c49e84f3b872b2a7e1fe52a096b8596efa4ca5679919299a62bf522a3c77c341f91e43c19ae7933b7e", + "0xa73d3cae6e347841b01abc4e62aedfc5261ac54063801191433879fb32b4132b60b6ee0178c4594b7fa066203b4c3152", + "0x84b60f78b74c91556ab24d4bdccb47c5e5cbb270ff6e1674a49875d8aa87045fdec61aea4822cd70f446f614dfcb5475", + "0xa3c7e79a471692cb09f64618e9f35c3bb0c93a1ec7e67228c445f96d2fdd3cfb4da0cf62f8bd7377c78293710953be3f", + "0xa271fc3515103d589086c6cfe90af9309d84e8370a5683951d0b8a311d0f5328f8eb7b58c2cc5f83f96d1b7496a57259", + "0x91ae168d307eb34039a17250bd1847ad712962ba69f990bf3936c021809ac4f6c8b78035687367e97049424a684279b1", + "0x83d3027479334a78793b8943d0be4ce6eb2199258c48b08f0facd010bb9f79dc0e136393ac777c8f4b7584e724897462", + "0xaadbad51a9666202f07c9ef6b7a285c7e6e6661f6e795325101af47c480c701c43d33ef977bb3f4c14c25534d2becb29", + "0xa52e50b15748787f1fffbe856107e942e325502d43ab1427a8b0be045b58b3938577f888535f880ef14ff86e17ce4cb5", + "0x8c7d9d964084371f668610731ebf4d622cd390b37f19c2d258b4992a357b9153d156a48f0f7a68181e3de9c5232c382a", + "0xaa7744289f79d5cf5680c797d0c294210546791b1f53c9e84994538b1d5683e7ca1db42757809f76be6fdb7109bdf591", + "0x8b6fdb77e51200d14d6476be78a44a96af057a3081f34536f63a8ef41c7c5e8ef368c335fb87d1c079c1b805e2be7ab2", + "0xac86026d4e4865e240612a9bf1a3d221ba884e4a31ea6f2e1d72b16c86c74c77b67ab81efff5aec66b8bbd42d513390e", + "0x88f0158920550b0f4f2c79da1db3085491de2fda883c1e5a774033beba07160374f12b0c60e9153a0ee4deb94a20ddbe", + "0xa7c1be35b9189cbbae2607876e333ebcf1dfc65e458345da4d10026ba292427658711b8c6de1965342a66defa020d3f1", + "0xb08ba45c1b7f1365a4cdc3126f647d446d963d51be83d473e13ca36a4d52e4817f01825358377777f48d4b6f1208bc81", + "0x887c6a7ded6dd119d19417350d17c5ed92944da779d17ed6d8fdca145a2b81cf5a95194f8fb1ca46e4f3b33dbba3b3e5", + "0x871010f799daeca469889814e562db9b3e14f9a4d1104df309781973ab30209ba4127e289526acfb4df4704986dff85d", + "0xb75653c997a02afc33577b1732e5934701768f648b9210fd3b674643151f96ed4855655e8160ccb28ffad1d20bf9eedb", + "0x90ac845a2e5f1659ae295713510b1c5e8bb4edd31dacb8b617c0aa9aaf3294017821be3d17b51d35b018a0482bd12b64", + "0x8214724a033a9532146656ae87bb4c27018fe70508314ca45dd7689750996bbc6674e61cebcc454fb7d8764e280ed7d0", + "0xa4a93e3c6622be0fb64fa2763171369c01934e9e1581af67c36658f26d3fa675658dd5395b3169cf8144bdd46a52bb51", + "0x906a4379a56e5c20b7ea23b8f17ace9e7696921ab86886f0039d2395deccba64d01a01c2205f3e0d41b61c4ecc4eca5d", + "0x85c3ef2c14561cb9d95c23c41852761d9cbfb9ff3091568d0deb843b9d13d519a5ee6fd03ac4acb068a5fa9460402f21", + "0xac66e1ef4dd6e2da2a781dcfca88a07ae207046a55bec9603449ab47a61e440902641a698124745b0e218f8ccd2e95f7", + "0xae57df7d50c0d5d5f3bf9b5ea1a67dcbb3b075b9437ac8b115b35441376edeb2608279980ddb13eae22a48fd2a89af3a", + "0x893c4e20f7fbea696c9a393617e74d7fd4f43bfc6d6e2be209e98c9fd0280ec66f8a64698879060bc29c66c3607fca5e", + "0xb6a3949aefaab10cc7a38271b244fe01ef0e6ae730fcb38229c99e8a451b5baa9f108135f1b48943a20b13329a7ba297", + "0x96ecfa5e35cbdf30e5f0f8d82dbc56f7cdfea69122fcca202fb07ab7d677a725ce9f1ad3ffa136287117861a3e7500f1", + "0xa5a4d1d5052570c99ed9588b0ec93affc49ddff5d799fc9faedd258811e24f5e10140ee657d85bd2e161b72ac0f18ad0", + "0x93cb231f9fd6e2837c97725dd61b6dc7783e5daee7134c29817cd98c138685873227db47b99f89551d9a3e56cf5ce4a4", + "0xb39418faeb99508448b1508e2b0913cdaadbbf96a4ebbfc94a74c66af9bb441d7f2ef0e82935f55312ef68dae68be4f0", + "0xa250265aca0d0e9eaade3ed6f5cf0aa17137e951b94de272bd0cdcba79c22bcdf916eb972d95dd4b6d714c62868dd5e3", + "0xb71c693a725a919ec902d37b81e1b0687e746278d45df4ed3b3d1df0d679e3981da655959ce4f79773b6e86f45e0e939", + "0x8358c7d65067a5dec07adc8dd597f8b4dc3749424f7ac01ebc42ea9c55d3db0c2b60e2eb34b83cd7b3724e8ba2c2500c", + "0xab3a7c7f275e7e81263582b9cd44d3966bb54a03a42be4ea9ec58ad25ddec1651642da74326d3a37250078ddb7c0e837", + "0x88247cf7a7cea0b7a346c005ccdbd96d2efa1b155b31e43b4ba948608ceea71520e6ac0186244b11cf01b82dd212c6a1", + "0x9358e029668e2c8dcb598d6a494af075b6efecbc13cf1b2904f70b73f72ff0e299395803ccaf6c410a092b0d97ba36f8", + "0x831fbc24857bb669f854e5655d05655ef4725e1c0b1db0a34d62500f7f26d61803c61987d36cdcbce120af186eb1358e", + "0xa3bf033af51517448046f163559e15f9147c93d406e633573307e31f65805b176e10756e62f8b88314512d7c9d0dd5e1", + "0x85a85026cf08553d5b9af8ba208ce1abe31b1b15c8734a53c3b79cbfde5f47e83ede461d1df2705b6a74e5af0e407482", + "0xa4f12f36c3a496c00647602f4beb05f48849f7f0a8df6b2e4b4258ce9884531ec777f32b081d67556f779b63ad5c7e4c", + "0x84ad851aaed02b22f70f7d6d5d62f95c82784d697f2bba5ba41521ee0bbc3aff76118683aaa8ff28466d8b395bd8c17c", + "0xa38995dadb6bc89a794f1ad04a370606a5b240b70b3989391681f8280d8446b242f4474e24803ded5250bc2ec72a0458", + "0x8bc8cb4593c7ed5c07992d933397ad8605339a9df9796fdcc97511f86716246398f934c55c63351752fc218c299a470a", + "0x8647274c95902c276d7ebd15d02e9a04c25ea1f28863bcfe41cd9d3f765ed5718f612b99369db7728cf845da3600755c", + "0x8e67e654a30aca33b6d63236faf4d0562e6955e350415c3020efa5af6d52f8b4801dfb9edd07bd608103879dc53ef340", + "0x8507301b1185e659fdc45f3d197cfd004e8de38959f6328d98020ca230173ab8e7cd507bc1d53af949f91cfbe9af9f33", + "0xa9a09d1f1f58d2b4beb30b6e92252fdb6c17886fa44f2ae90584256958b370225b8adbb6d5aa1d7965d7faeab159b45a", + "0xa922245f185140f579d6ee37a42f9b6f31ef8a61f26e040a24c904395550834132de9400d4bdcc6cf42085875bab8f1f", + "0xb8ff68d5e852ab96590684bac27d3380feb2e2c46e56ba98c77781f8c76a36f3f71627fa3a7effdd2e07b33b56a9c3b6", + "0xa9d34578fea73683192726dfa135fb1e284910eafbd857a3a080e96c03cd7d5bf2e3cec030c6c7cb19556a3fa70bc549", + "0x93b489cddf4f2c7897961b6c920f6703d55819ec08135618e28ec33e692d1145d1a047d016d0cda4a23c8da2b909932e", + "0x8cfeded85f0f61669b63e587b6bf2ceaa8271876c9307bb0cabbf1eaa6f52bd22ad3327d969da9619cd47c5f8fb1aee6", + "0xb832b5f59d72fe5a40bc9fbf6eeb82c0ffb6c65d88d660fa32432f57d7ab17da3cee88a379e790948f67174fe648398a", + "0xb20722aead4293c9a1b16c97d48299afb1470b6f1077a295207315c09f4889d758ea43cb703d2c31601866bd56cb0935", + "0x95b5a954062a36ff9a0f63cabd58ab833adf6090cec5cfc7e5b4a093b455ca1fbce87b1c3fd766f193cb03f404f7b39f", + "0x90cdfe49c6af1cd071fd1b2fe619b0a0c7e941f91c292a93e4646a676f067deefab36e580acd2223b0c383e2cdf7d88b", + "0x8dad0edb694e5ac31172cfad41558925411c522f17f5d4019c3aec675c98e552c7a317afc981d82c556e96e8bb96d968", + "0x914b69ba73d2423bbe596810bab3dce4915d19a3611d6747dc46a1a0d246727599c719f6969fcac3dcf36241618cd286", + "0xb7bbb5c3b9b54dffb21fc7dfb2a1dda2349940958487dbd8845bc7c611a46bf0a9255928bc42493e89e61dfac82e0461", + "0x82d9e940d1f23b7b5b2aa80dc575f2c3de0e92484bc9d150887c466408fa9077d736b5b7bac4dc313da007e8dcf546c3", + "0xb6c94b49afc9513750a0a948bb7b0ddb4cf1bf8f2f8ed6645431494ac25ba478fa5a8aa7a86447e12cb57022d1272134", + "0x82262fc54f093738a68424e7ade1c2ef817d1cf84cbad5fd9800312febc190aabeb5d0c93c13cd9888d1bd55a000260f", + "0x965792b63ca7cda2115cf4fc6f35175aae3aca45a07d17ca1b1a3bab3af63058c002e0d153e786c32df7f166cd43e0e3", + "0x88e4cb4592a673977e636b58cde7301035ac73d2fa5d08e291d8688234e7182e63eddfe7d7809e09efa156c9bb8728af", + "0x89035a4010c2eb2834ba75dd591dbfd6df92cca86b71bb4d814c6d04fd564482fd2f03ed1a84923016643b97195adb71", + "0x8e124af330c65a2f562cce221f4707ae591ac300a20108b1db5c39f5bd69d2e0ce3fbac101f0cca2607cd3309872f409", + "0x962c0f209284b281823561b346e8a3b577bdc6da1859b913b4686bf013afd156d4b36ec3177d337e4937643490285e2d", + "0x97aeaf80aa83fe5d544bd8883150d3ffb59937ec2690c6608ff1a4d12d8523e4fb6e0efa97eb027c9ed0e3aa5b3c8dbc", + "0xaa3b86741ea517c913e7aebb5aa718224223b20242e057343e7a634543267e29371b712bbbd628eca9f17e109208fe41", + "0xa68dd6ea537043c6d420082c93523796639cccef93433f779b7c26b02fb8354e331d88990afc27d7f6338955e3a9f002", + "0xa7bfe92ed40176e0681f7e65c444bd42a306a4955f3f5ed25fe94f1a5a0df1a63ab5ed36d8cd54857682500fa6f08bbd", + "0xb9dc7f50fee864150842bb5df063b5d544c3bb70c78c9d23f45d50d7d14c0cb5e76f1f806d0e97c539cbcd6958e0f834", + "0xb576d422aaf0aab3bf6270af4ce584a5efc3ce28564621e1450a13f9f8c1cdf8a59a167ee1f21a19761d743f1b611cce", + "0xaf120ffe20753a6b3f1cdda5533aaf77f27817641ac152f715df9552d5af2f388d356069961c1b36c45e6379c6fc286d", + "0x922aef0b3b81828ffd65b9956135792962282345e1539e559214c181302bd67cf9846e9579816fa40774e8a319eab59f", + "0x8c2bd8df234f7037c919849d093c818cdd9164a62b6af204918200ae48ea6f52927df667e01e5424b82c8c1d7f29ac38", + "0x87175510975b27821cc67d7291c557baa2aa1bd0bba788a2b94f9acc5d8b4838e03263a0ee22127d8d7c4ac4d33846ab", + "0xa4bb4664a7d677ec3ead73506df829bf31615945b571ed31ff809de04ae54a1b1d0706b7c26ec0849337a30b1d3fe71a", + "0xa51afa139513fa2218fe8807e66a022fdf431174826a71008e48732695946442ea7aa237d6e755cb0e0c9637f0ac89c0", + "0x9593d4956bfebe0d42d80d660d6e9c3d333190ff5c8c423bf1bfdec5363a758daae2bf48e907c4e5d13b8e5a9d7c1050", + "0x94fe9ccdf9f4a4e366ef91ef0b53948b70251a89172961c2877cd855122f6bf393b9cda6a58415a0bd7d5b07a831dfd2", + "0x92237904eaeb743ac511c3707c915363aa32ef276c2d0cea846583dc39013961cf1a435fdac68b76f37a1f87dc6858f2", + "0xa9d2cbdcc5c35a95e5b9c9038679dd8e92aa4ce5381159bf2bb9b87170363c8459dde4f147928c6bc26300de7f90fbf1", + "0xadd16027f59761d21c8b257dd937008e2853f351219e42a03c3a01c953f89add674289e3d240f3582a4028189be57b28", + "0x807513f2c4cf9ed5c2f36d7ae94e1be579a14f8e29bad0c812cd371e77fea2a1b61975db744074a77b81358783877662", + "0x8fee1dcee030172f22c7b1c478fb95f4cef2a5e2d5e44fd8836e235c88380b2d829b9d51195cbba0afe432fd6bdc50a7", + "0xa651e781f1f14ea79d5b46eb47221bc76ce879d179ed4920d18710b2ac130e94c7b22048ee8aec3c1a0d3405644d0c3b", + "0xb21f8567c0c5bfc2104b9f95c62806ee985e4babe45d9a6420e46afca26d12c99b850e07722b082843ba384eb44dd80d", + "0x86ea27e0e1fe11b634a80b8b51ae7202f50d859df537807595801a32b7c5745fecd90b15ece32788d51e7cfc93df477e", + "0x930349c9d9193fdb52ce93420b9717fe78813476824ec1bd873c324041056b10bd7132924f575ebb702a1a096c9dae2e", + "0xad0ad959e2e39c7282476d10da314d660d64fce704b6ce6592ca995b54807421f3ac2aab5f46dcb7b7837444cc7f9e54", + "0xa494529ddb307637a62e88ca70f052a61cded32d0424ef3b22fb4e58748646cbbaef55f916137938717666b7bdf28587", + "0xac1181cc3fe2d4059e8622bc0beb769e9aae8827ac5cecdd7c9a7b4c263c2837644ca3e6e21c513decb14a3d9977fd56", + "0x8b0213ffb5ae76333684970addd371cf525516aa7d3be024bb8672b7b2e4306c033c94d9d00633bda85327287b45ac7e", + "0xb127ee805b33f63838b790367eee24f1fd267be2e559b8a9962b7bf891531f62402f15a12d3e2ca4578701bb705ac47d", + "0xa1c6314d2d8601b063c4c070c6f6dc8e777f4bd0563a05364e77af98fa7acd6c08d9a719414d44e152738baddc31993c", + "0xb866bbd2ffad56ef5ad1c590181ef99aec78ccbc61c72ae0a898a3d1580ec491acbb6acf6dd63ef5c57522abdf3e4731", + "0x8476db10e758e76e74299bbfafdb825e4197e8726c449ffd2d3628192f79854f2afc406e8c343e62ba4cc909443205e3", + "0x96c867702bf38f2e0f592b31f222b4b231f8ecb0605d8fdc15d202ab932c45503a322e1b91e87a828fbd7050ab9205eb", + "0xaf638a3caab1a2181dfe3b2469e6e004add0aac58deb2861883f598825a4f4132e2ad0dfffea992973d7a23049348a9c", + "0xa5a6bba37558e15b07ce6d5dfc8b53da093284348cbca5711b5275992a460ad55a42e53b67ca07e5d6b4b3e5472dd831", + "0x83d4aac4b330b17dbe6758de8a4e733f73fdae0a528c13e720a80e0edcf90f8e83939feebadd25471be0b7d7cbb106f5", + "0x9363d59286a31e01f97d15e6ad60f9aec94988c43cf3f94951ffd1a2fb2833f93510f9004077f868726cad544b54d1a5", + "0xa4ea630372ee521775036c3eecfd2d8cd1c16ec35383589e046fe36d0abcf349b4d920eabc7e8c9b31d085f7b2fc2821", + "0xb8a797ad4026f596b68b9525475c2854b3722e754effa52c9168e0d905042f630d1689f31435f22b0ab1d94587bf2771", + "0xa540e50d62598c3d6b126302f9e7cbee6dfc857e61a032389f1d337f9299506072bd639452876343bd3dc33a8e9e1331", + "0x9073c68d90e00c0ee56a0dc64d8fbac08a6bf8f62a512573da078d05690ece2ff151d8d152e26a3760a04dc87d7d12a4", + "0xa32008440b5d96ea23ae72688a18621260f9af5dfdca6c392e8d3b1643cc7710e87ad4ed2dfaff7cab4f293308b99485", + "0x91600bdd0f7bbc88e8a421caf6e327ab16e1ec2defd5b6d87935cf19587c03273dd8e381e3dc5d17a81b54f20bfa0952", + "0x85005462f830e814c990c08a55510067420ea56ddf8ea445247351105eb62107df1079164efb9335582fb9c8fd9dd195", + "0xb9fa04ec5b7c49393907d8a3b16f74f855f1e9e2e5695f0cb56ec07d35f86f58295003d5e3fd53415e8566c98e7050d4", + "0xb914489b77cb79d29bb971e48e4d0366db8c79926b213f8cb1f5ba5367c8f7053ffcbcf6737c0fd99de77302f71a356b", + "0xadd47e76b26f9f0569f7c8a5b3a89f0c55a00103b5ae813119d2ea3d4474c65777d3a0ac016f17d68a08667039c2ec53", + "0x922e179357fb49091cef18dc11656e6090908f74bf8100e11782764254f12ca64ec8b47ac8a95507944f504fe34b09ee", + "0xb25c2547dcded8a5795070144da897cc9d900d2e615a00c60fbeb1c45d2d57b545ffd0fb73a143724e652dda73581e1a", + "0x885c5fe84966a10e14f22755af01a1b7eb9a9fbb05cfb16a80a3413fc7b383c588c22d2afe2aaf3881bad524ee2f0592", + "0x812509c0be0dc74ffd1fd9da826043beff3f7901dd8fe6ff86515325506b21809c3ea4173f5e893e0daae7cf2a782238", + "0xb9d8e7706a83d48cbf7ed8419d722576d4a6a7daa2a14ac2b628c0d2218bb0f06813c787839420f81557196d198f0c25", + "0xb7ea383c2396fcae3eae73c4c52f2e7b243052d122ebc46f912a3e0fe4e0a8e5018dc10face16c29fce9d1334849e2f9", + "0x85c8086b750648ae744c9a92e97777ab4db04db60f3bf69e0fd68f35f8891252d641bf927c91a38b63ae6e078314bcf6", + "0xae9811539639a92c3e104f85b3d46822c95bebba3f38d3c60935217b5b29992214fa1aaa6d30178a00c18dc7970400cd", + "0x815de103c833ffafda55766e0193fb62336664dd5aacf0def05b67098de811890e6aad6b5309e800e4ca1ae51bb18cd4", + "0xa7f3b001d6aa6ae74b5787f3f216fdf1bc9dca6a9e2010bac3146b46d957fa36ba83d41e3dd89c184ee6318705e3155a", + "0x916318608176aea132d8fcdeede99df08546a4471d37003070ccec0a48d1bff02619ec7f029adf95935206972e5491eb", + "0x8c84a89268fbe239b3f072ad691c008bd8ece185dab9d4a8b1746505b23c3d9e8d098fee7a8436f776a11150fdd622cb", + "0x98f370dbcbe6cd8b48fa40e988366128e47a5cb32cece9b7c28cc395f296fe7fc1ef0b5c72705897bf1efd2ac2eac70b", + "0x9346dc4ac8eaf406d7577e6af7d02a21f49a88924b54063132159d73d0e6d986ac96c43a2cd4d743d278a9a123fcd24d", + "0x9037ed35c214fa8eb15411d129d5a8d2e6f61a3992c978b5d8b7c5bd8bbd35ca54d547e158597f23c287e68c2c8bd88f", + "0xb580e8753ba4ea4a1cbd610dc855fc6b22c7b66bea5c9c48a2177a90455f40ca181793e486d6dd657c244ab0d7bb80d1", + "0xad6d4abad24c3cb4bfcaa497f61450d8428f782aa65175c587ed06e43ec4c5e4e5bfdc80a47f4510aee00de9cd93685b", + "0xaa984f00c1fc26f50782f51345809f87e05e23dd3124b93d2dfd1e82f525a6a6d56300d06e54f284750ec0fb0826292b", + "0xacaf7ec58247ac71e2d29e930f47985de96f1e2f576448148fd377d73a0759d9b857899c187a65cb11e3dd45a31a16cc", + "0xa91c3619ab6d1ac0e35308edb3f1f78209116b5da7aaa1d57d8527e1adce51bc39258227ea7b190d6c8d49039cbb5029", + "0x83225ac205d74382105dcd7442a0254affe10d29e6ad055227fe0e14f1f7a3356764df7a9c387bf4fd93a549460c9e3e", + "0xa7d89ac8a3e98b98226fe580eb9f2e34f8cadad31beae44fb0ef9e432dcabc987eceb07747ca295f2cfc3b4e343d99b1", + "0x8dbbcfcbb6b3e5eeae9c774355e60b29379ed21e2ae065dd42b98614ad1ec62a8529c94f5cca1fe100448e3bf5614846", + "0x89ef9c401191ee79478cdb7528c2121b72ed34fd38912134fa0c3f7924182b32f1e3b4f9c9427d2375d98fc3e93b68cf", + "0xb5ac7139339d1ef21840158ba3993ae8ad86b35bc45248fe341165870dad82867f8b6a4c62678b4326f5d490d5f93b9a", + "0x924f7f2823bac2a23290e32e429fe32ee02a6d0a5333b04dc61bd480c86e70e301887acdf6128f241d89b5482e46592e", + "0xa1c30e47e5c7b09752889cbd410e7d127f300988d098a6fc05411b1af9b821247ca57f735fb1e7608aebe09c05218c73", + "0x83285b8de1d0af4bdc0a0ae490376cf94a4d9bd9fe92c88f2225a4193f0982ce008daa20986ce33ba3468eb10161c188", + "0xa50e540bf9f756f800a329c318246a3eb25bed3232e80e693b03526877d67b5d28f9a9562a870de7b149ef52465fbca2", + "0x8bb95dadc00a832115601dc507b0ee5eb83031f95cf2278e6773b693d616bfcb003012371581f4b6c80da2fa087ed784", + "0xb7385b3c7ba0a940d2924ffe00e6d429257ca637749134496bd47d656eb4d1ff880650d7f72b8d3ab204a77d6e040b44", + "0xa1030e27a2c639668ee58f5bcf24dc07f63655e540067c51ec72b1ff08f7226e5c5ccb881dd44b4e5cdb67b342946682", + "0xb877dedfb9eaa22622bc68a1369392bed044e97a4b101c3172e897534552ef3df0e9bd1b7646834a65cf2886c2fa06c4", + "0xb2c343fc90b08d6c01873a39636538f26952877144ca8f94afedb344fb1cfbfcb707dbb577938b70e52a739ff7d62247", + "0xb6b333659ef2961818b3c4aa5f4068a7e59168832f2f6a1e73ffb73698aa97c969ab0c439a5c95e788204cc557b817e2", + "0x98234cf5c87d2f84d19a8106e0540bc6579c9e0aa2450e1a218ad9d87f9a4de4297357fe65a87238541120e6e4206063", + "0x8c8c0925f08b294a0d7717fe57b96789b54919bb04543994f3d8a18f3b7aaf34f84133c9a12bacf13a3178f9d6c2d0ea", + "0xae58a9a30e21e61098a2a2b5e340f5643ed8bf35885a88903c6b2fbb1d05a7e6fee1951a91c1f50926fcece299519f9e", + "0xa3aead34dffc307c862993f32427a02dfe3f6711119fad7aafc7de55d87d2aab23c14af3b6a42393718cfa99839d088d", + "0xacc41c8081f10364fca4fd4de24e0e1767645e0ff4f1c65dbc1cb11767e593f641e2bb6a8a70412dba6bc82101fccca7", + "0xa5d4d5387a58556c81a75da12538c9e7fc992ea513de26cd090d06a6e96113fc36062df8e7274e8434c368e380678aa2", + "0xa717123797e33d549bfb638e8e4ce90c960075bba8ecc4f60c85acd106fc177796d177939148723ebd5a8f7a40c36dac", + "0x9992c16098fd423539e257c6ef88807f1471153064c91b9f755bfb30ed8ffec1ccb7b59a50379cc8a84439bf83dc8b25", + "0xaf505f0481f39d3edd839de27eb17ac2152fd315a7944b47efa71026df8897f8391331c6623e7e23acf846c130d12769", + "0xa776f5a0024ede353365fc3fa292c5476a51a97e65ead8dcc60245720bbd8e1c7f5b4f58c2e55dd0dbe0ab414bda4fc9", + "0x87e44c1e718fa0457a765914d303ed76576b12af1af10f96b43a61b3d67bb69b5a091e0068bf6801990fb024d6b7515c", + "0x859eb2388e867ebbc7c55857ecf9a55ead8135b79d4dd618b6ac0b19fa294f29917744a48df2904718e3818cfbbe7087", + "0xa08110b7ba6f7089727676b76b986de081c85e8bb9b2fe920d423727a91124bed721ec6918b0a51b94315f90831a3858", + "0xa5413b02cd9ba7476ddfb03bfde92b35c01b4a998680591a843c2eb07e728ecf8add3be7a9445750ad5c2fc6747696fa", + "0x95aee06254f994e392f26e3f09c54e88a1cf90167df5b657fff80c4fa3b2bd4cc57a9491676eda69d2dc14f8365509a0", + "0x8f726d3facf96fb4678ad32c407356df4ec7e467214a394d00d101ca31dbbb064f9753ffaeff9508d0ec19db796d019b", + "0xa53444a9761ef6dac185b9cfc0e327fdc48b676dbc494a3cf61e029c68860bd3156cbec6c27d99fc001c66573d27bb9a", + "0x888c66598602b80b52f728a1c37887962a2019a97e14ea5f3b0cbbe2c686040f1d4a20cdc937848d0ac26e408f27a320", + "0x8dc6342ae70160555878f4f5a476e02ba622e80a22dc4ed93b0448d49d9b4f1222b55d8b9096123b79f60fe737e853d5", + "0x8d6d75df5e9925fbaaba597858e62fbe769be22fe09471804272a87735f02ee47df4e7ca1cd41ceb7ea7b481ae83f6b4", + "0xa6e29559f3bdc9b7550eedd8fcebe1f6016e6ff32c3c0a192007c1c729b2c5bc561244855a7f07059df04fb2aa02039c", + "0xa6d160be4f023976b01f571c35aacc62a28c993ec9513c74463f8a0865a9a8ae3bb557ae2ee7a63b3bf034cb75dd34f2", + "0x92fc82e8e1b38363b070207e61b567fbcbc41436647d2fce45a29d231fa86c0f00e095f3f32f919a571a65af7270c779", + "0xaddf610f76b6cdf2ac57c0d3712838f5667e57f777b3e38e3a4ac820db1bc3398ff3210bfa6b5e3472cd198ce5c6923f", + "0x908eb3cafd58683aa3f031b8ae3801da74870bc347f1963b3e11a702c52876ff20bb2c69dfd879ba5a0d482da60c002b", + "0x9367fe4755ea8ce5baa28bcf1aa4a591d7161444fd2e82b5314f299783277ba7b8f36a35f0278703610c0c0b1cf3af10", + "0x8bf0c5758137ee3b80c0def010ebcb79f5e188afe24e2fdde2fb59e35d0039ebcb4ce51388d53585233bdf8328042661", + "0x958cec92e9d5a9b11b8071663c66b8136f93f3a913960a597829e58b8148ba365e3680cfe2bdfd19ac9268bfc04d3a80", + "0x8db0948415fea63fcd464c5d13805af7b0e351c68997a8b749d60c870464b2418269589b6de5b8a68c1d4bc76c72e273", + "0x8a0ab794e01dff90fabe2540ec59792e667314982fd150cf3e233ca97146ad0f276708f683a3160266023c77b2992515", + "0xa03f3ca86883ea193b1b1eeb08b53ccac54447dd42bdf670b7d8da8b5da54d07ce044ca020d1038f942e3762ac1da94d", + "0x82cbef000c9efb1b2bd24d45ae2bac16ac2fe1564d0de2468cf9693bc154f9be04cbda7c9d662911291ecf29d05a05e6", + "0xa0aa02166c2ae9cc0f87f5bbaacf656cae54eaaf1b9270537e8eb2c9be48e916471e90a40c9bc1a0a79f7e91f39eeb8d", + "0x8626234dd2b78f91387014aa23385d9a60a5b5a7a7e8ae074131c31c49f091c2cce02ec0d80c07b593df7f0de2de6e7a", + "0xa08ce898d25d7ef9187c397b5e826f58262bcdee7f588b396b8adbd068d13344dbdfae901b1105137491893bae8f3afc", + "0xb7471d76a48d9f5bf4342d24cdc0cc95751f526ad999e0bf5255db7358aa07d9e0bc98bb36323f0b2ba827a7801e446e", + "0xa0b593913354c194abfdbd58140f4e414013e433255e6d228db8fc2e6b9ad4e841f02873ba3e70532b4a9e6326be0392", + "0x9086a3ed5a3a432b6b5b6523fb8b64edec8cf819f02b8487f935f475f22e414acfab695d1d4ae124b03fe20d30b0ab10", + "0xb3888e241cc3910ba1645681bedb8b29d5b074c8d45809dd8034d7f09251c6940689c5b8f7f584cb4cef86993e6f19e6", + "0xa7b14c5157ba7740d53495f0303eaf647b08165d9309f44f82aeb3c687b3f99d71326be51bb935c25a9e9eb82ed788a1", + "0x8386529490b0992a3ce77b70f8eba82dc66d5b8cb0d0c05d8b73159781923e37f96c85d0581ae057ce48040c69968b02", + "0xa70261f23ee534fbb3bb5555add1b24cea6d510d9517cf5e60a61ae8c844618b13a38dca6aa59569f707f968841b95b8", + "0x89b901d3260805f0b9f2506cfd4e8518d6cc62e8603b3deccdb4b16d61f108a95c7b30c62cb0625034130e23e2bf146b", + "0xa789282365e86e882afbff2d42c0a8ac004701e8afcad997686a9cc90f5b280074187c33894d3303e8f1a24c1066e30c", + "0x88ea302df8ae760422193e9492418ff34b291dabb39e04f84e3d5f125e77cdf8e710dbe2a7d3d02c4bf01ade284c49ec", + "0xad2c080b9da400125eec7d927e93b4ed0e99e35a5784c2048a76aa812afa80492aee1ea2503b71f3e3c3613a9b1766ce", + "0xa5c5d3f3f34a0d1fa9d27dce7f015ed3ee9b71c651bbc15175136f107c493b2acf5255c7576f2cf0f71b55a2549396b7", + "0x95431c1ae9b5b1d1db9999c7944eeb59224f6b85c2517f1682dd306ba0c1267fe8d3f53bb23a445b3612475077a177da", + "0xa82f7eb8b9f7c3a56ed233f11952076839b563c980f47ee82ab3176fe1401c2448c178abcb600f3e3faac10fe3be4e3f", + "0x90d5719d2da89da8c3c867ab338dd1753435f4fb4695a9a41bcc76c507cdfc5e18c4c2d60b7246b02f1b6060f167d39e", + "0xa4ad24dda2ec1658a23e1b429dbd1785f21b8285f7207761b244710feb324c69350a2a41b95dfbf1f8943a3854246b25", + "0x97ce3ddaedb0a8c0aa9c27b152dfddae24d453bfad257589592b694e6a37e7c2672a23e20785a0ef2ad9774f62d3a4ab", + "0x8d828c86965a7e4deb8984f8462de1819ecbe4954d91698e737b02d25938881d773b4f749d72699b3a442e151bffc0e5", + "0xb4776259a2554f1484464c39accaa0abb007cab380427409ae10ea06f69856959db624fee1c51dfde8ce04ca2dbffc95", + "0xa423877c1908e97a6baf771a6ffe0f9dbd0abe3c5f3a8142b6099c71d8bc545b47a5e957819ab097ebbffbbfaf1a3b54", + "0xb7e49bc2be70b1a96a9928d60ed3573e0a88794cc1e5d516c56de16da4557583efead6620af8ee012459bccebd6d615a", + "0xa28ebd67ae589a74c4b40522b725f1786d8c4e4afb6cae519cfe77cb0940ab67b6ed8349d3e64f5e4660353654f12d3c", + "0x99827ab5353c7a5eb8c850e1c88d91075ffa1c71377a759baba6a8f946fca88fdf88e03a53b22b32d103cea5a3ee1c7e", + "0xa689c0161697495c8ee105e9bae2591ece8b86e3b5b5642a2545daa21a59f7f80fda868829340d31f35e76d21f950fe9", + "0x81fa61500833f765cc19089cdcfad9b07b2d131a54d1991504a067bd969c2139efb86d48233c097da117ae38d83df318", + "0xb9b3e511e5835a05a1bceacebbdca840728e5d1f18b3f28043a12ee5c1ce3d5da5cbf2847b6b721e8265262bb6bd3c61", + "0x8611cd61370c2b41314743538dcd24292406071733c7a60aa50a599f5a35dd5bd925d46bf6a15ebaa19d1ee623b69daa", + "0x960bc7d1ca0cb9465a71b979472d44f33bb487730412a41f95dfe290b692e0f960d7c2d917412cc331a973360d652b05", + "0xa35f9c2067239dbfb45a5eb9fcb9aefbb3c2ca6d780571342dff5e0673bfed11135595415141af2d55edf46789a2c9eb", + "0x8e2b1653ac640ec2ce2f150f28d4fda7453cec1592514f5293af9ebafd1d6ff4fd43a08845f9685232ae284cce963e98", + "0xaa6acdffaddbd4accf4ddd2bec63a587231023d52e1a8954a4324000a1ffeb98aa531599d04d525b37a8aaeca5bbd14f", + "0xaea1a3cf49e3db44502dc76ea55f83852ac45e52ffb7f35242ed0e693e62645b32f86fdfe36a968fab68cf8c7a91e382", + "0x806b575722f2eadaa0e5aac972f01043e9d74f0dc38b75800191c15de2c0a4fae9b47c4d195a4cf8aa16175595ee1b70", + "0x8c8c42ded431558b32ba44588f916fc09bca7fc287a1b35061770536d381976a727fea33edbcbd45e4cf74ae6c54910e", + "0xa4923110b0d8265e7f241f5018434facd4cfd886e124b446316529f6874679aa0cb591deeaa558037363fc14f36c057b", + "0xa9743661cf9a26c87b7adccce2c7d087feee14ee0832e4adaa4e50d1d5092f00766ed4985a94f34c704b5aba540a41db", + "0xafd1ce7276a446963f8e4054376e0947fe6a383bac125bbc159c83fda5c1aeeb0a82131231988973b478008032932875", + "0x8ddbc10bd630628e5cfeb046072c9ce6fe3349de04cc2dbeab2b0df9400bb42fda4e408b72d4333a37cd281b03ef767d", + "0xab77bd26ecbe8d8a0d4cd001591a297b514d668e557632748824a9781be6bb0824ac4baaa835cfcaec83e61c4f5536e3", + "0x83f836429eee97397ce31f3e94c07c2e18d23fffd00ae1f38758bebce7f482a7bfa279e7cfc41125f83eb4764fb57758", + "0x969a6081fb02c769f4a973a23fedbfb5ebae4644af358954354d55ef4362488c1c21dbd363610bebdf5022b9e2e5d8b0", + "0xb5ef57dd98f5eb1621b5a79b4e71b9e306e5920cecf45d38775bc0b3174e55b7b4badd65d3688d92b01fec566ecd0e2d", + "0x929d2743c4db03a783a27a21ef6e15a2f94353a2d6a3dd44bdc31ca29221c0269eb1ab61e8de226e172edcb0a80c69a5", + "0xa052c2b8999273342897ac6133c97deeda3ad0ecdac7fa699d877011eecea8838afd2419e90cd41ca3381fde8840df8c", + "0x98ddb9ae551f03efda8d625dec41b390fb61fd40b63519ce5a1bb778cfdec88b83a4e2322d04e7c210bd5869bfe1388f", + "0x92b5060f978a5193001c5d4b7ae76f64f48ff2394d31ee75824b1a5b09c38c097ee3e6813fcd379a8b53fb978e15904a", + "0x894e74aee08dd3e56333e78ff92668b5769aa56e435eb1b758beb6ade7809e38684b65ba2033c54e6b69089ce3a9ea31", + "0xa54d6a79883b35041d019e74a9a78ea83d2f2163d190c4fc90e4018986f3bdb0613fe6fdc8ef9b1173622241bf31cae2", + "0xa3a7d9ebf9c180172b3dc4e1b5461b6215fd07ebf1028eadfee29ea2ddaad8f3fc4e1a86de15f28610d85c911c0af611", + "0xb2ec8823d869fb51521a12627f11139f2f567f2d7140fc73e162b88e577307f8c7b4fbfda5fdc28b23fdd5373f656811", + "0x8d6baa5fddc1905897271118cef61f86686bda8c22b31212d255db4db49cc8cd2103d25fcceb696efdfee01c0d3ef99a", + "0xb472bcbaa7d98c519b74a732a40b43f18e38450cdfde8558fbafd8197b065f0505058886b715492dbfa9802d09a2af1d", + "0x95fe716eb999bc46d160c2daeb3966ad183ea574a605fe21ef06bc5e4c117776dacbc041c1962a5de5f0e046ebc58ed4", + "0xa9781e1dabccd1b3ec361205daf8151e9c189b7b613a620ca17a013e6439c4a71f8434fef04c588839bc4eff91a084a9", + "0xa937d83881a9f829eb94ab8228a6f2e2d74f883829b1214d54b4ee7a5ec3813756c2781b3a3940dc547604e7e3212570", + "0xa0159b0588f3f3c097f36f178b7c7fccf7805bc9f607e1e6acf62aef13f10f6d6f4f5caab0572b0b6f951b116842363e", + "0xa3ec9071c8caa5069d2dca497ab094734df60016a41c0294b3c95b924a8b90418f3fb18ff17279a7410413eb37261b5e", + "0xab6d8a50b6dc663da6ec9d7abc72d46449cd7d7c557745406b7717ab0ca231dae1a30eb616f245ed38f4e5617094db47", + "0x81a776dee2bc75c1bfb43dd47c9049acfc5362b6c3635d716b595034fdf635568e87376789ca0049235999702746585f", + "0xab507cadd6d45e036ecd349dfa2e169565c5e8f165b40a17540624d0aa002e8a00124e139b3f42200aa1df207d4f697f", + "0x869de6eb9058b5a7fd2c0f400b7c151df30e283da3cd914db2967e274e1c7faf37931825e7bf3aa72d7e8ac567ea649d", + "0x831e8624304be9b2ada69cb6df15fae3ca07c8377929e6e3897d59333a27d63fa7b7f03799bd3f32bbf9a294cb7fc5fe", + "0x80bb1f720d5302d306fbef96d1551575e195c7fe6105e72ddca24273156bf5de7e075b2eb675cd66b5bef4979864455f", + "0x982e7816899543c8bf6f19c1bf74536047e7bd267b83234792da3bf81b14d9a6d522f1d0e470ce55d60be8a9c2b26115", + "0xa0eff165a54dcbef6ae2323c343b135d9033ea856423465eb32ceb8af874da4e16e11aeb548c3b2003ae1e1be4fd332e", + "0x85635b2816af5fe8481f089b0f49f281ead80a23be02744e83b709f7ea01b8062c3c75b78f30ccc5f88fe4d09652b001", + "0xa0e47eecca10f8cb5064b15d38dc9b8e7a5352c278f1c5d8d11edf455da7653bbc9dc0bc43866f96fb8bbb3f8206f1dc", + "0x94dabfdb02a7c54386a31f44a798f3b0167c6344f748025aee98f7e630b9039541d29680cc61e7eeb27d215ea468307b", + "0x9606d935c42c32e4f32e272bd62d2cd4b066962db9a966c73a03cc1d0d57f27d3cf92d12c41eb5a5bf1e8c998ecb84ad", + "0xaebb0d4e554f2536d39fd74d2c60525b4f33e4bf47ebd8f3e71e6d64025cded91447bfdb3f7b8b4990f26c29419442d6", + "0xa4d0e33eacf61e342d7b55807545cc2554fe75da3a8a4239fc60aa0b43859cd3567aade52a34522e1bec8745c0d8b9d4", + "0x912713921fc75456662157114832f3f2c29dbf49090e271493dcd526dad66ec288893e7795f26b4cfb29bc1021b5ca8d", + "0x8dce307d4cdef770f86ff1fabe5c06f4695d1bc102b31ddfc7bcf55a0a40d449c3a6d90f70d219b1f90201973673a187", + "0xa0b3e3aedba7d812d5f65a37e160495a4f5ab9b602e3511ce80da310c5129f4a32b5fef76c4f1bb499e9587bbd11ae75", + "0xb6911b71f3b43d9c0bec4ec9e228ec787ec628dc7d8016c8b0f2ca3e7820add17b3d63fb6d351080409984a47bcfd149", + "0x8977a3facb80bf39b134cdfce17e442b03ff843cca2170ad058e3b740a652f0f8f9e3b713a0ac3c7d186e1ab2ab22ce7", + "0x88d3a4d2a57242ab497f39c70656bc2635ce5a985139b30684e76479a08789ce690ccd34ec4d0e0d3f7e08ed1082fd4e", + "0x81917ba7ff351c6334de6bc0adc2fea286ee09eebc4e259c0248819d81ac57e98a755560607df2b0c37a9de04fe906f5", + "0xb483a38db5d15f79b2657f2cf53f689a29fd2e639d3d5973dea8005ee521137d0ee163cabc095f7ef5c31eed18d96250", + "0xb69c0a22dc5dc8f3fb1af8aff0a85c7912533f242b9d678897533ae731e01363dcc0ad1984081fcae5e5c8ceb4614980", + "0xa34c259a3e41d252c0a448d8c80c0acde6c17c94d1cfb9dff0b7e757b5f6c9771c439399e8b83b776a2976541698ee00", + "0x8ca80a7f50645c4e49d5441eb01f5bb3574c941a849c1ea1be2a75522dec0ceb42a1ca6f8d1d8d077beec781c09867f5", + "0xa142a9d93671afc696089a19ad1d48fdb4a775046b88ee825e0a15893b88bcebdc49907da6e05f628dd1fe18d3070487", + "0xa2225b1e69af34e406d21479ba0e6a6c0f7b19e77daa14fdb59975df4e1087cb959c42d745962a4940d27bfb91368339", + "0xa808cb150b3be88730f56030c49a005384f22307ea010ea66dc72e236a890b2bad17be521bdee252b5a1d8aecb43f005", + "0xb76e4d772f4c0d547b9a84a5c1f94cd10adfd9352edf85fb85510417e6b105ecd1ba1950115264e7fe087d6a95457a38", + "0xb6dfab9c387936128f83ecad99deace83af2c65175467ee70707ce1c7ad8adf060948b99e0134954184d48039717f558", + "0x8231b3f160ff95b2ddd79df5836ff6001356b2b1e0aa9a3e58a23b85bba279a57d83d3a946b816f486d8dfc1054dcf9b", + "0x8cee87d1865566fd5d09f26bcdb8dfd9f98fc2fa14e63a57ae3c88bd4485a24ed572f683fefa3c71d900a4e68667781c", + "0x992bfe6a33f987afa0b3e26f9cbb33557725951f7d08a18c9a299c98d9458f30036f7466fd7ab5f028f2d3c2b2d6a66e", + "0xafad68bed72fb64a301fadc8c13da687e5bb99890f7e0d152195bda72994ba8166a29c64371b5c6a5523112b81610bdb", + "0x8fd285ca5e5a2c25d41cc56bda24d93300d891feb847c9dc53e0d672a59cc66c4fe331ef81ceafae47f84f9ff8f3ad12", + "0xa73271d7e86676c01b14a3a64b00ca02078ed158bd19236dd927fedb6a03ce5ebe1b28ac37c0d58844124354e50cfa5c", + "0xa781eaba26849c868900d6f60313424bbd9adbf9275b7cc9e7e4595ce43fa542a6326446716c6b7a3f19d20e46b908f6", + "0x92d90b66ad6b7c194506348f8629e1ab994e4dc826f610059b16e7aa50c4442b50dc387c25ff449a2a5a877941b311d6", + "0xb2474845aa07b9f3cf108f697bd97590b39356fe347a93d0e48491f317744c45581844d8b7ebc75878ae96a6da51c2d3", + "0xb382176c8ceeec875bac7676373f04f3eb825a998ab01d395aa40cef1a863af600dc4fecb3b28322c26bf54b1c9732c2", + "0x9635ea9cc7e469a656af8c6715c587ff364ce270d7c20c0b31d0da438af49dcaaecc50e984ddb345f85967548951975d", + "0x83e37d0274b1312cd17bf31aa38a3c4d33539b40021298322c38c1f3f2a91b8292a7d11eeedbc70f60537bb670037beb", + "0x829fa06797b60e186bdea0e45d8ef3bb542daae698ce55657fad5c753d75c3c22c292f974e202dab2176880dcb9b4c58", + "0xb6780b51fa6405dbd345131ae9a61023fdc9a89bf602da63d41777177ea1410fffccb55ed8aa8a268e87d3d2a88ee1b5", + "0x8415970fb7f17b66aed95f28ab6088a6404bdaf98191122c4be9f0f7fe39714cca59b672c22af33902eac409f6456d9e", + "0xab6a9c4aa917fc0c679bba79f91869ea6a8f1035ef897ff965416304ad241deecb71c8302076160a0c6922b088340ff5", + "0x960c35ba191f0ae3c19ae6845cacf08ef2f180099aad80ccf663bb08dbd10bf825d4b405a505d559d553cdbbc087b246", + "0xaf1157c44d8334720b407774a55774d89c0e14fb9465b9aaf372e3d54f3bdd889c4ba2c8663b27e87c0399a799ba7708", + "0xaeca3452e0565c6b51c72084016378f7bcdb0f1fe591363983353f9ef56fb528da53956ba37dae2e874e0340026bcc9a", + "0xaceb4cc1929bf4eb7c400ff915b5cfed8298edf996a0f547a8ca602ddf8ff3f8150c242614e30ed8bbc641dc5ede4b69", + "0xa0d44d9e087e68da107a3a135772a875c327723baade622e78edf325036df0863f6f5625331fc05c8a39c0ea6821217f", + "0x93763fff47b7545d87f6b58c2ee84e47ccd8f41129fe78ca547dbf7a727a5d1ac533ef012032ec5480b9e0e2cfe149f1", + "0x8e7f76b2134a2b77914ed93351a1655f84c478500cfc5ae3aae092f85272376169fc18f28fab921ec20ebac6aca0a144", + "0xb39028e4ab93c6ef3bd72e1b97bcdee5950a37963fd55c460c7db1b2cc1f30742a52898eb3809622ba01b218abb687c8", + "0x8caca98b137ca4c2c9d120aaea6dadd70fab0b8310e2b59e2dc8d629904e7f019345aaf070f8b3955e823b5a15e4fafd", + "0x97496d47d7019695d03e7e3f3a4d7b007a5b80d8c1e39fb77b762831b6578dc99659942a0e2532bc470a7934d5bcfda1", + "0xa3927eea0e4d2d9cb7e8e27c548fb6eec8265609fa09a980034788899c3efb996862ec92212c6d67b2ab1225f46e9701", + "0xa7c01279fafcee94239287792f10ef2d14993ba3fbfa2ec312a79a19afbad8807d66a39d65d2e39be75c2ee9723bbbd1", + "0xb8d556c24bcc592d42c3c6793867cbecf2916251b334836882ddfe9873bc44145c9864ba979c1a1756cfae403a2c2aba", + "0x903e2f50438153bd2f27f731f7d9542a6c3e28b33756122c01a0fc421e3a6d63cd46ffb48dfd89f39e96c79010339430", + "0xad94c3ac79c5edd127d053117af448f52952b71d22c77fd45c9535e0e7e63bb96d0140ddb216506c4308778ed37c06f6", + "0x8b07cb3cc056a07fcdf787c2270b1bdb381e9c9aff1c5e899ff0d75f70f8a3c04cd7b996d08d58dc2b0af3cf322710e7", + "0x9936cb9651b9e4379bb1b6651d4e4e6ec07c2834c8cfef293a0feeade743a1b9d6e259f2d9a96e498d8aa8cc1bf212e9", + "0xa304f1078b47ec4d36fd6a2bcd5baabdea439284b797208330c63c4ba11c34eb4a9112ca6f5785652e7afa80f43e181a", + "0x8365e8e15a82af2a2569dc2e411d4657fc3e5a647e4fcb1ae7af3ad5233200c0c5c70f0d1272e1d01a1f27b7d1ac6c46", + "0xa8c7aaa2151f0b1291d15d37926f1390f09cdb4189fc06768f84368864952e15755d5d792345ff3abeb0749ffe92c357", + "0xa84440e77e606f7e0930d27966e58bd48d04c7090852e20a0d71e237bb853de77d9246782a07ee719cc428e4dd4bcf73", + "0xb1dc625dbdc77cb5ac1ea8b8ea96df34fe75c78b04a7ea26b2a9c9b894c74165e43d35335b2263e208f98862c8e34e7b", + "0x904d57241c3efd482ee3066dda9928d458db83697a2f26e26b2340380bd024305bbdb908fae098ecd9be1a6ed9bdf785", + "0xa7e9a34fa50644c7e3ae79857c4b6ab5cb722f11ba61ecf2fbce57c752926f973b4e150bfe9669b7227ab33878139a14", + "0xb12c5047dbb32a6c25e39deee82a4ab35b994f87125f46ec7e54bbc0d921f23bad1c53d95c616b6107d1ffc4f0e9bad6", + "0x93251a062b2c3d0454c676d63bca3ffb3007cfc021dfd202a984d017171dc306b55f549377b1d4cdad58da03e7001d64", + "0xa3c92d3c190d09ff6e6c0ec0e38581564bea9c5631975908a6f383e5a4dd409455aa38f05d9ffa6333fa0f4c1cdcad21", + "0x8383d53750e7a66cd5ddd49903c3b736f71d8dbf874d1dc1bcb3382e39371ac96c90e14ca7252d99e0b4a2a98c321bc8", + "0xadda0ca30e86ff110ecad87962d25b8a799f07b00ac4a6dae28731170a1bd368acbdaed043a5aba82e695eb055264400", + "0x93bdc012a0b4cc2614772e778cc3d5d1408fdb9a075e8ac3bc938841c021edf175e8d70b218e53e2a363c114d5c8492e", + "0x8421a0387f6c3b72dcbddf8308951827e6039cbf3e2673f0b587b7c9c76a29ab995729e93d706f77ea7b72ff800d308a", + "0x829667e1adfc87cf0a7adfd51633469be48104d4f8a4eb91c99036482de823f296b5a2083d5dddfa9e585d4e8f5515b7", + "0xb3ef2912a59592c81e78a526adbf7198bdeeb4a85c30b530390fc92335f6ba0e86e1a02795056fbe3551f1ffba0bad02", + "0x85ca2c65a34ebb4959d84e48cf714cce09a18d61de6c77de8b5667bc25b352814490c39f2f91e1a757bfbfdd4870a924", + "0xb32378e7dfab116b718bc2b487f6ba4b928ca8c13cb8c111575b0cca3c1fb35bd0d02706d9116e74c8963c9e2c9c8aa6", + "0x814cbcc92e24d0f463cf07e1078fe75c0fdba99ab53d8d1cc106661264f6c7a2ccc7ca543478dad9d598b9b846d46b17", + "0xa6c335afc4145aa6e0e9a1686434f32e02293a87342262e1a36baf3f9146926553e6b23d75443d057e07fdd8181e7be8", + "0xa00350bad7a51d377684de8699810e9f3cbcacb4abfb0458ea134e1c51c49e33ee94ba491152cba572fdacd11b7e365c", + "0x82a78cbc2e297f6adb4cd57fea7b5e146f34de0fbea02165fc3327ef913230709658820eb45f845bbf04abdf83b6b809", + "0x8c8b42bf64d395f8e5d49b321774974e259a80c290db78bd05364e0683afd47c2ce3c0a7163f09791ff17ac8a5c37262", + "0xac177996048b060fe88c58750580990938e8923cc7c13815cb451fa8fa1c940d1187f390789c0b98e11daaaf369de5a4", + "0x826e47cb657a9046f916efb6395cf46bc57f323afff8a61b10afec92ca2d403e418e5a270725e5e5ba1f79bbd4997ae9", + "0x8804c06fd38d16bb9e0880d41636dd44e8abfa4fdef71b8d0ed7da8d50692c80fa26c22590c1e64832e4e1db79f75581", + "0x9477d76da92b836e0d70d8dde4ee8b7b760d21fd024ebd8ad842bc77d145042c30cad1bb3c31a6b6e767353693089ed4", + "0xa2bc88d001b1d579c9c50d071b55fb22528c67c4ea2510b7e3f6b923d6adf0b00652a9d76270af9504c8dad68ba73315", + "0x8b411c362792b7042aaaaec65f0aadcbdecf35717e3cb069e1ddbf565a526f0b5d70959893f35e5317b114b30de7dcd7", + "0xa7e4c4784ad12a2dfc850dc59f87e42dadfb6cb8279a1b4ec0f50a9c7ae66a307f697fd862e7858b66ceeea6ed1969a9", + "0xa6d0ed194062ccff53f70a4f36a1c78d0eda297870a7fca269ff9da938f3e17254bd8006c8f94d76d42fb27517eb3acc", + "0x8658b1d1cd82e2b230100d159c98855554d7cd12787c0b884d9f6d98f91b27584f2cd88a85d9ef15d17e4f22bcc9e81d", + "0x965775a7bab0f59b6de9b781fc7dd1e723e73bf712babf2473117c8e824ee559bbb16d49e02272a1938e21b67870cf98", + "0x9217ee03a21f6d7ae205470e30b62b0b180b4510207ef35d885edcca731aedd4b014c439508c62b37e00b0c4ffd263e7", + "0xa94b6ed5d559d566fec0ac305a93d82eb319e5d697d6d27aa5c09ce87ba7e795b391e80dd9a3cdf78836e9b6bb79195e", + "0x81fb9f3d046490550050db3e9e9233be5a3738b97363313fc6665e526f0301ac0cdd4395244300148a8f4bcf9ea6ab5c", + "0xaa53e71caa80a47f60aefb655094a092351d52fc160066bb39921ba364f8ac7e5e4f018410d784fc2941aa3a22986c55", + "0x82450aea8def5d68b7245f93226804575c6d06e3c11d6c25203e5d84fb75991f17a61a7205a016fa6adb74f9555a3fe9", + "0x8f4962d56f5485646735b5a715b00bb37f57942ed24ceca5e5ef4c3c105d057a5b1da5d2a4225c627462b71ed0fa7040", + "0x95a9bd584350ee4493554082676d6bfe849f9a74e78573e4d7e0b686c5f946a32e56f4f0e701842f6d60f61b7de64b83", + "0x8d1401b555b60a0967bf924835029a5dca72509b2db01bf2d30ed3f5b06d221358acfaa38da1b9006982df7dbef7e005", + "0xa80a14dde188d070fd3854573a64954285bc01d09798a519a17f4a64151065009dd962e4b7af78e4050b915879a97d90", + "0x8755f5bb7cf6ca7405acf99f0cd5df4496b57a8749c4532f5acc2aac3a99a53d0a9686673432cd30cb0d0937dcb9ba75", + "0x8f7fe8bb6528c064007c69640f714be5656d03760478e95e796703049e82587e27cedf6b53d9f82b77a1ba4a9f4e8597", + "0x8cbe8ce8bb9ce9a2cf5419a556d66f156fda52282edccf015a980931199482bb6a14fe66ec267d0b3fb1e64351fef97e", + "0x8293a92d55cbd4f525c7f55aaffd00a7a5c5770e9e580a463f1e55400b314997a8b8129517c8dbe3e0b1f37e92533b4f", + "0xacb42f77b5f70ff46e6012722d792a18b9299f817f0c89098197d6b61963c79bd7410fa8bc74c2a70f06787ad2007854", + "0x98277e76bb8a47c448e43d72f08d07b35e8ede8976eaf6697fece93a838f7ffab7c3ce62481145daca92246850a5397a", + "0xaa75eea0b8b0935a1fbcd7be7c1b96d9907f7a091db6c20ada90a36d9f090594b8a06594b0c472b8f53722e5f0354d1b", + "0xb1833cb2fd74a91110a458082a381c47814754960c7eb625d5b3b6594a0f22f2f9d22a8d62618849ff87bbed1081a45c", + "0xaad55d53831a3fcc1b6c4fd3ae9c27731e6a46457196f2c4999389430dad043f3efa3612d2c6bd43189f6fba99ffa6fe", + "0xb9a8cf92a8348cca83ed862e39e50c7f1b6deee68e25842a844a4a3145033ab6f310cb1ce809efdd88dca36f824252ad", + "0x8b186751447e40fa455bc20ebccbd61b1dd2ccde4c6b7ed658d212ce578b45f365d16a33a95b147b42f865d08d30c813", + "0xa25122d54d147a8e8908ffa58c70cf31810e0c25a859a70325c0ffb911fce99f3dc5f6e2eaf164aa9daf17debfe9b729", + "0xb77be42c957d99a2b46bda20099b173cf6d5a6b0dba8416d4f0b5ed083f4c5c7139ff59e5abe258207c9dd587937f161", + "0x8c51b06dd3aec06f5cf3488dee5f380e63bd11ec87062078fc3bffb3188fa80b274217c6d0965925d0af0d4d9e58a1af", + "0x8063b95a9d55cc9119e30ffee0594738cfec0d0b29670ebdf7f44ca81998c2d31677f7e54a776766912de7a2f75a6634", + "0xb15d2435caa939fc5c332ddeb234486874434a4f07cc0a8aaebb18c37a3b1d44d4d3b5d5ccdf025b9a8a5a0a9e565814", + "0x821809125de614dd27f1344d16e82e1f34981aff7b93713fcb00fb851b9b049f18ea5a2a69aeaa06e645a1ab251a660d", + "0x8d80f8c68a46a256d472c073fd2ea1db8223d9c126fc5e9911b69a926ed06abcef532c27c4230169366c758e31e580df", + "0xb40a372b2ed411dee1cf4477cd4e17af88916d20987af0a38364eb463d3b393382fb9158890a31e7fc1a0d3bc653069d", + "0x8b706d4ddcdc3f1b1d3339a6059d70cf18167ef7b145d39fd8ff9e44e045e6a82f1e137bfd9093e3524c7637b032b0f0", + "0xaf6151619d783e13e4295896e3f63b22a451f646fcf1b80698074a7aac69d127645b0377e52b2f52d8fee90eb5b1873d", + "0xa49c5303120fb95b3f055e7b22eef1f621a578781dec3941d4b41eb4eaed6658b08d7993ee3fae28f20e187a3616dcff", + "0xb77373ec2f1de9a1222f7660d8690613830fbbcf2dd9db2e3c237017971eebe93164b8344aceb14e7f33bb7aac15793a", + "0xae5b41bca1e414632bb971f5cdb7ddf4cc678f2c7624ecd11d12c642a3503746160f558682b9f603512ef95960e68e35", + "0xa46097adfb76f2073e4155d13f145436857366cb99c1f2c068801384178a5a1ec5683bf5a156d20d6ac3d984b91afe85", + "0x8a01ee29ce6c161941141527fcf5ce35151d82e4d8cd8e78a7becbb2a68ea1a221bab15cff3bca4e4efda73e051a2e3d", + "0x85fb0b56a2d226438f6067566f75a44ae6a196241e3c2ec156cbe94bd28f85ed7a7dc39983ab9adef01c368d90bd7a2a", + "0x809ff5c145e18ab96a32d8085432d975c5639ea595e12c64ceda9fb5a000fc7f13d0362c0beced0c657fa5ce5825496e", + "0x8a99e9eb1106344a8998783a55a1765760340c7b5a866efb2d7a6844a3c92a443051c348f5eb257f96f6f4736a027c33", + "0xaa9fc93e93859a65f75658303c77cb67db5954d83424bdbf8cca130a39c49985a92e59485588bf7bf3a2803f761623f2", + "0x8ff235268cf630ecf3a04e4f152bf374d7bd4535dd2990b75a6695e9329027642a069f7ac890b69ac01ff9b2496cf874", + "0xb53646d90c89aaf589a0b6ace476f4d509b120746c16270767c93005231377b2a801b35879cbbec3dbd0af1f7b7f7cfa", + "0x9362e67fee91255b52815a2343a44c1a817ea5e5e8a8f800c3114d48ac874f2fc4a11f9ee688b68eae252c2b7c2374dd", + "0xacae4d9f1a8c0f06d1c760f74aece702b53c65f8e65ce6180179af843d43bc81d7059c96967def551163f2b3be23caa3", + "0x90eee9756c7e151d8dc1f1863caa0b247326b0ab3d222ee1bd2f1ed8212e97cc9a15ac2af42992b892cfce43b3bc178c", + "0x866bf5c4920b13d9508c29c343e3ecc174ed858963c9bd588d2238cfbf077d60d7fb36ebbddce93c335f636252896c09", + "0x973ebdd864ceda7dc2dfbdc6d34b77dd211ef2f51ea36d9054684c28dd03d849659d69921c36aaf02c24c518430f7e0d", + "0x8fc7f2504a152b777084a82ec829aecb3fde5585f5aaf379f10ea5650bcae53d5b6180b2d2eedca7582a5b79d31a1a31", + "0x8059caefcce1edbb7d23119cc58a3856d36124cc698f7579bb52074d3ccd5feeeffbcd258891c7777ea50acea43ff895", + "0x962fa5749997612759b1e51d6ff14f928fbcddde885ae40b34c95179bda860d8920467797d56800eeb5093de276c7f2d", + "0xb26cd66d110a25c0e8a37af926d210be83c9622dac3e885427ee2bcd778dde368115127cf2e66e124fe2f07074064ae7", + "0xb1c574fdef757b8d7f6f62d5ef8fedc29ad04077576a065d64ade5b19cdbbca860c40de91529e2f9418cbfc7672e89d5", + "0xa94d923d38c1eac8bf0c7ca3110ca68c9159eac1f1b091b3338eb0f34110285d1742d65e6577770ffd626a8334dd6d6b", + "0xab8523d4d177083cdba269459ecbd7bcd20c752ab4792ed1af4adeeebf0750303d3751b5a1c7b3c650340ca0ddf15f50", + "0xb3c20c2841bfc5c0e11b24d283884b2644851a42009b88cac3fa109982897773ed615da2dd56c2c269fcbc60ecb5e07b", + "0xa343eede0c1e05371b68f421af73c2df3c46049d83af66e0d5c06cef4551c2f98c674683213a80e997f81b4da6eef63b", + "0xae0f803d0cbdd624bef23004f57d9d1fd682cf974fd538a28081b3bd0de5651a0945444c0cb90bff358151e120325fe1", + "0xa2dcee015dbe46ab42b1d3f18b698f9599f9e771c2cfd06b7609c5e81f2250403608d7414995fa9e764232db9778f672", + "0xb7565387591aed9ceaec9dd1db53704342a01d6ac0bdac516c7b22f2a5666a190a639ee1b78f42accb4cd2b0336c8c2b", + "0x91ebe58a637af30ea7cc72384956d47dfcffd54c6d38c2807163b17afa901c5b5fd6c3d10204e6de970357181a87388b", + "0xa0a1a505d4b0a624370ca93fb5cf5f214dfeada9f65aae3e4677b6edf3d1a92b4068aa431b4a7dde547b85eabe2a944d", + "0xaae9a5fe76a20fee17c9d9be0f4e327fd90f1abaf96a1a5033db0a0e4372fdfa3ad51852ee351b891c47ca60ff429961", + "0xa4f86900d83f77a01a920c6237b2dc779b8262dbbac74081c2fe8004a6f507eb2e5ad9a25fb60ba419f5e4afc227f213", + "0xabc641139732c853cc6446581ba0fc388d574305f6d4544463e41a94d22aa35b93ff412af3492ea7bd2e43b47766f485", + "0x8660d660c49e08460a740922bc226ee1c735301800b0da5f50a976367d03f1594833cc8d3474d6daa0a0250765cf5cbd", + "0x950e04c1ddb3790c783a26d374eb1789e827bfaf73d5c3427bcaf9b0e7dfd9a924123211c3647bdbf74743d2bb818c76", + "0xa34c36d628efa72470aec0cd2b597b2bb0733c5a7b5546c0e85349c5a1bff8ee766e800c185056e0b603e20451d146f4", + "0x94af4c628b16668a1820e45b8a79ded43e68a53f32e6f49174c65ac5fa989ca5128abeeebf38cae59601e24a8457f63b", + "0xb78664fae775140f86c010e423f008989299924162ac877afe54f5808d1f969c9cbd5b37e0fdf0c664834b8b3fcb68b3", + "0xb49143e185bd0d25c69f9a9e594d616bd66dba5cb3a70e52c93d549e9ba4b7c10ac3d12a0a4e5a9b7ad90cda6d3de523", + "0xb04a8bc6fc924c03b3b64ddaf3a76da9134ff6fc4080ef9b14735be4b792f72c2817a9c5232a50c03a2dc9dc69d83e12", + "0x8d76924898f0d15d88e53b9e25a295fad7fd03f3536c60a70539fb605d3f5a42b03fa11a2749fc8ade46f7d73e2f81e7", + "0xad3239da7b25946cb40ac17f680d309b1768452df69a8a11715dd7f593842acc6c4ac56188c7473b3f6cb8af85f5d14b", + "0x9717520d401c5147b283b531437c28c281cb269c8d60360ec77d622c1d46ea118ec597d3d05f83230ac3fdcc2f63885c", + "0xa2565449b773905333a85a9903dffadbeea8f890ae4eeb35d222986a718f7812a8a3dde24d8b2d6a3b27dc4af09ea31b", + "0xb9a0e7b11d709516be97dcf8f429f798ffb45ca06706afe5306149c4b0651e6ac7bb29588e6a5711f99e90a99e1dd346", + "0xa5057ce3b30da949cac7222608f4f59387928253ca6a207575a9c6d056326a8d4b645ce4d91557dd4797897fe5f0ef2a", + "0x80fb7c03e639422bfd0cbff3ab0b186068af9bd21a88ca93c5439bc0821a44c197efcfefb787093e892fdebc75aa7db2", + "0xa347fa1709adf95eac195c252932617e0e05299a5b656658ac519e8ad2c43823d15b75054676318e8b957ecb79e9e682", + "0x802e730c4d93693c35b15a6094709ca57c724a6b8336c38a8e79de135f16ee02c7bf0314dacc58e5d75117441f0a2bb8", + "0xb0e07344f33b4dbadb450b0d65caab8f3fa13981ca83123521b574b95a7a9c39f8c8ad7a0ec1ee2f084a11f717dfa31f", + "0xa99d819f8ed05c9182cb3242766683b28ad3cf3a87c7030a8373056446212f8b0d17a995f583fb88b2b7c0e3addb609d", + "0xa61c205603a1252f19133c8a833080a08e626ae2d60e37ef1f2fc50fe8b8beb8fd90c34868e3c0282ad646936c566f5f", + "0xa0aaa1f0ca99f0df7383f24f8dc7dcd18cfcb1779798a1be2049bc37a787dc063bd9c54a1fde5862acffdcd4911f61de", + "0x97076ebadc6594e11d29fbf186923723f14045b51b3c579019840ad9eea5968af8c9b16b40f85eae5ee0de6451267469", + "0x8d26c32e2731e5b746ac105f49e5942f447e0dccf804f295376a97e7c26c4d68b0a6a0343bb13ca990cb882f88999acc", + "0xaf08321aec91e31299b3f226a35686025d48553a2d4ad1640bbac894e59938c500f62d857eae1c0db015607c95384e78", + "0x936256b5f1a62496587cc2013429a6703ea4b0467ada8cd6adbfc4e2a57366d29e43a5aa7fc9e40fec766ddf745a2639", + "0x8eed1add8554ec7212abf90a4184a864849dd12ad35f63e7548e77223a96858e88060d19393a61934698f06c4f975b4c", + "0xa321b9a8f8537d07b02a42d1e92d56c3ebdd8aa74ef66032a2119896dc3fe0da83fa464c55a8a1e4f5d825ced822ae3a", + "0xb3fcb01ff46f75e19f00cad13ec4aa3a8b9440965833e276ca82ea480df71e5c1e1e89524bac990141bb89dd3c10fbad", + "0xa6c70103260e1016245b30ca795b5c0a7b6976fcf9d1ee5e6effc6659aadf738f2c41ed73e1004a6decc58cb9ff9ab96", + "0x84aa0d28dbebcd621386fe884b59ceb5e338770f208fc18b68c3463ea15d476b60bd3bcac4dd6c901ca1d22551f41433", + "0x8b833203f3888630a4db318d7b9f99a4a639a526a797684a2d764933703180eceab806cd48e4e8c656eed7972652d57c", + "0xa0aa52969169947958ae82084cb77c9d39fb1837656945fa5ad7328b551d9e23c56a2ea49323aefa0300246b28b0d215", + "0xb4132c0a81fa9949005558a27b29c167afd5aa5e2145d6ed5b225843a1065d7c6378b274ad3468b21fd6d4666a2c539f", + "0xb1a957d00fa07f7ee1a34f2570e448e726e9c8fc4c353d6afa010822263bed2b3e853d34af86cd6d353ca17fa7f101db", + "0x8733bdd3930d359756034fc08ed40641cbc658ceccf7abcbd82475290d2932e8d5413b7ea3063744871608b6d60a0fd0", + "0xaa4f16c7936a9b2f73924648c32ad5bcd937e6bc2f511f903f53624390e4e897333bcc9f84628fefa5fef1f539433a32", + "0x8995f8c01bcf9a895e01e3680afccdf8aaba4e5f6c78346019aeab6275d9228fd05ff97394bfbc5020f7dc78f93c160e", + "0xb3c591da42398e8afe1a71cf1097625728f99f3bb054403a493851613b0e707dbcf925f2173981a5d31e5bcd81548038", + "0x88ac5a4b0a7fe699f2d1dcebd5a2292d6cf6fcfd0c6d7434ca0d3225256f04fa5e947069040a2b3199d7c3ea4543f87b", + "0x913dfb969398ee060ca5c6cedff54d86f32c504379a1425bc40d90e42306245583afb878760973298fc3139599f01c74", + "0xb32fb7504c6759b089ebe219e0a3f1d5447ea0b172f8afbddd8e14b9cd2ff0daf2ee9c2741456907f7c9cf6e24a39be8", + "0xb0b8f5ab960e027e063434dcc32b83a92d8a4b1b7303460b068cfc73190a3c47993c08e47772600aa95db2bd5fce5c98", + "0xacd0c2a4fa9650d827ec423c5e9ea400a10c3f8c8ee896fa798651da501b59997ffb91953a6c5422fabb8ace8d461407", + "0xa94ab1ccc7e5b744058c69ffc92577945f8c18520df4f9067ffcea18ed0693c05156506e71fc5b3eb5c68c11f8493756", + "0x8175a958518919e4ba8fcad0bd3219975e7b3290ebf833507d03ac2bfebceae69525969f498dcecbbc6da55c46e9047d", + "0xaeae03ff7cf648c29b06c2740d511151c1fbfb44c3519a8aae38a7da35f7006695071e33fb0bb925a536163e1c2d48e3", + "0x8d7c16f585c9386ba44c1c98f7a46e919ad20e39aae911a44de46a467b668877d8ad82f19abb89495dbca34b3a7da9f0", + "0x8d184407e4c368a2d0c5e2066462b61c1970371d0618c879dd7af9c32d4d205a8de515af331ee78c9302751564a9a25c", + "0xa8f5d6dbebf7d82dcc0dbe0efc6fbc05e90932fd75dc1526898e7bb9f51c64a6474e42f9f38613f518dfd276a516e8bd", + "0xb430f16a2f3f5bc205bf5f647f6faa7c91fabc571074ce68a7a9b1b04e1a52d813c8db2dcacfab95c646dbfe44fd3014", + "0x91d1d3e0f74fe9f6dc914a604e4d904bffea70866b183a83b7e251c03e79126fbb414c56ec592fe3007254e7006de5bd", + "0x92bcb06498bbc486f9c140e7c3830adbe71d0a69191d521f83d3163323b383836c125dac6dad2a3efa18c7fb67781a3c", + "0x81f0b9d24a59f996cfa6c9b780195c770d378220513f3f0f9f53b18a056085911d3206096e08a85a2d82fb6875899b91", + "0x813ad2a970e807ada5beb1b40627357d461b7bb8317034da44d6d8c510aa882b0d252b73bb9cc92a2899c4641d335028", + "0xb4ff7fc789149f08e2d57379421c29cecad918d6d62e80adf5b102b99fb8518a344b6d6edc5123ea425c5b92af6d7319", + "0x950fec107e3c7bb38547b15d739fe59bff62f047af9a74e4d3b3e8d6d35147178d95381a0864b5355afcfcc04ddc7486", + "0xa43cfe0c5b4123010e5864e7ca5cee519535089b9ad53fa50fda10084f0e5ca57468d4c526e422cb4f46d8b9db33e7d6", + "0xb1d4db4988528e5dd63b5c7c9ed4401ff12a553c5693fa0ec8dd9cedb79b4af9ff44f9f053b141759107233eeeb318c8", + "0xb46578723d36118435dfae0ec1bb2a104b3e409da265eb3de777fbea96298d3b83bc1d2cd23b9bc0b6e2288d0b4f573f", + "0xa802018ec2ca2931b59bd710adbf9af6adb3da6539551e23a8d568dc239368c6c35e56cb99c408352f1b32a2c06753bc", + "0xae2cbb716b3940aa431b9b933537b8af7212cf727bb7ed6d339c37628fd4348d4c52c7d43d2d98f780862cd844c115b8", + "0xb30934c7c1857b50202554ae7f623d9f0a601bcca75716c28f2ac83f67dff70887b5273416f2ac5f136899eecc7059e5", + "0xb825c84b33b85aa647369e14271c74c43f49d58c3088141a101227cc92159abed1a5f98cb971036e49524968a012f1b3", + "0xb32833c015c3ec380c3884b3aa09ead736d13a8321586317e9bfa925f699daf2f39b535282326503a11d23c45fdc8150", + "0x878bc1ffbbb37901a3e54410fabbb8de3aa99c79fefada24f80effa438399454e5946e4bac07fd84e8c53ae1367a5aa1", + "0xaea247addec5476d0c068a26a3dbd320c4a7a34cccfc4e65ae44d2791d88cceec9af1701142985e57349414372f1b372", + "0x8758ef1f9d2740f4b8ddd4e0368df6f2354965d9aecf978c5f81365e0c42bde8ff5768aa7e92cdd9a7380603c4742e0f", + "0xae273c61b5814ded524ac6dfa1b622421095e2482ecb8b3d591dc3eb68f5ae25d3a1676deb51e68eaf226e29ae40c4ff", + "0xb723ed2902192246d64cce6a8f64d75bbd8d1215d39691008a07c33a26b73db8b7577fba80470771ad95db6eaa67bba5", + "0x81b35e6c937482757a300538c4f1a1412330b1ff1da2068a660bb2bb3aab0a40c560d6a398c4f88c72b5ba66b7bc6447", + "0xa0050838d2838b8a452a908f4979cf464b28c4be29b882ce686d99dbabb2f13f4d2482953d87c164613f6add5b1bc216", + "0x9918a2f8d0b55af47d4155848f60e5e35369f0279b48fe7c191b21ff23527118f4d3b5f98844da965ac752206b0b53e1", + "0x9452b654c2b2119e0c3ef973aaa6cde0be1df8961d2770e32cad2cba582a58c79151cd5d42ff9d6948516bbc149670b1", + "0x80a9e749215b2f624f8bf3a528eec0a5136b16406a42dd06dbdbc47e91a11538551bd40511d255a21adcab69414f4f43", + "0xb94ed61ded6ef898a7253ba351d92cb73e576b4091ac46f418aa329fd734f0feb152d21f5cf320a04f11b83b8117bedb", + "0x9437be25657bcc7b48b6dd3dcbac92a7fa5b188dbfb03a234d3c5b53167bac26793d77ed558a2c8f2e1ac743eb8a12e4", + "0xb3e5faeef2fc0bdac6940ea6fdc7d1b0731ec51a76b3f65fa5dab62ac63032cc65a4b0a1384079580748bb6e73c793ad", + "0xa71d4268ecd910f83e22fc02eacfb4152435783d3b6d03413b7ed25f513e21be7f80f4b0c610ecc5dc94a352f14a176d", + "0xaa12807422bc116138472eddc2de05cfe16a2d978c17262752f1af45d131b3996fd5a777e434279da12f41cf20c173ee", + "0x96a164441c42149c82efbb99601705ea118efceff8b6f7d2280e6162181f294530796dfe256362678519b6e09a33af3a", + "0x9189125ca5db6e3af53798877846a3087d728da5ee160d99d5d85d9e3d14cca69e7aef2f10bc27f70621f7b03a51ba62", + "0x83276b13c6391da5059183cf76b23c5ef7e357cf24e6c70fd3a73aecc60f5aadb667db85b0830ded7599e5a77fc1bdf9", + "0x99da0fd758a90fd4f7a3f1019beec34436d9112ca1a9f5d6f547fd36d3219239607f8577bc373619fbf37f803660cd70", + "0xa35d801cc2cbb1914f4dc6b503312f6975cf3b7fd819877ac20aa40a6fee4047aa35e943a0ed1ad213877099374ad173", + "0xafe6b9ef799603478e863bb46f7868aa16bc133f1e74eeb3721e038962df00e25b2bb05597ee87eaac9d72703bd349bc", + "0x8b3fb2672ff11c19b97f56eef25dff76583e1390ba9ddc0750ca65071d83519b0b157e59b0a1d6caaa4544e209fb09a4", + "0x8dfe6c45068e36b8b6c0e8a86c754a7a7d80c7fd78b5b10ff4bbf4724c66cec05844a23aac2df9d194436edfb5aa652b", + "0xa1f0e8281e5be9a3d585c6e356388caa8c6196524742f444d46b089fefc15ed298cb83dda7dd8e1f8ea41a9233c80f62", + "0x905d6ac4b91b946eee75cdebf77ec997705180d6515b0a3fab253c0eaaea4ebd7807af06ad8f6908cca9363ce2e0f8f7", + "0xae88d116a760b59f576106cbc066c8a0e35da57c9528904ed229313d7d4efd50a25bf6e9e1339f37de6980922b497dc6", + "0x82a803a3ef6cde5f7f3bae9f4bf3731302c5e9a29b3ba6e2af0aaa3b99648932c52ab87d849a4d0c2e187a8f6593de04", + "0x86370464e03ee82cc953f9b5b1164448781e717d856a00e759dad5dabb3c3b02ebb26c9a2f29760ffe6bae6825b81b2c", + "0x999b58509e49f7d32de77e48425a38be3bd87370d2dd72f0b7de25c903988cc0ce3cef3a7127c774295f062ca1fd81b3", + "0xa0cefde4cd1af1a2b673e9a8dd76341c9fb4a41b77b3ea5ca527df9dc793d79d14925bb5f199167a14b5f866b661e511", + "0xacb9c2d0ebdf97698d479e34dd991d769ed4395b621c8c17c96dec6292ec84b9b68a2bceb2e60583ffae4ce6a32b9a9e", + "0xb8a1fa238fe5981a217bfd6c47c85c40d6f0c239e0ca46312483accaa9ea3a7550dc7d7f0b7cff98c1de46161b57e92b", + "0x8883ca2996bf21d5c218230df887593c07349ab8c41ae20a9c04e3c49dee147ca8a62ecd2810d995139bdb1aa270cde2", + "0x8ec4db04c659b82424c91b9426c01a127e987add424abb846af1e672dc7cc4d196ecacb2ca75c87d831db89865858fb1", + "0x8899ea8b3bb90c1edb1f4a00afb1cc135a59d6c30e3ba7a904af3bdc6f54fbccc3bfd3796dd86666a279250d82e1d1d3", + "0xb0218ec99b1fbe695ac3ec15dddb58c8b080847b22ce8b0079168494b5556eb0a375baa00efcf52982ea08130d32babc", + "0xb443d1e1071f2161b2093f7f87b41e8081c3a6f594f39cf17eb6f49e5fc0d0394fd2789ef357a67c15525b740c7e9bcb", + "0xa5cbe600e4e9285a1d7913ae3aae7e0096762ca5e2649be96aafcb433e33aa391150883758d525de744966849bab3f05", + "0x8a952cab73160e3cb8598c30cb7782748000e99e4fac8402db304c826cdd94b494c5d9545c4a46bd4999c1e578ba039d", + "0x92f283d165209d7d5e4a10c6ece58ccc33d7021ec4df883068daddc1455d5055d7eee13b5a46b45175504b3f9017a51f", + "0xb99b80652f28e7b7a9c43c16cb90df04c4aa5714bde99aa924b88725a3fae857d670ff5a7af8bfcfb08fea7ebd9592e9", + "0x8c1a46c49029224ad8175d01cb99ab750a7c6e5dea5e1abce5ebd38ec7ae09a96ebd712046942745832d07c96d218527", + "0xaf129f4bf9ddae490e618d9a1b43c661467ddd26a93f0d4c8534e8642d25585d7857d3551855ecf67ac466017948d3d5", + "0xad1934cc808a5e0ddf24164c06bdc6f24d9552f85a7893a1983856b382def183cd9459a970373ac86cbdd137c625576a", + "0x90f541d12baed1034cc43ea231fcfc2a7882e8c4de0a3ba54030708f650d1ad3f2767f50a56c8551d9503e78423182ab", + "0xa8d50eda0cdb7b24b0a5ec45d4c7249cf871f55732928d16bcdb7183d12aa18b656345fb10360b75e07a1b76b556ee6f", + "0x9429a34d99665fb12a066d6b11941190e7212d86ff6c8a8300ff156153184fd76cb2904dccdcb2c2f2a161fd571522bf", + "0xb06986ec4adcd974b90e17c75bd196fc8ff66abb13f03f3aa82f851978a7a940e4881a2f610d97ef33d29aaef1add5f1", + "0x8d59560119c9f9056d700757c226fedf6ab9eb3cd14e901fb0e3e9fd01c6545579a2e5df24cd41825e8f18b3768d3bfa", + "0x92d695fa5b6fa56ea3935c0b4276def693520bdc0cc30c519598399d8a67a50dc1829700472e7cebf277614d203c498d", + "0xafb353f44ef188d29ebe4eb9716d35a4d7ec4ebe045388d4572ec346f0b92c81fe00a03093b2e1a49f4f39783595e837", + "0x94f2560303a37b4a430ed5a954f24c4e1c59270f4ec0f2e4bd83dd8f8e99a8097ae87b2474fc22ac9552574bac4fc35b", + "0x8cadded8bad108e8093677c2f15541a025fb938037d40061795ed270a7a40aee69573a76490fd4311b2dada8567877fb", + "0x8593364bcab323f4487d800d3b5cbb37d0d47d95c1550adfd0b30d60da6249330f0810a5e5dc3b24eee803a8cd3ec0c9", + "0x99ac1d3f0876a85b528ce435142aedea4ab0ed84eb6eaf06aac9408a3a3156afe9ba79472d996b53716476411deaaf78", + "0x8fcabde394e1ce6c1da53a6f287a542af6f9f715547c5955eacc02e0010fd0574902c69365e1f7c8e3948f79a416bffb", + "0x95f803fa343cc8139859d70d9eddecdc51822be73824806a012e1c846bd9b87590e4aefde525a917915d6bde31679a7a", + "0xa833fb118d3cbd6095764cf6d5c97b6362ddc6da1885d758aca3bcd6dbea441e191cf2dbf8f0ff90bce1c9d174dc09d5", + "0xb3989a2fba4a7b6158c870bda38394236cdb9ba65642d14c262ff29975afdea0e94e0afee7a63f2954ca724397d795ba", + "0x9604fb5207b7a0aa1723abcbf3710a39404749c0e863ccfdf94689c8c7d7d8e6ccd966d731fbd50616a912910db762aa", + "0x859f02df70c7c0a2a1c05c10a261cf3801c3d3ad98a838936315c6e147a3a1863b04f243a80ba55f5287fe859e9b40f5", + "0xaeb9e76cd60a16ca17e82cf3b5fe8550dc83595dc43aceb97eac16f5da6c62937f21c9045fc6e7d99f001469229fd06b", + "0xa4593d8edf792a385853cfc3b85adecabad912c03460cf825466191e2678121696bd1f60e0830b7179a3c3736017565f", + "0xb0fa2ebbe24ca9e524acdfa789af62f264eea4fe66252b98431156d4b4e956c877f9d764a015a666d461231f0211642a", + "0xa1133fc5dc6540a89360b9a716e7922a1a336cd2b85138bac1a42d03273bd03a6457d966c672e485cc88bfdbfa3ea0aa", + "0x82acf4c25abe58aee08cbb1a8bae87bb0798a95f7b0794862b9b0067bc712347b64473d7f8be32eaf656e0c332d64f9d", + "0xa6704cc75941daad2f054b9005e4ca915d36c7b9ae63e1e753f598f0552ad3f712a8a9dd01ee8bbb54c555db162cffcd", + "0xa9617394c3c5a6b85b739c260926afa3fd77ba99f175e3cf23939336596d761ac4016a5a9606e553d8af1ece04f8dd45", + "0xac581b14a05fb20101e68df0fef20a1d130cce1200dce3e2764bb5eedee930884bd59e3bf3887fd2fda8cfcf18f2fa29", + "0x8a82a8fba30adbd10c96f7a511842fa038e30e4a441ca0447321886f5ec119d0cdb3654c643b173a9dfcdf3ebd9a22ef", + "0xb3b083b9d572e7a7d5707ccd13af45903b44e8724c0c54f04e30240b9662a9fe3cbb2393cdbe35a5251bcc0df8cadd54", + "0xa57191c529e49acfc037b5114e9fbad8952bd967b39818a76f0035f266b2cdad8e689037957c461489b17bf31e6fdc49", + "0x93dc245c88b0184df4387d0ab160631f91ce30abe7ef85ca12bf4a922321e910b7dd0b9deaf9684fe6f31b4a9fa78029", + "0xb3831692c23e6c3f2bca31eb145478a6e3fc1971f878e2813202ef1024a12ed57debe7de1ae49eb10928b64eec651fbd", + "0x83531dd694d4bba2ebc0abb0296230e82cd7ad52bb022ba55696439e60e5e4902e96e0dfa43fa79acb4b3366bf4a2f97", + "0xa80a986744a2358e092a3cb29f027628b1bb951d87fa8582edca8dbbd258e158d9fe5c9f12f0e844abce9818959583e6", + "0xa721ad2a5f8cae9ea16f3f63bf583cfb50ec8fd70bf6c032570149878b96142699b7eaccecf31b2b4bf63947a37ae4f7", + "0x8a6b5332daa58cd2f75a4c5faaf55fbe2dedf8838aa0d0588bb43806ecbbf12f62b4f41c0ffe0490575711d52a04a085", + "0x850b296904b4aa694c15e8d7c1a25c5351940f0396ab447664a25363c56b657e4cd9d155c1aa3b887d318bcaabfa1159", + "0xa4096a5049c4bf32c85b6215d09ffcfa6781477749560724572e2fdd297c76b53e2b1cf75db271f2a53b73a8bcc9ee69", + "0xb25ede64bed004455341ce39d251da42ea8cc37a8382014dab7f8304926d242efd83ede613651b837b695142c49d98a0", + "0x943d3df24177200f7cc4b2b92bc769c9efd35953c220d44318c19003e3cc7d57756fac7d2ddc68c5e8d88442264ba0e7", + "0x870d57f3d5b2761a17540132019c0bdf1e158c412c9bdb4bac54f10291d8d55dfb355c16c1a0b710366c588f6e6e3b13", + "0x9185ee85ffd236d96fb32f2e669f4c42c4ff7061c184c9071a7f9d75bae7b7172a706aea31c70890add9f33ab6656e92", + "0xaf4d0edc395989d5f1df9546607bb8915b40260c36a1b3798190592ee9a1d42a509f19697786eda8edcb12217acd2418", + "0x8f2c88d459e45a80bc7c02ed93b6d5e6e533071ffca1dc2f6b3c2274a5b15bf7db699597aa573442057390b1130a19c8", + "0x905c2370398cd4300da3495e0380024af9951bab353e421267b70c76234939bfeabe12bf779f7a80eb119c8964a864be", + "0x86728a5729d51fb13e0f990bc22c333bf47daf3f7add709b41a58b931dbdc53a0ca018744ebfabd090974eab78442ec6", + "0xb75a67ec49598726d9aacd2843a5b8fe84a4ce7615b631a847df7c9657d84acba7d72806af655577890e540aaa46cb69", + "0xb1e2022ee657e97afd60fd4c8f511cab4834196bd1898e904c4ea8608b8fb898eeda85ad896e2eb58467fbb2951ad10a", + "0x945b9fc8531bc9daab8f5870fc172c052f6aed8f1b959179efd806c3ceed3f184d9847262853bbb761c0b62f9771fb8c", + "0xb18bc45aa52df35b71be78e7dd07021fc34e0a1f55de7e00dd63de834a98df2cc69c7ecc375022c89b3ee4d97950fc60", + "0x83a9d790c226dc809bb1603c9b07de5ef08e99afab591a6faedcdc070131368227ad3c75e5be436646b4baf840ec1310", + "0xb1820d575df685531ec984ce3ef09d3b267ab281481dfee7ed58fcd30e7ff989d34430a42f0af6b66873e31b73a82741", + "0x83caacc4db39b753812c7c6298abc3042355a0e8d94e7d996de6ec61c4e72996881ea40d5717d81c4d041173371c4034", + "0xb48dc637c4c57ec537255a17dbdce2df7061a5350005639e340a346df7adc83d1ddc2f90d61b38b509be5c5bf148bfb8", + "0x97eb21bcb5d3b1713b45d7c01ff55f1f7e8db84f80060756366ba416f89ee4097d483e1ca42bae40110a53b75dc3ad62", + "0xaa961e3ea614c86145e106b8c738e2b95857ab6629cb7f9f82d07f859536f077ebc42fbf3148999a2dd045045f78fa6b", + "0x962406c0227f0fe638cb4f74389376f4b41234347d56f693e083503840fe46685a772fa8e2baafaeed36c514139519dd", + "0x8b5548e7b09cbc770b12f9e6c2a2bdbcdba8ae5c8da7724adfad4c5dab90a150e6cb087dcfd3ac2567379c2431bdfd64", + "0x88bfb09ef5d4fadc833e3d7db6153e24c92ff13c62fe10f5e1b9e50eded73113301aa7cbe2d718f19cd4a6fcb50734b7", + "0xb915994f439342f3d720edbbf73f6532a104e03ce7292c44fc387c9a791399a05b8d22c820c7fe5498ad5351f457c4a2", + "0xaf3bba71f4673ae851dbd83af27fd6bc91f888fb5a904dd5b096fe22be2299d962944549c30eded009c929e6445780ad", + "0xb233a53db0b5df62a1c38ee55c9f8752d4d47ab910de28a887d2cd7e63674c492aa4834bf927fce36444e880d4cbfa0f", + "0x8303e602af7edb77bb41fa971ab5db73c02a13b83c13845dc2bcd2db50cda54cc4b7ba1896337eb8514590821004e934", + "0x8a060d1592dee3a984c7c88e0314efb39d5d0141d489b326bc1116c366b6823af593bae4f9e7d444610f3ebd8a5c109b", + "0xa0f6625abc51ddfaed443bb9a273dca87063a58fd2fc32111a2e94b4c41e56014f34e6e4084f031fdc3dfdf114d8b28e", + "0xb76e5dace5090fb0d6bc0226852ed6a5001eacde665a668e2a7f21882ddab015e05d84e31eac64bb3e9b857da0794dae", + "0xa6214a0d7d5d6ab5786f533fdfbcb983efc88459fc92e691cb19753da332b973b3e281fdbae8fc6c1bc66556071679e8", + "0x860b5b32c5829fe431088d1735352589f329c730d2fd6b6d2096824d539c943513b2330470048dfaf2b86ecaf8947343", + "0xb4a623732f38397a9eb345d47b27398a36b146f3929d11d44fca0d4d2640a2172bd35121c1027b2ad0aa8d1b70398036", + "0x801cdec1fbe467ccfaba4dcff83fd12f1a5ca890b133dedb156579b9c82be9f43f3ed9314a3aacbaa8cd5957d5302074", + "0x96de1507963a480b8160c4ccf124461e4fa6d8f1d276ead8ee91aa712e3ad20f2f32ee52e67b107998e224c16a0cd9db", + "0xa021bdac5177e5636d840cafe4ee23d88297dd1f91754cf8bca1b870bde44969813c2ee6400020b38dd87f296ee84777", + "0xacacb7b0ff8c5c8f98fb001b223b48155e1a214b86a5765a5d5ab17d5e5a48942bfc4cfd773355d2972c193fc3dcd01e", + "0x870a304736c7ef331c039173d2e134cc3b4fcb099dbf4144f9b5dd13d0ce26ce9c357dde1880e52529f9a215534cf795", + "0x866a779758d86bf9fa269d02d3e6b896c5ed774301ca8369d69958636936a937adb8657b1ccdf81c00de806fab631663", + "0x817e2225c395ea76866c718b4b223c9ac90310235d147b7cc848aaf9a90a4c01ebacd4cbfa53aa32240f29d71d62a6c8", + "0xa2c6f07bfe6f4e93a165d416a8ca90b2bc1fb4c58a884ea3bdd9e5c3b25d48c721a45b08f0398eefceda90076c65909f", + "0x94be69221cc9bcce3096abfc1ebc746164fcf4f731d689caa062ccaf1c0cdbdae3db0178b718145ff10f2920f73e4fd8", + "0x826280b56b0c7e677b1190a776dc3f4de7fa41e3931185fd710223c060ae0b02d75f17728c74eb54965f60bba6f63970", + "0x8c24f215905d2dc307950bff797facf5d82de96a468a33b9dd9ac6ea4a1d7796eb2867a70062737a0ddd2d6cad07f52a", + "0xab36e626f588ccb20dd7783b4ba0258d232303bcce1394f1d3a9a59c9aaa58bb0230953d47688606fbce4c0b87fa0567", + "0x8172911143141067274b4f7340b6d203d1a7afe2214384a50a5ae42ea7bb6eba6e185371ed7de1f2cabff8b18f906019", + "0xa244677545278f9b3912161557efdb1ffe442f000d5bfad9fe25eda21b82dd4864ff6903bbb0f70a1ca3ff1e9ca56e97", + "0x81d86855c8777b7ff6ddde8c133a06045aaa7d79a05e91ac4c997c306758717807301269e29a2435673bb6798551d6f7", + "0xa7d668c6bd4e40a0fc1c02f2913517b9492c37d62e16ab0379b480b5f0fb0ec8cd5b3f51fff12cec451a914a006149c8", + "0x8738af98d52b09473e3ea55b2dc24089804619a90af05e1d76aae4e5a094e5bb027ab85aec572cf1456fb38a81fd69b9", + "0x8b6342ea8fa37e4852ced8a840d6989f054ed194a40f445bac639f83c1d9a7ccb9a384203e74f7c4486e67c0e7daee39", + "0xaf54265971dcaa4c1139dc337fac81d419de97ac234cc098a80d4d4551483998a4dbbcb599437ddbf0602bca6427239e", + "0x8299ea2df00a7e0071491f30c2926fa9d7d67e64b4b0b279ace915b76b24c58d7bbaa2f5bd62c26057721fffedfa89a1", + "0x8335afe1e63e5fb2625f1e5579c78e7d93effa55e8d4ac097a31c858658aaeb040af5d0f678ee24592202781979a7fc8", + "0xabe8a656330b00652fb86740136836acedf40f93a625872d3a61327065279f9e3e5303b456f1ee71dd1ec0c1f59a42ee", + "0x99efd89a8fc1a8d7d4892131459701080d0a17b3f8cbdc8e5c5c66bcc95ed47480ea08bfeda77075500f78a0e1ea2772", + "0xa2141ecfd6989f083e71837a982030b86c461f1f3fc8c43d34eb5a7218409638155f0d73dc3c827a2249da62e36ae155", + "0x8b0f28861ae9614d0dda1abe5b8698ad380c6c68ccf5678816d23df3919768f7aa07a628b155dc6cdf75b2432cba781f", + "0xb0b8c191ccf01c55dc356881c21fd184f468497fd9cda2536f1bd00f27f3a9a41a5cb47b18efb851a0715ab4bb110eeb", + "0xa8cf220f9c7fea4de646144ded78568fa5fbb79ace36c8900581db3ead314bee50dacdf739e61955abc57198a373411a", + "0xa6879bd127fe595603571a4ce8b50a782f4b723111fbde209f33915e27fec8931e77efad1e9b05d6663f1f429ec9c0d6", + "0xa93887c38f0c543c530eeac5f35a462424f41eea5d5eeb5bc4ee44a20db5e7d91fca84f9ce4ebe92b766e26af9b728b7", + "0xb34366acd93475abed2c4430dd8c88a7df3757667c31a768efa001446170d9891d5a7deb12efb0a3bd843c0edc805682", + "0x911051d0b2c11499ca610a4a9e250202486cb5edc64a2d937fc9585cf0d9ff90b7f7dbf7a408722cc283513a465b8b60", + "0x952023029bc75f63da973c9990eb21beeef6c0c1f8a0458b2b5fae4754b26d51ca970cd15bca12ee4cc64aff6c1f5b51", + "0xaa2fcca3833c4ca2632d1d701fa9c8b7d64d728d06205eaa3f81a464752361d6a6ab95e2fad2767615791810728cec3b", + "0xa973e47ee550dfe377190a29420be0f752e75bc494a96e88d68b4458d4bfc3ef07f13b6eb19718f969df6602d3d331c3", + "0xa94ae7208ae8521bf5254ca61101365f815f2a99443744d3d7256b9d010ee50eed51f412eb9d42ea1b36fb81e78506b8", + "0xaddb42e70d0f517e12e966e70448709bf8f9683aeef7852f757d356cfb63fff937272bda388744d05814bfbcd64c17e9", + "0xac2299fb62692bfbacffd958478d929d47217f1fe161a51094e4f728f6c64076291a5177550d3c538f9ec7d245134baf", + "0xa2518eff9aec43daa79a09eeb231d16eb4f94683493f583ca246b64755cfad3738c621164501f78e129dbf093d107199", + "0xb5e920558c5758343f4c4ab5540722a3f8851f135fb774e2cc4df98267cf3dc851a240f2c77ab3b402696b8efc511b30", + "0x8fdeaa4b83a3922dd031229f8d9d485f62b71bb6e4d86100f819d5ca8144b38fb337b22dc496a53b525862792b5f4111", + "0xa9e65be1acb1edd1aedef68ba61b4520dcd84c955fae9d1811f6822324360e8e5caa7143c8374be8ba1af9e7158725ea", + "0xa44b098ef13ddd2b800ba6a2f46dfa0d9ca228398edc45945c8f8878c5d6509f8331387d73015544894f01a098d74536", + "0xa26deaa7cac03fbd3b0048108203ec30277f094af8df56bcffe08de2b42654157ecc4bb2bcb6fb28262fbdb6e181065b", + "0xb9f010fbd275385422ee76f6b16371de37e3bb5d373e4037e78af522cd4e2bfc443b36186e6eb1bda9eb979c9c9269d9", + "0xa8ded08485aa7ddfe2534251e5375739f6d1f1e4b0db1c9cf3dabd2a2d699f530067ebed74145f6b6343e679b9a25298", + "0xa07204f1087381ab46bd0c8e63ff30b3aae42a617720642054fbc681d1e4f1a80ff8485a29a90c3f24d6411b242c457e", + "0xaaab560bb33c31b7d2a67e27b1bcf7843b595d46f125c13cf81dcfea6a1187f62d8b4b44885097d4d1baf4a3b560855b", + "0xb9057687bc68f50386851219b71ae191bfedd1be665e55a19f5c56cdef3aaee4fa33000d398437504065c2cc1bd1ebe2", + "0x8791334a22e2f5330a752637f4c947e724ecff7e8a2a0c29214f35a2e923bb0e3f70daffaa3e9d6d26e703c350cc7ad9", + "0xa6039e514be836088c2fc6ef1dfbaa91356dd0a45e299a72386f2e16fa9fa7f97791b599e8f9b41518b1d815f59cfeab", + "0xaf95bfc17c6d304435a487579ebd408eac666c4f352ef51c2fdb451be34b4904253264705d9f839c2ef15e4e31fce71f", + "0xacee4834f6e7c57b0fb0c273aff4aabf4c3ccf3d8bc51cc7fb71d22cb6f18da030b1a7ecdafb71bee3b23b2812382499", + "0x91bf81c53f87331f6179ca9afd6a8182b2c82f97c0c5bdcca650c4587b1a2981fb3c4ee185c385e721ed0a6e4bcc0080", + "0x815c729f263b356ddc0e2b22a453a6968857e73e11a4055361d3b7f385ef05c63b4db06d281635478ff980ad0ef66a7d", + "0x8dba0295230dbfee9e1dbaf2b50f3ed6115a2f9e5d148fa10bab8805c6f7c74a7f96254db7542cd47cb28118aae16480", + "0xb177162bec6b8f6938782e3756979eca4e432602aab4d156a90c1b5db5c1b3ecea61305f9fabfd5e5b61bad3a164f761", + "0xa145ee3ed30a099aa145bd35112f55363b0cf8f4a306ef9d6ed70b3c820c06dbc01e11a8b08c300e6169b50b47f3df2f", + "0xb7c22ed8749e7bc8a6aac64282ef4b29e625f1984a57c6cfacaac478e771ad4cd74b0bd3c4c8522dee12ee253ac063ba", + "0x80be7ebcdc8c8f0f22b2e11abc0e5796a9e3e877b51cb01e1c746889edfbc1c9cd3737fa0e266edc04a443dff7a41a55", + "0xaa8559dce60079e75af25270f724ce00985b7acba5f91a586464e5a2d0e3e7002a6d5e536c297e20da42e92bc1befead", + "0xac342c75f28308bdb213f20d6a840ff4e47cb4e50b64082a00069bf2a835a954e42597ce116fb2ab6d4930417f4bed22", + "0xacc7f38da3f036f01a355ace327982ebca363be5277a82edc5f76c697b80ad48b93423126769154b0f1392ed9574a067", + "0xb2a433979fe484ecb19d9d1fe455d6bd1ea0f146f9e745c6fcaaa5d485d2b4a83e8732fa3e9b77fd36b697cea9876c7b", + "0x8804d9eb381c3dd9b612d596d7e688b20e95163f21e84ae0dea92a2a60b8387244c430a2af911ebe6d75d97c63038034", + "0xb23aff809de7cd790e9a9e74e304777958957a2058909efd78685cc08cb4ae10488c31905053a9e4b75ef9f660c1024a", + "0x94afca48bd96ce6d765ed58383276b78acbc49cbf11738625164f8fcdf78f5dde5461ef56441438bc312372f4725eadd", + "0x822db53184928ba960283dbe7a05fcc3918281ec00b8c5e7932fbe8f2ecdf289eff71b52602c23e952ef12e6e78b531b", + "0x874a4c2c305b9db5aa9ea3fd9d63d9cc92e3f0d86fdc00387af56e4ed5cf2acfb8daf022e871740d3014fb432a24d92b", + "0xb979d6ad2cbc3a68739e59f044adc0923186dd6e419f5a4d574a1445a2e0ead661599e404896183ccdab8d2d72cc0fc4", + "0xb1c79f82e1d33a3a4a6e6d6b2eaab94984735433720ccc1bcedb70f88fb5d393cdee2a37cc109a5de662aaca4c68b576", + "0x884bdd8ddb09f3fc37f3ca507998eaa8dcc7634f79fb3b22d8c8d1e838c2ea7fe68e81fb4d092481b9d195d8c308d8aa", + "0xaa589309d11d4561edc067e368c782e2efd29aa19758ca372e7e7dc2260961688d2db8f845976b21fdefae023d9def0e", + "0x92c9dc8e1250fbf8b3b8a4c13546edab5783d09361f3675186cf44b5620e86bd65c1a0e1cfafe205ac0d2e79b5314a46", + "0x98d5f52298e1174029235773aa7068c10311c1ccf79b6e243f9381b9fb5bdf33c410dffd9c3de552a29de574e1a2fb7d", + "0xa9bfe938859a7d56311666d29da0c4d0486258a0bc3598a91b5466947225ca6e4beb843f6034ef15736ddd1478d74e59", + "0x832091309b964f832c87df3a130700f4274a19b9b34ef187e53000d9148050e4735e2c64a135fdc0450607ec7709ec2c", + "0x8d41481a71a1c19b70ee11925f78a348609fc854b83ed5d98f1ae419c3638b968ede64fd479a866b887b4c69b6200573", + "0xaf5de205d9801344690f828ae38d36b1d37200151dac23ab2b99e55b6674d71697f23cc481fe7b4677414d496ab08110", + "0x879b816c3d456952f4fe53ab9de1314bffd6276cc49d1742fdc4acfeac69024a75971b83f5fac1d5a785355e7244a02b", + "0x8b87d6983777566dcda6ecf32836ccbc50d7fa296a1711763ea4cbd9c148c78d7baa2b10f89103b8787bc01e9b2f48b4", + "0x8e2107301dd66f89ec7e2cdab39ca5a0896ca6998e8e506f76a9257845c92008ca099f2891831f9eee4a03d49d241335", + "0xabbfc7f2b8004a4ec44eafce707d3d6070c89dcf3bbb4a4c4b44007af19e0037ae9e021b87b48cdd95148bcb0e053826", + "0xb72390001cc3fc6280ade9b331c14fe90a952f94c4266ab042c65b3442689ed22ad2f7fdeb11d0800af2f6fdff24ea9a", + "0x86d476fbe2f5fb7c4c5cb9c9ff5aa6388f1e416d7815025ec759fbbfc5fa89b89e8fd48ee09316b878471ed52504cd3d", + "0x8b415ec2281ee5432545746e41f9c2429cd35b1bccb580aeceb1839de5f90474370ccabd2edadf04f89f86fc259d13bc", + "0xb60c5c0ffbd796db2fd160dffccef06f624f1760e22adf657587bfdc41589df07a4d5c9984705dd15eec6597266fad9a", + "0xa00fe14cba51be4d192715a11e34a207ce1244e899a32e4d3e9247f936bb73e9f4d32e487fb4fc2bdebc2bb5c83a654a", + "0xb2b7b774701187244835cb1f1dac7b2e2abdd99e858eab378fadc7ba960ba512bf9026d33bfd9f6fec1b25a5e5865a98", + "0xb025f10e12ff731c78ee6805c226bf2e6cd24b28becc4767ccbcc3fe28d423d131be176b127536e9abab1edeea23f155", + "0x8d79d35bf45db42134ec57fe89429afc4e9fe8216f428a135ef14786f89f9149bdefddc9decd58653e1ad4f8ce40034e", + "0x8b927e3d50524cafcb51c89f8de506072b310dba8a5008a2158b7bdb5eb0bd59539b98bd74f42a4274502f0c0396c083", + "0xa4f965effccb3550a8d272f678314b96f6c98c0c9d4a5a805c3b8eee3509c36542825d7ef67d93c3aba5846ea6579847", + "0xb161b62466ec88fcf5b744de4621ffd705d3221fc502fd4b824ab0082b7dac89d01b0fb7af3cf7d5769f519edf3e6800", + "0xb1f900e99c344b96d7cb9aa6d08c0de4c3c86f864a7471c3ae7a36af1e338c64cc16a861d98bc1842c1f6710c6cb2e60", + "0x9704ae91268f674694e2a819818cff1b4f20e6f6414388595188782f24de2649d37f9e3f3333e7e7550f014fdb8205c3", + "0x97873ce99656302a8a4fc147d26bfcbb396eb9507e0b25284eb5d8270ef359e9fa30a25ce211e14f99034dd00993eba8", + "0xa4e3c05d54d5a5a6811e55e2935257cb9fd123c45f0228928b97888b93491a13acabcb2bb0a8253044ed766a77ee6a6b", + "0x97cd9c747d2467e6a28623b51e1f36cc35c3c6a1a2fb3cdafc5882faf1e6ec590360f45e73064d21e40e743085a13327", + "0x95f9f32547a309405564956888c7689ed5db1cfc7dcdc2e41cc4327c9e1bc0f14623a2479f75b532ea37314b0204a6bc", + "0xb92218d9f89effc784eb84c80588053ff3c41d7822deaff51f247b29c0a34426cf45152ce176a236eb0cecbdc3cf24b8", + "0x89a28fcbdfa89fc653e70351813cf456f84f0d91eaf5a60d0c8928a25d4463a319db32a5f950fe8d2deb9c5add071f63", + "0x824f1018f9ca053571b57f2428a389bcee85fafe05d339c33ccfe0cd79fdba00d2a870989616ad0f94d770aa048540fa", + "0xb103fdc7d996b7e8ad178bc0070f817c7b7f99982cd768bcd46cc4081bcbd7bca06694430118f8ead3421558cbc06a06", + "0x83e45bdc7f5b0041640da702a4f7e7447bbb5f4744a5eb88d96e829fca6b4620033e3036997c6006326fc0269e5a75d3", + "0xb9f4870695752a4e5c127e3e399b8ae198e7aa61b4e21efc16a399f1be0872737d8db215ed534445ce929e2dfd38a33a", + "0x8923381feca6aae583000d8bf2ede1fcfde644bff7359de4245dec6cc184dd38fa34fa6bfe1174a257d03b32c8e29a88", + "0xa64ad9f07b56aef408730d8c914b72cde13e5f6cba03c4485c04a4ea2da0f62462f2dde0ac9cd71e615f3222a35c72fb", + "0x89160473c9182dd701ab6483c4f8605d12706807b531ddc61c0c6a0861014cefb8eb50bfa767c55f313008bf5f0ee42a", + "0xb53fc1c7f01c272bb6b6116d5a7b813bada06610f151d071f1725c179706ceab481cbe4e3cacea7b5253d97410c4a462", + "0xa48871f50514f4fd9cecc6726f1db6a972d5468f4d9acde56f90455cbe9acecbe25525d3faed61deb3084f633abbc0cd", + "0x9261bece74beef7ba910154e9d03f575cd057cc0586739258ef9049e7db3882f3661899a714f96d0676ae770fa118037", + "0xa2cb6327fbca47e5d5bb92a12c95e4fce995057e2cc9c7c91a76e4c1be1f6ba2c146ac00f3c014b2e9b52e85280963b4", + "0x8109f9d98ba02ea93e33cc429e143b542215ec2cad9fe8f9c57925fa2e010763ba32f8db68f8c4560aa11da44255ec07", + "0xab2a27b40936f82cd3e116ebbe98a725a0e1cf33767f6fb56dbba43f377995666ba13fffeb76d1e72c5e2e171ce1a80a", + "0x93f4aab16003cdce98f5ad66f171cc36e6c2563808e6059557571190677921de1f1767d6fdd8536ebf08695cd51bc3d9", + "0x8b5a82ce007a9d115d6f7e9c3d0a905f56a3fb827d6fc738098ff415fc24ca58dfc7a493d8811049077598ed8bd48e71", + "0x937cf47150576f3ea028c5a56bb01060398b206e043c4232cafdbf94a1593b7036f101b0443a5d2818bf4f8b4216b18b", + "0x8e886a8dae63348f2cdae77aa9fc28c656c720bf4c0f4535580249d7d7f44f9771705db2caa3b85fd9b9a99ec9ddbf6e", + "0x984b0b7ae633bf8d7598d29d6ac623a552124445b352ce4bdbd483570c8575fd8cbad2855c6fd757870d03924e5f9904", + "0x870816dbb4fac9b56ed2c2dbcb48a5a940dd9a5327df98f9745be5bdd12b6c37e2360a0eac4d65f329942aff46bdb08a", + "0x940dfbafee299fd73200b54e7e28ec2e590f7165d34943feb17d543d1243e00c1602281a0030aab434949122532ae445", + "0x914eb073333297a4abf341481d55539de46a7861f404bc90c8ee8a927ade639b91e809c4baa294a489ed927d60d0590b", + "0x8a7f4e1826ba41c9d135a59ddc07cf394ddc141e86689fb823fdc17d8cd45d6052f0fe689cd14cbabf68cd82aadc5eb2", + "0xa9072800744f98d48070d4fe4894e5a0e45a8854ca6c384465b23521042690c35275b0cf331f61fb4fd0862cf7e7cd9c", + "0x8aa8a4facd259383a3f9a11171615418cbb23b9b0540abd9cf37ba3a5763c2a49423496369aa3c7a75b78666ce72226e", + "0xa0d758d55a8befcf86c8425435a5f07170eb39db97f01c8464842052ae19609e9bc38397683305ad85faf206859f07b1", + "0x8721c6c9707a2c634fa21b2e7b6695e7fec57ffcf297914c3aa8e9a3ac9b3d98d182dea313b5630c1ba50ee960473b1f", + "0xb3a461af236595eb9d8a2356de1f3f29e36b37a3d268f993377e782f0bffa35a4be0b136062bf6981c9d0a2cc9be8dc2", + "0x96739058a5a1af61f896d6a76df9065cce73a99051c902f07b87d9b3a2041b866d6eb2cd26e3a64a7665e63afc179d72", + "0xb8d6a229145d0a4857cbe6308b2fc2cb6d50a23412180fbec124f83b2f6cb048a6664a84edc7ac2006961c60d28e79dc", + "0xb6b00aa92e3599c2d82380080fc8933ef3b6a951c56a55ab59f72b3846041616b5feed82185810c1afbf0a8a464459cb", + "0x82ed7010181e814ffa4d9c2d637eebb11e31db188e11e19c3d6cf01eab4f22c9527bfef5f8243262fa6032adb963f90c", + "0x86c28822d6d160ed2a5c2595eb1e23f745ce8ade67cf528b26d9063ebb858f38cb354d73cc4348c421449550536c21ee", + "0xaa0c6468ce55d248f66946b33ae5f1f52f3d6cd0397f2ef269f0819c6203e7d8866201f108571720a4e99e19125d2702", + "0xae15db22642e412f897fe5e28cd7c6f558eee993751b31d1e619703aadb1f5b618386004749da77412bcad8275d129b9", + "0x934166d4d544bcfbc1660c6019a56354d34ec573e425f3ed2c1d8f00495f3c8b4c117c9c46cbda7fd1bcb8d5236efc1a", + "0xa4573823899338101448d1b276a1fe698ada0105323b0c0a05e9cd4c2726247f759f4987b1a86171beb39be73f6504d8", + "0x82f5e4af83e916c92dd8c5aa46e9e069b412d21b6e0398fea488c8505c53dabd975464172d9e25973f4791679f2e2851", + "0x8e7545e7f0b3a799703e8f21f9f740821794f99162048cecc18a26734f224c8f04dbed52e58505961b6ed3d0cbc898cd", + "0xb61f33de4e6465ba3d065d402e93ea9b1de358d12ec14875f77ae262a02d7525fd31314b3784281944988841b2d02c30", + "0x8d8b19b52c7fbd9e240a05378c8ed8dcd99a3473757391467cc266098c860b662985fecc6ff996ef36c2fb65f49eaee7", + "0x8778b623417d1c302e766f0730a75cf8d1e17288cf704e90479db1548d326404a3cd8985313075c3a8a775ae49499e70", + "0x94b955186d96b3293e81c25a1b26a6506a9d93390966fb9ad0bac81847bda7de7de1d5b339083726a9be8ed0ef2b5619", + "0x8816e077a0c20b68307dcab753b055432ec4d6b761461364fa844e25415610ddf90a9ab9e0bd063df61557919d636e82", + "0x91d19dda3e23d75efd57af8837c2df072fd647134f74fdaf08d31f761fb708e39580fe65d208305bea61a95c0ff0090d", + "0x8993790773a7956e2092a3426fdfc637ba9fc691edbbf32dcb94b9545484681fce68d2b60ca59b7e8c14ff162f94539d", + "0x8f973e8124857db7691c79bc415e8f6c60624b2fc18fdc1a02e724deaa5c0d3288a569c9350b35dbd4481d1f3fbfd064", + "0x96dc89e5a4196815046b617678a393444c40da1329f27cb29b0b04eb6e4f7fbe32db530b2abdbc3be29b0189782f0235", + "0xb3e1ee127b53bfa81a97a6ff24ab2ab80c08e7088a53ec12f0d629016646f508133a18554de92da5b558a60aac26ab9b", + "0x8a8d9cfd6bcc3ced46e827ad960cd6ca8b2d188a6ca695b3d8765b07c38e029d3b861515e8b83574ff1728b922359944", + "0xb661a0d4ef83aa69e6cd5436e9eb5bd521006ddad6c4f5d01c9a59f5c239d7f7bca9997e84eff9a8b206dc33af0701c5", + "0x82e4092a2db1c7902ab661d8b9999c9df11029deb188bd0f05bfba3fb76887052b3f3e7e46f79ca206f5bd82f841af0d", + "0xb5cd4588d13d240aeaa4d74e5f86b41aa0c036fe3ef6a7dd7461109673e1d26f6922ddff0deb366e67f9f3a6ab8c3743", + "0xaa947486797603a84adaa1accb7dd804a5924331e7799adba28905a2f9b1abdb5029738e9a8313591a4451f5acc9e233", + "0x8f50126c75f944033b0b50f7a2d4d6292b38be845e06bbdc74555c71b6717b7f05639c9499b1496c6b4b32f34ec19c2b", + "0xb3b6bcaf509d43a52b0bf00ba752c248339d23d5b9f01e11655b35bf32357f518a24e030d2d2a4a43701662f7879f7a0", + "0xa31b6c83a4542ee6ab4d40f0b39e7b3aed220ef80f631e2a897cd152bfc54c19d58c7aebb65557c231eb7666e0c4c7f0", + "0x94fb4f68a702acf93e4f5edb046d55738f6da1f4ae4fc8ee32e0689443af80a1a3b3a37c748081ca390e5868294372c1", + "0xb0047613f2f9f97cf8d2a99c6e2a209f539e3f492d432e74cd6b8af6ab5eeee5febd13bdef12c2c0fa3ee3784486a70b", + "0xb90c85d137e576ea5447d1839d1479fa8092b54b1eb5a84aaca83298e04c30edd4b04c9260f2280b2d5c16b2a70ca711", + "0x8a3a2ecf98bbb6accc7e4ba10910d4a21ba224c8b871f235e5493efcb90e0c9eda9d8aa32a00136870516369eb0c483d", + "0xa168654480febd2674154c7c18e0581e78bedaacfd68506e18ccae3cc63cc08bb9aa521303247174ddbef239e0819237", + "0xab4194b2077120098b25582bf454b1b99aec653963d3b8805053ff8337c6ff5e086ba9dc299f901af55ce8df0cbf7607", + "0xa7d34a906b5aeabf09e16c9c23d4f283aa9f582523abc3d523b537c8c3427f0a60d76d5d5b019f466deb149ae782bd83", + "0x8353e347469f3884377493bb0e5e96bd876a8d956745eedd13b4346376849d087ffac02bc796aeb76cc60496ac7284ec", + "0x9336981ffa0025a11cbc66abe050ef0464ef768398465b030f11bc33f3863bc6dd15c6a120624216a751a93faabf07a1", + "0xa43616e49ef19ab8d1fddc3172aed3415f428d72ce2d273c6e3cb6a351e9615941f79981753ef137b179ba4ff2ac9f87", + "0xb36b60b93fc91c39e42417d2cdf299bd8c3bd95b880eb94a2fe3bcb7ef49841d88293a3dbdf4abce3d7232d0ace4a58d", + "0xa9870f6ee29ea9689c9edad65ec3f420d3c79d20830505fc76ab32b28dbe44621ad1fc2185dc4d6170f3dbf56db31e57", + "0x95f65e731a901af2f4d1b635e44d76af3ede0184d12add8a8b7a591d63852cb230543170dc363fe669f9655f38cc63e4", + "0xacc68d2e32a50239ffb7a8ee36d1bdb2421d9a2f972f3e13efe8398ae3ff064675bba6cb6c2746da13ef90cf1d0a3e9e", + "0x85f9f4e5d300c2e61fe0638b34d33d859a21da24c449fe95d13966dc09b00a750bb20e4c5a3c23f3d8fe24f5deb74069", + "0x8cd2d214798aa56ed5e6e74b0fb76b5fd89da6a6015705b74532554990b14b23858fbfe6b55364edb09008d61a952fa8", + "0xb35df48c03ecf289d43f1e478c691e71750aad7aa8777ebdcad3ef8c1e63c1e162f07805596a925592631028fc6a5fef", + "0xa46baae9ffd10dc777b8b32580798213f2ce096b480f14086d6ed5f62e177d85e0313063a8c4ec75da0e6c4e71882419", + "0xb6ce274c18278a8e8a24e3d197e46e75660d0f03d25c66aa55cc85594cf3597d99803b018ae791e2fd2f15446f23a97f", + "0xb36bc6a6598705d7897843cce6c071713d905c0beb0f4f99b135095cc540b1b0ecb03fe18d22bb5a8a58e01a16e4cf9b", + "0xb644c6978d46ee43b559ca09be2982b9a71093cc6879a3bd45c9969e96a72a2b2217aa837768d5dc5ed2b4c9ec40ec69", + "0xb29ac9cc3e48d21e66f62885852e8ee0cc9a451d4b6b6b9180d732d41b150eebbd2fd0a0980f4b55d7f4cb887c20a1ec", + "0xb74a5f8227dfe3cc7d1badb1c9e46672054c80c8992fecfa4a09e532c8cbe69f764473e564f71d0b38c42da3469d2b63", + "0xa0e25731c82bc591531fd9f7d6bbeb7fcb2a1051c210e0221f1a4ae1d5c8dfd0e0486f7704b9b82abb8ca00fa788631a", + "0xa735d56e7b8b0f186255c469217a8e3fbdecf5139362b7842d845af391ceb727fd2415d2cbf8de43300968ae17c9678e", + "0xa7f183044f87d45c4ef203aad6efdb583bbd0fd72e8e8defc96882a25ba51a80cdd4f8fa4a13975eb44a682952aa5716", + "0xa8df5fa3ecf45b98995e634536d29d05df9d1f8080b87003154a4074ecf6524f88d3ff62d68d5bf0f444335374b8f9e2", + "0x87ff926931933af9f566c10de3dcb4c7d209edf5c2b36b2a788188b852d87811d74d83fb704dcd60b2491c828bedd55b", + "0x8ec5b0be9075069b681e3d2dfb1ddb85f15057644f5b072c043dcecb10fcd22f4006e447d070217b7e1d30deecd85a71", + "0x99a90d449c572b45df8375f8dbe826ae4887b57e15de83f95a78aa793cdcd39f57ebc51da3ef6e3db9878f7eeb6b71c9", + "0x8a37b4628246f866d1142b7a1bb34e89193e5d43264e19246cae04a8384d0e26b24300e300dd118e09c6668ba16a601c", + "0xb5da7f13d95d1aa9bb86e2aa05d805401fb19a4226b53f71dc7f61cd5a1acd819192adc5eeeffd2d27386c21a473b280", + "0xb49876a5f1631a90310aa2cd0822e113f183c1dfc12f480aaa80366c2948b923b7e70e6047f0b294c2e8d88ca36117e8", + "0x939feb61cebd3d63c2e61fa27d9564e1ad724524bad4e431862e60d15a3965a6d188f638681d7014547ea9af51de9761", + "0x910c5de3a876b7a7ad5897a8c303c54df06b141beb2f3aebe00d8e5e9d5ac4171dd5e920da1e33e46a3e29c525dbe60f", + "0x8b00b18aabb300603ca19679a32ff458cefea3940e5b064d9bff784ecae0d9b1b08c57d830cfc8fc769048a6b570ea5d", + "0x89ed3bf81129a60f1e904200172611a538f502cefc84742fd6de091348ad5d244d26e35281ed9b625c747acbcebcfc72", + "0xa787f11c4991e740f45f40eaf87ddb5b474ea1f113cd74fef1ab6d9e4be9fdc7b5050e0dfdf5d5aca5af8f6889e809cc", + "0x874fe320b2ad55f59ba3b6d95418972b48767fd5f44dce52f9ff2bc79a1e2ac9ed78988ed473a2c740901d29b144745a", + "0xad93293118ad3f0b613e57a6b732b2c9a6eeb5898aaedb12983ad329f3b404115db1a62b1ca1df05dc0eccfda71921a1", + "0xb4575d97821a952f149bb001ce74bfe5af41cbf348a731a8040660473a82a06b2fcf017ce2b7cb37ec1c7803144d41f6", + "0xb8953f58ebbe3a0f3cb9f2fe82d81911ab4799c9dd32ed940cbbf2b9154263e7708804102c4c590bdc6170aba7923098", + "0x818c9b4ae3f4fd7fa2482eff7627979fdcb0662670ac2d3e98d0054e4a8b79688785111c42dc3640288abf9e5c984cb0", + "0xb1679d9fd42a181246489ff46682915a10996e859d22ab985d8b316b7818d45773ce3d91f4699d8068749b6e73a62fc0", + "0x92363e3f935ee3dfa6e731dab13d9f5fea912d81b760f79f9afb3cc16c1eb14714af83d6c352744b3529da3e35fb6240", + "0xad19b62ff04a856ba7dc1c00f4b0a1fc9d683887be43b658c93876a142b0841c50b4bc705c3a3f9884390023c6e82b1d", + "0xb07eaccb232c1bb51992e6e4cb5393b45c2a31ba6c4486056ea455983a7fa7d65156c536ca945019d885178d3f142695", + "0xaebf380cbf290d27d9837c5a84aeb961641a5c0a173a4f22708aa2f4775c7a19c0b3eb0732a6884b1c7887141e906d3c", + "0xb0c3c99eea8d5cc849884914ce53684b8ffb6c699cf6ffcbe72666051d5f7f765bdd26265913cc0137d52b1e98704305", + "0xa906dae746d2cd95c3bcd6f936bba000e8af74cda8224a014f80cfba193a39dd65b9099a9b99f05d654b52f9bfe3a6ea", + "0x887ab7e07ad91f9b9885c750cd43aa0b3214e20f0f1b000a95eb5ba1bca63d9f24509ecfd0109383a2895a173fbcd95e", + "0xa0d157e9f0ef6d639521bdc582549da253a15ff70631afa94412d8bf1e9eb3365d7f7b92dbba3bed892cc7cf38a5cebd", + "0x8022bacac1f7085246c1185fe2b6127a57b910151b4d178f02ed751616403aeae6d1db97aa75b48aaae4c0296f49014e", + "0x9447b94ade9ce7dde660107e3f5c183b196800985fc4b8c8395c7587bfe3e1eb04f34b3f80003805ddf19b407352ac47", + "0x96a91ee2256406fd99d008697f77a120ebf81321071534e8554a9746196b766e07f7655a02692f8a8d8d28edadbbda2f", + "0x8e12871ffb9b748b0eade7038b9f81485fde7147afdb07c32d9ec51404644d10a835acf6dce7c7e691e467685b255e94", + "0xad4e0959c6bafab60e85de8b97c056b898348669f4c1200a58690c13c7f2a50efe2a5d0ffea5ce5eec96add3bd6436d5", + "0x86af4e9d654b31e12fb91a6c6ff6b2a27d5d9ff695f694928b92fd52626a9f03dbd26b1b5ff75b1d44391071c925ad69", + "0x8a9b24633d5653d62322098dd7a98a06a9ef2d562c6ee4617fe36f3a426c0cae7998a622a9ca42394c8affd21d21ae99", + "0x8d3943af16a1c993443b47aec13915540fd31d1b7232a33660515ab6e988a18e511dbb474364ee33a4f26ed04d4d2bd6", + "0xa7cc5995b41b790143f6b2efd31bd93f060c4d4af01b74c7a56a5f960eab660eee17b3d6b5977413d0332cc6bed1ea07", + "0xab349b6e1356e6a586a6716eb644463bb467e038d1417f9bfa90e5be24df3599a4131164ab32a0f88ec5da40cc4da4ab", + "0x9458caeade293dcaa3dcd43d292fbde1f3709310aa4d705205d90c880454a87344abdf5bb17223b176e599d39cd84f61", + "0xa342206005f9503a71f050ca015b048dde9cee3308e46cc88bb9bb7f589d4fc6b6f16a1366a00757824362f65471485a", + "0xa96c5ac0a30b983addfae83dd7e90784fb8cc512363f32e948b4b921e9d293801815695d868523e903d66b795c063daf", + "0x88501ebee1786595f1fbe627c33fac10fe0d16a994a7d53af4ff5b1fdb62c7c363a96d64bca6cd63a64d342a175c3a63", + "0xb0f27f2f757c3540dbf509b6b78ef66197d99cca6897b5322a0ebee1419734e0466675f911b5d76e7ccb93e5d3081f00", + "0x90e908a125ad59c115af8c64706b329d0d1d44aac9c614dcefc881d39b44962997ccf2d893e3e1801869078626eeef9b", + "0x880ebe5146de2e689c5c16be43f3e401eed555647f5420c515fba1fc7f80df0c255cc22ab06c0d827900000bda418360", + "0xa495818641f873c0cd40a92927deca7eb58ca6ca202f6af14bd9ffa9a2e020b020eeb13be18fad3b4e3ff32f97385cd6", + "0xb856ded58166a37f82b86d833a4b67527bdf03c04d2fb2d9c3726e02a99fbd221c02b1cb3340edfdb48c85f1e2757ceb", + "0x86d6c4eec099674f0e14de5562caa5e1420151cf4c2d7d952385115515a3938f20f26518127edbcce7d2f7507834d1ad", + "0x9493e62d9fbcad3fe4e253426effc74f2663126e981b24d3d9f03ea08e70d850c3da739539fed16e72a6b9a280d256ec", + "0x95dd78b31f794b7a520a103a9378e4d6cca98e7d34cb3a4ea3ff75b2fd4fefd4c842c7657042813fb90319b190218f1f", + "0x87dcbf453bf0321510c7040c9049c699d359f25ee68550e53db82938b6df91f23a5c7b962ea9b2f40f6e072e32eb157e", + "0x87e03325fad1f8fdc9d95588aef889aa3d971aaaeb448b1bc26bd151fae11c6a8b35061cfe4931d033c5fb7ef0dad7e7", + "0xabfb05a4ea15ddee4bbbed7844d434191d9ad4d9375d68307634a2cd79ded47c3ead6ed152a45a21ebdbf46b6e01a1cc", + "0x919d4c7619cbaa859c7ac0495604f0d98593d1f7fbb8888fc006c7df615c16e4b1714ef5daadf06421d2c85559fe2bcc", + "0x8e306b73513f99451307d62507753a30e31adf32109646b03229e6a8933852493c07516693a67b11bc6bd2b625ba3e1f", + "0xa898edabfa061d04d61e9f0fdf1edd0b4694d8375ba7e7a755f33101ee4ef336987eb4d7d14b7806667359dabca24903", + "0x96f553f33113cdd81ac57e829a7fd1fa6d89737deb0f02b863e64966be0a0e86d3bf720472d5ec48aba1ecff4b9f8cfa", + "0xa09456d9c5cd3b6054c463ebe25b51e718e424f5ae211c3b5ce6457190b82507afc55a10a44ff10135bb0fdf2d594c26", + "0xaf8e6ffad3fd6da4f86439c23736f2b36c3001336d773452e1d18c8d88e2f8f590754f2da9594036e11306f2ad37e38a", + "0x8a9cd8a1367531ac0fdc7b7f20de11c8af0dfcc613b297c01f8d9688cf08850bd4db4c3287d3086588a9311169a3be18", + "0xb9a10b3d9619a7c68b6efbfee19c517bfe37821f085b2f248de5871644ce5adee85ca4dc360bcaf7cf2d968b2513a8a4", + "0xb23688a22ad35ca3bee1cdd56430c4c2dbbffc885d3007115e625e11ba962f746c10e04684c13bcd819959be7ed5109c", + "0x842ad40d08a79f9456c7e38d0cfe76016fe0e904fd5230461bf854ce3c737c69be21c158c7e405a630d97a13aa4d4d63", + "0x8f39a142da9fc26be7bee7c6bafe8f19b9b2aca0e098a95a910fd37b2839be57372acce1fe5ba85a01035bf379dc6bef", + "0xa53b2ce3c423f80e3821c17874c39aa8ddb9a996061ff9650bb00a1961616f06b3534e907a842101314590ea63173526", + "0xb872f82fdddb62c26b39972ef7629cd29d276f98945910fac173821bf9984183df66a687831e7768ab9c2c512e06583f", + "0xa008ca0d0964d5f075aa02a1ad999797a71ea0dcad78711b18b6fc59593d2684a1513f5bf5b86adb9802eff69772b923", + "0x9052a0217b6610a224f91ca9a30800d03962f8ad1a0b965bf52bdb51f8414508cf9f1e0eee5760e94a6362249a6b2393", + "0x81a9f4170349605dad4a21a1d76392c2ee9b79caea9c8ed592212af52df5109b18ae8abe006b81d00e44cbb4148a7da5", + "0x9747605bcfaee90812751106c5aa82544c30634220174fd2537d13333565e60f405533a614e7ed7617e16d6b160e5e7d", + "0xa7df7d9a2f15c8008a79e89339522201356b2cffa1d33eb950aedb91bd83b353eb98b75ed986272e09c72d2ecfc471c8", + "0x85c0136d675602fdbf20ef2b3777ac452e7a5e1bd91ce24ef76cd41056179da8b73a8c9047f1a125f1679ee4fe379439", + "0x99a244c4ecd8376b10de8a716d1e33ff4688fa236c3e64318e34cedf99ad5046c559848c935fad1b70f274273a4b743b", + "0x898a8401c63a122474e4011368c73aeb4840abc69fd3b4362dfe206791b5a85949384e7539598a47a5e35d72241ebb58", + "0x9832d46290d6fbde01156e42919f9cc01db14ad6f966214cf8bfc04cc6e5165c8fc45e269984cca251bcbeee31a6cee9", + "0x8c019d453bef6005b15f3c9a8dad4c36cc03a7aa67a3dcc07a2fdebfa216c990c1cc5b3654deaae0186a731365c2d7d0", + "0xa4625cfca0f4cd8251dc02be5524c193edbfe57e0108d8bf09e4d064dba3db3eb2aeeffeedb60868519aeb3f30e9a055", + "0xa30967521f928e4de4851a80b87cddcfe59718e30cfac0e92c0f4c53edd332fafa4d3723a7342a00a4bfe4d9364c3c9b", + "0xabbf6c70e5fa6d9822fc923ebc0c0a1334d9c0ca79e9daedab545dbb3d27e523bc26e845ff79af7f4ee4debdc992d9bb", + "0x97c62cd682d42c33d5233e40afbbe460806d15ac47e0c3b4c02155226b33a477185f6cef138e5e7023ae0cbff8370d89", + "0x92cbf2eabbc0a721a48dabeddbdd0916d6aaea3ad039a2400331cdbacd00a32c28951b61d4bb38ac940f26276722130e", + "0x8edff9f51c16d62c45853a76665072c1fd94d87024f59444875865317bd4fb2c4e5ca35fe4c20de583596ca7f8cad836", + "0xa023389b79410fd6a4c4582912fd1f2d13cc6ce53d0dbd1f3545652004d9c7bb2470ba4a56f5d9bc5269f6e4a129d46e", + "0xb36a363e216f3d31df359fb05f3150e350b4255e736973a7c735e411b6ec39e3c526b0b50b84ac1861576ea90e2f4434", + "0xb218af1e2068ef9a5b6d9516acbcba15646e1fa35634aa43870cb43fdaa447884c1e74163b2ff2a380cf85f9b53138a4", + "0x8c9623273f628151c2d3a574fd80d47ef6e4135390f1a664afa32e44e352392a5ac8ce84a7b3423ca6bfeacc56b23af1", + "0xae4ea34112ce653af8abb8c1ffd4e885ba4caf7d043b6a8cf83d02d7f1f4be5540f693009f583c28483a6cd60cb54246", + "0xb164f0d2de81fef3777b024634430edd2bcd9be1b239af88d60b8436689548213fa45caa176ca5519fb4e9b04d499862", + "0xaf5003f87ac5a0caa3f7f60cbbba85bfd7aefd975b18af5e8c7292793a46c166f8a03eb7920ecf91c1d9224957fef0f3", + "0x84868644f18214c524d79284af8c20c9e4535694740b95a6b9720f8cf6900a9292201029a0b15384b1439cdc9e8950a4", + "0xa68bf3a82b3d21a37e7d0bbda8f79550fa39980ff8dbed41e2da05f48d817779fdc771936d1f5069bc283a8a52d279e6", + "0xaf7d0e336f38b05b36c5e54cec125864bc469c71a0308e9c3aada346244af6efd5ec8f529df19110b2e6152ad47ea2a6", + "0x87aa68f26a3b97745d779679312592f94535bda8a693aefd3c2a95f92c4cc778b7e91f0621ea9ac2c8ad07c8ecbb7633", + "0x90f8719afd3e462f17cdf1817e81ca3b18eba6ebaa1a136a509fda54b93486ae69de39f71d71f7da144945a66a7ffb4f", + "0xb204c84424abebd5108a56520a2753ade3db9137a551ed0db6765b5cdb1248d8cfdc564ffdf793c043e5733dab99c8aa", + "0x895a90865b17a2b27c8dcc0f02b4bbf05a957062f086ff68deea8c82045244f52081a62288a4ab58bba3d3bdc94db7fa", + "0x9836012a4bbe149ed65ae7478beb29a8b68a5bb8ad4b1f29cce8a51660c9d0f2e69690b199ddec69a8d342f667a8eddc", + "0xb4f4b829428a8f559fc725af4d0916625e5a24a2102f0bd0bcca08deb2f8ca53161a6d98e5f9ebe123563e287e919400", + "0x8a8aaa7eede2683ceebd92e9058603e9a32504162cd4c2f1f61246df70caccbf7cfff69aef1524fdb568e175471d992b", + "0xa3f761fa4281d41b500be34a3e43ae2e7c8eb37867a808bd43e1b041f9b87cf498194c51e9b21d64b44d091f60a246c5", + "0x8b04f79ae00c4a6b9c474a0d980e590babe4495b0fba9873d48a18f1281c5eed392330a5f74a715cbfed19f0aed998f1", + "0x82ebe70c3b56ece516838c419fe4634d1e7028dc5793ae990f404d09e881f5d0e86268710c5adf86059db75eea4ef50a", + "0xb18f8b71bb6ee01fd15022b6c1f3137a6060f1fd41f6b4ec828ea20cace55163ac3ddbe078df32109d35223e092195da", + "0xa4a8b75df315859c615472a53add5debdee138b489ae0f6b8117b8817570e280e984f44096580906260354f714390408", + "0xb6b9a81c799f4e02e87a664abf5567a272f8d6cad8112bb867dbc86fabfa5e6d694dc7ffa1ca61fa0c291bdb5c437b32", + "0x90a0b307356167a17cd46f5094906b16078ea80aad2b8f28605f39e3589e91f0808f5356bf6a14416e1e8f9a62777cf5", + "0x8976e306b130fdbb11f3e0266defe9d5cde1e43704a7b56ea002fda3d286a94c0fed9670ebc576dde5d9f63f61fbdd51", + "0x89c312e1ae329b331d7d2c057da54006e67739f30d274dfd6366833f9d51b0428e64bf01c8bfca1e16db7f38d131d126", + "0x8832debddb239001aceee3d3b4028f92e1ff1c1babf8102c175d68e92caefb0cb57e5b0a572e68c58a27f6a811d841a7", + "0xa420d43e4236ffe809306dfae829c58332d219535e4b95234f2ff6ebce725f2ce354d52df0e07344898dd150a5a18af3", + "0xb1acb76599225c5b40ba692ed4014a31e2b7e108eafe3427bef2fa942ff0d9aceec5daa006bbeb770a133bd71b75390b", + "0xb571065ea0939443925c0a337d2820691ee713d59d67c46b00d1b8118b59937a9dd34531401d80d9d5d9d66f36d92b1c", + "0xa3af801faa79e5f18ee08f2d653f9529a75d298a10f641440e1a18b9cca768bc8331b2d9fa01f0262442a68d27635b4d", + "0xb2c46aba83ea31b34cfa98435eaa93539bbd08e5a02eb8d547585730e7ad4eafaf8d57253768e82c145113166e9ada62", + "0xb1a3878f3ecdf212855f3df8421e2bb5c2aaf66296d14b6c892059352622508cee684a6a0275967d00969c04eeb177f0", + "0xb6af51f0f4dda9a38c18ee8f7bac002fb45501e8f67d286519e1dde8d0fb5b46f2c4cd7bb2612013faa3962a95aa59d4", + "0x8a4f97338c8a8149beb054aff5cd743d9149b7a4375ecb8c1eb34aa217ef39ae5a0d9d7e8b5f1bb9fba524f7a07b0add", + "0x99a25fbb5c469c7c882fc10c9455e67059480abbcbd145d271f95bfa5cb54736b8bcd625e3f3410658f315bda62eed1a", + "0x87f7f16ce6c34bd9eb0b7e96eeecc1ce79be934bf1b2ad581c9610339b8f4eb5a2a6ba96c06ef9aa127f0aef942a9cad", + "0x9189e20823e5ddfdc7313892e6c477768b3e8653c263b3dc94a0495197eb7135b056728ac34717652f3a8ee4e8f537fc", + "0xae7a968ce642fc123376e7c6e05a85c04733189b5dd81c3e7983843248caaaaf295fc74b7cffa1567387c9886fd36fb5", + "0x96627e7870a781fb86225367cad002da20f55972e59d7b0cd891f033c2f6caed68373be956748a737fe0570e57daa588", + "0x82eef13409e8ff6481ba718092acac802670c294def7a934d3a6142ca7bee80175adcaf62b1877cd3b2d94e2d2a30ca1", + "0x862df9653896f13830fff24a30ff402f945d9edf325b6fb8883d952fdf6a4a7702e9f20e188549e92eae209663538525", + "0xa67a4834e7330f913bc7912069c277cea2aca085ad08d3afacf4fd1bbeec22d7950caa9b0eb2041b741154c9e27439a9", + "0x89bfe6cce5a736e93ead2514e4a2eabb83bd9487599745ff76eddf8c1f114fbf662c4f197b7256c326f760f9254081f1", + "0xac2e14a95cbc76dd005b7341a48b13acde73717e133e8ab1e748ec5face05152e4a2e910505e8e9e69fac00282bd9e27", + "0x94847c10e690b7ce8146a8bd6b4aae9b38c6d3157d1f54aa6ec94cde0c810d97944d1c07d116d71ca9e1c2bc552aa74c", + "0x88e5096d646aea78c0766e94379be3d5b3ed57031f24efe4e472417a65361e259b88ef3f59daa69a07c9765d49035674", + "0xa01731d1ce8c70da3d30d7c8135f47c52e6c4d240bf47c48e5cec707ef51440f94178963c517143eb60627c526d4d499", + "0xa192641ad1fea4679328c8fc83da22de1cfa08554f41ec227e6359002fcbb4201148921f0eb05a3d3f82583e7d10a97d", + "0x8f31c0bc42b71c2fd74f9cdd2f2dae86a8c38aed3c1a7dc2c245d286556c9d55db1cfc8707333eb0c2115eb69d91709b", + "0xb6a33af531d06656cf84b4a5602574407c615b4b42e7f7def56d07cc51183782aec8cfc99eadd4650a038e5e918b153e", + "0x9092f408c6b7d8fb7f92f30d5cdc7253e8fe53bc52d2a101f09d1a1383d1e43beff27bea2d1cb1767f740fd944bddc80", + "0x844ec372dc1faa3fdac1b24b0463c90dfc9b8e612b51c8cf22bd1c84d73fa526c53dc88939f05de217d3c109f8479980", + "0xb1ad68c88a3240d9c615d2dc5ab22b8b352ddf06c1115c078a757339ff5d2a676479f784b04b432f5dd538c184e76230", + "0x8c3c26fc15dd3b067fe0cd1e3f028f74c4e41dc5840af076efc883ed7674df08cbe5656114c8c5c69aef6aa9d2a45de0", + "0x813be1db79af4208b8029ddfe3ba11618d7057bc523c671e79d7744cb4360f9ebc4204809c3364b2f08030e9a688f78f", + "0x8d70bc3598a158e6c9d43813ca9837ff039b4768ac94f651bc29dac1c8620e00198ecd6cae11d002e44f81f552b770da", + "0x84b119037fe6a4a42e7ecfdc6c0ed77d17aab4e59aefec38fd96456101ca4441b943f569508439ff62090f43583af61f", + "0xb6e55e533faae833e710b2d78baeadd45012949208bc96b4537784c44a92ac7a23e874419d69b1a4a234283f29aebf70", + "0x9260ca6ebb1d0746f6891048577006174971ec85ebf073bda9ff5f90ce66f0907d4c70cbff3b89b43a31de88107bd126", + "0x88bcb82a501e87dc5190698d1696a84cde1e8500d92bbaeb40f45e9a326bb5516d12e97441ff8e265c896e4f457113d0", + "0x8edcbacb38131f0ac194a8bd32111852507bdb4d52ac04268475d943369c8f00145613bd14e014b6d91d82f79035275c", + "0xa2d1a8c6c99ed5df48e49e6d3ae2c6006e0120780db7f5a13f5dea6cee2c2ab3da7e478a31df20c4d71d6af9e17a8910", + "0x8c117676f3754c8810b1c6dc928672c5b6a02cbde1d755c6b33df21054f8bfdf41972809a1d8f453d4194ac87e6fa257", + "0x865cd9dac33fe1ce645af017eeb4e545a3aa8f9eaadb1dda3403df21be6552ee537e48117b878e4ff7f83599e34a8148", + "0xa2387d8e447d4f27ee62a28fbf1bae3505ffbee558fa982fcf6ba2f061e55b9e5ad87176c017fdb0725adc4232d671cd", + "0x8d639eb8aa75db20877c977acd9156e85707ee038f5b19c0a06a563f6c8226a3e84c84630ab3bedfa881fe2d0244ed55", + "0x8e4923febe9f82925f582129cb8cf2f85e4e57347448e594a2c6d5e629a77dd329f5f268803254c74de7cd77bfa82244", + "0xa276df10c16b2d195cad846683191e945ed71ab7f03c033c02a97b0830ec28ac28e4c939d92a6b496f3d82b283113da4", + "0xa82e6b642a8dcb9e6c01398ef31d1cab51127e14b2b1aae554cd337767c65c1dda4e6627b91055b2db2428a8db81a81e", + "0x8709a2a0f0140e570c9e7eb74f08a13fda74d2ac70a028d20ed72afcfa3df889b548e9a2faab7259c594c4cf390124d4", + "0xa59ae48006b2a390076550aa08b4188a251b1c7a69ab74106d00d61bad8b4372fbc62b8c530b12cb9da638344a2dd1a6", + "0x8b736164c479064d3f288805f47f76d3c56d5025fedb5934c9429dad8fba016fbc01e9131595c2f1785d084aca2392b3", + "0xae07eae3bfac09c494125bbf649e4d0daf65c994d9bf7e9178bdd1b80a3a50fa89d2946b1135d131a1f159356deb0b69", + "0xa6da8d6aa67a688d6ea998a32e487f877310226d710e7aefa6abb7e498c55e890f5e545a8cfd3539cc9285439f905a4e", + "0xaaf39c40a25b8699e8514df7e1367ab8585a5834238d399a06736ed619716f3827153d6abf84632cbcfaab4d1f2fd0a5", + "0x9866f52a99fc4ba7229db9426e2c93f7e7de8784a53e357f4aa3abdb9ee9f4b8c48e2f76da039435e36eb9a92f3ed4e8", + "0xa6d3ca645de2b4911ae9d0f5eadc7bd5fe56fff33082bc10de1dbc0f38176bedf405888221125da41096feef111598d1", + "0xabc89791629a1f527270a4528ca4d79c5aba82a7eac4fad563236442ae61f515d5bb49662679620c40f962f7f23c5ed4", + "0x865696e035825e74cdaabe7ed088f1ad952340bbc6b9e7898618c5e3cbceb38f4e62282d0ee9810fc429a988ee7bd78d", + "0xa08d5dc53e6522d38c704313723cea8985cfb58354545a69f21ce5ac55f9eb67a34ef809e0c4f28c30fa344854ff7f22", + "0xa8c6cad24aabccd86f1b7dcfc31df9e8dc516d0215cffa3ccd80a3a3135b762cb0a99106ecc4b6e4c245be7ad0a0119b", + "0xa7b2a0c913bccb3be40e488016661063c7a19ccbc25e81a3ebe981fb48591a274e39b2f472de9a589270d0e5b4fab164", + "0xaaf1c5ad2a0440f1c873f3ef5a1ee486a970759613b4d057092d75a973bb8b05aa2d37b4d5f07d001ae15f9bb339f096", + "0x836eaa52bf35029d7524f022911875c1f863df5979785dd55231d8ead4fc8280ad4e2b03f9032982de6d608907037bfc", + "0x8968081f7b0328152c6e32f78c9847dc05b9cd12d06dc1e53a574892e2a7596c0cb1792d00352c761b8fa1e6f2263d84", + "0x8656fdafa26fcb4baeaa35d309622f976364b3f54ab0f119ec5de79888be660b3d1dba99a09911ea59a99512eeb0031e", + "0x940bdb024b690bda2e40bbf9d6281cb6be052a73e147a6d6ac3bae7dee3f1488d2671182b9b857845b22e5dfbf845ab9", + "0xb92b39cb4286ce9ed2b438d0084385fa0f094eaa16e5597d234daed3daa791fbda364b43a2a0e29b8d570d3cdd8a6942", + "0xb6fc4c2b94b5012a082a7e1b5579d000da5a1842f7a0502fae80d14cf83d1544e9b1bf8ba66fae4a858dad978cadbe8d", + "0xa7b715f266ea6e2dd3c1cc56e5d0e490296840ac59f0b9b29b4254409fc8cf8fe916923da9e503e40a1a29bf32c5eb0b", + "0xb967eeed5333bce3816b987e595093941ad3a8c6698a637438e48f104fa90b198fe11cf5851e3844e7f6d28e00b06981", + "0xaa8e8f3cf11f0d745e1ff6230cf81790f3392c5fa986adb4750068df37bc79b2d34f1c78def80020707e9d5db7a4daa6", + "0xa4a0b272ca629685f14a9517ab6e808695475373464928b7a7ec1b37425144ace34e3cb1e9e1c461f03b10b34ddadcf6", + "0x8b61e874d5e4e15498af68075fc649e04c70c3acb390f7c59d3126fb9efcff921577d882742aaca6ee2a6f82d73e1b6b", + "0xacada12ba72b4ba6beeed2e5678e3dcc513e2161347cb41bc71a311269fbc34ee08f5288022148fd7470247abbb29558", + "0xb02d83bebb7533c9a31850a1d314a4f46cd3c9934ec46585ba2ebfaafad04dd8bf89c138a7c6017e04099762b5616f44", + "0xac63444a41d7a7f527cc8354e81dde61e1fc76374b701ada721b3458ce1a774d69d8200f73346a94d529c61a48d6ddba", + "0xa64e24544285db2f683c6033ac74290d55aa007d1f049040a0ad7a70755bf39207cd79ca54bd8cd495d2ddefc3b40467", + "0x932b955207b1feaf0f7e8a0d392f8031af1c9a88d4ebad55b9b6cb87a4ab264a34fc2c40a6e11b816eb88540c062fca7", + "0x8c10cba0745ffce653145ce5a516cecfd0bc1af54a36a6f11ccdd8264ae30a980a7ac0f7235703d135c34a41277f81c3", + "0x8deb51ad128a06a1cd61ebf12b222f8ae4dc3185ea6737025921595baa9805d835f37a4eb142b2ef6b6e93e0bc013250", + "0xb005f742812d8d7d0efbc106101ff0a568844a805771c82592f381cf20c38ee9e29a2697efdc60b15547de3346b7649c", + "0xa01487a3f8c55c8aaceaa160d0b8a9f7bb8e220caf96bea3bf3aa602e7a20cf3393a64ecc886e79075fdec39ba8b0026", + "0x83f2143712afdc50e019f908e67e4eb18a8761b5f4c1757e282cb6ca5355c0d33b8640e52be59a894e994466a8ee1ae3", + "0xa8a78aa72570bf89afa287b8c2ffdb20e4b3767e098fce047001ecb3afdb4b6d04ab119aaa506161f5597a4616f4bd2f", + "0x9492e9fa31825b62300ab054afe1ed8fe0507c1669400e9dc8e1773d2d4913a39e93ea89184916c73540e0ff904aea81", + "0xa819774f93815d6bf8f3d3fa134caabf9485a84eca477819cb8a8a5b971b8715a3e78927df604df787cff331f32755ea", + "0xb8b1c8fc02424ea95089f340441f09332ac7f03b5d07b168cecfa68e1e7eff6b59e2273fdb690af01ec44473f78a4226", + "0x87869273d85d1c8b2256d69fb5b9305dcd966de003cfbd25ef0c500ce8e86a739baeca474e51195f04550530197a1b99", + "0x8c8f2e32a3e2235aeb4ccc5a88defdae94abab95662b3bfa5bf7aaff826df2c8be7465f3852878e5acd7ab470b38c286", + "0xa81bb32815a78912229b418153d248685af9f8b0fbe62a4a2fb29624200ab4d00ab7d0ed1d87379a5ae6b3cb4e2f71a2", + "0xaed0d47e9404c5065b728e08f0d6d6b197555b0d5d9eec8da8632ef89ddda8cc07ed2e1f06e03128e45a1d76b9843781", + "0x961861c4d706353d760147a46811244732f74d80146d18e87aa3752c648eb7d14f86781e269eb5b363601a4dc603d835", + "0xa1dad0ed5356de927eb81d5d7e1237a2d995f8989e905b3b19b67bf1116094dccf883e2ffec7b9f7a79910e6dfd8f62c", + "0x8e78ab3caac0357739fddda39b2854617ff69aeff9da020724369aec1e6003152de11def9ffd3c6b604a7a0005635fbb", + "0x88f4da41ad166d59b70a58945cdbab1ce73bd849dc0873801b6d1a4b1c2e9f760cec7550fe77069602114091e060bedf", + "0xa41fc3a8657deb48624ef540fc1c612d28664ac9455b97a6e5c05af5091efeb89faf44626dda21f37dd96f6e833cf422", + "0x8c9d9252599c46f43c8b83ae3695eae78608dd073548b07247909bdf4e9d5617898e1d19fb21346fe1a0cf462789086f", + "0x87ae53ef752b4eb3e9e2f9e5950f0332e7a49d3a947cd489b511dd861b9ae757438b6838c3f54344d7244dba723700dd", + "0xb8bdb25af82294aeadf59d58e43b30d05aeef2a9e1be3ee7c7678921d5356f2e8d6903f9120f3ef633801f6908760209", + "0xb54137ed9e5d0e136a019bfd63886c9a6c99c71e54307f19a450e48b31c032322a56362b4b6e6d23202a59760c5ad91f", + "0xacc303afdfa7643d42ced99129bc5c11bb2bf4853f297f646d1f05454a5ac0e3da9cb9b444ba8225038b385788d24109", + "0x948e43afef69b57b32877f85e15f47109fb50d71f5ca59e6491e4d88db8d40a4a7e5a2b2bf9f2e85cc728640a8355228", + "0x9662e84af4719987c53437356db468b115ff951cdb992164c4e7bea852435473bc05dec5a7ffc1dc8217f97e4e7a5f41", + "0x81095b767cdc236d3967f64f0a9f9d1cf35de765ba8efc334fcac06db6deed0e6bc145eeb1ecbe08c1527717e80ec98e", + "0x8f171e822ac2eaff0039a7e23034e71205581e8d3b87a83af1db48cf850beb085c6e6027676676314f3dba3a6a361da2", + "0x8997a143d578d3a725b0aab8fe6d28c6b6775eb59760dab5a639e587e3ad84133e1715c0eb5ef40c7bbe61083db33d22", + "0x84d0d29da4c0742f105ba1529464748c959f835bbbe3b8cd26201728af971209909298b2027697d8303f42f10c05b7f0", + "0x91a3e22d193c5c2cff216340dd0d87c4c1d4697858c4457d5b595a44cad90adcdfb756b42c5e33187a7e8c50e2dc27a7", + "0xa92e9f8fe2b3c829dc1ec9332e05c4b733a38079d6708a48d1e797ba720841f0cef07527789ca492ec973ab42856d65e", + "0x96723a32d16b9b5decb9b545ce426ca3e3bad65428876deceadd37420892e4340bc47049fc49eec2eb5daaf2485f8d75", + "0x8352739914beff7b2e5e5f9ac950efd6f344e2accbde455768250e64b35aabadda646c6cd64d7398294953200c18559a", + "0xb3336192963a86e043affaa81a02de9790e79aee45b5e9c3bc03a3d027c132659eedcc22a631b2ab4d506369cc554644", + "0x945b9587c85a1df95fb854b89dc8c6ea9de37131c485055a1f8d9635fac22d8ff67f97d6b0b1eddfd4b02d7c7c82a1d5", + "0xaa3d57cc56eb5f6a265e228fe0def40cede42f058e2a4a230debac910217a551a6c5a18a07f863bcb4b3995f459ea6b4", + "0x8f96a7174c453cee95ae7d22b48c596c163898f8ece3322baf350ca6f99d107aa6d8a3c12c9e9ba1c0f3bbcd1862d193", + "0xb7723a928e94f665dc34d3f76a9778e54b5bfca6f93324537aa9576891ac49580fc9e19ecbb75e08bb515696155ec222", + "0x8f0ab23c618b10c5c75bb6c0b8540538d5c44e749bfdcfc93d44f1a3111675801ed62774d688cdf0f8935a0f1f678ed0", + "0x808dc36cd140a7e0015b442abf31b61d7ca0ec4928e12de215e1886d1e64f4ce3512235d3275bd47edb0b9e278519969", + "0xa6839c661b7ab71166d630500bfc38d7f9b0da6e834d9125aa1e39fb720e781b1bbec7e893083d5d2166bcdab309a9de", + "0x82c767989c2ba2cdb3e2c3da2b39f61945360303732f6c795fb0eb5f88c2312118fe99cad6f033b26198158aa77e2137", + "0x83756d4d85fae34722d46359bbff48fe2898d69705b6d460c26aa1b3441fe6b0e29c552a6a2378633dd20a961e477925", + "0xa5f9b85dcc44f80c38f847c05b68b3028c08b8c6a885c75183d8d97f58e116e46be232fc94fcb7bbe2593bd516f7c3b2", + "0xb560a564bdc2dac7ce5e62aaaea6d1b43f91b310b72edd6c0cb0ea27e247ade0ea2ac14534d0c32263513c3b2e4da8e5", + "0x84f6f3a3ba168da2e653f6bc8500ef984bc7f4cb0d11192652195625d89ce1ece41d007c9326638ae665ff7649bbf8af", + "0xa6bb1c0ce41367df2cac238d53c3102b0c2f72d7abb4b8aa5dfee8216ed3296ba770cc3d02a340c0000f5dea9dbc19c6", + "0xab07de3c250eed39d4da0ad873d907a0cf44e1d2f94310ebddd5b1400a77d1efd9e548478f9756db3a42544af5ce697a", + "0x941b3394ecd9ee898c989fe2ec7c13735b76958f039e81e060a1f12c304dc9c3cbad487a4368c1406c449953724ff2b2", + "0x83b536d2a586650f2a38690e27f1624063d0f2bb5361110b6a03ffc868ece9bbad89da93552c09d8552ab1e992580086", + "0xb77913b0b79dd73ffb4b0c078c121a0a75c351b0bfc05836b016884416bb42a3d60902818c4675a9c675c6be251979e1", + "0x90a795cf59dae3b23948df72cd109c51d250c913a0decb9ae1fb3c8488372252e55c9064e495900e0d9102d588121ddd", + "0xb4bc45c465857059bb88d78b5ed9a914e674e1b644a41a2d77523115560b88b6316780a370ee77233a3b4f27a42d91a6", + "0xa32bd7cc49c6221eed3c7756ae7af426db24082bbafdbd3b4af1f42d87f89adb9a68ad59076ec0900c73cc76fa554089", + "0x8b5ee2261f5bfa894b96957e2ae1b6e2e69b6c400907d2da23f5696bc57be47a7aee91185a6156b6dec8b76e4847f55d", + "0x8e532a0c487889eb803d2c50968f0bf2434656504bcb17c49e936fbb77b757407bdfd39b27a7c0f9036cedcbde328407", + "0x9706365b908b7a4b6e7e04995a219b9b19215c10fe3e7e7fb44993af248a14ee1ec5fc3d48da7e05508317885a9ecc67", + "0x8b8ab12d9b9bc98af0ca3768183692b1422de32e50301861e510479b5db3b939192f0f8cc2be57c3f84061e018aa6a93", + "0x8195082dad9f87175b5fcd44432e7d84a27d7566658cabf989cb6b1b4c30508f67309cf0eb7ae770ee36ff98f0d82618", + "0xaa971ee5459f1e24a076a5f72838f2deb6bf211d74bdb07f13ba1180d08b2aea015c3c3126d08fd5db55c6e3a3f6651a", + "0xb06666897af1a446357d1d5647903ef76fed273436ae5ecae7f338bc4c7466d99011e2455deccafad782270420e09d75", + "0xb6d024e47712ea737d7b3b08333984fb899523a78835842757d05c146df6b9c3544a92cf93a7c3f56db0dd8df46692ba", + "0xb120807e785075266f416039d0fe7247a8f0cb14726062be471c3bc581629b077d2a4c6a70cdf825f0f20edeb96a5c9b", + "0xb06fe68fd0acbbd39de680e9f58387c0f75c7597845601fa574dff6f7564d1f7a16d90b10f35729ce6dbf1350e61ae28", + "0x84c26d381f3ec62112e60457150633fa209801ae70587073bd2d2a19550bd7ce1947a961640d4feed3f38e398f019a3f", + "0xb1359c4d39bc05770573e600decd20fb5b6d33d554f14a0836d86657b3488b284b5cdd24acfe3766f2328da2a1b1c0b5", + "0x80c8a2743fd147a095295be49eec4e82e212a5d99abf8114dcc88aa273c3c789844208f4466d78c73b67ea9146653a49", + "0xa583c7ad187cfa761d6c1cb26f0e338654bb4015b1427fd3828aa3b86be7d478e737811479880d7cdd769952f869a7a1", + "0x8f71352a88f1aa199a6c2c85543fa86dafef4d285eb84e34fc05dde9c8be1e27a1b2a3c2bf8e998c4aa0d79448e6d2d6", + "0xb8641fd3b1f7583a311c2191fbd44fc45ea73eb13eee81c1bfe524dd62805fb10e37a949e0e9066f8e9f6638f167f515", + "0x95ef70361b02aa9370cad0b68a5a069720ba81b5d1fea6ca5f5e72e35e3a54f6ff5659b1f10ed869fd2d7b0f77bc058d", + "0xa155d0e45b7bffce4305525e86e0a38ad001792122ad770f1791a0899be66e0a47d72b48304c339b2a426be6db46c3ef", + "0xad42848cda56f18f7b43f01b22b621aa9b5d43c5d3aa399ddcfb9caeb1d207686f6626d5b32f53c43714cee873bbeea9", + "0xa08c4618aeeceeb190b8ade481173265f950c18339572da47e50d33d131e80594d7ca033925314cc2a9173f276a6d022", + "0xb2859ed69b7161d0844423c7e452a3f41c05a314880675f18ba4dce900f5398ad212cd8e7d7714aa0aef46206a60261b", + "0x8a7674352ab07c0262b19e12b5dc025b77b413bf0383e6241b4ae3fd095ea128005959f6b28e8fb080d21662b5d043f6", + "0x8b57888a0953696941b4e424cf2fc2d5cc0c12386f07edf1424a0ae1a30dba4173855718f2b25ee72cfcd0ee82ea13d3", + "0x8b827d8e6ffa62057103a10ba7c690a8c9d0e3b8dc060c0cad4a7ee2d1155aac99c62f6a7333b4d99b57c39f781b989b", + "0xa8e4046962da252d6c84aca4bd25e9d0c8d4750f3902cc4c2027e82327108622e60c49409ff11c808aafc9b6df6e896d", + "0xb54ad95a5526f8ff0b1735fb29d543e7501731865751c109aa3eb49ade1dc81724481e939ad9b876ea4fbe6f9061a81a", + "0xb9d277b75dc10d195bc01dd6e6afd27e12e1d3b196f642b38d5478637492b60440e5bde0af2236f0e35184e27197fa65", + "0x99765727a0857feeef557cc0f001319ab6060a436a538fefc171eb0a04539b7d36194aeae04b068e57549e338accaf0b", + "0xa8f91bf989660d064afdd449da12cc8ff560842c34f6ba6dfc984702d59097db876e4fd8697fa1a731e5d0c943542a29", + "0xaf6e8ef87cd9b83ac344025e46a876d922d851326cb2d7acff97dbc96af9b837cc96d4a9760fcfe7d6049a87fac5bcf8", + "0x91fd3bb543b90233a6b1c817c87a3d656c8dd9b29ffba393034e421f81a178d9c379fcefea63b9f6477d54c4b42414fc", + "0x8873ea4be569a4bae07ea5168052b6f98a08870f6a807ed0c21608034291299154c95d5789031da7c5ca3e850337e8ff", + "0xa489acf746671f7f441f857f3c2601c1b4569881dc89c6aeaa215afa03a465b2b1051253188c5e0d84758ab7e34a8023", + "0x97354bcdf9548501685620490d9e22a36911d22a5dee352b43031f3dbb39b061b841d8245d37c775ce373ee98da9fb40", + "0xadab6f8a17a5335ed7f65acb9dc9704fe2b406deab0507da7411bf0b5d4deafa3fa7319c302b704465f9d93009db70cf", + "0xadb239fc359cddc3e1e36ab6cc7de06729a3938f7e720b04d0dd470d6a4e7f15e2013fe5cbbf22837697429767480f6b", + "0xa3778ad4bfb5df3e32ed9a646b124757c032da00124658f697fb95ca81d3947ce0fc216ffc934a838275d15cf00fa6fd", + "0x94365da722aa4ac2cc1f6efa1bfde863b2fab9cfde7343c36b81442e75d9ba37cf7019ab23deedee85c3bd76805ab534", + "0xb03a2003d070d00fb1f33f96d93db41fb01e167103e1a37784b05430b2e3d285451b22c14aeed12210360905fa1720aa", + "0xa75f875db50e93aea2f0665c680e594f425e2384f437f6cfe995c4217873dce150d27ce48baff3f2fff447551bf0280d", + "0xb4ad96f0997bcb98693c703dd41aa71324f9fed2818616d669d5dbcde3ea4dc6fde4e27cba4a0ba4de3ca7d287ee5550", + "0xa4a04a661862b1647af69b32f7520e72f269d60d28289ec6a98ffbe4dcf008672f10fd24488c8b8d41ac1f0532fd1e8c", + "0x8b8573f5ba246c72f16c3586f39119c9f5925c8ddd15bcc8258584da8f3fa59fc1b9b3b16edcc260f232f7affb012bb4", + "0x944f0db27cd1ddc02e5e4a1793cfd2b631c07f677f5a04ce0ff0d2cd4b3c0a9462b81ac017b8dc7b45e4b4b493e4ad15", + "0xb6ffaf32e29027df2c7dae5d93353be8f4cd962c451d5dd7b8b48d2a19d823523151cb7bd7161d828064532a7db0d16b", + "0xaf82ee0839d34f1157d539ee623529561630764a4ed4a2552c7431a7f92bcb346d4f3e63ef63272a26c1bb7d0303b3af", + "0xb5e06bf52c0d377168f3e03c04aaf31637690c4a842d72878acaad239f53793bb5fc14e64af3fff61009d282be33e7ea", + "0x97ecb765e44ba9db6646a5023e98e654a2789b5e21ca1b70d64987771392995b92b8fb5851892d0319e929cd720c1cad", + "0x97d05ee0b507e43130a60c09c3c82542f403ae74c58bbc9c388ee4d6d8f6e0b23515dafb7e6c489401e367e47008f21f", + "0xa83598e5cff35f9be0473852cb2aa76e8b1c207798564fe91de5da244e8635dd534aa736727f7601a3c482dc8705a6af", + "0xa9ba8323de948ba8e505905e07a72f33cd63168c41a9f718004cbd0d26a920815dd6354178165e39792d4d0db7000ccf", + "0xadcc26d1581278abe1c17f96934f29553d0b0472d8e3b161fb06df0322a94ee2467ef9678951a12ec2ed106d487af2e0", + "0x88529c4b93784e255c6f495432e9e30b9242b1e82966ef951d49b57204a9b3748fb768fabcb556f172cab70ce0c73099", + "0xa21679c69ef257201ca349ea01e1119677b403e7aa1f63c12ba335c237dfab7663061f55a6ccd1dd3a54bf0f1bdfd871", + "0xb9af81726736a0f5f7ed3074e98f5cddd0e36e24ef170dc2cfbfbbe8094b5b24566e9c7c50d72a893855ee99166cbd76", + "0x904d3584230100719a941ccd421db29342a94a9f264d30ffa090996ce14d504a442ac0b674cd453610df3fc6711c7641" + ] +} From d6675c8ece1a507149aaab0ae5ce2e8cab71c0b7 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:20:52 +0100 Subject: [PATCH 219/272] feat: use separate validators library --- .../src/contracts/BoltValidatorsV2.sol | 172 +++++------------- .../src/interfaces/IBoltValidatorsV2.sol | 11 -- bolt-contracts/src/lib/ValidatorsLib.sol | 118 ++++++++++++ bolt-contracts/test/BoltValidatorsV2.t.sol | 2 +- 4 files changed, 160 insertions(+), 143 deletions(-) create mode 100644 bolt-contracts/src/lib/ValidatorsLib.sol diff --git a/bolt-contracts/src/contracts/BoltValidatorsV2.sol b/bolt-contracts/src/contracts/BoltValidatorsV2.sol index 326894357..4a6e601d4 100644 --- a/bolt-contracts/src/contracts/BoltValidatorsV2.sol +++ b/bolt-contracts/src/contracts/BoltValidatorsV2.sol @@ -6,6 +6,7 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeab import {BLS12381} from "../lib/bls/BLS12381.sol"; import {BLSSignatureVerifier} from "../lib/bls/BLSSignatureVerifier.sol"; +import {ValidatorsLib} from "../lib/ValidatorsLib.sol"; import {IBoltValidatorsV2} from "../interfaces/IBoltValidatorsV2.sol"; import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; @@ -18,6 +19,7 @@ import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; /// You can also validate manually with forge: forge inspect storage-layout --pretty contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpgradeable, UUPSUpgradeable { using BLS12381 for BLS12381.G1Point; + using ValidatorsLib for ValidatorsLib.ValidatorSet; // ========= STORAGE ========= @@ -25,35 +27,10 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg IBoltParametersV1 public parameters; /// @notice Validators (aka Blockspace providers) - /// @dev Validators are blockspace providers for commitments. - /// - /// Validators in this mapping are identified by their sequence number. - /// The sequence number is an incremental index assigned to each validator - /// in the registry and is guaranteed to be unique. - mapping(uint32 => _Validator) internal VALIDATORS; - - /// @notice Mapping of BLS public key hash to the sequence number of the validator - /// in the VALIDATORS mapping. This mapping is used to quickly lookup a validator - /// by its BLS public key hash without iterating over the VALIDATORS mapping. - mapping(bytes20 => uint32) internal validatorPubkeyHashToSequenceNumber; - - /// @notice Sequence number of the next validator to be registered - uint32 internal nextValidatorSequenceNumber; - - /// @notice Mapping of controller index to its address. A controller map is - /// used to identify entities with a 32-bit index instead of their full address. - mapping(uint32 => address) internal controllerIndexToAddress; - mapping(address => uint32) internal controllerAddressToIndex; - uint32 internal nextControllerIndex; - - /// @notice Mapping of authorized operator index to its address. An authorized operator map is - /// used to identify entities with a 32-bit index instead of their full address. - mapping(uint32 => address) internal authorizedOperatorIndexToAddress; - mapping(address => uint32) internal authorizedOperatorToIndex; - uint32 internal nextAuthorizedOperatorIndex; - - // TODO: adjust - // --> Storage layout marker: 4 slots + /// @dev This struct occupies 6 storage slots. + ValidatorsLib.ValidatorSet internal VALIDATORS; + + // --> Storage layout marker: 7 slots /** * @dev This empty reserved space is put in place to allow future versions to add new @@ -63,13 +40,13 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg * * Total storage slots: 50 */ - uint256[46] private __gap; + uint256[43] private __gap; // ========= EVENTS ========= /// @notice Emitted when a validator is registered /// @param pubkeyHash BLS public key hash of the validator - event ValidatorRegistered(bytes32 indexed pubkeyHash, uint32 indexed sequenceNumber); + event ValidatorRegistered(bytes32 indexed pubkeyHash); // ========= INITIALIZER ========= @@ -94,21 +71,21 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg // ========= VIEW FUNCTIONS ========= - /// @notice Get all validators + /// @notice Get all validators in the system /// @dev This function should be used with caution as it can return a large amount of data. - /// @return Validator[] memory Array of validator structs + /// @return ValidatorInfo[] Array of validator info structs function getAllValidators() public view returns (ValidatorInfo[] memory) { - uint32 validatorCount = nextValidatorSequenceNumber; - ValidatorInfo[] memory validators = new ValidatorInfo[](validatorCount); - for (uint32 i = 0; i < validatorCount; i++) { - validators[i] = _getValidatorInfo(VALIDATORS[i]); + ValidatorsLib._Validator[] memory _vals = VALIDATORS.getAll(); + ValidatorInfo[] memory vals = new ValidatorInfo[](_vals.length); + for (uint256 i = 0; i < _vals.length; i++) { + vals[i] = _getValidatorInfo(_vals[i]); } - return validators; + return vals; } /// @notice Get a validator by its BLS public key /// @param pubkey BLS public key of the validator - /// @return Validator memory Validator struct + /// @return ValidatorInfo struct function getValidatorByPubkey( BLS12381.G1Point calldata pubkey ) public view returns (ValidatorInfo memory) { @@ -117,28 +94,11 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg /// @notice Get a validator by its BLS public key hash /// @param pubkeyHash BLS public key hash of the validator - /// @return Validator memory Validator struct + /// @return ValidatorInfo struct function getValidatorByPubkeyHash( bytes20 pubkeyHash ) public view returns (ValidatorInfo memory) { - uint32 sequenceNumber = validatorPubkeyHashToSequenceNumber[pubkeyHash]; - _Validator memory _val = VALIDATORS[sequenceNumber]; - if (_val.pubkeyHash == bytes20(0)) { - revert ValidatorDoesNotExist(); - } - return _getValidatorInfo(_val); - } - - /// @notice Get a validator by its sequence number - /// @param sequenceNumber Sequence number of the validator - /// @return Validator memory Validator struct - function getValidatorBySequenceNumber( - uint32 sequenceNumber - ) public view returns (ValidatorInfo memory) { - _Validator memory _val = VALIDATORS[sequenceNumber]; - if (_val.pubkeyHash == bytes20(0)) { - revert ValidatorDoesNotExist(); - } + ValidatorsLib._Validator memory _val = VALIDATORS.get(pubkeyHash); return _getValidatorInfo(_val); } @@ -176,7 +136,8 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg uint32 maxCommittedGasLimit, address authorizedOperator ) public { - bytes memory message = abi.encodePacked(block.chainid, msg.sender, nextValidatorSequenceNumber); + uint32 sequenceNumber = uint32(VALIDATORS.length() + 1); + bytes memory message = abi.encodePacked(block.chainid, msg.sender, sequenceNumber); if (!_verifySignature(message, signature, pubkey)) { revert InvalidBLSSignature(); } @@ -197,6 +158,7 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg address authorizedOperator ) public { uint32[] memory expectedValidatorSequenceNumbers = new uint32[](pubkeys.length); + uint32 nextValidatorSequenceNumber = uint32(VALIDATORS.length() + 1); for (uint32 i = 0; i < pubkeys.length; i++) { expectedValidatorSequenceNumbers[i] = nextValidatorSequenceNumber + i; } @@ -245,19 +207,12 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg /// @param pubkeyHash The hash of the BLS public key of the validator /// @param maxCommittedGasLimit The new maximum gas limit function updateMaxCommittedGasLimit(bytes20 pubkeyHash, uint32 maxCommittedGasLimit) public { - uint32 sequenceNumber = validatorPubkeyHashToSequenceNumber[pubkeyHash]; - _Validator storage _val = VALIDATORS[sequenceNumber]; - - if (_val.pubkeyHash == bytes20(0)) { - revert ValidatorDoesNotExist(); - } - - address controller = controllerIndexToAddress[_val.controllerIndex]; + address controller = VALIDATORS.getController(pubkeyHash); if (msg.sender != controller) { revert UnauthorizedCaller(); } - _val.maxCommittedGasLimit = maxCommittedGasLimit; + VALIDATORS.updateMaxCommittedGasLimit(pubkeyHash, maxCommittedGasLimit); } // ========= HELPERS ========= @@ -269,20 +224,17 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg if (pubkeyHash == bytes20(0)) { revert InvalidPubkey(); } - if (validatorPubkeyHashToSequenceNumber[pubkeyHash] != 0) { + if (VALIDATORS.contains(pubkeyHash)) { revert ValidatorAlreadyExists(); } - VALIDATORS[nextValidatorSequenceNumber] = _Validator({ - pubkeyHash: pubkeyHash, - maxCommittedGasLimit: maxCommittedGasLimit, - authorizedOperatorIndex: _getOrCreateAuthorizedOperatorIndex(authorizedOperator), - controllerIndex: _getOrCreateControllerIndex(msg.sender) - }); - emit ValidatorRegistered(pubkeyHash, nextValidatorSequenceNumber); - - validatorPubkeyHashToSequenceNumber[pubkeyHash] = nextValidatorSequenceNumber; - nextValidatorSequenceNumber += 1; + VALIDATORS.insert( + pubkeyHash, + maxCommittedGasLimit, + VALIDATORS.getOrInsertController(msg.sender), + VALIDATORS.getOrInsertAuthorizedOperator(authorizedOperator) + ); + emit ValidatorRegistered(pubkeyHash); } function _batchRegisterValidators( @@ -294,78 +246,36 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg revert InvalidAuthorizedOperator(); } - uint32 authorizedOperatorIndex = _getOrCreateAuthorizedOperatorIndex(authorizedOperator); - uint32 controllerIndex = _getOrCreateControllerIndex(msg.sender); + uint32 authorizedOperatorIndex = VALIDATORS.getOrInsertAuthorizedOperator(authorizedOperator); + uint32 controllerIndex = VALIDATORS.getOrInsertController(msg.sender); uint256 pubkeysLength = pubkeyHashes.length; for (uint32 i; i < pubkeysLength; i++) { bytes20 pubkeyHash = pubkeyHashes[i]; - uint32 sequenceNumber = nextValidatorSequenceNumber + i; if (pubkeyHash == bytes20(0)) { revert InvalidPubkey(); } - if (validatorPubkeyHashToSequenceNumber[pubkeyHash] != 0) { + if (VALIDATORS.contains(pubkeyHash)) { revert ValidatorAlreadyExists(); } - VALIDATORS[sequenceNumber] = _Validator({ - pubkeyHash: pubkeyHash, - maxCommittedGasLimit: maxCommittedGasLimit, - authorizedOperatorIndex: authorizedOperatorIndex, - controllerIndex: controllerIndex - }); - emit ValidatorRegistered(pubkeyHash, sequenceNumber); - - validatorPubkeyHashToSequenceNumber[pubkeyHash] = sequenceNumber; + VALIDATORS.insert(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex); + emit ValidatorRegistered(pubkeyHash); } - - nextValidatorSequenceNumber += uint32(pubkeysLength); - } - - /// @notice Internal helper to get the index of a new or existing operator by its address. - /// @param authorizedOperator Address of the operator - /// @return Index of the operator - function _getOrCreateAuthorizedOperatorIndex( - address authorizedOperator - ) internal returns (uint32) { - uint32 authorizedOperatorIndex = authorizedOperatorToIndex[authorizedOperator]; - if (authorizedOperatorIndex == 0) { - authorizedOperatorIndex = nextAuthorizedOperatorIndex; - authorizedOperatorToIndex[authorizedOperator] = authorizedOperatorIndex; - authorizedOperatorIndexToAddress[authorizedOperatorIndex] = authorizedOperator; - nextAuthorizedOperatorIndex += 1; - } - - return authorizedOperatorIndex; - } - - /// @notice Internal helper to get the index of a new or existing controller by its address. - /// @param controller Address of the controller - /// @return Index of the controller - function _getOrCreateControllerIndex( - address controller - ) internal returns (uint32) { - uint32 controllerIndex = controllerAddressToIndex[controller]; - if (controllerIndex == 0) { - controllerIndex = nextControllerIndex; - controllerAddressToIndex[controller] = controllerIndex; - controllerIndexToAddress[controllerIndex] = controller; - nextControllerIndex += 1; - } - - return controllerIndex; } /// @notice Internal helper to get the ValidatorInfo struct from a _Validator struct /// @param _val Validator struct /// @return ValidatorInfo struct - function _getValidatorInfo(_Validator memory _val) internal view returns (ValidatorInfo memory) { + function _getValidatorInfo( + ValidatorsLib._Validator memory _val + ) internal view returns (ValidatorInfo memory) { return ValidatorInfo({ pubkeyHash: _val.pubkeyHash, maxCommittedGasLimit: _val.maxCommittedGasLimit, - authorizedOperator: authorizedOperatorIndexToAddress[_val.authorizedOperatorIndex], - controller: controllerIndexToAddress[_val.controllerIndex] + authorizedOperator: VALIDATORS.getAuthorizedOperator(_val.pubkeyHash), + controller: VALIDATORS.getController(_val.pubkeyHash) }); } diff --git a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol index bd485840d..f9cbeb754 100644 --- a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol +++ b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol @@ -11,13 +11,6 @@ interface IBoltValidatorsV2 { address controller; } - struct _Validator { - bytes20 pubkeyHash; - uint32 maxCommittedGasLimit; - uint32 controllerIndex; - uint32 authorizedOperatorIndex; - } - error InvalidBLSSignature(); error InvalidAuthorizedOperator(); error ValidatorAlreadyExists(); @@ -36,10 +29,6 @@ interface IBoltValidatorsV2 { bytes20 pubkeyHash ) external view returns (ValidatorInfo memory); - function getValidatorBySequenceNumber( - uint32 sequenceNumber - ) external view returns (ValidatorInfo memory); - function registerValidatorUnsafe( bytes20 pubkeyHash, uint32 maxCommittedGasLimit, diff --git a/bolt-contracts/src/lib/ValidatorsLib.sol b/bolt-contracts/src/lib/ValidatorsLib.sol new file mode 100644 index 000000000..ac40ecbd4 --- /dev/null +++ b/bolt-contracts/src/lib/ValidatorsLib.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +/// @title ValidatorsLib +/// @notice A library for managing a set of validators along with their information +library ValidatorsLib { + error ValidatorDoesNotExist(); + + struct _AddressSet { + address[] _values; + // We use index 0 to represent a non-existent value + mapping(address => uint32) _indexes; + } + + /// @dev The internal representation of a validator in the set. + /// This takes only 1 slot in tightly packed storage. + struct _Validator { + bytes20 pubkeyHash; + uint32 maxCommittedGasLimit; + uint32 controllerIndex; + uint32 authorizedOperatorIndex; + } + + struct ValidatorSet { + _Validator[] _values; + mapping(bytes20 => uint32) _indexes; + _AddressSet _controllers; + _AddressSet _authorizedOperators; + } + + // ================ VALIDATOR SET LOGIC ================ + + function get(ValidatorSet storage self, bytes20 pubkeyHash) internal view returns (_Validator memory) { + uint32 index = self._indexes[pubkeyHash]; + if (index == 0) { + revert ValidatorDoesNotExist(); + } + + return self._values[index - 1]; + } + + /// @dev DANGER: this function copies all data into memory. This should be used off-chain. + function getAll( + ValidatorSet storage self + ) internal view returns (_Validator[] memory) { + return self._values; + } + + function contains(ValidatorSet storage self, bytes20 pubkeyHash) internal view returns (bool) { + return self._indexes[pubkeyHash] != 0; + } + + function length( + ValidatorSet storage self + ) internal view returns (uint256) { + return self._values.length; + } + + function insert( + ValidatorSet storage self, + bytes20 pubkeyHash, + uint32 maxCommittedGasLimit, + uint32 controllerIndex, + uint32 authorizedOperatorIndex + ) internal { + self._values.push(_Validator(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex)); + self._indexes[pubkeyHash] = uint32(self._values.length); + } + + function updateMaxCommittedGasLimit( + ValidatorSet storage self, + bytes20 pubkeyHash, + uint32 maxCommittedGasLimit + ) internal { + uint32 index = self._indexes[pubkeyHash]; + if (index == 0) { + revert ValidatorDoesNotExist(); + } + + self._values[index - 1].maxCommittedGasLimit = maxCommittedGasLimit; + } + + function getController(ValidatorSet storage self, bytes20 pubkeyHash) internal view returns (address) { + return at(self._controllers, get(self, pubkeyHash).controllerIndex); + } + + function getAuthorizedOperator(ValidatorSet storage self, bytes20 pubkeyHash) internal view returns (address) { + return at(self._authorizedOperators, get(self, pubkeyHash).authorizedOperatorIndex); + } + + function getOrInsertController(ValidatorSet storage self, address controller) internal returns (uint32) { + return getOrInsert(self._controllers, controller); + } + + function getOrInsertAuthorizedOperator( + ValidatorSet storage self, + address authorizedOperator + ) internal returns (uint32) { + return getOrInsert(self._authorizedOperators, authorizedOperator); + } + + // ================ ADDRESS SET HELPERS ================ + + function getOrInsert(_AddressSet storage self, address value) internal returns (uint32) { + uint32 index = self._indexes[value]; + if (index == 0) { + self._values.push(value); + self._indexes[value] = uint32(self._values.length); + return uint32(self._values.length); + } else { + return index; + } + } + + function at(_AddressSet storage self, uint32 index) internal view returns (address) { + return self._values[index - 1]; + } +} diff --git a/bolt-contracts/test/BoltValidatorsV2.t.sol b/bolt-contracts/test/BoltValidatorsV2.t.sol index 062605a40..5399d52eb 100644 --- a/bolt-contracts/test/BoltValidatorsV2.t.sol +++ b/bolt-contracts/test/BoltValidatorsV2.t.sol @@ -47,7 +47,7 @@ contract BoltValidatorsV2Test is Test { } function testUnsafeBatchRegistrationV2GasUsage() public { - BLS12381.G1Point[] memory pubkeys = _readPubkeysFromFile(620); + BLS12381.G1Point[] memory pubkeys = _readPubkeysFromFile(615); bytes20[] memory pubkeyHashes = new bytes20[](pubkeys.length); for (uint256 i = 0; i < pubkeys.length; i++) { From 17a20eaa8147ce1fb2db6c37b7ce01bb73c59379 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:29:11 +0100 Subject: [PATCH 220/272] feat: upgrade validators script --- .../script/holesky/admin/Upgrade.s.sol | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/bolt-contracts/script/holesky/admin/Upgrade.s.sol b/bolt-contracts/script/holesky/admin/Upgrade.s.sol index 9f75f7e08..bc6611dd7 100644 --- a/bolt-contracts/script/holesky/admin/Upgrade.s.sol +++ b/bolt-contracts/script/holesky/admin/Upgrade.s.sol @@ -13,12 +13,15 @@ import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerM import {BoltEigenLayerMiddlewareV2} from "../../../src/contracts/BoltEigenLayerMiddlewareV2.sol"; import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; import {BoltSymbioticMiddlewareV2} from "../../../src/contracts/BoltSymbioticMiddlewareV2.sol"; +import {BoltValidatorsV1} from "../../../src/contracts/BoltValidatorsV1.sol"; +import {BoltValidatorsV2} from "../../../src/contracts/BoltValidatorsV2.sol"; import {BoltConfig} from "../../../src/lib/Config.sol"; contract UpgradeBolt is Script { struct Deployments { address boltManager; address boltParameters; + address boltValidators; address symbioticNetwork; address symbioticOperatorRegistry; address symbioticOperatorNetOptIn; @@ -103,6 +106,31 @@ contract UpgradeBolt is Script { console.log("BoltSymbioticMiddleware proxy upgraded from %s to %s", opts.referenceContract, upgradeTo); } + function upgradeBoltValidators() public { + address admin = msg.sender; + console.log("Upgrading BoltValidators with admin", admin); + // TODO: Validate upgrades with Upgrades.validateUpgrade + + Options memory opts; + opts.unsafeSkipAllChecks = true; + opts.referenceContract = "BoltValidatorsV1.sol"; + + string memory upgradeTo = "BoltValidatorsV2.sol"; + + Deployments memory deployments = _readDeployments(); + + bytes memory initBoltValidators = + abi.encodeCall(BoltValidatorsV2.initializeV2, (admin, deployments.boltParameters)); + + vm.startBroadcast(admin); + + Upgrades.upgradeProxy(deployments.boltValidators, upgradeTo, initBoltValidators, opts); + + vm.stopBroadcast(); + + console.log("BoltValidators proxy upgraded from %s to %s", opts.referenceContract, upgradeTo); + } + function _readDeployments() public view returns (Deployments memory) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); @@ -111,6 +139,7 @@ contract UpgradeBolt is Script { return Deployments({ boltParameters: vm.parseJsonAddress(json, ".bolt.parameters"), boltManager: vm.parseJsonAddress(json, ".bolt.manager"), + boltValidators: vm.parseJsonAddress(json, ".bolt.validators"), symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), symbioticOperatorRegistry: vm.parseJsonAddress(json, ".symbiotic.operatorRegistry"), symbioticOperatorNetOptIn: vm.parseJsonAddress(json, ".symbiotic.networkOptInService"), From fe5a2745db90cb9d964f334a505de6952005aca7 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:53:39 +0100 Subject: [PATCH 221/272] chore: added more tests --- bolt-contracts/test/BoltValidatorsV2.t.sol | 74 +++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/bolt-contracts/test/BoltValidatorsV2.t.sol b/bolt-contracts/test/BoltValidatorsV2.t.sol index 5399d52eb..81bac376e 100644 --- a/bolt-contracts/test/BoltValidatorsV2.t.sol +++ b/bolt-contracts/test/BoltValidatorsV2.t.sol @@ -46,8 +46,78 @@ contract BoltValidatorsV2Test is Test { validators.initialize(admin, address(parameters)); } - function testUnsafeBatchRegistrationV2GasUsage() public { - BLS12381.G1Point[] memory pubkeys = _readPubkeysFromFile(615); + function testReadRegisteredValidatorsV2() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); + + vm.prank(validator); + validators.registerValidatorUnsafe(pubkeyHash, PRECONF_MAX_GAS_LIMIT, operator); + + vm.resumeGasMetering(); + IBoltValidatorsV2.ValidatorInfo[] memory registered = validators.getAllValidators(); + vm.pauseGasMetering(); + + assertEq(registered.length, 1); + assertEq(registered[0].pubkeyHash, pubkeyHash); + assertEq(registered[0].maxCommittedGasLimit, PRECONF_MAX_GAS_LIMIT); + assertEq(registered[0].authorizedOperator, operator); + assertEq(registered[0].controller, validator); + } + + function testUpdateMaxGasLimitV2() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); + + vm.prank(validator); + validators.registerValidatorUnsafe(pubkeyHash, PRECONF_MAX_GAS_LIMIT, operator); + + uint32 newMaxGasLimit = 10_000_000; + vm.resumeGasMetering(); + vm.prank(validator); + validators.updateMaxCommittedGasLimit(pubkeyHash, newMaxGasLimit); + vm.pauseGasMetering(); + + IBoltValidatorsV2.ValidatorInfo memory updated = validators.getValidatorByPubkeyHash(pubkeyHash); + assertEq(updated.maxCommittedGasLimit, newMaxGasLimit); + } + + function testUnauthorizedController() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); + + vm.prank(validator); + validators.registerValidatorUnsafe(pubkeyHash, PRECONF_MAX_GAS_LIMIT, operator); + + uint32 newMaxGasLimit = 10_000_000; + vm.resumeGasMetering(); + vm.expectRevert(IBoltValidatorsV2.UnauthorizedCaller.selector); + validators.updateMaxCommittedGasLimit(pubkeyHash, newMaxGasLimit); + vm.pauseGasMetering(); + } + + function testUnsafeRegistrationV2() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); + + vm.prank(validator); + vm.resumeGasMetering(); + validators.registerValidatorUnsafe(pubkeyHash, PRECONF_MAX_GAS_LIMIT, operator); + vm.pauseGasMetering(); + } + + function testUnsafeRegistrationInvalidOperatorV2() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); + + vm.prank(validator); + vm.resumeGasMetering(); + vm.expectRevert(IBoltValidatorsV2.InvalidAuthorizedOperator.selector); + validators.registerValidatorUnsafe(pubkeyHash, PRECONF_MAX_GAS_LIMIT, address(0)); + vm.pauseGasMetering(); + } + + function testUnsafeBatchRegistrationV2() public { + BLS12381.G1Point[] memory pubkeys = _readPubkeysFromFile(600); bytes20[] memory pubkeyHashes = new bytes20[](pubkeys.length); for (uint256 i = 0; i < pubkeys.length; i++) { From c5a03ba10b8c59020b23d7f445f871357cc60bad Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:56:47 +0100 Subject: [PATCH 222/272] feat: updated libraries --- bolt-contracts/.gas-snapshot | 33 +- .../contracts/BoltEigenLayerMiddlewareV1.sol | 1 - .../contracts/BoltEigenLayerMiddlewareV2.sol | 1 - .../src/contracts/BoltManagerV2.sol | 296 ++++++++++++++++++ .../contracts/BoltSymbioticMiddlewareV1.sol | 1 - .../contracts/BoltSymbioticMiddlewareV2.sol | 1 - .../src/interfaces/IBoltManagerV2.sol | 56 ++++ .../src/interfaces/IBoltMiddlewareV1.sol | 1 - bolt-contracts/src/lib/EnumerableMapV2.sol | 71 +++++ .../src/lib/OperatorMapWithTimeV2.sol | 70 +++++ 10 files changed, 513 insertions(+), 18 deletions(-) create mode 100644 bolt-contracts/src/contracts/BoltManagerV2.sol create mode 100644 bolt-contracts/src/interfaces/IBoltManagerV2.sol create mode 100644 bolt-contracts/src/lib/EnumerableMapV2.sol create mode 100644 bolt-contracts/src/lib/OperatorMapWithTimeV2.sol diff --git a/bolt-contracts/.gas-snapshot b/bolt-contracts/.gas-snapshot index 6720cd1f3..893c5e1c9 100644 --- a/bolt-contracts/.gas-snapshot +++ b/bolt-contracts/.gas-snapshot @@ -13,18 +13,25 @@ BoltChallengerTest:testProveTransactionInclusion() (gas: 176543) BoltChallengerTest:testResolveChallengeFullDefenseSingleTx() (gas: 562694) BoltChallengerTest:testResolveChallengeFullDefenseStackedTxs() (gas: 939716) BoltChallengerTest:testResolveExpiredChallenge() (gas: 426457) -BoltManagerEigenLayerTest:testDeregisterOperatorFromAVS() (gas: 755578) -BoltManagerEigenLayerTest:testGetOperatorStake() (gas: 919551) -BoltManagerEigenLayerTest:testNonExistentProposerStatus() (gas: 901546) -BoltManagerEigenLayerTest:testProposerStatus() (gas: 928359) -BoltManagerEigenLayerTest:testProposersLookaheadStatus() (gas: 2221042) -BoltManagerSymbioticTest:testGetNonExistentProposerStatus() (gas: 1168685) -BoltManagerSymbioticTest:testGetProposerStatus() (gas: 1415457) -BoltManagerSymbioticTest:testProposersLookaheadStatus() (gas: 2488651) -BoltManagerSymbioticTest:testReadOperatorStake() (gas: 1448345) -BoltValidatorsTest:testUnsafeRegistration() (gas: 149361) -BoltValidatorsTest:testUnsafeRegistrationFailsIfAlreadyRegistered() (gas: 148862) -BoltValidatorsTest:testUnsafeRegistrationInvalidOperator() (gas: 22820) -BoltValidatorsTest:testUnsafeRegistrationWhenNotAllowed() (gas: 33183) +BoltManagerEigenLayerTest:testDeregisterOperatorFromAVS() (gas: 757038) +BoltManagerEigenLayerTest:testGetOperatorStake() (gas: 916622) +BoltManagerEigenLayerTest:testNonExistentProposerStatus() (gas: 902889) +BoltManagerEigenLayerTest:testProposerStatus() (gas: 927230) +BoltManagerEigenLayerTest:testProposersLookaheadStatus() (gas: 2197665) +BoltManagerSymbioticTest:testGetNonExistentProposerStatus() (gas: 1309103) +BoltManagerSymbioticTest:testGetProposerStatus() (gas: 1556603) +BoltManagerSymbioticTest:testProposersLookaheadStatus() (gas: 2632992) +BoltManagerSymbioticTest:testReadOperatorStake() (gas: 1590527) +BoltValidatorsTest:testUnsafeBatchRegistrationGasUsage() (gas: 29088733) +BoltValidatorsTest:testUnsafeRegistration() (gas: 138036) +BoltValidatorsTest:testUnsafeRegistrationFailsIfAlreadyRegistered() (gas: 135421) +BoltValidatorsTest:testUnsafeRegistrationInvalidOperator() (gas: 17352) +BoltValidatorsTest:testUnsafeRegistrationWhenNotAllowed() (gas: 12330) +BoltValidatorsV2Test:testReadRegisteredValidatorsV2() (gas: 8051) +BoltValidatorsV2Test:testUnauthorizedController() (gas: 3613) +BoltValidatorsV2Test:testUnsafeBatchRegistrationV2() (gas: 29122477) +BoltValidatorsV2Test:testUnsafeRegistrationInvalidOperatorV2() (gas: 10332) +BoltValidatorsV2Test:testUnsafeRegistrationV2() (gas: 215472) +BoltValidatorsV2Test:testUpdateMaxGasLimitV2() (gas: 4434) TransactionDecoderTest:testDecodeAllTestCases() (gas: 0) TransactionDecoderTest:testDecodeGasUsage() (gas: 53281) \ No newline at end of file diff --git a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol index 34d4b1b9a..f1756b6f3 100644 --- a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol +++ b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol @@ -10,7 +10,6 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeab import {MapWithTimeData} from "../lib/MapWithTimeData.sol"; import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; -import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; diff --git a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol index a73921f37..0a491d0aa 100644 --- a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol +++ b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV2.sol @@ -10,7 +10,6 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeab import {MapWithTimeData} from "../lib/MapWithTimeData.sol"; import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; -import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; diff --git a/bolt-contracts/src/contracts/BoltManagerV2.sol b/bolt-contracts/src/contracts/BoltManagerV2.sol new file mode 100644 index 000000000..45ed460b4 --- /dev/null +++ b/bolt-contracts/src/contracts/BoltManagerV2.sol @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {OperatorMapWithTimeV2} from "../lib/OperatorMapWithTimeV2.sol"; +import {EnumerableMapV2} from "../lib/EnumerableMapV2.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; +import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; +import {IBoltValidatorsV2} from "../interfaces/IBoltValidatorsV2.sol"; +import {IBoltManagerV2} from "../interfaces/IBoltManagerV2.sol"; + +/// @title Bolt Manager +/// @notice The Bolt Manager contract is responsible for managing operators & restaking middlewares, and is the +/// entrypoint contract for all Bolt-related queries for off-chain consumers. +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltManagerV2 is IBoltManagerV2, OwnableUpgradeable, UUPSUpgradeable { + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableMapV2 for EnumerableMapV2.OperatorMap; + using OperatorMapWithTimeV2 for EnumerableMapV2.OperatorMap; + + // ========= STORAGE ========= + /// @notice Start timestamp of the first epoch. + uint48 public START_TIMESTAMP; + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice Validators registry, where validators are registered via their + /// BLS pubkey and are assigned a sequence number. + IBoltValidatorsV2 public validators; + + /// @notice Set of operator addresses that have opted in to Bolt Protocol. + EnumerableMapV2.OperatorMap private operators; + + /// @notice Set of restaking protocols supported. Each address corresponds to the + /// associated Bolt Middleware contract. + EnumerableSet.AddressSet private restakingProtocols; + + // --> Storage layout marker: 7 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[43] private __gap; + + modifier onlyMiddleware() { + if (!restakingProtocols.contains(msg.sender)) { + revert UnauthorizedMiddleware(); + } + _; + } + + // ========= INITIALIZER & PROXY FUNCTIONALITY ========== // + + /// @notice The initializer for the BoltManager contract. + /// @param _validators The address of the validators registry. + function initialize(address _owner, address _parameters, address _validators) public initializer { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + validators = IBoltValidatorsV2(_validators); + + START_TIMESTAMP = Time.timestamp(); + } + + function initializeV2(address _owner, address _parameters, address _validators) public reinitializer(2) { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + validators = IBoltValidatorsV2(_validators); + + START_TIMESTAMP = Time.timestamp(); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + function getEpochStartTs( + uint48 epoch + ) public view returns (uint48 timestamp) { + return START_TIMESTAMP + epoch * parameters.EPOCH_DURATION(); + } + + /// @notice Get the epoch at a given timestamp. + function getEpochAtTs( + uint48 timestamp + ) public view returns (uint48 epoch) { + return (timestamp - START_TIMESTAMP) / parameters.EPOCH_DURATION(); + } + + /// @notice Get the current epoch. + function getCurrentEpoch() public view returns (uint48 epoch) { + return getEpochAtTs(Time.timestamp()); + } + + /// @notice Check if an operator address is authorized to work for a validator, + /// given the validator's pubkey hash. This function performs a lookup in the + /// validators registry to check if they explicitly authorized the operator. + /// @param operator The operator address to check the authorization for. + /// @param pubkeyHash The pubkey hash of the validator to check the authorization for. + /// @return True if the operator is authorized, false otherwise. + function isOperatorAuthorizedForValidator(address operator, bytes20 pubkeyHash) public view returns (bool) { + if (operator == address(0) || pubkeyHash == bytes20(0)) { + revert InvalidQuery(); + } + + return validators.getValidatorByPubkeyHash(pubkeyHash).authorizedOperator == operator; + } + + /// @notice Returns the addresses of the middleware contracts of restaking protocols supported by Bolt. + function getSupportedRestakingProtocols() public view returns (address[] memory middlewares) { + return restakingProtocols.values(); + } + + /// @notice Returns whether an operator is registered with Bolt. + function isOperator( + address operator + ) public view returns (bool) { + return operators.contains(operator); + } + + /// @notice Get the status of multiple proposers, given their pubkey hashes. + /// @param pubkeyHashes The pubkey hashes of the proposers to get the status for. + /// @return statuses The statuses of the proposers, including their operator and active stake. + function getProposerStatuses( + bytes20[] calldata pubkeyHashes + ) public view returns (ProposerStatus[] memory statuses) { + statuses = new ProposerStatus[](pubkeyHashes.length); + for (uint256 i = 0; i < pubkeyHashes.length; ++i) { + statuses[i] = getProposerStatus(pubkeyHashes[i]); + } + } + + /// @notice Get the status of a proposer, given their pubkey hash. + /// @param pubkeyHash The pubkey hash of the proposer to get the status for. + /// @return status The status of the proposer, including their operator and active stake. + function getProposerStatus( + bytes20 pubkeyHash + ) public view returns (ProposerStatus memory status) { + if (pubkeyHash == bytes20(0)) { + revert InvalidQuery(); + } + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); + // NOTE: this will revert when the proposer does not exist. + IBoltValidatorsV2.ValidatorInfo memory validator = validators.getValidatorByPubkeyHash(pubkeyHash); + + EnumerableMapV2.Operator memory operatorData = operators.get(validator.authorizedOperator); + + status.pubkeyHash = pubkeyHash; + status.operator = validator.authorizedOperator; + status.operatorRPC = operatorData.rpc; + + (uint48 enabledTime, uint48 disabledTime) = operators.getTimes(validator.authorizedOperator); + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + return status; + } + + (status.collaterals, status.amounts) = + IBoltMiddlewareV1(operatorData.middleware).getOperatorCollaterals(validator.authorizedOperator); + + // NOTE: check if the sum of the collaterals covers the minimum operator stake required. + + uint256 totalOperatorStake = 0; + for (uint256 i = 0; i < status.amounts.length; ++i) { + totalOperatorStake += status.amounts[i]; + } + + if (totalOperatorStake < parameters.MINIMUM_OPERATOR_STAKE()) { + status.active = false; + } else { + status.active = true; + } + + return status; + } + + /// @notice Get the amount staked by an operator for a given collateral asset. + function getOperatorStake(address operator, address collateral) public view returns (uint256) { + EnumerableMapV2.Operator memory operatorData = operators.get(operator); + + return IBoltMiddlewareV1(operatorData.middleware).getOperatorStake(operator, collateral); + } + + /// @notice Get the total amount staked of a given collateral asset. + function getTotalStake( + address collateral + ) public view returns (uint256 amount) { + // Loop over all of the operators, get their middleware, and retrieve their staked amount. + for (uint256 i = 0; i < operators.length(); ++i) { + (address operator, EnumerableMapV2.Operator memory operatorData) = operators.at(i); + amount += IBoltMiddlewareV1(operatorData.middleware).getOperatorStake(operator, collateral); + } + + return amount; + } + + // ========= OPERATOR FUNCTIONS ====== // + + /// @notice Registers an operator with Bolt. Only callable by a supported middleware contract. + function registerOperator(address operatorAddr, string calldata rpc) external onlyMiddleware { + if (operators.contains(operatorAddr)) { + revert OperatorAlreadyRegistered(); + } + + // Create an already enabled operator + EnumerableMapV2.Operator memory operator = EnumerableMapV2.Operator(rpc, msg.sender, Time.timestamp()); + + operators.set(operatorAddr, operator); + } + + /// @notice De-registers an operator from Bolt. Only callable by a supported middleware contract. + function deregisterOperator( + address operator + ) public onlyMiddleware { + operators.remove(operator); + } + + /// @notice Allow an operator to signal indefinite opt-out from Bolt Protocol. + /// @dev Pausing activity does not prevent the operator from being slashable for + /// the current network epoch until the end of the slashing window. + function pauseOperator( + address operator + ) external onlyMiddleware { + // SAFETY: This will revert if the operator key is not present. + operators.disable(operator); + } + + /// @notice Allow a disabled operator to signal opt-in to Bolt Protocol. + function unpauseOperator( + address operator + ) external onlyMiddleware { + // SAFETY: This will revert if the operator key is not present. + operators.enable(operator); + } + + /// @notice Check if an operator is currently enabled to work in Bolt Protocol. + /// @param operator The operator address to check the enabled status for. + /// @return True if the operator is enabled, false otherwise. + function isOperatorEnabled( + address operator + ) public view returns (bool) { + if (!operators.contains(operator)) { + revert OperatorNotRegistered(); + } + + (uint48 enabledTime, uint48 disabledTime) = operators.getTimes(operator); + return enabledTime != 0 && disabledTime == 0; + } + + // ========= ADMIN FUNCTIONS ========= // + + /// @notice Add a restaking protocol into Bolt + /// @param protocolMiddleware The address of the restaking protocol Bolt middleware + function addRestakingProtocol( + address protocolMiddleware + ) public onlyOwner { + restakingProtocols.add(protocolMiddleware); + } + + /// @notice Remove a restaking protocol from Bolt + /// @param protocolMiddleware The address of the restaking protocol Bolt middleware + function removeRestakingProtocol( + address protocolMiddleware + ) public onlyOwner { + restakingProtocols.remove(protocolMiddleware); + } + + // ========= HELPER FUNCTIONS ========= + + /// @notice Check if a map entry was active at a given timestamp. + /// @param enabledTime The enabled time of the map entry. + /// @param disabledTime The disabled time of the map entry. + /// @param timestamp The timestamp to check the map entry status at. + /// @return True if the map entry was active at the given timestamp, false otherwise. + function _wasEnabledAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) { + return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp); + } +} diff --git a/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol b/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol index a844b78c4..e9366b1c0 100644 --- a/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol +++ b/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol @@ -18,7 +18,6 @@ import {IVetoSlasher} from "@symbiotic/interfaces/slasher/IVetoSlasher.sol"; import {IEntity} from "@symbiotic/interfaces/common/IEntity.sol"; import {MapWithTimeData} from "../lib/MapWithTimeData.sol"; -import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; diff --git a/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV2.sol b/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV2.sol index ae0530fa6..85a6bfda2 100644 --- a/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV2.sol +++ b/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV2.sol @@ -18,7 +18,6 @@ import {IVetoSlasher} from "@symbiotic/interfaces/slasher/IVetoSlasher.sol"; import {IEntity} from "@symbiotic/interfaces/common/IEntity.sol"; import {MapWithTimeData} from "../lib/MapWithTimeData.sol"; -import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; diff --git a/bolt-contracts/src/interfaces/IBoltManagerV2.sol b/bolt-contracts/src/interfaces/IBoltManagerV2.sol new file mode 100644 index 000000000..c30801227 --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltManagerV2.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +interface IBoltManagerV2 { + error InvalidQuery(); + error OperatorAlreadyRegistered(); + error OperatorNotRegistered(); + error UnauthorizedMiddleware(); + error InactiveOperator(); + + /// @notice Proposer status info. + struct ProposerStatus { + // The pubkey hash of the validator. + bytes20 pubkeyHash; + // Whether the corresponding operator is active based on collateral requirements. + bool active; + // The operator address that is authorized to make & sign commitments on behalf of the validator. + address operator; + // The operator RPC endpoint. + string operatorRPC; + // The addresses of the collateral tokens. + address[] collaterals; + // The corresponding amounts of the collateral tokens. + uint256[] amounts; + } + + function registerOperator(address operator, string calldata rpc) external; + + function deregisterOperator( + address operator + ) external; + + function pauseOperator( + address operator + ) external; + + function unpauseOperator( + address operator + ) external; + + function isOperator( + address operator + ) external view returns (bool); + + function getProposerStatus( + bytes20 pubkeyHash + ) external view returns (ProposerStatus memory status); + + function getProposerStatuses( + bytes20[] calldata pubkeyHashes + ) external view returns (ProposerStatus[] memory statuses); + + function isOperatorAuthorizedForValidator(address operator, bytes20 pubkeyHash) external view returns (bool); + + function getSupportedRestakingProtocols() external view returns (address[] memory middlewares); +} diff --git a/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol b/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol index f48885ffa..bd01b6ca5 100644 --- a/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol +++ b/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.25; import {BLS12381} from "../lib/bls/BLS12381.sol"; -import {IBoltValidatorsV1} from "./IBoltValidatorsV1.sol"; interface IBoltMiddlewareV1 { error InvalidQuery(); diff --git a/bolt-contracts/src/lib/EnumerableMapV2.sol b/bolt-contracts/src/lib/EnumerableMapV2.sol new file mode 100644 index 000000000..b4f1a1c84 --- /dev/null +++ b/bolt-contracts/src/lib/EnumerableMapV2.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +library EnumerableMapV2 { + using EnumerableSet for EnumerableSet.Bytes32Set; + + error KeyNotFound(); + + struct Operator { + // RPC endpoint + string rpc; + // Middleware contract address + address middleware; + // Timestamp of registration + uint256 timestamp; + } + + struct OperatorMap { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 key => Operator) _values; + } + + function set(OperatorMap storage self, address key, Operator memory value) internal returns (bool) { + bytes32 keyBytes = bytes32(uint256(uint160(key))); + self._values[keyBytes] = value; + return self._keys.add(keyBytes); + } + + function remove(OperatorMap storage self, address key) internal returns (bool) { + bytes32 keyBytes = bytes32(uint256(uint160(key))); + delete self._values[keyBytes]; + return self._keys.remove(keyBytes); + } + + function contains(OperatorMap storage self, address key) internal view returns (bool) { + return self._keys.contains(bytes32(uint256(uint160(key)))); + } + + function length( + OperatorMap storage self + ) internal view returns (uint256) { + return self._keys.length(); + } + + function at(OperatorMap storage self, uint256 index) internal view returns (address, Operator memory) { + bytes32 key = self._keys.at(index); + return (address(uint160(uint256(key))), self._values[key]); + } + + function get(OperatorMap storage self, address key) internal view returns (Operator memory) { + if (!contains(self, key)) { + revert KeyNotFound(); + } + + return self._values[bytes32(uint256(uint160(key)))]; + } + + function keys( + OperatorMap storage self + ) internal view returns (address[] memory) { + address[] memory result = new address[](self._keys.length()); + for (uint256 i = 0; i < self._keys.length(); i++) { + result[i] = address(uint160(uint256(self._keys.at(i)))); + } + + return result; + } +} diff --git a/bolt-contracts/src/lib/OperatorMapWithTimeV2.sol b/bolt-contracts/src/lib/OperatorMapWithTimeV2.sol new file mode 100644 index 000000000..ac4a1e5fb --- /dev/null +++ b/bolt-contracts/src/lib/OperatorMapWithTimeV2.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; + +import {EnumerableMapV2} from "./EnumerableMapV2.sol"; + +library OperatorMapWithTimeV2 { + using EnumerableMapV2 for EnumerableMapV2.OperatorMap; + + error AlreadyAdded(); + error NotEnabled(); + error AlreadyEnabled(); + + uint256 private constant ENABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 private constant DISABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF << 48; + + function add(EnumerableMapV2.OperatorMap storage self, address addr) internal { + if (!self.set(addr, EnumerableMapV2.Operator("", address(0), 0))) { + revert AlreadyAdded(); + } + } + + function disable(EnumerableMapV2.OperatorMap storage self, address addr) internal { + EnumerableMapV2.Operator memory operator = self.get(addr); + uint256 value = operator.timestamp; + + if (uint48(value) == 0 || uint48(value >> 48) != 0) { + revert NotEnabled(); + } + + value |= uint256(Time.timestamp()) << 48; + operator.timestamp = value; + self.set(addr, operator); + } + + function enable(EnumerableMapV2.OperatorMap storage self, address addr) internal { + EnumerableMapV2.Operator memory operator = self.get(addr); + uint256 value = operator.timestamp; + + if (uint48(value) != 0 && uint48(value >> 48) == 0) { + revert AlreadyEnabled(); + } + + value = uint256(Time.timestamp()); + operator.timestamp = value; + self.set(addr, operator); + } + + function atWithTimes( + EnumerableMapV2.OperatorMap storage self, + uint256 idx + ) internal view returns (address key, uint48 enabledTime, uint48 disabledTime) { + EnumerableMapV2.Operator memory value; + (key, value) = self.at(idx); + uint256 timestamp = value.timestamp; + enabledTime = uint48(timestamp); + disabledTime = uint48(timestamp >> 48); + } + + function getTimes( + EnumerableMapV2.OperatorMap storage self, + address addr + ) internal view returns (uint48 enabledTime, uint48 disabledTime) { + EnumerableMapV2.Operator memory value = self.get(addr); + enabledTime = uint48(value.timestamp); + disabledTime = uint48(value.timestamp >> 48); + } +} From bec46baae6d7c16dddd81e5e72710ca8e3064f82 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:33:30 +0100 Subject: [PATCH 223/272] feat: update library versions --- .../holesky/admin/helpers/RegisterAVS.s.sol | 4 +- .../admin/helpers/UpdateSupportedVaults.s.sol | 8 +-- .../RegisterEigenLayerOperator.s.sol | 16 ++--- .../operators/RegisterSymbioticOperator.s.sol | 14 ++-- .../validators/RegisterValidators.s.sol | 68 +++++++++++++------ .../src/contracts/BoltValidatorsV2.sol | 16 +++-- .../src/interfaces/IBoltValidatorsV2.sol | 2 - bolt-contracts/src/lib/ValidatorsLib.sol | 11 ++- 8 files changed, 85 insertions(+), 54 deletions(-) diff --git a/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol b/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol index f4a3117d4..c61acaa75 100644 --- a/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol +++ b/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; -import {BoltEigenLayerMiddlewareV1} from "../../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltEigenLayerMiddlewareV2} from "../../../../src/contracts/BoltEigenLayerMiddlewareV2.sol"; contract RegisterAVS is Script { function run() public { address admin = msg.sender; console.log("Running with admin address:", admin); - BoltEigenLayerMiddlewareV1 middleware = BoltEigenLayerMiddlewareV1(readMiddleware()); + BoltEigenLayerMiddlewareV2 middleware = BoltEigenLayerMiddlewareV2(readMiddleware()); string memory avsURI = "https://boltprotocol.xyz/avs.json"; console.log("Setting AVS metadata URI to:", avsURI); diff --git a/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol b/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol index 14fdb19be..917dde939 100644 --- a/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol +++ b/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol @@ -6,11 +6,11 @@ import {Script, console} from "forge-std/Script.sol"; import {INetworkRegistry} from "@symbiotic/interfaces/INetworkRegistry.sol"; import {INetworkMiddlewareService} from "@symbiotic/interfaces/service/INetworkMiddlewareService.sol"; -import {BoltSymbioticMiddlewareV1} from "../../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {BoltSymbioticMiddlewareV2} from "../../../../src/contracts/BoltSymbioticMiddlewareV2.sol"; contract UpdateSupportedVaults is Script { function run() public { - BoltSymbioticMiddlewareV1 middleware = _readSymbioticMiddleware(); + BoltSymbioticMiddlewareV2 middleware = _readSymbioticMiddleware(); address[] memory whitelisted = middleware.getWhitelistedVaults(); address[] memory toWhitelist = _readVaultsToWhitelist(); @@ -64,11 +64,11 @@ contract UpdateSupportedVaults is Script { return vm.parseJsonAddressArray(json, ".symbiotic.supportedVaults"); } - function _readSymbioticMiddleware() public view returns (BoltSymbioticMiddlewareV1) { + function _readSymbioticMiddleware() public view returns (BoltSymbioticMiddlewareV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")); + return BoltSymbioticMiddlewareV2(vm.parseJsonAddress(json, ".symbiotic.middleware")); } } diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index d96da34ad..322c9120a 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -9,9 +9,9 @@ import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyMa import {IStrategy, IERC20} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; -import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltEigenLayerMiddlewareV2} from "../../../src/contracts/BoltEigenLayerMiddlewareV2.sol"; import {IBoltMiddlewareV1} from "../../../src/interfaces/IBoltMiddlewareV1.sol"; -import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; +import {IBoltManagerV2} from "../../../src/interfaces/IBoltManagerV2.sol"; contract RegisterEigenLayerOperator is Script { struct OperatorConfig { @@ -43,7 +43,7 @@ contract RegisterEigenLayerOperator is Script { uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); - BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); + BoltEigenLayerMiddlewareV2 middleware = _readMiddleware(); IAVSDirectory avsDirectory = _readAvsDirectory(); OperatorConfig memory config = _readConfig("config/holesky/operators/eigenlayer/registerIntoBoltAVS.json"); @@ -76,7 +76,7 @@ contract RegisterEigenLayerOperator is Script { address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); console.log("Checking operator registration for address", operatorAddress); - IBoltManagerV1 boltManager = _readBoltManager(); + IBoltManagerV2 boltManager = _readBoltManager(); bool isRegistered = boltManager.isOperator(operatorAddress); console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); @@ -91,12 +91,12 @@ contract RegisterEigenLayerOperator is Script { } } - function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { + function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return BoltEigenLayerMiddlewareV1(vm.parseJsonAddress(json, ".eigenLayer.middleware")); + return BoltEigenLayerMiddlewareV2(vm.parseJsonAddress(json, ".eigenLayer.middleware")); } function _readAvsDirectory() public view returns (IAVSDirectory) { @@ -122,11 +122,11 @@ contract RegisterEigenLayerOperator is Script { return IStrategyManager(vm.parseJsonAddress(json, ".eigenLayer.strategyManager")); } - function _readBoltManager() public view returns (IBoltManagerV1) { + function _readBoltManager() public view returns (IBoltManagerV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + return IBoltManagerV2(vm.parseJsonAddress(json, ".bolt.manager")); } function _readConfig( diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index d57902e9b..0bcea2005 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -3,15 +3,15 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; -import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; -import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; +import {BoltSymbioticMiddlewareV2} from "../../../src/contracts/BoltSymbioticMiddlewareV2.sol"; +import {IBoltManagerV2} from "../../../src/interfaces/IBoltManagerV2.sol"; import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { - BoltSymbioticMiddlewareV1 symbioticMiddleware; + BoltSymbioticMiddlewareV2 symbioticMiddleware; IOptInService symbioticNetworkOptInService; address symbioticNetwork; } @@ -54,7 +54,7 @@ contract RegisterSymbioticOperator is Script { address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); console.log("Checking operator registration for address", operatorAddress); - IBoltManagerV1 boltManager = _readBoltManager(); + IBoltManagerV2 boltManager = _readBoltManager(); bool isRegistered = boltManager.isOperator(operatorAddress); console.log("Operator is registered:", isRegistered); @@ -68,15 +68,15 @@ contract RegisterSymbioticOperator is Script { return Config({ symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), - symbioticMiddleware: BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")), + symbioticMiddleware: BoltSymbioticMiddlewareV2(vm.parseJsonAddress(json, ".symbiotic.middleware")), symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) }); } - function _readBoltManager() public view returns (IBoltManagerV1) { + function _readBoltManager() public view returns (IBoltManagerV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + return IBoltManagerV2(vm.parseJsonAddress(json, ".bolt.manager")); } } diff --git a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol index 52fd1d0b3..cdd69ff2d 100644 --- a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol +++ b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IBoltValidatorsV1} from "../../../src/interfaces/IBoltValidatorsV1.sol"; +import {IBoltValidatorsV2} from "../../../src/interfaces/IBoltValidatorsV2.sol"; import {BLS12381} from "../../../src/lib/bls/BLS12381.sol"; import {Script, console} from "forge-std/Script.sol"; @@ -12,9 +12,12 @@ contract RegisterValidators is Script { using BLS12381 for BLS12381.G1Point; struct RegisterValidatorsConfig { - uint128 maxCommittedGasLimit; + uint32 maxCommittedGasLimit; address authorizedOperator; - BLS12381.G1Point[] pubkeys; + // Note: for Unsafe registration (aka without BLS verification precompile) + // we use compressed pubkey hashes on-chain instead of decompressed points. + // BLS12381.G1Point[] pubkeys; + bytes20[] pubkeys; } function run() public { @@ -23,7 +26,7 @@ contract RegisterValidators is Script { console.log("Registering validators to Bolt"); console.log("Controller address: ", controller); - IBoltValidatorsV1 validators = _readValidators(); + IBoltValidatorsV2 validators = _readValidators(); RegisterValidatorsConfig memory config = _parseConfig(); vm.startBroadcast(controller); @@ -33,43 +36,66 @@ contract RegisterValidators is Script { console.log("Validators registered successfully"); } - function _readValidators() public view returns (IBoltValidatorsV1) { + function _readValidators() public view returns (IBoltValidatorsV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return IBoltValidatorsV1(vm.parseJsonAddress(json, ".bolt.validators")); + return IBoltValidatorsV2(vm.parseJsonAddress(json, ".bolt.validators")); } - function _parseConfig() public returns (RegisterValidatorsConfig memory config) { + function _parseConfig() public view returns (RegisterValidatorsConfig memory config) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/validators.json"); string memory json = vm.readFile(path); config.authorizedOperator = vm.parseJsonAddress(json, ".authorizedOperator"); - config.maxCommittedGasLimit = uint128(vm.parseJsonUint(json, ".maxCommittedGasLimit")); + config.maxCommittedGasLimit = uint32(vm.parseJsonUint(json, ".maxCommittedGasLimit")); + console.log("Max committed gas limit:", config.maxCommittedGasLimit); string[] memory pubkeysRaw = vm.parseJsonStringArray(json, ".pubkeys"); - BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](pubkeysRaw.length); + // NOTE: for Unsafe registration (aka without BLS verification precompile) + // we use compressed pubkey hashes on-chain instead of decompressed points. + bytes20[] memory pubkeys = new bytes20[](pubkeysRaw.length); for (uint256 i = 0; i < pubkeysRaw.length; i++) { - string memory pubkey = pubkeysRaw[i]; + bytes memory pubkeyBytes = vm.parseBytes(pubkeysRaw[i]); + require(pubkeyBytes.length == 48, "Invalid pubkey length"); + + // compute the pubkey hash: + // 1. create a 64 byte buffer + // 2. copy the pubkey bytes to the rightmost 48 bytes of the buffer + // 3. hash the buffer + // 4. take the 20 leftmost bytes of the hash as the pubkey hash + bytes memory buffer = new bytes(64); + for (uint256 j = 0; j < 48; j++) { + buffer[j + 16] = pubkeyBytes[j]; + } + bytes20 pubkeyHash = bytes20(keccak256(buffer)); + + pubkeys[i] = pubkeyHash; + console.log("Registering pubkey hash:", vm.toString(abi.encodePacked(pubkeyHash))); + } - string[] memory convertCmd = new string[](2); - convertCmd[0] = "./script/pubkey_to_g1_wrapper.sh"; - convertCmd[1] = pubkey; + // BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](pubkeysRaw.length); + // for (uint256 i = 0; i < pubkeysRaw.length; i++) { + // string memory pubkey = pubkeysRaw[i]; - bytes memory output = vm.ffi(convertCmd); - string memory outputStr = string(output); - string[] memory array = vm.split(outputStr, ","); + // string[] memory convertCmd = new string[](2); + // convertCmd[0] = "./script/pubkey_to_g1_wrapper.sh"; + // convertCmd[1] = pubkey; - uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); - uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); + // bytes memory output = vm.ffi(convertCmd); + // string memory outputStr = string(output); + // string[] memory array = vm.split(outputStr, ","); - pubkeys[i] = BLS12381.G1Point(x, y); + // uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); + // uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); - console.log("Registering pubkey:", vm.toString(abi.encodePacked(pubkeys[i].compress()))); - } + // pubkeys[i] = BLS12381.G1Point(x, y); + + // console.log("Registering pubkey:", vm.toString(abi.encodePacked(pubkeys[i].compress()))); + // } config.pubkeys = pubkeys; } diff --git a/bolt-contracts/src/contracts/BoltValidatorsV2.sol b/bolt-contracts/src/contracts/BoltValidatorsV2.sol index 4a6e601d4..1a533c139 100644 --- a/bolt-contracts/src/contracts/BoltValidatorsV2.sol +++ b/bolt-contracts/src/contracts/BoltValidatorsV2.sol @@ -217,6 +217,10 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg // ========= HELPERS ========= + /// @notice Internal helper to register a single validator + /// @param pubkeyHash BLS public key hash of the validator + /// @param authorizedOperator Address of the authorized operator + /// @param maxCommittedGasLimit Maximum gas limit that the validator can commit for preconfirmations function _registerValidator(bytes20 pubkeyHash, address authorizedOperator, uint32 maxCommittedGasLimit) internal { if (authorizedOperator == address(0)) { revert InvalidAuthorizedOperator(); @@ -224,9 +228,6 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg if (pubkeyHash == bytes20(0)) { revert InvalidPubkey(); } - if (VALIDATORS.contains(pubkeyHash)) { - revert ValidatorAlreadyExists(); - } VALIDATORS.insert( pubkeyHash, @@ -237,6 +238,10 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg emit ValidatorRegistered(pubkeyHash); } + /// @notice Internal helper to register a batch of validators + /// @param pubkeyHashes List of BLS public key hashes of the validators + /// @param authorizedOperator Address of the authorized operator + /// @param maxCommittedGasLimit Maximum gas limit that the validators can commit for preconfirmations function _batchRegisterValidators( bytes20[] memory pubkeyHashes, address authorizedOperator, @@ -256,9 +261,6 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg if (pubkeyHash == bytes20(0)) { revert InvalidPubkey(); } - if (VALIDATORS.contains(pubkeyHash)) { - revert ValidatorAlreadyExists(); - } VALIDATORS.insert(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex); emit ValidatorRegistered(pubkeyHash); @@ -279,7 +281,7 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg }); } - /// @notice Compute the hash of a BLS public key + /// @notice Helper to compute the hash of a BLS public key /// @param pubkey Decompressed BLS public key /// @return Hash of the public key in compressed form function hashPubkey( diff --git a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol index f9cbeb754..fa4fed993 100644 --- a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol +++ b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol @@ -13,8 +13,6 @@ interface IBoltValidatorsV2 { error InvalidBLSSignature(); error InvalidAuthorizedOperator(); - error ValidatorAlreadyExists(); - error ValidatorDoesNotExist(); error UnsafeRegistrationNotAllowed(); error UnauthorizedCaller(); error InvalidPubkey(); diff --git a/bolt-contracts/src/lib/ValidatorsLib.sol b/bolt-contracts/src/lib/ValidatorsLib.sol index ac40ecbd4..f5de2474b 100644 --- a/bolt-contracts/src/lib/ValidatorsLib.sol +++ b/bolt-contracts/src/lib/ValidatorsLib.sol @@ -4,7 +4,8 @@ pragma solidity 0.8.25; /// @title ValidatorsLib /// @notice A library for managing a set of validators along with their information library ValidatorsLib { - error ValidatorDoesNotExist(); + error ValidatorAlreadyExists(bytes20 pubkeyHash); + error ValidatorDoesNotExist(bytes20 pubkeyHash); struct _AddressSet { address[] _values; @@ -33,7 +34,7 @@ library ValidatorsLib { function get(ValidatorSet storage self, bytes20 pubkeyHash) internal view returns (_Validator memory) { uint32 index = self._indexes[pubkeyHash]; if (index == 0) { - revert ValidatorDoesNotExist(); + revert ValidatorDoesNotExist(pubkeyHash); } return self._values[index - 1]; @@ -63,6 +64,10 @@ library ValidatorsLib { uint32 controllerIndex, uint32 authorizedOperatorIndex ) internal { + if (self._indexes[pubkeyHash] != 0) { + revert ValidatorAlreadyExists(pubkeyHash); + } + self._values.push(_Validator(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex)); self._indexes[pubkeyHash] = uint32(self._values.length); } @@ -74,7 +79,7 @@ library ValidatorsLib { ) internal { uint32 index = self._indexes[pubkeyHash]; if (index == 0) { - revert ValidatorDoesNotExist(); + revert ValidatorDoesNotExist(pubkeyHash); } self._values[index - 1].maxCommittedGasLimit = maxCommittedGasLimit; From 240a2bdf7a67a261b40c461d34fc8429cc51285b Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:00:25 +0100 Subject: [PATCH 224/272] chore: ci --- .../script/holesky/operators/RegisterEigenLayerOperator.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index 322c9120a..3fa63dbe8 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -81,7 +81,7 @@ contract RegisterEigenLayerOperator is Script { console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); - BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); + BoltEigenLayerMiddlewareV2 middleware = _readMiddleware(); (address[] memory tokens, uint256[] memory amounts) = middleware.getOperatorCollaterals(operatorAddress); for (uint256 i; i < tokens.length; ++i) { From 6f4ed595f2a01d1a5e3333a816abb4a565f8a7c2 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:54:02 +0100 Subject: [PATCH 225/272] chore: upgrade manager script --- .../script/holesky/admin/Upgrade.s.sol | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/bolt-contracts/script/holesky/admin/Upgrade.s.sol b/bolt-contracts/script/holesky/admin/Upgrade.s.sol index bc6611dd7..2776e93ef 100644 --- a/bolt-contracts/script/holesky/admin/Upgrade.s.sol +++ b/bolt-contracts/script/holesky/admin/Upgrade.s.sol @@ -9,6 +9,7 @@ import {Upgrades, Options} from "@openzeppelin-foundry-upgrades/src/Upgrades.sol import {BoltParametersV1} from "../../../src/contracts/BoltParametersV1.sol"; import {BoltValidatorsV1} from "../../../src/contracts/BoltValidatorsV1.sol"; import {BoltManagerV1} from "../../../src/contracts/BoltManagerV1.sol"; +import {BoltManagerV2} from "../../../src/contracts/BoltManagerV2.sol"; import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; import {BoltEigenLayerMiddlewareV2} from "../../../src/contracts/BoltEigenLayerMiddlewareV2.sol"; import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; @@ -125,12 +126,37 @@ contract UpgradeBolt is Script { vm.startBroadcast(admin); Upgrades.upgradeProxy(deployments.boltValidators, upgradeTo, initBoltValidators, opts); - + vm.stopBroadcast(); console.log("BoltValidators proxy upgraded from %s to %s", opts.referenceContract, upgradeTo); } + function upgradeBoltManager() public { + address admin = msg.sender; + console.log("Upgrading BoltManager with admin", admin); + // TODO: Validate upgrades with Upgrades.validateUpgrade + + Options memory opts; + opts.unsafeSkipAllChecks = true; + opts.referenceContract = "BoltManagerV1.sol"; + string memory upgradeTo = "BoltManagerV2.sol"; + + Deployments memory deployments = _readDeployments(); + bytes memory initManager = abi.encodeCall( + BoltManagerV2.initializeV2, + (admin, deployments.boltParameters, deployments.boltValidators) + ); + + vm.startBroadcast(admin); + + Upgrades.upgradeProxy(deployments.boltManager, upgradeTo, initManager, opts); + + vm.stopBroadcast(); + + console.log("BoltManager proxy upgraded from %s to %s", opts.referenceContract, upgradeTo); + } + function _readDeployments() public view returns (Deployments memory) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); From eab494435e606064bb458b63e0f9c0a5e053efba Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Wed, 30 Oct 2024 10:12:00 +0100 Subject: [PATCH 226/272] chore(contracts): forge fmt --- bolt-contracts/script/holesky/admin/Upgrade.s.sol | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/bolt-contracts/script/holesky/admin/Upgrade.s.sol b/bolt-contracts/script/holesky/admin/Upgrade.s.sol index 2776e93ef..1dddab502 100644 --- a/bolt-contracts/script/holesky/admin/Upgrade.s.sol +++ b/bolt-contracts/script/holesky/admin/Upgrade.s.sol @@ -126,7 +126,7 @@ contract UpgradeBolt is Script { vm.startBroadcast(admin); Upgrades.upgradeProxy(deployments.boltValidators, upgradeTo, initBoltValidators, opts); - + vm.stopBroadcast(); console.log("BoltValidators proxy upgraded from %s to %s", opts.referenceContract, upgradeTo); @@ -141,17 +141,15 @@ contract UpgradeBolt is Script { opts.unsafeSkipAllChecks = true; opts.referenceContract = "BoltManagerV1.sol"; string memory upgradeTo = "BoltManagerV2.sol"; - + Deployments memory deployments = _readDeployments(); - bytes memory initManager = abi.encodeCall( - BoltManagerV2.initializeV2, - (admin, deployments.boltParameters, deployments.boltValidators) - ); - + bytes memory initManager = + abi.encodeCall(BoltManagerV2.initializeV2, (admin, deployments.boltParameters, deployments.boltValidators)); + vm.startBroadcast(admin); Upgrades.upgradeProxy(deployments.boltManager, upgradeTo, initManager, opts); - + vm.stopBroadcast(); console.log("BoltManager proxy upgraded from %s to %s", opts.referenceContract, upgradeTo); From 89c08a6463b13d843678b40a16f7c185605f261a Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Wed, 30 Oct 2024 10:46:30 +0100 Subject: [PATCH 227/272] feat(contracts): update tests --- .../test/BoltManager.EigenLayer.t.sol | 50 ++++++++----------- .../test/BoltManager.Symbiotic.t.sol | 39 ++++++++------- 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/bolt-contracts/test/BoltManager.EigenLayer.t.sol b/bolt-contracts/test/BoltManager.EigenLayer.t.sol index 0edfe0186..4b7f0e33b 100644 --- a/bolt-contracts/test/BoltManager.EigenLayer.t.sol +++ b/bolt-contracts/test/BoltManager.EigenLayer.t.sol @@ -3,13 +3,14 @@ pragma solidity 0.8.25; import {Test, console} from "forge-std/Test.sol"; -import {BoltValidatorsV1} from "../src/contracts/BoltValidatorsV1.sol"; -import {BoltManagerV1} from "../src/contracts/BoltManagerV1.sol"; +import {BoltValidatorsV2} from "../src/contracts/BoltValidatorsV2.sol"; +import {BoltManagerV2} from "../src/contracts/BoltManagerV2.sol"; import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; import {BoltEigenLayerMiddlewareV2} from "../src/contracts/BoltEigenLayerMiddlewareV2.sol"; import {BoltConfig} from "../src/lib/Config.sol"; -import {IBoltValidatorsV1} from "../src/interfaces/IBoltValidatorsV1.sol"; -import {IBoltManagerV1} from "../src/interfaces/IBoltManagerV1.sol"; +import {ValidatorsLib} from "../src/lib/ValidatorsLib.sol"; +import {IBoltValidatorsV2} from "../src/interfaces/IBoltValidatorsV2.sol"; +import {IBoltManagerV2} from "../src/interfaces/IBoltManagerV2.sol"; import {IBoltMiddlewareV1} from "../src/interfaces/IBoltMiddlewareV1.sol"; import {Utils} from "./Utils.sol"; @@ -28,12 +29,12 @@ contract BoltManagerEigenLayerTest is Test { uint48 public constant EPOCH_DURATION = 1 days; - BoltValidatorsV1 public validators; - BoltManagerV1 public manager; + BoltValidatorsV2 public validators; + BoltManagerV2 public manager; BoltEigenLayerMiddlewareV2 public middleware; EigenLayerDeployer public eigenLayerDeployer; - uint128 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; + uint32 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; address staker = makeAddr("staker"); address validator = makeAddr("validator"); @@ -70,9 +71,9 @@ contract BoltManagerEigenLayerTest is Test { ); // Deploy Bolt contracts - validators = new BoltValidatorsV1(); + validators = new BoltValidatorsV2(); validators.initialize(admin, address(parameters)); - manager = new BoltManagerV1(); + manager = new BoltManagerV2(); manager.initialize(admin, address(parameters), address(validators)); middleware = new BoltEigenLayerMiddlewareV2(); @@ -186,10 +187,11 @@ contract BoltManagerEigenLayerTest is Test { // pubkeys aren't checked, any point will be fine validatorPubkey = BLS12381.generatorG1(); + bytes20 pubkey = validators.hashPubkey(validatorPubkey); vm.prank(validator); - validators.registerValidatorUnsafe(validatorPubkey, PRECONF_MAX_GAS_LIMIT, operator); - assertEq(validators.getValidatorByPubkey(validatorPubkey).exists, true); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + assert(validators.getValidatorByPubkey(validatorPubkey).pubkeyHash != bytes20(0)); assertEq(validators.getValidatorByPubkey(validatorPubkey).authorizedOperator, operator); // 2. --- Operator and strategy registration into BoltManager (middleware) --- @@ -204,7 +206,7 @@ contract BoltManagerEigenLayerTest is Test { _eigenLayerOptInRoutine(); vm.prank(operator); middleware.deregisterOperator(); - vm.expectRevert(IBoltManagerV1.OperatorNotRegistered.selector); + vm.expectRevert(IBoltManagerV2.OperatorNotRegistered.selector); manager.isOperatorEnabled(operator); } @@ -220,9 +222,9 @@ contract BoltManagerEigenLayerTest is Test { function testProposerStatus() public { _eigenLayerOptInRoutine(); - bytes32 pubkeyHash = _pubkeyHash(validatorPubkey); + bytes20 pubkeyHash = validators.hashPubkey(validatorPubkey); - IBoltValidatorsV1.ProposerStatus memory status = manager.getProposerStatus(pubkeyHash); + IBoltManagerV2.ProposerStatus memory status = manager.getProposerStatus(pubkeyHash); assertEq(status.pubkeyHash, pubkeyHash); assertEq(status.operator, operator); assertEq(status.active, true); @@ -235,7 +237,7 @@ contract BoltManagerEigenLayerTest is Test { function testProposersLookaheadStatus() public { // This also opts in the operator which is needed _eigenLayerOptInRoutine(); - bytes32[] memory pubkeyHashes = new bytes32[](10); + bytes20[] memory pubkeyHashes = new bytes20[](10); // register 10 proposers with random pubkeys for (uint256 i = 0; i < 10; i++) { @@ -243,28 +245,20 @@ contract BoltManagerEigenLayerTest is Test { pubkey.x[0] = pubkey.x[0] + i + 2; pubkey.y[0] = pubkey.y[0] + i + 2; - pubkeyHashes[i] = _pubkeyHash(pubkey); - validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + pubkeyHashes[i] = validators.hashPubkey(pubkey); + validators.registerValidatorUnsafe(pubkeyHashes[i], PRECONF_MAX_GAS_LIMIT, operator); } - IBoltValidatorsV1.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); + IBoltManagerV2.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); assertEq(statuses.length, 10); } function testNonExistentProposerStatus() public { _eigenLayerOptInRoutine(); - bytes32 pubkeyHash = bytes32(uint256(1)); + bytes20 pubkeyHash = bytes20("0x1"); - vm.expectRevert(IBoltValidatorsV1.ValidatorDoesNotExist.selector); + vm.expectRevert(abi.encodeWithSelector(ValidatorsLib.ValidatorDoesNotExist.selector, pubkeyHash)); manager.getProposerStatus(pubkeyHash); } - - /// @notice Compute the hash of a BLS public key - function _pubkeyHash( - BLS12381.G1Point memory _pubkey - ) internal pure returns (bytes32) { - uint256[2] memory compressedPubKey = _pubkey.compress(); - return keccak256(abi.encodePacked(compressedPubKey)); - } } diff --git a/bolt-contracts/test/BoltManager.Symbiotic.t.sol b/bolt-contracts/test/BoltManager.Symbiotic.t.sol index 75b7a16ad..898802ee8 100644 --- a/bolt-contracts/test/BoltManager.Symbiotic.t.sol +++ b/bolt-contracts/test/BoltManager.Symbiotic.t.sol @@ -20,15 +20,17 @@ import {IDelegatorFactory} from "@symbiotic/interfaces/IDelegatorFactory.sol"; import {IMigratablesFactory} from "@symbiotic/interfaces/common/IMigratablesFactory.sol"; import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol"; -import {IBoltValidatorsV1} from "../src/interfaces/IBoltValidatorsV1.sol"; +import {IBoltValidatorsV2} from "../src/interfaces/IBoltValidatorsV2.sol"; import {IBoltMiddlewareV1} from "../src/interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV2} from "../src/interfaces/IBoltManagerV2.sol"; import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; -import {BoltValidatorsV1} from "../src/contracts/BoltValidatorsV1.sol"; -import {BoltManagerV1} from "../src/contracts/BoltManagerV1.sol"; +import {BoltValidatorsV2} from "../src/contracts/BoltValidatorsV2.sol"; +import {BoltManagerV2} from "../src/contracts/BoltManagerV2.sol"; import {BoltSymbioticMiddlewareV1} from "../src/contracts/BoltSymbioticMiddlewareV1.sol"; import {BLS12381} from "../src/lib/bls/BLS12381.sol"; import {BoltConfig} from "../src/lib/Config.sol"; +import {ValidatorsLib} from "../src/lib/ValidatorsLib.sol"; import {Utils} from "./Utils.sol"; import {SymbioticSetupFixture} from "./fixtures/SymbioticSetup.f.sol"; @@ -41,10 +43,10 @@ contract BoltManagerSymbioticTest is Test { uint48 public constant EPOCH_DURATION = 1 days; uint48 public constant SLASHING_WINDOW = 7 days; - uint128 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; + uint32 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; - BoltValidatorsV1 public validators; - BoltManagerV1 public manager; + BoltValidatorsV2 public validators; + BoltManagerV2 public manager; BoltSymbioticMiddlewareV1 public middleware; IVaultFactory public vaultFactory; @@ -174,9 +176,9 @@ contract BoltManagerSymbioticTest is Test { config.minimumOperatorStake ); - validators = new BoltValidatorsV1(); + validators = new BoltValidatorsV2(); validators.initialize(admin, address(parameters)); - manager = new BoltManagerV1(); + manager = new BoltManagerV2(); manager.initialize(admin, address(parameters), address(validators)); middleware = new BoltSymbioticMiddlewareV1(); @@ -213,10 +215,11 @@ contract BoltManagerSymbioticTest is Test { // pubkeys aren't checked, any point will be fine BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); vm.prank(validator); - validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); - assertEq(validators.getValidatorByPubkey(pubkey).exists, true); + validators.registerValidatorUnsafe(pubkeyHash, PRECONF_MAX_GAS_LIMIT, operator); + assert(validators.getValidatorByPubkey(pubkey).pubkeyHash != bytes20(0)); assertEq(validators.getValidatorByPubkey(pubkey).authorizedOperator, operator); // --- Register Operator in Symbiotic, opt-in network and vault --- @@ -327,12 +330,12 @@ contract BoltManagerSymbioticTest is Test { assertEq(networkRestakeDelegator.operatorNetworkShares(subnetwork, operator), 100); BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); - bytes32 pubkeyHash = _pubkeyHash(pubkey); + bytes20 pubkeyHash = validators.hashPubkey(pubkey); vm.warp(block.timestamp + EPOCH_DURATION * 2 + 1); assertEq(vault.currentEpoch(), 2); - IBoltValidatorsV1.ProposerStatus memory status = manager.getProposerStatus(pubkeyHash); + IBoltManagerV2.ProposerStatus memory status = manager.getProposerStatus(pubkeyHash); assertEq(status.pubkeyHash, pubkeyHash); assertEq(status.operator, operator); assertEq(status.active, true); @@ -345,7 +348,7 @@ contract BoltManagerSymbioticTest is Test { function testProposersLookaheadStatus() public { _symbioticOptInRoutine(); - bytes32[] memory pubkeyHashes = new bytes32[](10); + bytes20[] memory pubkeyHashes = new bytes20[](10); // register 10 proposers with random pubkeys for (uint256 i = 0; i < 10; i++) { @@ -353,23 +356,23 @@ contract BoltManagerSymbioticTest is Test { pubkey.x[0] = pubkey.x[0] + i + 2; pubkey.y[0] = pubkey.y[0] + i + 2; - pubkeyHashes[i] = _pubkeyHash(pubkey); - validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + pubkeyHashes[i] = validators.hashPubkey(pubkey); + validators.registerValidatorUnsafe(pubkeyHashes[i], PRECONF_MAX_GAS_LIMIT, operator); } vm.warp(block.timestamp + EPOCH_DURATION * 2 + 1); assertEq(vault.currentEpoch(), 2); - IBoltValidatorsV1.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); + IBoltManagerV2.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); assertEq(statuses.length, 10); } function testGetNonExistentProposerStatus() public { _symbioticOptInRoutine(); - bytes32 pubkeyHash = bytes32(uint256(1)); + bytes20 pubkeyHash = bytes20("0x1"); - vm.expectRevert(IBoltValidatorsV1.ValidatorDoesNotExist.selector); + vm.expectRevert(abi.encodeWithSelector(ValidatorsLib.ValidatorDoesNotExist.selector, pubkeyHash)); manager.getProposerStatus(pubkeyHash); } } From 7997319f19b6e6644028e40ba9799bbcc6d302ee Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Wed, 30 Oct 2024 11:12:24 +0100 Subject: [PATCH 228/272] docs(contracts): update deployments --- bolt-contracts/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bolt-contracts/README.md b/bolt-contracts/README.md index f335c8f52..7d65d90b1 100644 --- a/bolt-contracts/README.md +++ b/bolt-contracts/README.md @@ -57,7 +57,7 @@ The values of these parameters can also be found in [`parameters.json`](./config ## Validator Registration: `BoltValidators` -The [`BoltValidators`](./src/contracts/BoltValidatorsV1.sol) contract is the only point of entry for +The [`BoltValidators`](./src/contracts/BoltValidatorsV2.sol) contract is the only point of entry for validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. The registration process includes the following steps: @@ -76,7 +76,7 @@ will allow us to test the registration flow in a controlled environment. ## Bolt Network Entrypoint: `BoltManager` -The [`BoltManager`](./src/contracts/BoltManagerV1.sol) contract is a crucial component of Bolt that +The [`BoltManager`](./src/contracts/BoltManagerV2.sol) contract is a crucial component of Bolt that integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and coordination of validators, operators, and vaults within the Bolt network. @@ -156,10 +156,10 @@ request is valid according to Bolt Protocol rules. --> | Name | Address | Notes | | ---------------------- | -------------------- | ----------------------- | | [`BoltParametersV1`](./src/contracts/BoltParametersV1.sol) | [0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12](https://holesky.etherscan.io/address/0x20d1cf3A5BD5928dB3118b2CfEF54FDF9fda5c12) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | -| [`BoltValidatorsV1`](./src/contracts/BoltValidatorsV1.sol) | [0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8](https://holesky.etherscan.io/address/0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | -| [`BoltManagerV1`](./src/contracts/BoltManagerV1.sol) | [0x440202829b493F9FF43E730EB5e8379EEa3678CF](https://holesky.etherscan.io/address/0x440202829b493F9FF43E730EB5e8379EEa3678CF) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | -| [`BoltEigenLayerMiddlewareV1`](./src/contracts/BoltEigenLayerMiddlewareV1.sol) | [0xa632a3e652110Bb2901D5cE390685E6a9838Ca04](https://holesky.etherscan.io/address/0xa632a3e652110Bb2901D5cE390685E6a9838Ca04) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | -| [`BoltSymbioticMiddlewareV1`](./src/contracts/BoltSymbioticMiddlewareV1.sol) | [0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8](https://holesky.etherscan.io/address/0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | +| [`BoltValidatorsV2`](./src/contracts/BoltValidatorsV2.sol) | [0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8](https://holesky.etherscan.io/address/0x47D2DC1DE1eFEFA5e6944402f2eda3981D36a9c8) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | +| [`BoltManagerV2`](./src/contracts/BoltManagerV2.sol) | [0x440202829b493F9FF43E730EB5e8379EEa3678CF](https://holesky.etherscan.io/address/0x440202829b493F9FF43E730EB5e8379EEa3678CF) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | +| [`BoltEigenLayerMiddlewareV2`](./src/contracts/BoltEigenLayerMiddlewareV2.sol) | [0xa632a3e652110Bb2901D5cE390685E6a9838Ca04](https://holesky.etherscan.io/address/0xa632a3e652110Bb2901D5cE390685E6a9838Ca04) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | +| [`BoltSymbioticMiddlewareV2`](./src/contracts/BoltSymbioticMiddlewareV2.sol) | [0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8](https://holesky.etherscan.io/address/0x04f40d9CaE475E5BaA462acE53E5c58A0DD8D8e8) | Proxy: [`ERC1967Proxy@5.0.0`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v5.0/contracts/proxy/ERC1967/ERC1967Proxy.sol) | ## Testing From 0d6e5129e33719fcb1d2c27871cd047c57abf6ad Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Wed, 30 Oct 2024 11:13:56 +0100 Subject: [PATCH 229/272] chore(contracts): TODO comment --- bolt-contracts/src/interfaces/IBoltManagerV2.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/bolt-contracts/src/interfaces/IBoltManagerV2.sol b/bolt-contracts/src/interfaces/IBoltManagerV2.sol index c30801227..c267c2272 100644 --- a/bolt-contracts/src/interfaces/IBoltManagerV2.sol +++ b/bolt-contracts/src/interfaces/IBoltManagerV2.sol @@ -6,6 +6,7 @@ interface IBoltManagerV2 { error OperatorAlreadyRegistered(); error OperatorNotRegistered(); error UnauthorizedMiddleware(); + // TODO: remove in future upgrade (unused) error InactiveOperator(); /// @notice Proposer status info. From 2561f55c04cab141722d096da3e9fe87faa7aacf Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 30 Oct 2024 15:14:26 +0100 Subject: [PATCH 230/272] chore(holesky/docker): update bolt-mev-boost image --- testnets/holesky/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 397ce0e63..9828c0084 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -14,7 +14,7 @@ services: - bolt-default bolt-mev-boost-holesky: - image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc1 + image: ghcr.io/chainbound/bolt-mev-boost:v0.3.0-alpha.rc2 container_name: bolt-mev-boost-holesky restart: unless-stopped env_file: ./mev-boost.env From f0d2f88efe64d8706e5ee9439e9b16af0b8d8539 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 30 Oct 2024 15:23:23 +0100 Subject: [PATCH 231/272] chore(holesky): tag rc2 for bolt-sidecar --- testnets/holesky/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 9828c0084..e4c08c327 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -1,6 +1,6 @@ services: bolt-sidecar-holesky: - image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc1 + image: ghcr.io/chainbound/bolt-sidecar:v0.3.0-alpha.rc2 container_name: bolt-sidecar-holesky restart: unless-stopped ports: From 9023b10ce9e5d476cd2a8e1a3db4d5d44d808d5f Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Wed, 30 Oct 2024 17:38:39 +0100 Subject: [PATCH 232/272] chore(sidecar): error log --- bolt-sidecar/src/chain_io/manager.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bolt-sidecar/src/chain_io/manager.rs b/bolt-sidecar/src/chain_io/manager.rs index 8479e7bdb..30df2e575 100644 --- a/bolt-sidecar/src/chain_io/manager.rs +++ b/bolt-sidecar/src/chain_io/manager.rs @@ -1,4 +1,5 @@ use std::str::FromStr; +use tracing::error; use alloy::{ contract::Error as ContractError, @@ -80,6 +81,7 @@ impl BoltManager { } Err(error) => match error { ContractError::TransportError(TransportError::ErrorResp(err)) => { + error!("Error response from BoltManager contract: {:?}", err); let data = err.data.unwrap_or_default(); let data = data.get().trim_matches('"'); let data = Bytes::from_str(data)?; From 00cc3fb224c4d29f485b53babc10d8982f77542e Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Thu, 31 Oct 2024 12:02:35 +0100 Subject: [PATCH 233/272] feat(cli): basefee + priority fee flags --- bolt-cli/src/cli.rs | 8 ++++++++ bolt-cli/src/commands/send.rs | 25 ++++++++++++++++++++++--- bolt-cli/src/common/keystore.rs | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/bolt-cli/src/cli.rs b/bolt-cli/src/cli.rs index 646c0a546..c0e708c18 100644 --- a/bolt-cli/src/cli.rs +++ b/bolt-cli/src/cli.rs @@ -101,6 +101,14 @@ pub struct SendCommand { #[clap(long, env = "BLOB", default_value = "false")] pub blob: bool, + /// The max fee per gas in gwei. + #[clap(long, env = "MAX_FEE")] + pub max_fee: Option, + + /// The max priority fee per gas in gwei. + #[clap(long, env = "PRIORITY_FEE", default_value = "2")] + pub priority_fee: String, + /// If set, the transaction will target the devnet environment. /// This is only used in Kurtosis for internal testing purposes #[clap(long, hide = true, env = "DEVNET", default_value = "false")] diff --git a/bolt-cli/src/commands/send.rs b/bolt-cli/src/commands/send.rs index c2b7367eb..788894b70 100644 --- a/bolt-cli/src/commands/send.rs +++ b/bolt-cli/src/commands/send.rs @@ -4,7 +4,7 @@ use alloy::{ consensus::{BlobTransactionSidecar, SidecarBuilder, SimpleCoder, Transaction}, eips::eip2718::Encodable2718, network::{EthereumWallet, TransactionBuilder, TransactionBuilder4844}, - primitives::{keccak256, Address, B256, U256}, + primitives::{keccak256, utils::parse_units, Address, B256, U256}, providers::{ProviderBuilder, SendableTx}, rpc::types::TransactionRequest, signers::{local::PrivateKeySigner, Signer}, @@ -25,16 +25,29 @@ impl SendCommand { /// Run the `send` command. pub async fn run(self) -> Result<()> { let wallet: PrivateKeySigner = self.private_key.parse().wrap_err("invalid private key")?; + let max_fee = self.max_fee.as_ref().map(|fee| { + parse_units(fee, "gwei").expect("Correct unit").try_into().expect("Correct unit") + }); + + let priority_fee = parse_units(&self.priority_fee, "gwei") + .expect("Correct unit") + .try_into() + .expect("Correct unit"); if self.devnet { self.send_devnet_transaction(&wallet).await } else { - self.send_transaction(&wallet).await + self.send_transaction(&wallet, max_fee, priority_fee).await } } /// Send a transaction. - async fn send_transaction(self, wallet: &PrivateKeySigner) -> Result<()> { + async fn send_transaction( + self, + wallet: &PrivateKeySigner, + max_fee: Option, + priority_fee: u128, + ) -> Result<()> { let transaction_signer = EthereumWallet::from(wallet.clone()); let provider = ProviderBuilder::new() .with_recommended_fillers() @@ -73,6 +86,12 @@ impl SendCommand { for _ in 0..self.count { // generate a simple self-transfer of ETH let mut req = create_tx_request(wallet.address(), self.blob); + if let Some(max_fee) = max_fee { + req.set_max_fee_per_gas(max_fee); + } + + req.set_max_priority_fee_per_gas(priority_fee); + if let Some(next_nonce) = next_nonce { req.set_nonce(next_nonce); } diff --git a/bolt-cli/src/common/keystore.rs b/bolt-cli/src/common/keystore.rs index 464a1e5cd..76e8ce061 100644 --- a/bolt-cli/src/common/keystore.rs +++ b/bolt-cli/src/common/keystore.rs @@ -51,7 +51,7 @@ impl KeystoreSecret { /// Load the keystore passwords from a directory containing individual password files. pub fn from_directory(root_dir: &str) -> Result { let mut secrets = HashMap::new(); - for entry in fs::read_dir(&root_dir) + for entry in fs::read_dir(root_dir) .wrap_err(format!("failed to read secrets directory. path: {}", &root_dir))? { let entry = entry.wrap_err("Failed to read secrets directory entry")?; From 9e385fa822ef4ae378516d5cdc4aa8c69e5871fb Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Thu, 31 Oct 2024 14:00:27 +0100 Subject: [PATCH 234/272] wip: logs --- bolt-sidecar/src/driver.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 69ca91b7b..1f93f603d 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -262,9 +262,9 @@ impl SidecarDriver { const BOLT: &str = r#" ██████╗ ██████╗ ██╗ ████████╗ ██╔══██╗██╔═══██╗██║ ╚══██╔══╝ - ██████╔╝██║ ██║██║ ██║ - ██╔══██╗██║ ██║██║ ██║ - ██████╔╝╚██████╔╝███████╗██║ + ██████╔╝██║ ██║██║ ██║ + ██╔══██╗██║ ██║██║ ██║ + ██████╔╝╚██████╔╝███████╗██║ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ "#; println!("{BOLT}"); @@ -328,6 +328,10 @@ impl SidecarDriver { let delegatees = self.constraints_client.find_delegatees(&validator_pubkey); let available_pubkeys = self.constraint_signer.available_pubkeys(); + debug!(?delegatees); + debug!(?available_pubkeys); + debug!(?validator_pubkey); + let Some(pubkey) = pick_public_key(validator_pubkey, available_pubkeys, delegatees) else { error!(%target_slot, "No available public key to sign constraints with"); let _ = response.send(Err(CommitmentError::Internal)); From 9a11980216b141b6ae70a26697bb39766271657b Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 31 Oct 2024 15:44:06 +0100 Subject: [PATCH 235/272] chore(sidecar): remove extra logs --- bolt-sidecar/src/driver.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 6fbe4381a..14b219fd2 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -328,10 +328,6 @@ impl SidecarDriver { let delegatees = self.constraints_client.find_delegatees(&validator_pubkey); let available_pubkeys = self.constraint_signer.available_pubkeys(); - debug!(?delegatees); - debug!(?available_pubkeys); - debug!(?validator_pubkey); - let Some(pubkey) = pick_public_key(validator_pubkey, available_pubkeys, delegatees) else { error!(%target_slot, "No available public key to sign constraints with"); let _ = response.send(Err(CommitmentError::Internal)); From 9307f14bc42ee2c06a11945a261ee06841335514 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:49:05 +0100 Subject: [PATCH 236/272] chore: update send command impl --- bolt-cli/src/cli.rs | 4 ++-- bolt-cli/src/commands/send.rs | 29 +++++++++-------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/bolt-cli/src/cli.rs b/bolt-cli/src/cli.rs index c0e708c18..57cd595e8 100644 --- a/bolt-cli/src/cli.rs +++ b/bolt-cli/src/cli.rs @@ -103,11 +103,11 @@ pub struct SendCommand { /// The max fee per gas in gwei. #[clap(long, env = "MAX_FEE")] - pub max_fee: Option, + pub max_fee: Option, /// The max priority fee per gas in gwei. #[clap(long, env = "PRIORITY_FEE", default_value = "2")] - pub priority_fee: String, + pub priority_fee: u128, /// If set, the transaction will target the devnet environment. /// This is only used in Kurtosis for internal testing purposes diff --git a/bolt-cli/src/commands/send.rs b/bolt-cli/src/commands/send.rs index 788894b70..fdbdb1bd6 100644 --- a/bolt-cli/src/commands/send.rs +++ b/bolt-cli/src/commands/send.rs @@ -1,10 +1,12 @@ use std::time::Duration; use alloy::{ - consensus::{BlobTransactionSidecar, SidecarBuilder, SimpleCoder, Transaction}, + consensus::{ + constants::GWEI_TO_WEI, BlobTransactionSidecar, SidecarBuilder, SimpleCoder, Transaction, + }, eips::eip2718::Encodable2718, network::{EthereumWallet, TransactionBuilder, TransactionBuilder4844}, - primitives::{keccak256, utils::parse_units, Address, B256, U256}, + primitives::{keccak256, Address, B256, U256}, providers::{ProviderBuilder, SendableTx}, rpc::types::TransactionRequest, signers::{local::PrivateKeySigner, Signer}, @@ -25,29 +27,16 @@ impl SendCommand { /// Run the `send` command. pub async fn run(self) -> Result<()> { let wallet: PrivateKeySigner = self.private_key.parse().wrap_err("invalid private key")?; - let max_fee = self.max_fee.as_ref().map(|fee| { - parse_units(fee, "gwei").expect("Correct unit").try_into().expect("Correct unit") - }); - - let priority_fee = parse_units(&self.priority_fee, "gwei") - .expect("Correct unit") - .try_into() - .expect("Correct unit"); if self.devnet { self.send_devnet_transaction(&wallet).await } else { - self.send_transaction(&wallet, max_fee, priority_fee).await + self.send_transaction(&wallet).await } } /// Send a transaction. - async fn send_transaction( - self, - wallet: &PrivateKeySigner, - max_fee: Option, - priority_fee: u128, - ) -> Result<()> { + async fn send_transaction(self, wallet: &PrivateKeySigner) -> Result<()> { let transaction_signer = EthereumWallet::from(wallet.clone()); let provider = ProviderBuilder::new() .with_recommended_fillers() @@ -86,11 +75,11 @@ impl SendCommand { for _ in 0..self.count { // generate a simple self-transfer of ETH let mut req = create_tx_request(wallet.address(), self.blob); - if let Some(max_fee) = max_fee { - req.set_max_fee_per_gas(max_fee); + if let Some(max_fee) = self.max_fee { + req.set_max_fee_per_gas(max_fee * GWEI_TO_WEI as u128); } - req.set_max_priority_fee_per_gas(priority_fee); + req.set_max_priority_fee_per_gas(self.priority_fee * GWEI_TO_WEI as u128); if let Some(next_nonce) = next_nonce { req.set_nonce(next_nonce); From 0f660f53bcd806bf447abb58ee49fad658c61638 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:25:01 +0100 Subject: [PATCH 237/272] chore: cleanup libraries, rm unused code --- .../script/holesky/admin/Deploy.s.sol | 2 +- .../script/holesky/admin/Upgrade.s.sol | 2 +- .../src/lib/{Config.sol => BoltConfig.sol} | 0 bolt-contracts/src/lib/bls/BLS12381.sol | 14 +- .../src/lib/bls/BLSSignatureVerifier.sol | 10 +- bolt-contracts/src/lib/ssz/SSZ.sol | 166 ------------------ bolt-contracts/src/lib/ssz/SSZContainers.sol | 101 ----------- bolt-contracts/test/BoltChallenger.t.sol | 2 +- .../test/BoltManager.EigenLayer.t.sol | 2 +- .../test/BoltManager.Symbiotic.t.sol | 2 +- bolt-contracts/test/BoltValidators.t.sol | 2 +- bolt-contracts/test/BoltValidatorsV2.t.sol | 2 +- bolt-contracts/test/Utils.sol | 2 +- 13 files changed, 15 insertions(+), 292 deletions(-) rename bolt-contracts/src/lib/{Config.sol => BoltConfig.sol} (100%) delete mode 100644 bolt-contracts/src/lib/ssz/SSZ.sol delete mode 100644 bolt-contracts/src/lib/ssz/SSZContainers.sol diff --git a/bolt-contracts/script/holesky/admin/Deploy.s.sol b/bolt-contracts/script/holesky/admin/Deploy.s.sol index 554791237..447c077cd 100644 --- a/bolt-contracts/script/holesky/admin/Deploy.s.sol +++ b/bolt-contracts/script/holesky/admin/Deploy.s.sol @@ -15,7 +15,7 @@ import {BoltValidatorsV1} from "../../../src/contracts/BoltValidatorsV1.sol"; import {BoltManagerV1} from "../../../src/contracts/BoltManagerV1.sol"; import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; -import {BoltConfig} from "../../../src/lib/Config.sol"; +import {BoltConfig} from "../../../src/lib/BoltConfig.sol"; /// @notice Script to deploy the Bolt contracts. contract DeployBolt is Script { diff --git a/bolt-contracts/script/holesky/admin/Upgrade.s.sol b/bolt-contracts/script/holesky/admin/Upgrade.s.sol index 1dddab502..e37533803 100644 --- a/bolt-contracts/script/holesky/admin/Upgrade.s.sol +++ b/bolt-contracts/script/holesky/admin/Upgrade.s.sol @@ -16,7 +16,7 @@ import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMid import {BoltSymbioticMiddlewareV2} from "../../../src/contracts/BoltSymbioticMiddlewareV2.sol"; import {BoltValidatorsV1} from "../../../src/contracts/BoltValidatorsV1.sol"; import {BoltValidatorsV2} from "../../../src/contracts/BoltValidatorsV2.sol"; -import {BoltConfig} from "../../../src/lib/Config.sol"; +import {BoltConfig} from "../../../src/lib/BoltConfig.sol"; contract UpgradeBolt is Script { struct Deployments { diff --git a/bolt-contracts/src/lib/Config.sol b/bolt-contracts/src/lib/BoltConfig.sol similarity index 100% rename from bolt-contracts/src/lib/Config.sol rename to bolt-contracts/src/lib/BoltConfig.sol diff --git a/bolt-contracts/src/lib/bls/BLS12381.sol b/bolt-contracts/src/lib/bls/BLS12381.sol index 635302213..72fefdd93 100644 --- a/bolt-contracts/src/lib/bls/BLS12381.sol +++ b/bolt-contracts/src/lib/bls/BLS12381.sol @@ -1,13 +1,7 @@ -// ====================================================== -// Code below is copied from: -// https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/caf9fbbde0dd84947af5a7b26610ffd38525d932/SmartContracts/src/libraries/BLS12381.sol -// -// If/when a license will be added to the original code, it will be added here as well. -// ====================================================== - -// SPDX-License-Identifier: UNLICENSED -// Functions in this library have been adapted from: -// https://github.com/ethyla/bls12-381-hash-to-curve/blob/main/src/HashToCurve.sol +// SPDX-License-Identifier: MIT +// +// This library was copied from: https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/b3d7243fc948205e0783e7720cfcc38bf70496e1/SmartContracts/src/libraries/BLS12381.sol +// which was adapted from: https://github.com/ethyla/bls12-381-hash-to-curve/blob/main/src/HashToCurve.sol pragma solidity 0.8.25; library BLS12381 { diff --git a/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol b/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol index 43344e733..eded20e25 100644 --- a/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol +++ b/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol @@ -1,11 +1,7 @@ -// ====================================================== -// Code below is copied from: -// https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/caf9fbbde0dd84947af5a7b26610ffd38525d932/SmartContracts/src/libraries/BLS12381.sol -// -// If/when a license will be added to the original code, it will be added here as well. -// ====================================================== - // SPDX-License-Identifier: UNLICENSED +// +// This library was copied from: https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/caf9fbbde0dd84947af5a7b26610ffd38525d932/SmartContracts/src/avs/utils/BLSSignatureChecker.sol +// (If/when a license will be added to the original library, it will be added here as well) pragma solidity 0.8.25; import {BLS12381} from "./BLS12381.sol"; diff --git a/bolt-contracts/src/lib/ssz/SSZ.sol b/bolt-contracts/src/lib/ssz/SSZ.sol deleted file mode 100644 index 754864277..000000000 --- a/bolt-contracts/src/lib/ssz/SSZ.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -/// @title SSZ library -/// @dev inspired by https://github.com/succinctlabs/telepathy-contracts/blob/main/src/libraries/SimpleSerialize.sol -/// @dev and https://github.com/madlabman/eip-4788-proof/blob/master/src/SSZ.sol -library SSZ { - /// @notice sha256 precompile address. - uint256 public constant SHA256_PRECOMPILE = 0x02; - - error BranchHasMissingItem(); - error BranchHasExtraItem(); - error Log2Undefined(); - - /// @notice Modified version of `verify` from `MerkleProofLib` to support generalized indices and sha256 precompile. - /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. - /// @dev from https://github.com/madlabman/eip-4788-proof/blob/master/src/SSZ.sol - function _verifyProof( - bytes32[] calldata proof, - bytes32 root, - bytes32 leaf, - uint256 index - ) internal view returns (bool isValid) { - /// @solidity memory-safe-assembly - assembly { - if proof.length { - // Left shift by 5 is equivalent to multiplying by 0x20. - let end := add(proof.offset, shl(5, proof.length)) - // Initialize `offset` to the offset of `proof` in the calldata. - let offset := proof.offset - // Iterate over proof elements to compute root hash. - for {} 1 {} { - // Slot of `leaf` in scratch space. - // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl(5, and(index, 1)) - index := shr(1, index) - if iszero(index) { - // revert BranchHasExtraItem() - mstore(0x00, 0x5849603f) - revert(0x1c, 0x04) - } - // Store elements to hash contiguously in scratch space. - // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. - mstore(scratch, leaf) - mstore(xor(scratch, 0x20), calldataload(offset)) - - // Call sha256 precompile - let result := staticcall(gas(), SHA256_PRECOMPILE, 0x00, 0x40, 0x00, 0x20) - - if eq(result, 0) { revert(0, 0) } - - // Reuse `leaf` to store the hash to reduce stack operations. - leaf := mload(0x00) - offset := add(offset, 0x20) - if iszero(lt(offset, end)) { break } - } - } - // index != 1 - if gt(sub(index, 1), 0) { - // revert BranchHasMissingItem() - mstore(0x00, 0x1b6661c3) - revert(0x1c, 0x04) - } - isValid := eq(leaf, root) - } - } - - function _hashTreeRoot(bytes32[] memory chunks, uint8 count) internal view returns (bytes32 root) { - /// @solidity memory-safe-assembly - assembly { - // Loop over levels - for {} 1 {} { - // Loop over chunks at the given depth - - // Initialize `offset` to the offset of `proof` elements in memory. - let target := chunks - let source := chunks - let end := add(source, shl(5, count)) - - for {} 1 {} { - // Read next two hashes to hash - mstore(0x00, mload(source)) - mstore(0x20, mload(add(source, 0x20))) - - // Call sha256 precompile - let result := staticcall(gas(), SHA256_PRECOMPILE, 0x00, 0x40, 0x00, 0x20) - - if eq(result, 0) { revert(0, 0) } - - // Store the resulting hash at the target location - mstore(target, mload(0x00)) - - // Advance the pointers - target := add(target, 0x20) - source := add(source, 0x40) - - if iszero(lt(source, end)) { break } - } - - count := shr(1, count) - if eq(count, 1) { - root := mload(0x00) - break - } - } - } - } - - /// @notice Converts a value to a little-endian byte array - /// @dev from https://github.com/succinctlabs/telepathy-contracts/blob/main/src/libraries/SimpleSerialize.sol - function _toLittleEndian( - uint256 v - ) internal pure returns (bytes32) { - v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) - | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); - v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) - | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16); - v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32) - | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32); - v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64) - | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64); - v = (v >> 128) | (v << 128); - return bytes32(v); - } - - /// @notice Converts a boolean to a little-endian byte array - function _toLittleEndian( - bool v - ) internal pure returns (bytes32) { - return bytes32(v ? 1 << 248 : 0); - } - - /// @notice Log base 2 of a number - /// @dev From solady FixedPointMath - /// @dev Equivalent to computing the index of the most significant bit (MSB) of `x`. - function _log2( - uint256 x - ) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - if iszero(x) { - // Store the function selector of `Log2Undefined()`. - mstore(0x00, 0x5be3aa5c) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - - // For the remaining 32 bits, use a De Bruijn lookup. - // See: https://graphics.stanford.edu/~seander/bithacks.html - x := shr(r, x) - x := or(x, shr(1, x)) - x := or(x, shr(2, x)) - x := or(x, shr(4, x)) - x := or(x, shr(8, x)) - x := or(x, shr(16, x)) - - // forgefmt: disable-next-item - r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))), - 0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)) - } - } -} diff --git a/bolt-contracts/src/lib/ssz/SSZContainers.sol b/bolt-contracts/src/lib/ssz/SSZContainers.sol deleted file mode 100644 index 7d4302c7f..000000000 --- a/bolt-contracts/src/lib/ssz/SSZContainers.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import {SSZ} from "./SSZ.sol"; - -library SSZContainers { - /// @notice a Validator SSZ container - /// @dev As defined in https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator - struct Validator { - bytes pubkey; - bytes32 withdrawalCredentials; - uint64 effectiveBalance; - bool slashed; - uint64 activationEligibilityEpoch; - uint64 activationEpoch; - uint64 exitEpoch; - uint64 withdrawableEpoch; - } - - /// @notice a Beacon block header SSZ container - /// @dev As defined in https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader - struct BeaconBlockHeader { - uint64 slot; - uint64 proposerIndex; - bytes32 parentRoot; - bytes32 stateRoot; - bytes32 bodyRoot; - } - - /// @notice Computes the hash tree root of a validator SSZ container - function _validatorHashTreeRoot( - Validator memory validator - ) internal view returns (bytes32) { - bytes32 pubkeyRoot; - uint256 _sha256 = SSZ.SHA256_PRECOMPILE; - - assembly { - // Dynamic data types such as bytes are stored at the specified offset. - let offset := mload(validator) - // Call sha256 precompile with the pubkey pointer - let result := staticcall(gas(), _sha256, add(offset, 32), 0x40, 0x00, 0x20) - // Precompile returns no data on OutOfGas error. - if eq(result, 0) { revert(0, 0) } - - pubkeyRoot := mload(0x00) - } - - bytes32[] memory nodes = new bytes32[](8); - nodes[0] = pubkeyRoot; - nodes[1] = validator.withdrawalCredentials; - nodes[2] = SSZ._toLittleEndian(validator.effectiveBalance); - nodes[3] = SSZ._toLittleEndian(validator.slashed); - nodes[4] = SSZ._toLittleEndian(validator.activationEligibilityEpoch); - nodes[5] = SSZ._toLittleEndian(validator.activationEpoch); - nodes[6] = SSZ._toLittleEndian(validator.exitEpoch); - nodes[7] = SSZ._toLittleEndian(validator.withdrawableEpoch); - - return SSZ._hashTreeRoot(nodes, 8); - } - - /// @notice Computes the hash tree root of a beacon block header SSZ container - function _beaconHeaderHashTreeRoot( - BeaconBlockHeader memory header - ) internal view returns (bytes32) { - bytes32[] memory nodes = new bytes32[](8); - nodes[0] = SSZ._toLittleEndian(header.slot); - nodes[1] = SSZ._toLittleEndian(header.proposerIndex); - nodes[2] = header.parentRoot; - nodes[3] = header.stateRoot; - nodes[4] = header.bodyRoot; - nodes[5] = bytes32(0); - nodes[6] = bytes32(0); - nodes[7] = bytes32(0); - - return SSZ._hashTreeRoot(nodes, 8); - } - - /// @notice Computes the hash tree root of an RLP-encoded signed transaction (raw bytes) - function _transactionHashTreeRoot( - bytes memory transaction - ) internal view returns (bytes32) { - uint256 chunkCount = (transaction.length + 31) / 32; - bytes32[] memory nodes = new bytes32[](chunkCount); - - // TODO: this is most likely wrong, needs fix according to ssz specs - for (uint256 i = 0; i < chunkCount; i++) { - uint256 start = i * 32; - uint256 end = start + 32; - if (end > transaction.length) { - end = transaction.length; - } - bytes memory chunk = new bytes(32); - for (uint256 j = start; j < end; j++) { - chunk[j - start] = transaction[j]; - } - nodes[i] = keccak256(chunk); - } - - return SSZ._hashTreeRoot(nodes, uint8(chunkCount)); - } -} diff --git a/bolt-contracts/test/BoltChallenger.t.sol b/bolt-contracts/test/BoltChallenger.t.sol index b8dd716bc..aa28c0f3a 100644 --- a/bolt-contracts/test/BoltChallenger.t.sol +++ b/bolt-contracts/test/BoltChallenger.t.sol @@ -8,7 +8,7 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; import {BoltChallengerV1} from "../src/contracts/BoltChallengerV1.sol"; -import {BoltConfig} from "../src/lib/Config.sol"; +import {BoltConfig} from "../src/lib/BoltConfig.sol"; import {IBoltChallengerV1} from "../src/interfaces/IBoltChallengerV1.sol"; import {RLPReader} from "../src/lib/rlp/RLPReader.sol"; import {RLPWriter} from "../src/lib/rlp/RLPWriter.sol"; diff --git a/bolt-contracts/test/BoltManager.EigenLayer.t.sol b/bolt-contracts/test/BoltManager.EigenLayer.t.sol index 4b7f0e33b..52f85d7c9 100644 --- a/bolt-contracts/test/BoltManager.EigenLayer.t.sol +++ b/bolt-contracts/test/BoltManager.EigenLayer.t.sol @@ -7,7 +7,7 @@ import {BoltValidatorsV2} from "../src/contracts/BoltValidatorsV2.sol"; import {BoltManagerV2} from "../src/contracts/BoltManagerV2.sol"; import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; import {BoltEigenLayerMiddlewareV2} from "../src/contracts/BoltEigenLayerMiddlewareV2.sol"; -import {BoltConfig} from "../src/lib/Config.sol"; +import {BoltConfig} from "../src/lib/BoltConfig.sol"; import {ValidatorsLib} from "../src/lib/ValidatorsLib.sol"; import {IBoltValidatorsV2} from "../src/interfaces/IBoltValidatorsV2.sol"; import {IBoltManagerV2} from "../src/interfaces/IBoltManagerV2.sol"; diff --git a/bolt-contracts/test/BoltManager.Symbiotic.t.sol b/bolt-contracts/test/BoltManager.Symbiotic.t.sol index 898802ee8..80b22f978 100644 --- a/bolt-contracts/test/BoltManager.Symbiotic.t.sol +++ b/bolt-contracts/test/BoltManager.Symbiotic.t.sol @@ -29,7 +29,7 @@ import {BoltValidatorsV2} from "../src/contracts/BoltValidatorsV2.sol"; import {BoltManagerV2} from "../src/contracts/BoltManagerV2.sol"; import {BoltSymbioticMiddlewareV1} from "../src/contracts/BoltSymbioticMiddlewareV1.sol"; import {BLS12381} from "../src/lib/bls/BLS12381.sol"; -import {BoltConfig} from "../src/lib/Config.sol"; +import {BoltConfig} from "../src/lib/BoltConfig.sol"; import {ValidatorsLib} from "../src/lib/ValidatorsLib.sol"; import {Utils} from "./Utils.sol"; diff --git a/bolt-contracts/test/BoltValidators.t.sol b/bolt-contracts/test/BoltValidators.t.sol index 17f5187e7..75ce0530d 100644 --- a/bolt-contracts/test/BoltValidators.t.sol +++ b/bolt-contracts/test/BoltValidators.t.sol @@ -7,7 +7,7 @@ import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; import {BoltValidatorsV1} from "../src/contracts/BoltValidatorsV1.sol"; import {IBoltValidatorsV1} from "../src/interfaces/IBoltValidatorsV1.sol"; import {BLS12381} from "../src/lib/bls/BLS12381.sol"; -import {BoltConfig} from "../src/lib/Config.sol"; +import {BoltConfig} from "../src/lib/BoltConfig.sol"; import {Utils} from "./Utils.sol"; contract BoltValidatorsTest is Test { diff --git a/bolt-contracts/test/BoltValidatorsV2.t.sol b/bolt-contracts/test/BoltValidatorsV2.t.sol index 81bac376e..9dc1ece6f 100644 --- a/bolt-contracts/test/BoltValidatorsV2.t.sol +++ b/bolt-contracts/test/BoltValidatorsV2.t.sol @@ -7,7 +7,7 @@ import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; import {BoltValidatorsV2} from "../src/contracts/BoltValidatorsV2.sol"; import {IBoltValidatorsV2} from "../src/interfaces/IBoltValidatorsV2.sol"; import {BLS12381} from "../src/lib/bls/BLS12381.sol"; -import {BoltConfig} from "../src/lib/Config.sol"; +import {BoltConfig} from "../src/lib/BoltConfig.sol"; import {Utils} from "./Utils.sol"; contract BoltValidatorsV2Test is Test { diff --git a/bolt-contracts/test/Utils.sol b/bolt-contracts/test/Utils.sol index 731d1de18..cc31fb0e2 100644 --- a/bolt-contracts/test/Utils.sol +++ b/bolt-contracts/test/Utils.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; import {Test, console} from "forge-std/Test.sol"; -import {BoltConfig} from "../src/lib/Config.sol"; +import {BoltConfig} from "../src/lib/BoltConfig.sol"; contract Utils is Test { function readParameters() public view returns (BoltConfig.Parameters memory) { From 1fa53fecafabdfafc84ce0af72710b115e64756e Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:29:27 +0100 Subject: [PATCH 238/272] chore: fmt --- bolt-contracts/src/lib/bls/BLS12381.sol | 2 +- bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-contracts/src/lib/bls/BLS12381.sol b/bolt-contracts/src/lib/bls/BLS12381.sol index 72fefdd93..7288ea10d 100644 --- a/bolt-contracts/src/lib/bls/BLS12381.sol +++ b/bolt-contracts/src/lib/bls/BLS12381.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// +// // This library was copied from: https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/b3d7243fc948205e0783e7720cfcc38bf70496e1/SmartContracts/src/libraries/BLS12381.sol // which was adapted from: https://github.com/ethyla/bls12-381-hash-to-curve/blob/main/src/HashToCurve.sol pragma solidity 0.8.25; diff --git a/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol b/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol index eded20e25..ef2029cbb 100644 --- a/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol +++ b/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -// +// // This library was copied from: https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/caf9fbbde0dd84947af5a7b26610ffd38525d932/SmartContracts/src/avs/utils/BLSSignatureChecker.sol // (If/when a license will be added to the original library, it will be added here as well) pragma solidity 0.8.25; From 0ba0f1fcf1810ef03b5523e20f22db6d35b79661 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 5 Nov 2024 15:17:57 +0100 Subject: [PATCH 239/272] fix(holesky/docs): broken symbiotic docs link --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 476329d6e..8e6c50060 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -104,7 +104,7 @@ requests. What follows is a quick overview of the required steps. First you'll need to deposit some collateral in the form of whitelisted ETH derivative tokens that need to be restaked in either the Symbiotic or EigenLayer restaking protocols. Bolt is compatible with the following ETH derivative tokens on Holesky: -- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments#vaults) +- [Symbiotic Vaults](https://docs.symbiotic.fi/deployments/current#vaults) - [`wstETH`](https://holesky.etherscan.io/address/0xc79c533a77691641d52ebD5e87E51dCbCaeb0D78) - [`rETH`](https://holesky.etherscan.io/address/0xe5708788c90e971f73D928b7c5A8FD09137010e0) - [`stETH`](https://holesky.etherscan.io/address/0x11c5b9A9cd8269580aDDbeE38857eE451c1CFacd) From 52b40abd5dca17cfac2d18a1e3b5959b661fda41 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 5 Nov 2024 12:39:06 +0100 Subject: [PATCH 240/272] chore(holesky/docker): mount volumes on predefined paths --- testnets/holesky/docker-compose.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index e4c08c327..0219e0670 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -8,8 +8,19 @@ services: - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar env_file: ./bolt-sidecar.env + environment: + # The "+" syntax replaces the environment variable with the alternate valuee only if set + # Reference: https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax + # + # NOTE: These enviroment variables take precedence over the ones provided via `env_file`. + # Reference: https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/ + BOLT_SIDECAR_DELEGATIONS_PATH: "${BOLT_SIDECAR_DELEGATIONS_PATH+/etc/delegations.json}" + BOLT_SIDECAR_KEYSTORE_PATH: "${BOLT_SIDECAR_KEYSTORE_PATH+/etc/keystore}" + BOLT_SIDECAR_KEYSTORE_SECRETS_PATH: "${BOLT_SIDECAR_KEYSTORE_SECRETS_PATH+/etc/secrets}" volumes: - - "./delegations.json:${BOLT_SIDECAR_DELEGATIONS_PATH:-/etc/delegations.json}" + - ${BOLT_SIDECAR_DELEGATIONS_PATH:-/dev/null:/etc/delegations.json} + - ${BOLT_SIDECAR_KEYSTORE_PATH:-/dev/null:/etc/keystores} + - ${BOLT_SIDECAR_KEYSTORE_SECRETS_PATH:-/dev/null:/etc/secrets} networks: - bolt-default From d2101e6b59fa3082e961e6674ec9c6cff8302a8d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 5 Nov 2024 12:39:26 +0100 Subject: [PATCH 241/272] chore(holesky): remove defaults delegations.json file, no more needed --- testnets/holesky/delegations.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 testnets/holesky/delegations.json diff --git a/testnets/holesky/delegations.json b/testnets/holesky/delegations.json deleted file mode 100644 index fe51488c7..000000000 --- a/testnets/holesky/delegations.json +++ /dev/null @@ -1 +0,0 @@ -[] From 0679a49b1b69b703d475abef0efea946152b1c6f Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 5 Nov 2024 12:39:58 +0100 Subject: [PATCH 242/272] chore(holesky/docs): update docs reflecting docker compose changes; remove last references to deprecated TOML config file --- testnets/holesky/README.md | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 8e6c50060..e5d19bf57 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -456,10 +456,6 @@ containing the necessary environment variables: Signing](#delegations-and-signing-options-for-native-and-docker-compose-mode) section of this guide. - If you've generated a `delegation.json` file using the Bolt CLI please - place it in the `testnets/holesky` directory by replacing the existing empty - one. - 2. **MEV-Boost Configuration:** Change directory to the `testnets/holesky` folder if you haven't already and @@ -937,11 +933,6 @@ where: You can also find more information about the available key source options by running `bolt delegate --help`. -> [!TIP] -> If you're using the Docker Compose Mode please don't set the `--out` flag and -> provide `delegations_path = /etc/delegations.json` in the `bolt-sidecar.toml` -> file. - Here you can see usage examples for each key source:
@@ -1061,10 +1052,9 @@ themselves don't need a particular file extension. --- Now that you have generated the delegation messages you can provide them to the -sidecar using the `--delegations-path` flag (see the -[options](#command-line-options) section). When doing so the sidecar will check if -they're indeed valid messages and will keep in memory the association between -the delegator and the delegatee. +sidecar using the `--delegations-path` flag (or `BOLT_SIDECAR_DELEGATIONS_PATH` +env). When doing so the sidecar will check if they're indeed valid messages and +will keep in memory the association between the delegator and the delegatee. However in order to sign the commitments you still need to provide the signing key of the delegatee. There are two ways to do so, as explored in the sections @@ -1074,8 +1064,8 @@ below. As you can see in the [command line options](#command-line-options) section you can pass directly the private key as a hex-encoded string to the Bolt sidecar -using the `--constraint-private-key` flag (or `constraint_private_key` in the -TOML file). +using the `--constraint-private-key` flag (or +`BOLT_SIDECAR_CONSTRAINT_PRIVATE_KEY` env). This is the simplest setup and can be used in case if all the delegations messages point to the same delegatee or if you're running the sidecar with a single active @@ -1085,10 +1075,11 @@ validator. The Bolt sidecar supports [ERC-2335](https://eips.ethereum.org/EIPS/eip-2335) keystores for loading signing keypairs. In order to use them you need to provide -the `--keystore-path` pointing to the folder containing the keystore files and -the `--keystore-password` or `keystore-secrets-path` flag pointing to the folder -containing the password file (in the TOML configuration file these are the -`keystore_path`, `keystore_password` and `keystore_secrets_path` respectively). +the `--keystore-path` (`BOLT_SIDECAR_KEYSTORE_PATH` env) pointing to the folder +containing the keystore files and the `--keystore-password` or +`keystore-secrets-path` flag (`BOLT_SIDECAR_KEYSTORE_PASSWORD` or +`BOLT_SIDECAR_SECRETS_PATH` env respectively) pointing to the folder containing +the password file. Both the `keys` and `passwords` folders must adhere to the structure outlined in the [Installation and Usage](#installation-and-usage) section. @@ -1104,9 +1095,10 @@ However if you're already running a PBS sidecar like [MEV-Boost](https://boost.flashbots.net/) on the same machine then you can avoid the restart by following this steps when starting the Bolt sidecar: -1. Set the `--constraints-proxy-port` flag (the `constraints_proxy_port` option - in the TOML file) to the port previously occupied by MEV-Boost. +1. Set the `--constraints-proxy-port` flag (or + `BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT` env) to the port previously occupied by + MEV-Boost. 2. Build the Bolt MEV-Boost fork binary or pull the Docker image and start it using another port -3. Set the `--constraints-api-url` flag (or the `constraints_api_url` in the - TOML file) to point to the Bolt MEV-Boost instance. +3. Set the `--constraints-api-url` flag (or `BOLT_SIDECAR_CONSTRAINTS_API_URL` + env) to point to the Bolt MEV-Boost instance. From 114bd2515dbc53a748df555a953a03dbcbbfc5b4 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 5 Nov 2024 15:49:36 +0100 Subject: [PATCH 243/272] fix(holesky): mev-boost.env.example can lead to a wrong starting config --- testnets/holesky/mev-boost.env.example | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/testnets/holesky/mev-boost.env.example b/testnets/holesky/mev-boost.env.example index 942149d0b..74749449c 100644 --- a/testnets/holesky/mev-boost.env.example +++ b/testnets/holesky/mev-boost.env.example @@ -2,8 +2,8 @@ LOG_JSON=false # Set to true to log in JSON format LOG_LEVEL=info # Log level: trace, debug, info, warn, error, fatal, panic DEBUG=false # Set to true to enable debug mode -LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries -DISABLE_LOG_VERSION=false # Set to true to disable logging the version +# LOG_SERVICE_TAG= # Optional: Add a custom service tag to all log entries +DISABLE_LOG_VERSION=true # Set to true to disable logging the version # Server settings BOOST_LISTEN_ADDR=0.0.0.0:18551 # Address for mev-boost server to listen on @@ -20,12 +20,13 @@ RELAY_TIMEOUT_MS_GETPAYLOAD=4000 # Timeout for getPayload requests to the re RELAY_TIMEOUT_MS_REGVAL=3000 # Timeout for registerValidator requests # Genesis settings -- Not needed if using one of the predefined networks -# GENESIS_FORK_VERSION= # Custom genesis fork version -# GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) +# GENESIS_FORK_VERSION= # Custom genesis fork version +# GENESIS_TIMESTAMP=-1 # Custom genesis timestamp (in unix seconds) # Network settings -SEPOLIA=false # Set to true to use Sepolia network -GOERLI=false # Set to true to use Goerli network +# MAINNET=true # Set to true to use Mainnet network +# SEPOLIA=true # Set to true to use Sepolia network +# GOERLI=true # Set to true to use Goerli network HOLESKY=true # Set to true to use Holesky network # Retry settings From 98ccd0631ae21261a19bd7713988a0da6a55fda1 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:55:24 +0100 Subject: [PATCH 244/272] chore(sidecar): small nits --- bolt-sidecar/bin/sidecar.rs | 10 ++++++++++ bolt-sidecar/src/builder/template.rs | 2 +- bolt-sidecar/src/client/constraints_client.rs | 5 ++--- bolt-sidecar/src/driver.rs | 9 --------- bolt-sidecar/src/primitives/transaction.rs | 1 - bolt-sidecar/src/state/execution.rs | 10 ++++------ 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index c58f9a1a0..04a02fc0f 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -4,6 +4,14 @@ use tracing::info; use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; +const BOLT: &str = r#" +██████╗ ██████╗ ██╗ ████████╗ +██╔══██╗██╔═══██╗██║ ╚══██╔══╝ +██████╔╝██║ ██║██║ ██║ +██╔══██╗██║ ██║██║ ██║ +██████╔╝╚██████╔╝███████╗██║ +╚═════╝ ╚═════╝ ╚══════╝╚═╝ "#; + #[tokio::main] async fn main() -> Result<()> { let opts = Opts::parse(); @@ -12,6 +20,8 @@ async fn main() -> Result<()> { bail!("Failed to initialize telemetry stack: {:?}", err) } + println!("{BOLT}"); + info!(chain = opts.chain.name(), "Starting Bolt sidecar"); if opts.constraint_signing.constraint_private_key.is_some() { diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index 3561d9cc8..5a2d02f2e 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -1,7 +1,7 @@ //! Package `template` contains the functionality for building local block templates that can //! be used as a fallback. It's also used to keep any intermediary state that is needed to simulate //! new commitment requests. -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use alloy::primitives::{Address, U256}; use ethereum_consensus::{ diff --git a/bolt-sidecar/src/client/constraints_client.rs b/bolt-sidecar/src/client/constraints_client.rs index b1744855d..1a56ede47 100644 --- a/bolt-sidecar/src/client/constraints_client.rs +++ b/bolt-sidecar/src/client/constraints_client.rs @@ -100,7 +100,8 @@ impl BuilderApi for ConstraintsClient { return Err(BuilderApiError::FailedRegisteringValidators(error)); } - // If there are any delegations, propagate the one associated to the incoming registrations to the relay + // If there are any delegations, propagate the one associated to the incoming + // registrations to the relay if self.delegations.is_empty() { return Ok(()); } else { @@ -223,8 +224,6 @@ impl ConstraintsApi for ConstraintsClient { return Err(BuilderApiError::InvalidFork(header.version.to_string())); }; - // TODO: verify proofs here? - Ok(header) } diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 14b219fd2..1f15db880 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -259,15 +259,6 @@ impl SidecarDriver { /// Any errors encountered are contained to the specific `handler` in which /// they occurred, and the driver will continue to run as long as possible. pub async fn run_forever(mut self) -> ! { - const BOLT: &str = r#" - ██████╗ ██████╗ ██╗ ████████╗ - ██╔══██╗██╔═══██╗██║ ╚══██╔══╝ - ██████╔╝██║ ██║██║ ██║ - ██╔══██╗██║ ██║██║ ██║ - ██████╔╝╚██████╔╝███████╗██║ - ╚═════╝ ╚═════╝ ╚══════╝╚═╝ "#; - println!("{BOLT}"); - loop { tokio::select! { Some(api_event) = self.api_events_rx.recv() => { diff --git a/bolt-sidecar/src/primitives/transaction.rs b/bolt-sidecar/src/primitives/transaction.rs index c86eef543..ee8e7f299 100644 --- a/bolt-sidecar/src/primitives/transaction.rs +++ b/bolt-sidecar/src/primitives/transaction.rs @@ -1,4 +1,3 @@ -use std::fmt::Debug; use std::{borrow::Cow, fmt}; use alloy::primitives::{Address, U256}; diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 3e54ea3b0..71d3fbc2f 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -1,15 +1,12 @@ use alloy::{ eips::eip4844::MAX_BLOBS_PER_BLOCK, - primitives::{Address, B256, U256}, + primitives::{Address, U256}, transports::TransportError, }; use reth_primitives::{ revm_primitives::EnvKzgSettings, BlobTransactionValidationError, PooledTransactionsElement, }; -use std::{ - collections::{HashMap, HashSet}, - ops::Deref, -}; +use std::{collections::HashMap, ops::Deref}; use thiserror::Error; use tracing::{debug, trace, warn}; @@ -471,7 +468,8 @@ impl ExecutionState { let mut receipts_len = 0; for receipt in receipts.iter().flatten() { - // Calculate the total tip revenue for this transaction: (effective_gas_price - basefee) * gas_used + // Calculate the total tip revenue for this transaction: (effective_gas_price - + // basefee) * gas_used let tip_per_gas = receipt.effective_gas_price - self.basefee; let total_tip = tip_per_gas * receipt.gas_used; From 178b142b0b1530669132a5ce604843816e5a97e5 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:43:36 +0100 Subject: [PATCH 245/272] chore(sidecar): small nits --- bolt-sidecar/src/driver.rs | 22 ++++++++++++---------- bolt-sidecar/src/signer/commit_boost.rs | 4 ---- bolt-sidecar/src/signer/keystore.rs | 22 +++++++++++++++------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 1f15db880..af5118928 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -22,7 +22,7 @@ use crate::{ server::{CommitmentsApiServer, Event as CommitmentEvent}, spec::Error as CommitmentError, }, - crypto::{bls::cl_public_key_to_arr, SignableBLS, SignerECDSA}, + crypto::{SignableBLS, SignerECDSA}, primitives::{ read_signed_delegations_from_file, CommitmentRequest, ConstraintsMessage, FetchPayloadRequest, SignedConstraints, TransactionExt, @@ -326,23 +326,25 @@ impl SidecarDriver { }; // NOTE: we iterate over the transactions in the request and generate a signed constraint - // for each one. This is because the transactions in the commitment request are not - // supposed to be treated as a relative-ordering bundle, but a batch - // with no ordering guarantees. + // for each one. This is because the transactions in the commitment request are not supposed + // to be treated as a relative-ordering bundle, but a batch with no ordering guarantees. + // + // For more information, check out the constraints API docs: + // https://docs.boltprotocol.xyz/technical-docs/api/builder#constraints for tx in inclusion_request.txs { let tx_type = tx.tx_type(); let message = ConstraintsMessage::from_transaction(pubkey.clone(), target_slot, tx); let digest = message.digest(); - let signature = match self.constraint_signer { - SignerBLS::Local(ref signer) => signer.sign_commit_boost_root(digest), - SignerBLS::CommitBoost(ref signer) => signer.sign_commit_boost_root(digest).await, - SignerBLS::Keystore(ref signer) => { - signer.sign_commit_boost_root(digest, cl_public_key_to_arr(pubkey.clone())) + let signature_result = match &self.constraint_signer { + SignerBLS::Local(signer) => signer.sign_commit_boost_root(digest), + SignerBLS::CommitBoost(signer) => signer.sign_commit_boost_root(digest).await, + SignerBLS::Keystore(signer) => { + signer.sign_commit_boost_root(digest, pubkey.clone()) } }; - let signed_constraints = match signature { + let signed_constraints = match signature_result { Ok(signature) => SignedConstraints { message, signature }, Err(e) => { error!(?e, "Failed to sign constraints"); diff --git a/bolt-sidecar/src/signer/commit_boost.rs b/bolt-sidecar/src/signer/commit_boost.rs index f9fa399e5..f91a7c726 100644 --- a/bolt-sidecar/src/signer/commit_boost.rs +++ b/bolt-sidecar/src/signer/commit_boost.rs @@ -91,8 +91,6 @@ impl CommitBoostSigner { } /// Verify the BLS signature of the object with the given public key. - /// - /// Note: The default implementation should be used where possible. pub fn verify_bls( &self, data: &[u8; 32], @@ -103,8 +101,6 @@ impl CommitBoostSigner { } /// Verify the ECDSA signature of the object with the given public key. - /// - /// Note: The default implementation should be used where possible. pub fn verify_ecdsa(&self, data: &[u8; 32], sig: &Signature, pubkey: &EcdsaPublicKey) -> bool { let sig = secp256k1::ecdsa::Signature::from_str(&sig.to_hex()).expect("signature is valid"); let pubkey = diff --git a/bolt-sidecar/src/signer/keystore.rs b/bolt-sidecar/src/signer/keystore.rs index 413f69510..e1975978b 100644 --- a/bolt-sidecar/src/signer/keystore.rs +++ b/bolt-sidecar/src/signer/keystore.rs @@ -9,14 +9,16 @@ use std::{ path::{Path, PathBuf}, }; -use alloy::rpc::types::beacon::constants::BLS_PUBLIC_KEY_BYTES_LEN; - use ethereum_consensus::crypto::PublicKey as BlsPublicKey; use lighthouse_bls::Keypair; use lighthouse_eth2_keystore::Keystore; use ssz::Encode; -use crate::{builder::signature::compute_signing_root, crypto::bls::BLSSig, ChainConfig}; +use crate::{ + builder::signature::compute_signing_root, + crypto::bls::{cl_public_key_to_arr, BLSSig}, + ChainConfig, +}; use super::SignerResult; @@ -109,7 +111,7 @@ impl KeystoreSigner { pub fn sign_commit_boost_root( &self, root: [u8; 32], - public_key: [u8; BLS_PUBLIC_KEY_BYTES_LEN], + public_key: BlsPublicKey, ) -> SignerResult { self.sign_root(root, public_key, self.chain.commit_boost_domain()) } @@ -118,7 +120,7 @@ impl KeystoreSigner { fn sign_root( &self, root: [u8; 32], - public_key: [u8; BLS_PUBLIC_KEY_BYTES_LEN], + public_key: BlsPublicKey, domain: [u8; 32], ) -> SignerResult { let sk = self @@ -126,7 +128,9 @@ impl KeystoreSigner { .iter() // `as_ssz_bytes` returns the raw bytes we need .find(|kp| kp.pk.as_ssz_bytes() == public_key.as_ref()) - .ok_or(KeystoreError::UnknownPublicKey(hex::encode(public_key)))?; + .ok_or(KeystoreError::UnknownPublicKey(hex::encode(cl_public_key_to_arr( + public_key, + ))))?; let signing_root = compute_signing_root(root, domain); @@ -193,6 +197,7 @@ mod tests { }; use blst::min_pk::SecretKey; + use ethereum_consensus::crypto::PublicKey as BlsPublicKey; use crate::{signer::local::LocalSigner, ChainConfig}; @@ -365,7 +370,10 @@ mod tests { let sig_local = local_signer.sign_commit_boost_root([0; 32]).expect("to sign message"); let sig_keystore = keystore_signer_from_password - .sign_commit_boost_root([0; 32], public_key_bytes) + .sign_commit_boost_root( + [0; 32], + BlsPublicKey::try_from(public_key_bytes.as_ref()).unwrap(), + ) .expect("to sign message"); assert_eq!(sig_local, sig_keystore); } From 5130bee4c1192d6ddf7639374d7029487a2d0c1f Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:49:29 +0100 Subject: [PATCH 246/272] feat(sidecar): robust generic future retry system --- bolt-sidecar/Cargo.lock | 12 ++ bolt-sidecar/Cargo.toml | 1 + bolt-sidecar/src/api/commitments/handlers.rs | 10 +- bolt-sidecar/src/api/commitments/headers.rs | 16 +-- bolt-sidecar/src/api/commitments/server.rs | 20 +-- bolt-sidecar/src/api/commitments/spec.rs | 28 ++--- bolt-sidecar/src/api/spec.rs | 21 +++- bolt-sidecar/src/common.rs | 124 +++++++++++++++++++ bolt-sidecar/src/driver.rs | 41 +++--- bolt-sidecar/src/primitives/mod.rs | 28 ----- 10 files changed, 209 insertions(+), 92 deletions(-) diff --git a/bolt-sidecar/Cargo.lock b/bolt-sidecar/Cargo.lock index 249f061bb..9a7d8c013 100644 --- a/bolt-sidecar/Cargo.lock +++ b/bolt-sidecar/Cargo.lock @@ -1677,6 +1677,7 @@ dependencies = [ "ssz_rs 0.9.0 (git+https://github.com/ralexstokes/ssz-rs)", "thiserror", "tokio", + "tokio-retry", "toml 0.5.11", "tower-http", "tracing", @@ -7841,6 +7842,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand 0.8.5", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" diff --git a/bolt-sidecar/Cargo.toml b/bolt-sidecar/Cargo.toml index 44205ad09..0715f9ae1 100644 --- a/bolt-sidecar/Cargo.toml +++ b/bolt-sidecar/Cargo.toml @@ -13,6 +13,7 @@ tower-http = { version = "0.5.2", features = ["timeout"] } axum-extra = "0.9.3" warp = "0.3.7" futures = "0.3" +tokio-retry = "0.3.0" # crypto blst = "0.3.12" diff --git a/bolt-sidecar/src/api/commitments/handlers.rs b/bolt-sidecar/src/api/commitments/handlers.rs index fcdc557ef..3ef12eb2e 100644 --- a/bolt-sidecar/src/api/commitments/handlers.rs +++ b/bolt-sidecar/src/api/commitments/handlers.rs @@ -20,7 +20,7 @@ use crate::{ use super::{ jsonrpc::{JsonPayload, JsonResponse}, server::CommitmentsApiInner, - spec::{CommitmentsApi, Error, RejectionError, GET_VERSION_METHOD, REQUEST_INCLUSION_METHOD}, + spec::{CommitmentsApi, CommitmentError, RejectionError, GET_VERSION_METHOD, REQUEST_INCLUSION_METHOD}, }; /// Handler function for the root JSON-RPC path. @@ -28,8 +28,8 @@ use super::{ pub async fn rpc_entrypoint( headers: HeaderMap, State(api): State>, - WithRejection(Json(payload), _): WithRejection, Error>, -) -> Result, Error> { + WithRejection(Json(payload), _): WithRejection, CommitmentError>, +) -> Result, CommitmentError> { debug!("Received new request"); let (signer, signature) = auth_from_headers(&headers).inspect_err(|e| { @@ -71,7 +71,7 @@ pub async fn rpc_entrypoint( "Recovered signer does not match the provided signer" ); - return Err(Error::InvalidSignature(SignatureError)); + return Err(CommitmentError::InvalidSignature(SignatureError)); } // Set the request signer @@ -91,7 +91,7 @@ pub async fn rpc_entrypoint( } other => { error!("Unknown method: {}", other); - Err(Error::UnknownMethod) + Err(CommitmentError::UnknownMethod) } } } diff --git a/bolt-sidecar/src/api/commitments/headers.rs b/bolt-sidecar/src/api/commitments/headers.rs index 79ad3de34..bc130c899 100644 --- a/bolt-sidecar/src/api/commitments/headers.rs +++ b/bolt-sidecar/src/api/commitments/headers.rs @@ -5,23 +5,23 @@ use axum::http::HeaderMap; use crate::primitives::commitment::SignatureError; -use super::spec::{Error, SIGNATURE_HEADER}; +use super::spec::{CommitmentError, SIGNATURE_HEADER}; /// Extracts the signature ([SIGNATURE_HEADER]) from the HTTP headers. #[inline] -pub fn auth_from_headers(headers: &HeaderMap) -> Result<(Address, Signature), Error> { - let auth = headers.get(SIGNATURE_HEADER).ok_or(Error::NoSignature)?; +pub fn auth_from_headers(headers: &HeaderMap) -> Result<(Address, Signature), CommitmentError> { + let auth = headers.get(SIGNATURE_HEADER).ok_or(CommitmentError::NoSignature)?; // Remove the "0x" prefix - let auth = auth.to_str().map_err(|_| Error::MalformedHeader)?; + let auth = auth.to_str().map_err(|_| CommitmentError::MalformedHeader)?; let mut split = auth.split(':'); - let address = split.next().ok_or(Error::MalformedHeader)?; - let address = Address::from_str(address).map_err(|_| Error::MalformedHeader)?; + let address = split.next().ok_or(CommitmentError::MalformedHeader)?; + let address = Address::from_str(address).map_err(|_| CommitmentError::MalformedHeader)?; - let sig = split.next().ok_or(Error::MalformedHeader)?; - let sig = Signature::from_str(sig).map_err(|_| Error::InvalidSignature(SignatureError))?; + let sig = split.next().ok_or(CommitmentError::MalformedHeader)?; + let sig = Signature::from_str(sig).map_err(|_| CommitmentError::InvalidSignature(SignatureError))?; Ok((address, sig)) } diff --git a/bolt-sidecar/src/api/commitments/server.rs b/bolt-sidecar/src/api/commitments/server.rs index a4e68bf96..61cd7d23f 100644 --- a/bolt-sidecar/src/api/commitments/server.rs +++ b/bolt-sidecar/src/api/commitments/server.rs @@ -31,16 +31,16 @@ use crate::{ use super::{ middleware::track_server_metrics, spec, - spec::{CommitmentsApi, Error}, + spec::{CommitmentsApi, CommitmentError}, }; /// Event type emitted by the commitments API. #[derive(Debug)] -pub struct Event { +pub struct CommitmentEvent { /// The request to process. pub request: CommitmentRequest, /// The response channel. - pub response: oneshot::Sender>, + pub response: oneshot::Sender>, } /// The inner commitments-API handler that implements the [CommitmentsApi] spec. @@ -48,7 +48,7 @@ pub struct Event { #[derive(Debug)] pub struct CommitmentsApiInner { /// Event notification channel - events: mpsc::Sender, + events: mpsc::Sender, /// Optional whitelist of ECDSA public keys #[allow(unused)] whitelist: Option>, @@ -56,7 +56,7 @@ pub struct CommitmentsApiInner { impl CommitmentsApiInner { /// Create a new API server with an optional whitelist of ECDSA public keys. - pub fn new(events: mpsc::Sender) -> Self { + pub fn new(events: mpsc::Sender) -> Self { Self { events, whitelist: None } } } @@ -66,17 +66,17 @@ impl CommitmentsApi for CommitmentsApiInner { async fn request_inclusion( &self, inclusion_request: InclusionRequest, - ) -> Result { + ) -> Result { let (response_tx, response_rx) = oneshot::channel(); - let event = Event { + let event = CommitmentEvent { request: CommitmentRequest::Inclusion(inclusion_request), response: response_tx, }; self.events.send(event).await.unwrap(); - response_rx.await.map_err(|_| Error::Internal)?.map(|c| c.into()) + response_rx.await.map_err(|_| CommitmentError::Internal)?.map(|c| c.into()) } } @@ -119,7 +119,7 @@ impl CommitmentsApiServer { } /// Runs the JSON-RPC server, sending events to the provided channel. - pub async fn run(&mut self, events_tx: mpsc::Sender) { + pub async fn run(&mut self, events_tx: mpsc::Sender) { let api = Arc::new(CommitmentsApiInner::new(events_tx)); let router = make_router(api); @@ -271,7 +271,7 @@ mod test { let _ = tx.send(()); }); - let Event { request, response } = events.recv().await.unwrap(); + let CommitmentEvent { request, response } = events.recv().await.unwrap(); let commitment_signer = PrivateKeySigner::random(); diff --git a/bolt-sidecar/src/api/commitments/spec.rs b/bolt-sidecar/src/api/commitments/spec.rs index d499e69a4..3402caa17 100644 --- a/bolt-sidecar/src/api/commitments/spec.rs +++ b/bolt-sidecar/src/api/commitments/spec.rs @@ -19,7 +19,7 @@ pub(super) const MAX_REQUEST_TIMEOUT: std::time::Duration = std::time::Duration: /// Error type for the commitments API. #[derive(Debug, Error)] -pub enum Error { +pub enum CommitmentError { /// Request rejected. #[error("Request rejected: {0}")] Rejected(#[from] RejectionError), @@ -55,51 +55,51 @@ pub enum Error { InvalidJson(#[from] JsonRejection), } -impl IntoResponse for Error { +impl IntoResponse for CommitmentError { fn into_response(self) -> axum::http::Response { match self { - Error::Rejected(err) => { + CommitmentError::Rejected(err) => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32000, err.to_string()))) .into_response() } - Error::Duplicate => { + CommitmentError::Duplicate => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32001, self.to_string()))) .into_response() } - Error::Internal => ( + CommitmentError::Internal => ( StatusCode::INTERNAL_SERVER_ERROR, Json(JsonResponse::from_error(-32002, self.to_string())), ) .into_response(), - Error::NoSignature => { + CommitmentError::NoSignature => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32003, self.to_string()))) .into_response() } - Error::InvalidSignature(err) => { + CommitmentError::InvalidSignature(err) => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32004, err.to_string()))) .into_response() } - Error::Signature(err) => { + CommitmentError::Signature(err) => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32005, err.to_string()))) .into_response() } - Error::Consensus(err) => { + CommitmentError::Consensus(err) => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32006, err.to_string()))) .into_response() } - Error::Validation(err) => { + CommitmentError::Validation(err) => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32006, err.to_string()))) .into_response() } - Error::MalformedHeader => { + CommitmentError::MalformedHeader => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32007, self.to_string()))) .into_response() } - Error::UnknownMethod => { + CommitmentError::UnknownMethod => { (StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32601, self.to_string()))) .into_response() } - Error::InvalidJson(err) => ( + CommitmentError::InvalidJson(err) => ( StatusCode::BAD_REQUEST, Json(JsonResponse::from_error(-32600, format!("Invalid request: {err}"))), ) @@ -124,5 +124,5 @@ pub trait CommitmentsApi { async fn request_inclusion( &self, inclusion_request: InclusionRequest, - ) -> Result; + ) -> Result; } diff --git a/bolt-sidecar/src/api/spec.rs b/bolt-sidecar/src/api/spec.rs index 7e4be0495..d21695715 100644 --- a/bolt-sidecar/src/api/spec.rs +++ b/bolt-sidecar/src/api/spec.rs @@ -134,21 +134,27 @@ impl IntoResponse for BuilderApiError { } } +/// Implements the builder API as defined in . +/// +/// The Builder API represents the specification for allowing proposers to request +/// headers and payloads that have been built externally via PBS. #[async_trait::async_trait] -/// Implements the builder API as defines in pub trait BuilderApi { /// Implements: async fn status(&self) -> Result; + /// Implements: async fn register_validators( &self, registrations: Vec, ) -> Result<(), BuilderApiError>; + /// Implements: async fn get_header( &self, params: GetHeaderParams, ) -> Result; + /// Implements: async fn get_payload( &self, @@ -156,24 +162,27 @@ pub trait BuilderApi { ) -> Result; } +/// Implements the constraints API as defined in . +/// +/// The constraints API is an extension of the Builder API that adds a way for proposers to +/// communicate with builders in the PBS pipeline. #[async_trait::async_trait] -/// Implements the constraints API as defines in pub trait ConstraintsApi: BuilderApi { - /// Implements: + /// Implements: async fn submit_constraints( &self, constraints: &BatchedSignedConstraints, ) -> Result<(), BuilderApiError>; - /// Implements: + /// Implements: async fn get_header_with_proofs( &self, params: GetHeaderParams, ) -> Result, BuilderApiError>; - /// Implements: + /// Implements: async fn delegate(&self, signed_data: &[SignedDelegation]) -> Result<(), BuilderApiError>; - /// Implements: + /// Implements: async fn revoke(&self, signed_data: &[SignedRevocation]) -> Result<(), BuilderApiError>; } diff --git a/bolt-sidecar/src/common.rs b/bolt-sidecar/src/common.rs index 58ea71baa..4287c00f8 100644 --- a/bolt-sidecar/src/common.rs +++ b/bolt-sidecar/src/common.rs @@ -1,8 +1,10 @@ use std::{ fmt::{self, Display}, fs::read_to_string, + future::Future, ops::Deref, path::Path, + time::Duration, }; use alloy::{primitives::U256, signers::k256::ecdsa::SigningKey}; @@ -10,6 +12,10 @@ use blst::min_pk::SecretKey; use rand::{Rng, RngCore}; use reth_primitives::PooledTransactionsElement; use serde::{Deserialize, Deserializer}; +use tokio_retry::{ + strategy::{jitter, ExponentialBackoff}, + Retry, +}; use crate::{ primitives::{AccountState, TransactionExt}, @@ -239,8 +245,29 @@ impl Display for JwtSecretConfig { } } +/// Retry a future with exponential backoff and jitter. +pub async fn retry_with_backoff(max_retries: usize, fut: impl Fn() -> F) -> Result +where + F: Future>, +{ + let backoff = ExponentialBackoff::from_millis(100) + .factor(2) + .max_delay(Duration::from_secs(1)) + .take(max_retries) + .map(jitter); + + Retry::spawn(backoff, fut).await +} + #[cfg(test)] mod tests { + use std::sync::Arc; + use thiserror::Error; + use tokio::{ + sync::Mutex, + time::{Duration, Instant}, + }; + use super::*; #[test] @@ -251,4 +278,101 @@ mod tests { let result = calculate_max_basefee(current, slot_diff); assert_eq!(result, Some(28865075793)) } + + #[derive(Debug, Error)] + #[error("mock error")] + struct MockError; + + // Helper struct to count attempts and control failure/success behavior + struct Counter { + count: usize, + fail_until: usize, + } + + impl Counter { + fn new(fail_until: usize) -> Self { + Self { count: 0, fail_until } + } + + async fn retryable_fn(&mut self) -> Result<(), MockError> { + self.count += 1; + if self.count <= self.fail_until { + Err(MockError) + } else { + Ok(()) + } + } + } + + #[tokio::test] + async fn test_retry_success_without_retry() { + let counter = Arc::new(Mutex::new(Counter::new(0))); + + let result = retry_with_backoff(5, || { + let counter = Arc::clone(&counter); + async move { + let mut counter = counter.lock().await; + counter.retryable_fn().await + } + }) + .await; + + assert!(result.is_ok()); + assert_eq!(counter.lock().await.count, 1, "Should succeed on first attempt"); + } + + #[tokio::test] + async fn test_retry_until_success() { + let counter = Arc::new(Mutex::new(Counter::new(3))); // Fail 3 times, succeed on 4th + + let result = retry_with_backoff(5, || async { + println!("attempt"); + let counter = Arc::clone(&counter); + let mut counter = counter.lock().await; + counter.retryable_fn().await + }) + .await; + + assert!(result.is_ok()); + assert_eq!(counter.lock().await.count, 4, "Should retry until success on 4th attempt"); + } + + #[tokio::test] + async fn test_max_retries_reached() { + let counter = Arc::new(Mutex::new(Counter::new(5))); // Fail 5 times, max retries = 3 + + let result = retry_with_backoff(3, || { + let counter = Arc::clone(&counter); + async move { + let mut counter = counter.lock().await; + counter.retryable_fn().await + } + }) + .await; + + assert!(result.is_err()); + assert_eq!(counter.lock().await.count, 4, "Should stop after max retries are reached"); + } + + #[tokio::test] + async fn test_exponential_backoff_timing() { + let counter = Arc::new(Mutex::new(Counter::new(3))); // Fail 3 times, succeed on 4th + let start_time = Instant::now(); + + let result = retry_with_backoff(5, || { + let counter = Arc::clone(&counter); + async move { + let mut counter = counter.lock().await; + counter.retryable_fn().await + } + }) + .await; + + assert!(result.is_ok()); + let elapsed = start_time.elapsed(); + assert!( + elapsed >= Duration::from_millis(700), + "Total backoff duration should be at least 700ms" + ); + } } diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index af5118928..6f2739a1c 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -1,8 +1,4 @@ -use std::{ - collections::HashSet, - fmt, - time::{Duration, Instant}, -}; +use std::{collections::HashSet, fmt, sync::Arc, time::Instant}; use alloy::{rpc::types::beacon::events::HeadEvent, signers::local::PrivateKeySigner}; use beacon_api_client::mainnet::Client as BeaconClient; @@ -19,9 +15,10 @@ use crate::{ builder::payload_fetcher::LocalPayloadFetcher, chain_io::manager::BoltManager, commitments::{ - server::{CommitmentsApiServer, Event as CommitmentEvent}, - spec::Error as CommitmentError, + server::{CommitmentEvent, CommitmentsApiServer}, + spec::CommitmentError, }, + common::retry_with_backoff, crypto::{SignableBLS, SignerECDSA}, primitives::{ read_signed_delegations_from_file, CommitmentRequest, ConstraintsMessage, @@ -306,7 +303,8 @@ impl SidecarDriver { return; } - // TODO: match when we have more request types + // When we'll add more commitment types, we'll need to match on the request type here. + // For now, we only support inclusion requests so the flow is straightforward. let CommitmentRequest::Inclusion(inclusion_request) = request.clone(); let target_slot = inclusion_request.slot; @@ -397,22 +395,23 @@ impl SidecarDriver { error!(err = ?e, "Error while building local payload at deadline for slot {slot}"); }; - // TODO: fix retry logic, and move this to separate task in the constraints client itself - let constraints = template.signed_constraints_list.clone(); + let constraints = Arc::new(template.signed_constraints_list.clone()); let constraints_client = self.constraints_client.clone(); - tokio::spawn(async move { - let max_retries = 5; - let mut i = 0; - while let Err(e) = constraints_client.submit_constraints(&constraints).await { - error!(err = ?e, "Error submitting constraints to constraints client, retrying..."); - tokio::time::sleep(Duration::from_millis(100)).await; - i += 1; - if i >= max_retries { - error!("Max retries reached while submitting to Constraints client"); - break; + + // Submit constraints to the constraints service with an exponential retry mechanism. + tokio::spawn(retry_with_backoff(10, move || { + let constraints_client = constraints_client.clone(); + let constraints = Arc::clone(&constraints); + async move { + match constraints_client.submit_constraints(constraints.as_ref()).await { + Ok(_) => Ok(()), + Err(e) => { + error!(err = ?e, "Failed to submit constraints, retrying..."); + Err(e) + } } } - }); + })); } /// Handle a fetch payload request, responding with the local payload if available. diff --git a/bolt-sidecar/src/primitives/mod.rs b/bolt-sidecar/src/primitives/mod.rs index 3c70f6990..b4f046b3e 100644 --- a/bolt-sidecar/src/primitives/mod.rs +++ b/bolt-sidecar/src/primitives/mod.rs @@ -1,8 +1,6 @@ // TODO: add docs #![allow(missing_docs)] -use std::sync::{atomic::AtomicU64, Arc}; - use alloy::primitives::U256; use ethereum_consensus::{ crypto::KzgCommitment, @@ -174,29 +172,3 @@ impl From for GetPayloadResponse { } } } - -/// A struct representing the current chain head. -#[derive(Debug, Clone)] -pub struct ChainHead { - /// The current slot number. - pub slot: Arc, - /// The current block number. - pub block: Arc, -} - -impl ChainHead { - /// Create a new ChainHead instance. - pub fn new(slot: u64, block: u64) -> Self { - Self { slot: Arc::new(AtomicU64::new(slot)), block: Arc::new(AtomicU64::new(block)) } - } - - /// Get the slot number (consensus layer). - pub fn slot(&self) -> u64 { - self.slot.load(std::sync::atomic::Ordering::SeqCst) - } - - /// Get the block number (execution layer). - pub fn block(&self) -> u64 { - self.block.load(std::sync::atomic::Ordering::SeqCst) - } -} From a9e38fcf426db253366f16fd75ab846b0866384f Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:25:53 +0100 Subject: [PATCH 247/272] feat: rm validator indexes in favor of pubkeys --- bolt-sidecar/.env.example | 7 -- bolt-sidecar/README.md | 10 -- bolt-sidecar/src/chain_io/manager.rs | 4 +- bolt-sidecar/src/client/constraints_client.rs | 30 ++++++ bolt-sidecar/src/config/chain.rs | 9 ++ bolt-sidecar/src/config/mod.rs | 10 -- bolt-sidecar/src/config/validator_indexes.rs | 101 ------------------ bolt-sidecar/src/driver.rs | 69 ++++-------- bolt-sidecar/src/primitives/constraint.rs | 6 +- bolt-sidecar/src/state/consensus.rs | 88 +++++---------- bolt-sidecar/src/state/mod.rs | 2 +- bolt-sidecar/src/test_util.rs | 1 - testnets/holesky/README.md | 13 +-- testnets/holesky/bolt-sidecar.env.example | 7 -- 14 files changed, 94 insertions(+), 263 deletions(-) delete mode 100644 bolt-sidecar/src/config/validator_indexes.rs diff --git a/bolt-sidecar/.env.example b/bolt-sidecar/.env.example index faeb05ad1..0ad3d4157 100644 --- a/bolt-sidecar/.env.example +++ b/bolt-sidecar/.env.example @@ -15,13 +15,6 @@ BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 # URL to forward the constraints produced by the Bolt sidecar to a server # supporting the Constraints API, such as an MEV-Boost fork BOLT_SIDECAR_CONSTRAINTS_API_URL="http://localhost:18551" -# Validator indexes of connected validators that the sidecar should accept -# commitments on behalf of. -# Accepted values: -# - a comma-separated list of indexes (e.g. "1,2,3,4") -# - a contiguous range of indexes (e.g. "1..4") -# - a mix of the above (e.g. "1,2..4,6..8") -BOLT_SIDECAR_VALIDATOR_INDEXES= # The JWT secret token to authenticate calls to the engine API. It can be # either be a hex-encoded string or a file path to a file containing the # hex-encoded secret. diff --git a/bolt-sidecar/README.md b/bolt-sidecar/README.md index 2b11d6095..03c02f423 100644 --- a/bolt-sidecar/README.md +++ b/bolt-sidecar/README.md @@ -83,16 +83,6 @@ Options: [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] [default: 18551] - --validator-indexes - Validator indexes of connected validators that the sidecar should accept commitments on behalf of. - Accepted values: - - a comma-separated list of indexes (e.g. "1,2,3,4") - - a contiguous range of indexes (e.g. "1..4") - - a mix of the above (e.g. "1,2..4,6..8") - - [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] - [default: ] - --jwt-hex The JWT secret token to authenticate calls to the engine API. diff --git a/bolt-sidecar/src/chain_io/manager.rs b/bolt-sidecar/src/chain_io/manager.rs index ae8f7fbb0..01f0d488d 100644 --- a/bolt-sidecar/src/chain_io/manager.rs +++ b/bolt-sidecar/src/chain_io/manager.rs @@ -210,8 +210,8 @@ mod tests { .as_ref()).expect("valid bls public key")]; let res = manager.verify_validator_pubkeys(keys.clone(), commitment_signer_pubkey).await; assert!( - res.unwrap_err().to_string() - == generate_operator_keys_mismatch_error( + res.unwrap_err().to_string() == + generate_operator_keys_mismatch_error( pubkey_hash(&keys[0]), commitment_signer_pubkey, operator diff --git a/bolt-sidecar/src/client/constraints_client.rs b/bolt-sidecar/src/client/constraints_client.rs index 1a56ede47..ccd1e5db3 100644 --- a/bolt-sidecar/src/client/constraints_client.rs +++ b/bolt-sidecar/src/client/constraints_client.rs @@ -52,6 +52,36 @@ impl ConstraintsClient { self.delegations.extend(delegations); } + /// Return a public key that can be used to sign constraints with for the given + /// validator public key. + /// + /// Rationale: + /// - If there are no delegatee keys, try to use the validator key directly if available. + /// - If there are delegatee keys, try to use the first one that is available in the list. + pub fn find_signing_key( + &self, + validator_pubkey: BlsPublicKey, + available_pubkeys: HashSet, + ) -> Option { + let delegatees = self.find_delegatees(&validator_pubkey); + + if delegatees.is_empty() { + if available_pubkeys.contains(&validator_pubkey) { + return Some(validator_pubkey); + } else { + return None; + } + } else { + for delegatee in delegatees { + if available_pubkeys.contains(&delegatee) { + return Some(delegatee); + } + } + } + + None + } + /// Finds all delegations for the given validator public key. pub fn find_delegatees(&self, validator_pubkey: &BlsPublicKey) -> HashSet { self.delegations diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 2a8cb7ec5..20dec3ab4 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -1,6 +1,7 @@ use core::fmt; use std::{ fmt::{Display, Formatter}, + ops::Deref, time::Duration, }; @@ -75,6 +76,14 @@ impl Default for ChainConfig { } } +impl Deref for ChainConfig { + type Target = Chain; + + fn deref(&self) -> &Self::Target { + &self.chain + } +} + /// Supported chains for the sidecar #[derive(Debug, Clone, Copy, ValueEnum, Deserialize)] #[clap(rename_all = "kebab_case")] diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 7d05e1171..0a3797a27 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -3,9 +3,6 @@ use clap::Parser; use reqwest::Url; use serde::Deserialize; -pub mod validator_indexes; -pub use validator_indexes::ValidatorIndexes; - pub mod chain; pub use chain::ChainConfig; @@ -62,13 +59,6 @@ pub struct Opts { default_value_t = DEFAULT_CONSTRAINTS_PROXY_PORT )] pub constraints_proxy_port: u16, - /// Validator indexes of connected validators that the sidecar - /// should accept commitments on behalf of. Accepted values: - /// - a comma-separated list of indexes (e.g. "1,2,3,4") - /// - a contiguous range of indexes (e.g. "1..4") - /// - a mix of the above (e.g. "1,2..4,6..8") - #[clap(long, env = "BOLT_SIDECAR_VALIDATOR_INDEXES")] - pub validator_indexes: ValidatorIndexes, /// The JWT secret token to authenticate calls to the engine API. /// /// It can either be a hex-encoded string or a file path to a file diff --git a/bolt-sidecar/src/config/validator_indexes.rs b/bolt-sidecar/src/config/validator_indexes.rs deleted file mode 100644 index 74595ddbf..000000000 --- a/bolt-sidecar/src/config/validator_indexes.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::{ - fmt::{self, Display}, - str::FromStr, -}; - -use serde::{de, Deserialize, Deserializer}; - -#[derive(Debug, Clone, Default)] -pub struct ValidatorIndexes(Vec); - -impl ValidatorIndexes { - pub fn contains(&self, index: u64) -> bool { - self.0.contains(&index) - } -} - -impl FromStr for ValidatorIndexes { - type Err = eyre::Report; - - /// Parse an array of validator indexes. Accepted values: - /// - a single index (e.g. "1") - /// - a comma-separated list of indexes (e.g. "1,2,3,4") - /// - a contiguous range of indexes (e.g. "1..4") - /// - a mix of the above (e.g. "1,2..4,6..8") - /// - /// TODO: add parsing from a directory path, using the format of - /// validator definitions - fn from_str(s: &str) -> Result { - let s = s.trim(); - let mut vec = Vec::new(); - - if s.is_empty() { - return Ok(Self(vec)); - } - - for comma_separated_part in s.split(',') { - if comma_separated_part.contains("..") { - let mut parts = comma_separated_part.split(".."); - - let start = parts.next().ok_or_else(|| eyre::eyre!("Invalid range"))?; - let start = start.parse::()?; - - let end = parts.next().ok_or_else(|| eyre::eyre!("Invalid range"))?; - let end = end.parse::()?; - - vec.extend(start..=end); - } else { - let index = comma_separated_part.parse::()?; - vec.push(index); - } - } - - Ok(Self(vec)) - } -} - -impl<'de> Deserialize<'de> for ValidatorIndexes { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - ValidatorIndexes::from_str(&s).map_err(de::Error::custom) - } -} - -impl From> for ValidatorIndexes { - fn from(vec: Vec) -> Self { - Self(vec) - } -} - -impl Display for ValidatorIndexes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0.iter().map(|index| index.to_string()).collect::>().join(",")) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test_parse_validator_indexes() { - use super::ValidatorIndexes; - use std::str::FromStr; - - let indexes = ValidatorIndexes::from_str("1").unwrap(); - assert_eq!(indexes.0, vec![1]); - - let indexes = ValidatorIndexes::from_str("1,2,3,4").unwrap(); - assert_eq!(indexes.0, vec![1, 2, 3, 4]); - - let indexes = ValidatorIndexes::from_str("1..4").unwrap(); - assert_eq!(indexes.0, vec![1, 2, 3, 4]); - - let indexes = ValidatorIndexes::from_str("1..4,6..8").unwrap(); - assert_eq!(indexes.0, vec![1, 2, 3, 4, 6, 7, 8]); - - let indexes = ValidatorIndexes::from_str("1,2..4,6..8").unwrap(); - assert_eq!(indexes.0, vec![1, 2, 3, 4, 6, 7, 8]); - } -} diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 6f2739a1c..442e37155 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -1,10 +1,9 @@ -use std::{collections::HashSet, fmt, sync::Arc, time::Instant}; +use std::{fmt, sync::Arc, time::Instant}; use alloy::{rpc::types::beacon::events::HeadEvent, signers::local::PrivateKeySigner}; use beacon_api_client::mainnet::Client as BeaconClient; use ethereum_consensus::{ clock::{self, SlotStream, SystemTimeProvider}, - crypto::bls::PublicKey as BlsPublicKey, phase0::mainnet::SLOTS_PER_EPOCH, }; use futures::StreamExt; @@ -162,7 +161,7 @@ impl SidecarDriver { let mut constraints_client = ConstraintsClient::new(opts.constraints_api_url.clone()); // read the delegations from disk if they exist and add them to the constraints client. - let validator_public_keys = if let Some(delegations_file_path) = + let validator_pubkeys = if let Some(delegations_file_path) = opts.constraint_signing.delegations_path.as_ref() { let delegations = read_signed_delegations_from_file(delegations_file_path)?; @@ -175,27 +174,21 @@ impl SidecarDriver { }; // Verify the operator and validator keys with the bolt manager - if let Some(bolt_manager) = - BoltManager::from_chain(opts.execution_api_url.clone(), opts.chain.chain) + if let Some(manager) = BoltManager::from_chain(opts.execution_api_url.clone(), *opts.chain) { let commitment_signer_pubkey = commitment_signer.public_key(); - let validator_public_keys_len = validator_public_keys.len(); info!( - validator_public_keys_len, + validator_public_keys_len = %validator_pubkeys.len(), commitment_signer_pubkey = ?commitment_signer_pubkey, "Verifying validators and operator keys with Bolt Manager, this may take a while..." ); - bolt_manager - .verify_validator_pubkeys(validator_public_keys, commitment_signer_pubkey) - .await?; - info!( - validator_public_keys_len, - commitment_signer_pubkey = ?commitment_signer_pubkey, - "Verified validators and operator keys verified with Bolt Manager successfully" - ); + + manager.verify_validator_pubkeys(validator_pubkeys, commitment_signer_pubkey).await?; + + info!("Successfully verified validators and operator keys with Bolt Manager!"); } else { warn!( - "No Bolt Manager contract deployed on {} chain, skipping validators and operator public keys verification", + "No Bolt Manager contract deployed on {}, skipping validators and operator public keys verification", opts.chain.name() ); } @@ -213,7 +206,6 @@ impl SidecarDriver { let consensus = ConsensusState::new( beacon_client, - opts.validator_indexes.clone(), opts.chain.commitment_deadline(), opts.chain.enable_unsafe_lookahead, ); @@ -264,7 +256,7 @@ impl SidecarDriver { Ok(head_event) = self.head_tracker.next_head() => { self.handle_new_head_event(head_event).await; } - Some(slot) = self.consensus.commitment_deadline.wait() => { + Some(slot) = self.consensus.wait_commitment_deadline() => { self.handle_commitment_deadline(slot).await; } Some(payload_request) = self.payload_requests_rx.recv() => { @@ -290,14 +282,14 @@ impl SidecarDriver { let validator_pubkey = match self.consensus.validate_request(&request) { Ok(pubkey) => pubkey, Err(err) => { - error!(?err, "Consensus: failed to validate request"); + warn!(?err, "Consensus: failed to validate request"); let _ = response.send(Err(CommitmentError::Consensus(err))); return; } }; if let Err(err) = self.execution.validate_request(&mut request).await { - error!(?err, "Execution: failed to commit request"); + warn!(?err, "Execution: failed to validate request"); ApiMetrics::increment_validation_errors(err.to_tag_str().to_owned()); let _ = response.send(Err(CommitmentError::Validation(err))); return; @@ -314,10 +306,13 @@ impl SidecarDriver { "Validation against execution state passed" ); - let delegatees = self.constraints_client.find_delegatees(&validator_pubkey); let available_pubkeys = self.constraint_signer.available_pubkeys(); - let Some(pubkey) = pick_public_key(validator_pubkey, available_pubkeys, delegatees) else { + // Find a public key to sign new constraints with for this slot. + // This can either be the validator pubkey or a delegatee (if one is available). + let Some(signing_pubkey) = + self.constraints_client.find_signing_key(validator_pubkey, available_pubkeys) + else { error!(%target_slot, "No available public key to sign constraints with"); let _ = response.send(Err(CommitmentError::Internal)); return; @@ -331,14 +326,14 @@ impl SidecarDriver { // https://docs.boltprotocol.xyz/technical-docs/api/builder#constraints for tx in inclusion_request.txs { let tx_type = tx.tx_type(); - let message = ConstraintsMessage::from_transaction(pubkey.clone(), target_slot, tx); + let message = ConstraintsMessage::from_tx(signing_pubkey.clone(), target_slot, tx); let digest = message.digest(); let signature_result = match &self.constraint_signer { SignerBLS::Local(signer) => signer.sign_commit_boost_root(digest), SignerBLS::CommitBoost(signer) => signer.sign_commit_boost_root(digest).await, SignerBLS::Keystore(signer) => { - signer.sign_commit_boost_root(digest, pubkey.clone()) + signer.sign_commit_boost_root(digest, signing_pubkey.clone()) } }; @@ -429,29 +424,3 @@ impl SidecarDriver { } } } - -/// Pick a pubkey to sign constraints with. -/// -/// Rationale: -/// - If there are no delegatee keys, try to use the validator key directly if available. -/// - If there are delegatee keys, try to use the first one that is available in the list. -fn pick_public_key( - validator: BlsPublicKey, - available: HashSet, - delegatees: HashSet, -) -> Option { - if delegatees.is_empty() { - if available.contains(&validator) { - return Some(validator); - } else { - return None; - } - } else { - for delegatee in delegatees { - if available.contains(&delegatee) { - return Some(delegatee); - } - } - } - None -} diff --git a/bolt-sidecar/src/primitives/constraint.rs b/bolt-sidecar/src/primitives/constraint.rs index 1df85a547..2790cfe10 100644 --- a/bolt-sidecar/src/primitives/constraint.rs +++ b/bolt-sidecar/src/primitives/constraint.rs @@ -47,8 +47,8 @@ impl ConstraintsMessage { } /// Builds a constraints message from a single transaction. - pub fn from_transaction(pubkey: BlsPublicKey, slot: u64, transaction: FullTransaction) -> Self { - Self { pubkey, slot, top: false, transactions: vec![transaction] } + pub fn from_tx(pubkey: BlsPublicKey, slot: u64, tx: FullTransaction) -> Self { + Self { pubkey, slot, top: false, transactions: vec![tx] } } } @@ -140,7 +140,7 @@ mod tests { let tx_bytes = bytes!("f8678085019dc6838082520894deaddeaddeaddeaddeaddeaddeaddeaddeaddead38808360306ca06664c078fa60bd3ece050903dd295949908dd9686ec8871fa558f868e031cd39a00ed4f0b122b32b73f19230fabe6a726e2d07f84eda5beaa42a1ae1271bdee39f").to_vec(); let tx = FullTransaction::decode_enveloped(tx_bytes.as_slice()).unwrap(); - let constraint = ConstraintsMessage::from_transaction(signer.pubkey(), 165, tx); + let constraint = ConstraintsMessage::from_tx(signer.pubkey(), 165, tx); let digest = constraint.digest(); let signature = signer.sign_commit_boost_root(digest).unwrap(); diff --git a/bolt-sidecar/src/state/consensus.rs b/bolt-sidecar/src/state/consensus.rs index b4bcae811..3ba56860f 100644 --- a/bolt-sidecar/src/state/consensus.rs +++ b/bolt-sidecar/src/state/consensus.rs @@ -10,7 +10,6 @@ use tracing::debug; use super::CommitmentDeadline; use crate::{ - config::ValidatorIndexes, primitives::{CommitmentRequest, Slot}, telemetry::ApiMetrics, BeaconClient, @@ -38,19 +37,22 @@ struct Epoch { pub value: u64, /// The start slot of the epoch pub start_slot: Slot, - /// The proposer duties of the epoch. + /// The proposer duties of the current epoch. /// - /// NOTE: if the unsafe lookhead flag is enabled, then this field represents the proposer - /// duties also for the next epoch. + /// NOTE: if the `unsafe_lookhead` flag is enabled, then this field also contains + /// the next epoch's proposer duties. pub proposer_duties: Vec, } /// Represents the consensus state container for the sidecar. -#[allow(missing_debug_implementations)] +/// +/// This struct is responsible for managing the state of the beacon chain and the proposer duties, +/// including validating commitment requests and updating the state based on the latest slot. pub struct ConsensusState { + /// The beacon API client to fetch data from the beacon chain. beacon_api_client: Client, + /// The current epoch and associated proposer duties. epoch: Epoch, - validator_indexes: ValidatorIndexes, // Timestamp of when the latest slot was received latest_slot_timestamp: Instant, // The latest slot received @@ -61,11 +63,15 @@ pub struct ConsensusState { /// This is used to prevent the sidecar from accepting commitments /// which won't have time to be included by the PBS pipeline. // commitment_deadline: u64, - pub commitment_deadline: CommitmentDeadline, + commitment_deadline: CommitmentDeadline, /// The duration of the commitment deadline. commitment_deadline_duration: Duration, /// If commitment requests should be validated also against the unsafe lookahead - pub unsafe_lookahead_enabled: bool, + /// (i.e. the next epoch's proposer duties). + /// + /// It is considered unsafe because it is possible for the next epoch's duties to + /// change if there are beacon chain deposits or withdrawals in the current epoch. + unsafe_lookahead_enabled: bool, } impl fmt::Debug for ConsensusState { @@ -75,6 +81,8 @@ impl fmt::Debug for ConsensusState { .field("latest_slot", &self.latest_slot) .field("latest_slot_timestamp", &self.latest_slot_timestamp) .field("commitment_deadline", &self.commitment_deadline) + .field("commitment_deadline_duration", &self.commitment_deadline_duration) + .field("unsafe_lookahead_enabled", &self.unsafe_lookahead_enabled) .finish() } } @@ -83,13 +91,11 @@ impl ConsensusState { /// Create a new `ConsensusState` with the given configuration. pub fn new( beacon_api_client: BeaconClient, - validator_indexes: ValidatorIndexes, commitment_deadline_duration: Duration, unsafe_lookahead_enabled: bool, ) -> Self { ConsensusState { beacon_api_client, - validator_indexes, epoch: Epoch::default(), latest_slot: Default::default(), latest_slot_timestamp: Instant::now(), @@ -99,12 +105,13 @@ impl ConsensusState { } } - /// This function validates the state of the chain against a block. It checks 2 things: - /// 1. The target slot is one of our proposer slots. (TODO) + /// Validate an incoming commitment request against beacon chain data. + /// The request is valid if: + /// + /// 1. The target slot is scheduled to be proposed by one of our validators. /// 2. The request hasn't passed the slot deadline. /// - /// If the request is valid, it returns the validator public key for the slot. - /// TODO: Integrate with the registry to check if we are registered. + /// If the request is valid, return the validator public key for the target slot. pub fn validate_request( &self, request: &CommitmentRequest, @@ -123,10 +130,13 @@ impl ConsensusState { return Err(ConsensusError::DeadlineExceeded); } - // Find the validator index for the given slot - let validator_pubkey = self.find_validator_pubkey_for_slot(req.slot)?; + // Find the validator pubkey for the given slot from the proposer duties + self.find_validator_pubkey_for_slot(req.slot) + } - Ok(validator_pubkey) + /// Wait for the commitment deadline to expire. + pub async fn wait_commitment_deadline(&mut self) -> Option { + self.commitment_deadline.wait().await } /// Update the latest head and fetch the relevant data from the beacon chain. @@ -191,9 +201,7 @@ impl ConsensusState { self.epoch .proposer_duties .iter() - .find(|&duty| { - duty.slot == slot && self.validator_indexes.contains(duty.validator_index as u64) - }) + .find(|&duty| duty.slot == slot) .map(|duty| duty.public_key.clone()) .ok_or(ConsensusError::ValidatorNotFound) } @@ -209,54 +217,18 @@ impl ConsensusState { #[cfg(test)] mod tests { - use beacon_api_client::{BlockId, ProposerDuty}; + use beacon_api_client::BlockId; use reqwest::Url; use tracing::warn; use super::*; use crate::test_util::try_get_beacon_api_url; - #[tokio::test] - async fn test_find_validator_index_for_slot() { - // Sample proposer duties - let proposer_duties = vec![ - ProposerDuty { public_key: Default::default(), slot: 1, validator_index: 100 }, - ProposerDuty { public_key: Default::default(), slot: 2, validator_index: 101 }, - ProposerDuty { public_key: Default::default(), slot: 3, validator_index: 102 }, - ]; - - // Validator indexes that we are interested in - let validator_indexes = ValidatorIndexes::from(vec![100, 102]); - - // Create a ConsensusState with the sample proposer duties and validator indexes - let state = ConsensusState { - beacon_api_client: Client::new(Url::parse("http://localhost").unwrap()), - epoch: Epoch { value: 0, start_slot: 0, proposer_duties }, - latest_slot_timestamp: Instant::now(), - commitment_deadline: CommitmentDeadline::new(0, Duration::from_secs(1)), - validator_indexes, - commitment_deadline_duration: Duration::from_secs(1), - latest_slot: 0, - unsafe_lookahead_enabled: false, - }; - - // Test finding a valid slot - assert_eq!(state.find_validator_pubkey_for_slot(1).unwrap(), Default::default()); - assert_eq!(state.find_validator_pubkey_for_slot(3).unwrap(), Default::default()); - - // Test finding an invalid slot (not in proposer duties) - assert!(matches!( - state.find_validator_pubkey_for_slot(4), - Err(ConsensusError::ValidatorNotFound) - )); - } - #[tokio::test] async fn test_update_slot() -> eyre::Result<()> { let _ = tracing_subscriber::fmt::try_init(); let commitment_deadline_duration = Duration::from_secs(1); - let validator_indexes = ValidatorIndexes::from(vec![100, 101, 102]); let Some(url) = try_get_beacon_api_url().await else { warn!("skipping test: beacon API URL is not reachable"); @@ -271,7 +243,6 @@ mod tests { epoch: Epoch::default(), latest_slot: Default::default(), latest_slot_timestamp: Instant::now(), - validator_indexes, commitment_deadline: CommitmentDeadline::new(0, commitment_deadline_duration), commitment_deadline_duration, unsafe_lookahead_enabled: false, @@ -317,7 +288,6 @@ mod tests { epoch: Epoch::default(), latest_slot: Default::default(), latest_slot_timestamp: Instant::now(), - validator_indexes: Default::default(), commitment_deadline: CommitmentDeadline::new(0, commitment_deadline_duration), commitment_deadline_duration, // We test for both epochs diff --git a/bolt-sidecar/src/state/mod.rs b/bolt-sidecar/src/state/mod.rs index 373f3d13d..6e0e20e67 100644 --- a/bolt-sidecar/src/state/mod.rs +++ b/bolt-sidecar/src/state/mod.rs @@ -60,7 +60,7 @@ impl Future for CommitmentDeadline { return Poll::Ready(None); }; - match sleep.as_mut().poll(cx) { + match sleep.poll_unpin(cx) { Poll::Ready(_) => Poll::Ready(Some(self.slot)), Poll::Pending => Poll::Pending, } diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index c0bac0ddd..6eace3748 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -90,7 +90,6 @@ pub(crate) async fn get_test_config() -> Option { "BOLT_SIDECAR_COMMITMENT_PRIVATE_KEY", EcdsaSecretKeyWrapper::random().to_string(), ); - env::set_var("BOLT_SIDECAR_VALIDATOR_INDEXES", "0..64"); let _ = dotenvy::dotenv(); diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index 8e6c50060..69f2f8350 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -516,8 +516,6 @@ BOLT_SIDECAR_ENGINE_API = "" # The execution layer e BOLT_SIDECAR_JWT_HEX = "" # The engine JWT used to authenticate with the engine API BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. BOLT_SIDECAR_FEE_RECIPIENT = "" # The fee recipient -BOLT_SIDECAR_VALIDATOR_INDEXES = "" # The active validator indexes (can be defined as a comma-separated list, or a range) - # e.g. "0,1,2,3,4" or "0..4", or a combination of both ``` To initialize commit-boost, run the following command: @@ -704,7 +702,7 @@ with the `--help` flag: Command-line options for the Bolt sidecar -Usage: bolt-sidecar [OPTIONS] --validator-indexes --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > +Usage: bolt-sidecar [OPTIONS] --engine-jwt-hex --fee-recipient --builder-private-key --commitment-private-key <--constraint-private-key |--commit-boost-signer-url |--keystore-password |--keystore-secrets-path > Options: --port @@ -743,15 +741,6 @@ Options: [env: BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=] [default: 18550] - --validator-indexes - Validator indexes of connected validators that the sidecar should accept commitments on behalf of. - Accepted values: - - a comma-separated list of indexes (e.g. "1,2,3,4") - - a contiguous range of indexes (e.g. "1..4") - - a mix of the above (e.g. "1,2..4,6..8") - - [env: BOLT_SIDECAR_VALIDATOR_INDEXES=] - --engine-jwt-hex The JWT secret token to authenticate calls to the engine API. diff --git a/testnets/holesky/bolt-sidecar.env.example b/testnets/holesky/bolt-sidecar.env.example index 993e00cb5..5568f8e7c 100644 --- a/testnets/holesky/bolt-sidecar.env.example +++ b/testnets/holesky/bolt-sidecar.env.example @@ -15,13 +15,6 @@ BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT=18550 # URL to forward the constraints produced by the Bolt sidecar to a server # supporting the Constraints API, such as an MEV-Boost fork BOLT_SIDECAR_CONSTRAINTS_API_URL="http://bolt-mev-boost-holesky:18551" -# Validator indexes of connected validators that the sidecar should accept -# commitments on behalf of. -# Accepted values: -# - a comma-separated list of indexes (e.g. "1,2,3,4") -# - a contiguous range of indexes (e.g. "1..4") -# - a mix of the above (e.g. "1,2..4,6..8") -BOLT_SIDECAR_VALIDATOR_INDEXES= # The JWT secret token to authenticate calls to the engine API. It can be # either be a hex-encoded string or a file path to a file containing the # hex-encoded secret. From 3228368822b60828dc632af1910131cd9bf4a038 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:44:50 +0100 Subject: [PATCH 248/272] chore(sidecar): add docs --- bolt-sidecar/src/primitives/commitment.rs | 6 +++++ bolt-sidecar/src/primitives/delegation.rs | 18 +++++++++++++ bolt-sidecar/src/primitives/mod.rs | 30 +++++++++++++++++----- bolt-sidecar/src/primitives/transaction.rs | 19 ++++++++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/bolt-sidecar/src/primitives/commitment.rs b/bolt-sidecar/src/primitives/commitment.rs index c8d69958e..63764c1aa 100644 --- a/bolt-sidecar/src/primitives/commitment.rs +++ b/bolt-sidecar/src/primitives/commitment.rs @@ -7,6 +7,7 @@ use crate::crypto::SignerECDSA; use super::{deserialize_txs, serialize_txs, FullTransaction, TransactionExt}; +/// Error type for signature errors. #[derive(Debug, thiserror::Error)] #[error("Invalid signature")] pub struct SignatureError; @@ -23,6 +24,7 @@ pub enum CommitmentRequest { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(untagged)] pub enum SignedCommitment { + /// A signed inclusion commitment. Inclusion(InclusionCommitment), } @@ -89,6 +91,7 @@ pub struct InclusionRequest { /// this specific commitment to be included at the given slot. #[serde(skip)] pub signature: Option, + /// The signer of the request (if recovered). #[serde(skip)] pub signer: Option
, } @@ -186,6 +189,7 @@ impl InclusionRequest { self.signer = Some(signer); } + /// Recovers the signer of all transactions in the request. pub fn recover_signers(&mut self) -> Result<(), SignatureError> { for tx in &mut self.txs { let signer = tx.recover_signer().ok_or(SignatureError)?; @@ -237,9 +241,11 @@ impl From for CommitmentRequest { } } +/// Extension trait for ECDSA signatures. pub trait ECDSASignatureExt { /// Returns the ECDSA signature as bytes with the correct parity bit. fn as_bytes_with_parity(&self) -> [u8; 65]; + /// Rethrns the ECDSA signature as a 0x-prefixed hex string with the correct parity bit. fn to_hex(&self) -> String; } diff --git a/bolt-sidecar/src/primitives/delegation.rs b/bolt-sidecar/src/primitives/delegation.rs index 3d41f275f..1b7c391e7 100644 --- a/bolt-sidecar/src/primitives/delegation.rs +++ b/bolt-sidecar/src/primitives/delegation.rs @@ -16,16 +16,25 @@ pub enum SignedMessageAction { Revocation, } +/// A signed delegation message. +/// +/// This is a message that is signed by a validator to delegate its +/// constraint signing power to another key (delegatee). #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct SignedDelegation { + /// The delegation message. pub message: DelegationMessage, + /// The signature of the delegation message. pub signature: BlsSignature, } +/// A delegation message. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct DelegationMessage { action: u8, + /// The validator pubkey that is delegating its power. pub validator_pubkey: BlsPublicKey, + /// The delegatee pubkey that is receiving the power. pub delegatee_pubkey: BlsPublicKey, } @@ -60,16 +69,25 @@ pub fn read_signed_delegations_from_file( } } +/// A signed revocation message. +/// +/// This is a message that is signed by a validator to revoke its +/// constraint signing power from another key (delegatee). #[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)] pub struct SignedRevocation { + /// The revocation message. pub message: RevocationMessage, + /// The signature of the revocation message. pub signature: BlsSignature, } +/// A revocation message. #[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)] pub struct RevocationMessage { action: u8, + /// The validator pubkey that is revoking a delegatee's power. pub validator_pubkey: BlsPublicKey, + /// The delegatee pubkey that is losing the power. pub delegatee_pubkey: BlsPublicKey, } diff --git a/bolt-sidecar/src/primitives/mod.rs b/bolt-sidecar/src/primitives/mod.rs index b4f046b3e..c7cfe5f4a 100644 --- a/bolt-sidecar/src/primitives/mod.rs +++ b/bolt-sidecar/src/primitives/mod.rs @@ -1,6 +1,3 @@ -// TODO: add docs -#![allow(missing_docs)] - use alloy::primitives::U256; use ethereum_consensus::{ crypto::KzgCommitment, @@ -15,7 +12,6 @@ use ethereum_consensus::{ types::mainnet::ExecutionPayload, Fork, }; - use tokio::sync::oneshot; pub use ethereum_consensus::crypto::{PublicKey as BlsPublicKey, Signature as BlsSignature}; @@ -54,7 +50,9 @@ pub struct AccountState { pub has_code: bool, } +/// Builder bid, object that is signed by the proposer #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] +#[allow(missing_docs)] pub struct BuilderBid { pub header: ExecutionPayloadHeader, pub blob_kzg_commitments: List, @@ -64,19 +62,25 @@ pub struct BuilderBid { pub public_key: BlsPublicKey, } +/// Signed builder bid with the proposer signature #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] +#[allow(missing_docs)] pub struct SignedBuilderBid { pub message: BuilderBid, pub signature: BlsSignature, } +/// Signed builder bid with the proposer signature and Bolt inclusion proofs #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] +#[allow(missing_docs)] pub struct SignedBuilderBidWithProofs { pub bid: SignedBuilderBid, pub proofs: List, } +/// A proof that a transaction is included in a block #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] +#[allow(missing_docs)] pub struct ConstraintProof { #[serde(rename = "txHash")] tx_hash: Hash32, @@ -84,35 +88,43 @@ pub struct ConstraintProof { merkle_proof: MerkleProof, } +/// A merkle proof that a transaction is included in a block. #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] pub struct MerkleProof { + /// Index of the transaction in the block index: u64, - // TODO: for now, max 1000 + /// List of hashes that are part of the merkle proof hashes: List, } +/// Merkle multi-proof that a set of transactions are included in a block #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] pub struct MerkleMultiProof { - // We use List here for SSZ, TODO: choose max transaction_hashes: List, generalized_indexes: List, merkle_hashes: List, } +/// Request to fetch a payload for a given slot #[derive(Debug)] pub struct FetchPayloadRequest { + /// Slot number for the payload to fetch pub slot: u64, + /// Channel to send the response to pub response_tx: oneshot::Sender>, } +/// Response to a fetch payload request #[derive(Debug)] +#[allow(missing_docs)] pub struct PayloadAndBid { pub bid: SignedBuilderBid, pub payload: GetPayloadResponse, } -/// TODO: implement SSZ +/// GetPayload response content, with blobs bundle included. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[allow(missing_docs)] pub struct PayloadAndBlobs { pub execution_payload: ExecutionPayload, pub blobs_bundle: BlobsBundle, @@ -127,8 +139,10 @@ impl Default for PayloadAndBlobs { } } +/// Response to a get payload request #[derive(Debug, serde::Serialize, serde::Deserialize)] #[serde(tag = "version", content = "data")] +#[allow(missing_docs)] pub enum GetPayloadResponse { #[serde(rename = "bellatrix")] Bellatrix(ExecutionPayload), @@ -141,6 +155,7 @@ pub enum GetPayloadResponse { } impl GetPayloadResponse { + /// Returns the block hash of the payload pub fn block_hash(&self) -> &Hash32 { match self { GetPayloadResponse::Capella(payload) => payload.block_hash(), @@ -150,6 +165,7 @@ impl GetPayloadResponse { } } + /// Returns the execution payload pub fn execution_payload(&self) -> &ExecutionPayload { match self { GetPayloadResponse::Capella(payload) => payload, diff --git a/bolt-sidecar/src/primitives/transaction.rs b/bolt-sidecar/src/primitives/transaction.rs index ee8e7f299..95be1dd1e 100644 --- a/bolt-sidecar/src/primitives/transaction.rs +++ b/bolt-sidecar/src/primitives/transaction.rs @@ -7,13 +7,28 @@ use serde::{de, ser::SerializeSeq}; /// Trait that exposes additional information on transaction types that don't already do it /// by themselves (e.g. [`PooledTransactionsElement`]). pub trait TransactionExt { + /// Returns the gas limit of the transaction. fn gas_limit(&self) -> u64; + + /// Returns the value of the transaction. fn value(&self) -> U256; + + /// Returns the type of the transaction. fn tx_type(&self) -> TxType; + + /// Returns the kind of the transaction. fn tx_kind(&self) -> TxKind; + + /// Returns the input data of the transaction. fn input(&self) -> &Bytes; + + /// Returns the chain ID of the transaction. fn chain_id(&self) -> Option; + + /// Returns the blob sidecar of the transaction, if any. fn blob_sidecar(&self) -> Option<&BlobTransactionSidecar>; + + /// Returns the size of the transaction in bytes. fn size(&self) -> usize; } @@ -100,6 +115,7 @@ impl TransactionExt for PooledTransactionsElement { } } +/// Returns a string representation of the transaction type. pub const fn tx_type_str(tx_type: TxType) -> &'static str { match tx_type { TxType::Legacy => "legacy", @@ -113,7 +129,9 @@ pub const fn tx_type_str(tx_type: TxType) -> &'static str { /// A wrapper type for a full, complete transaction (i.e. with blob sidecars attached). #[derive(Clone, PartialEq, Eq)] pub struct FullTransaction { + /// The transaction itself. pub tx: PooledTransactionsElement, + /// The sender of the transaction, if recovered. pub sender: Option
, } @@ -173,6 +191,7 @@ impl FullTransaction { Ok(Self { tx, sender: None }) } + /// Returns the inner transaction. pub fn into_inner(self) -> PooledTransactionsElement { self.tx } From 523d31395b101a1d979ef081810b27a7667ad169 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:45:59 +0100 Subject: [PATCH 249/272] chore(sidecar): rm primitives todo comment --- bolt-sidecar/src/primitives/commitment.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/bolt-sidecar/src/primitives/commitment.rs b/bolt-sidecar/src/primitives/commitment.rs index 63764c1aa..e89f5fde2 100644 --- a/bolt-sidecar/src/primitives/commitment.rs +++ b/bolt-sidecar/src/primitives/commitment.rs @@ -50,9 +50,6 @@ impl CommitmentRequest { pub fn as_inclusion_request(&self) -> Option<&InclusionRequest> { match self { CommitmentRequest::Inclusion(req) => Some(req), - // TODO: remove this when we have more request types - #[allow(unreachable_patterns)] - _ => None, } } From 9717a749120241eccd70a730a40eca1d7afa9f40 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:50:55 +0100 Subject: [PATCH 250/272] chore(sidecar): rm state fetcher todo --- bolt-sidecar/src/state/fetcher.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bolt-sidecar/src/state/fetcher.rs b/bolt-sidecar/src/state/fetcher.rs index e2a8935bb..a8d3c4313 100644 --- a/bolt-sidecar/src/state/fetcher.rs +++ b/bolt-sidecar/src/state/fetcher.rs @@ -48,7 +48,8 @@ pub trait StateFetcher { async fn get_chain_id(&self) -> Result; - /// Gets the receipts for the said list of transaction hashes. IMPORTANT: order is not maintained! + /// Gets the receipts for the said list of transaction hashes. IMPORTANT: order is not + /// maintained! async fn get_receipts( &self, hashes: &[TxHash], @@ -77,11 +78,9 @@ impl StateFetcher for StateClient { addresses: Vec<&Address>, block_number: Option, ) -> Result { - // Create a new batch let mut batch = self.client.new_batch(); let tag = block_number.map_or(BlockNumberOrTag::Latest, BlockNumberOrTag::Number); - let mut account_states = HashMap::with_capacity(addresses.len()); let mut nonce_futs = FuturesOrdered::new(); @@ -94,7 +93,6 @@ impl StateFetcher for StateClient { self.client.get_head().await? }; - // TODO: add block number in params for addr in &addresses { // We can use expect here since the only error is related to invalid parameters let nonce = batch @@ -110,8 +108,6 @@ impl StateFetcher for StateClient { code_futs.push_back(code); } - // Make sure to send the batch! - // After the batch is complete, we can get the results. // Note that requests may error separately! batch.send().await?; From f91d7c39238888fffbf81fe59efa76be7c7f0bdf Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:03:04 +0100 Subject: [PATCH 251/272] chore(ci): ignore eigenlayer docs website --- .github/.linkspector.yml | 2 +- .github/workflows/contracts_ci.yml | 4 ++++ .github/workflows/linkspector.yml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/.linkspector.yml b/.github/.linkspector.yml index 01cc6723b..48e082910 100644 --- a/.github/.linkspector.yml +++ b/.github/.linkspector.yml @@ -9,4 +9,4 @@ useGitIgnore: true ignorePatterns: - pattern: "^https://.*etherscan.io/.*$" - + - pattern: "^https://.*docs.eigenlayer.xyz/.*$" diff --git a/.github/workflows/contracts_ci.yml b/.github/workflows/contracts_ci.yml index 58e55948b..97190cef2 100644 --- a/.github/workflows/contracts_ci.yml +++ b/.github/workflows/contracts_ci.yml @@ -10,6 +10,10 @@ on: paths: - "bolt-contracts/**" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: check: name: Foundry project diff --git a/.github/workflows/linkspector.yml b/.github/workflows/linkspector.yml index 92526550a..3fdf37940 100644 --- a/.github/workflows/linkspector.yml +++ b/.github/workflows/linkspector.yml @@ -23,4 +23,4 @@ jobs: reporter: github-pr-review config_file: .github/.linkspector.yml fail_on_error: true - filter_mode: nofilter \ No newline at end of file + filter_mode: nofilter From c6c4a3246cbc634e48abf1ac1c10f4c2734b78aa Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:34:44 +0100 Subject: [PATCH 252/272] chore: refactor sidecar modules and docs --- bolt-sidecar/bin/sidecar.rs | 2 +- bolt-sidecar/src/api/builder.rs | 4 +- bolt-sidecar/src/api/commitments/handlers.rs | 20 ++- bolt-sidecar/src/api/commitments/server.rs | 2 +- bolt-sidecar/src/builder/mod.rs | 3 +- bolt-sidecar/src/builder/payload_builder.rs | 6 +- bolt-sidecar/src/builder/signature.rs | 4 +- bolt-sidecar/src/chain_io/mod.rs | 1 + bolt-sidecar/src/client/constraints_client.rs | 4 - bolt-sidecar/src/client/mod.rs | 12 +- bolt-sidecar/src/client/pubsub.rs | 42 ------ bolt-sidecar/src/client/rpc.rs | 4 +- .../test_util/deneb_get_payload_response.json | 137 ------------------ bolt-sidecar/src/client/test_util/mod.rs | 120 --------------- bolt-sidecar/src/common.rs | 1 - bolt-sidecar/src/config/chain.rs | 2 + bolt-sidecar/src/config/limits.rs | 6 +- bolt-sidecar/src/config/mod.rs | 4 + bolt-sidecar/src/config/telemetry.rs | 1 + bolt-sidecar/src/driver.rs | 7 +- bolt-sidecar/src/lib.rs | 12 +- bolt-sidecar/src/primitives/commitment.rs | 2 +- bolt-sidecar/src/signer/keystore.rs | 4 +- bolt-sidecar/src/signer/local.rs | 2 +- bolt-sidecar/src/signer/mod.rs | 9 +- bolt-sidecar/src/state/consensus.rs | 2 +- bolt-sidecar/src/state/execution.rs | 15 +- bolt-sidecar/src/state/fetcher.rs | 20 +-- bolt-sidecar/src/state/head_tracker.rs | 6 +- bolt-sidecar/src/state/mod.rs | 5 +- bolt-sidecar/src/test_util.rs | 2 +- 31 files changed, 90 insertions(+), 371 deletions(-) delete mode 100644 bolt-sidecar/src/client/pubsub.rs delete mode 100644 bolt-sidecar/src/client/test_util/deneb_get_payload_response.json delete mode 100644 bolt-sidecar/src/client/test_util/mod.rs diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 04a02fc0f..4615e6bc9 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -2,7 +2,7 @@ use clap::Parser; use eyre::{bail, Result}; use tracing::info; -use bolt_sidecar::{telemetry::init_telemetry_stack, Opts, SidecarDriver}; +use bolt_sidecar::{config::Opts, telemetry::init_telemetry_stack, SidecarDriver}; const BOLT: &str = r#" ██████╗ ██████╗ ██╗ ████████╗ diff --git a/bolt-sidecar/src/api/builder.rs b/bolt-sidecar/src/api/builder.rs index ff4d9c035..855776d0f 100644 --- a/bolt-sidecar/src/api/builder.rs +++ b/bolt-sidecar/src/api/builder.rs @@ -26,8 +26,8 @@ use super::spec::{ STATUS_PATH, }; use crate::{ - builder::payload_fetcher::PayloadFetcher, - client::constraints_client::ConstraintsClient, + builder::PayloadFetcher, + client::ConstraintsClient, primitives::{GetPayloadResponse, SignedBuilderBid}, telemetry::ApiMetrics, }; diff --git a/bolt-sidecar/src/api/commitments/handlers.rs b/bolt-sidecar/src/api/commitments/handlers.rs index 3ef12eb2e..0c2a58f90 100644 --- a/bolt-sidecar/src/api/commitments/handlers.rs +++ b/bolt-sidecar/src/api/commitments/handlers.rs @@ -20,7 +20,10 @@ use crate::{ use super::{ jsonrpc::{JsonPayload, JsonResponse}, server::CommitmentsApiInner, - spec::{CommitmentsApi, CommitmentError, RejectionError, GET_VERSION_METHOD, REQUEST_INCLUSION_METHOD}, + spec::{ + CommitmentError, CommitmentsApi, RejectionError, GET_VERSION_METHOD, + REQUEST_INCLUSION_METHOD, + }, }; /// Handler function for the root JSON-RPC path. @@ -32,10 +35,6 @@ pub async fn rpc_entrypoint( ) -> Result, CommitmentError> { debug!("Received new request"); - let (signer, signature) = auth_from_headers(&headers).inspect_err(|e| { - error!("Failed to extract signature from headers: {:?}", e); - })?; - match payload.method.as_str() { GET_VERSION_METHOD => { let version_string = format!("bolt-sidecar-v{CARGO_PKG_VERSION}"); @@ -47,6 +46,11 @@ pub async fn rpc_entrypoint( } REQUEST_INCLUSION_METHOD => { + // Validate the authentication header and extract the signer and signature + let (signer, signature) = auth_from_headers(&headers).inspect_err(|e| { + error!("Failed to extract signature from headers: {:?}", e); + })?; + let Some(request_json) = payload.params.first().cloned() else { return Err(RejectionError::ValidationFailed("Bad params".to_string()).into()); }; @@ -66,8 +70,8 @@ pub async fn rpc_entrypoint( if recovered_signer != signer { error!( - ?recovered_signer, - ?signer, + %recovered_signer, + %signer, "Recovered signer does not match the provided signer" ); @@ -83,7 +87,7 @@ pub async fn rpc_entrypoint( // Create the JSON-RPC response let response = JsonResponse { id: payload.id, - result: serde_json::to_value(inclusion_commitment).unwrap(), + result: serde_json::to_value(inclusion_commitment).expect("infallible"), ..Default::default() }; diff --git a/bolt-sidecar/src/api/commitments/server.rs b/bolt-sidecar/src/api/commitments/server.rs index 61cd7d23f..c828a52b4 100644 --- a/bolt-sidecar/src/api/commitments/server.rs +++ b/bolt-sidecar/src/api/commitments/server.rs @@ -31,7 +31,7 @@ use crate::{ use super::{ middleware::track_server_metrics, spec, - spec::{CommitmentsApi, CommitmentError}, + spec::{CommitmentError, CommitmentsApi}, }; /// Event type emitted by the commitments API. diff --git a/bolt-sidecar/src/builder/mod.rs b/bolt-sidecar/src/builder/mod.rs index d63277a46..955990d0f 100644 --- a/bolt-sidecar/src/builder/mod.rs +++ b/bolt-sidecar/src/builder/mod.rs @@ -8,10 +8,10 @@ use ethereum_consensus::{ use crate::{ common::BlsSecretKeyWrapper, + config::{ChainConfig, Opts}, primitives::{ BuilderBid, GetPayloadResponse, PayloadAndBid, PayloadAndBlobs, SignedBuilderBid, }, - ChainConfig, Opts, }; /// Basic block template handler that can keep track of @@ -30,6 +30,7 @@ use payload_builder::FallbackPayloadBuilder; /// Interface for fetching payloads from the beacon node. pub mod payload_fetcher; +pub use payload_fetcher::{LocalPayloadFetcher, PayloadFetcher}; /// Compatibility types and utilities between Alloy, Reth, /// Ethereum-consensus and other crates. diff --git a/bolt-sidecar/src/builder/payload_builder.rs b/bolt-sidecar/src/builder/payload_builder.rs index c83382627..6da0d951f 100644 --- a/bolt-sidecar/src/builder/payload_builder.rs +++ b/bolt-sidecar/src/builder/payload_builder.rs @@ -21,7 +21,11 @@ use super::{ compat::{to_alloy_execution_payload, to_reth_withdrawal}, BuilderError, }; -use crate::{BeaconClient, Opts, RpcClient}; + +use crate::{ + client::{BeaconClient, RpcClient}, + config::Opts, +}; /// Extra-data payload field used for locally built blocks, decoded in UTF-8. /// diff --git a/bolt-sidecar/src/builder/signature.rs b/bolt-sidecar/src/builder/signature.rs index aebbcecc1..82978fa26 100644 --- a/bolt-sidecar/src/builder/signature.rs +++ b/bolt-sidecar/src/builder/signature.rs @@ -11,7 +11,7 @@ use ethereum_consensus::{ use tree_hash::TreeHash; use tree_hash_derive::TreeHash; -use crate::ChainConfig; +use crate::config::ChainConfig; /// Sign a SSZ object with a BLS secret key, using the Application Builder domain /// for signing arbitrary builder-api messages in the out-of-protocol specifications. @@ -116,7 +116,7 @@ pub fn compute_builder_domain( #[cfg(test)] mod tests { - use crate::{builder::signature::compute_builder_domain, ChainConfig}; + use crate::{builder::signature::compute_builder_domain, config::ChainConfig}; #[test] fn test_compute_builder_domain() { diff --git a/bolt-sidecar/src/chain_io/mod.rs b/bolt-sidecar/src/chain_io/mod.rs index 951e507e6..6bd916776 100644 --- a/bolt-sidecar/src/chain_io/mod.rs +++ b/bolt-sidecar/src/chain_io/mod.rs @@ -1,5 +1,6 @@ /// Wrapper over the BoltManager contract pub mod manager; +pub use manager::BoltManager; /// Utilities and functions used in the Bolt contracts pub mod utils; diff --git a/bolt-sidecar/src/client/constraints_client.rs b/bolt-sidecar/src/client/constraints_client.rs index ccd1e5db3..b59c519c3 100644 --- a/bolt-sidecar/src/client/constraints_client.rs +++ b/bolt-sidecar/src/client/constraints_client.rs @@ -1,7 +1,3 @@ -//! Module for interacting with the Constraints client API via its Builder API interface. -//! The Bolt sidecar's main purpose is to sit between the beacon node and Constraints client, -//! so most requests are simply proxied to its API. - use std::collections::HashSet; use axum::http::StatusCode; diff --git a/bolt-sidecar/src/client/mod.rs b/bolt-sidecar/src/client/mod.rs index c09795fc5..fb58e61cb 100644 --- a/bolt-sidecar/src/client/mod.rs +++ b/bolt-sidecar/src/client/mod.rs @@ -1,9 +1,13 @@ +/// Module for interacting with the Constraints client API via its Builder API interface. +/// The Bolt sidecar's main purpose is to sit between the beacon node and Constraints client, +/// so most requests are simply proxied to its API. pub mod constraints_client; -pub mod pubsub; +pub use constraints_client::ConstraintsClient; + +/// Module defining an RpcClient wrapper around the [`alloy::rpc::client::RpcClient`]. +/// It provides a simple interface to interact with the Execution layer JSON-RPC API. pub mod rpc; +pub use rpc::RpcClient; // Re-export the beacon_api_client pub use beacon_api_client::mainnet::Client as BeaconClient; - -#[cfg(test)] -mod test_util; diff --git a/bolt-sidecar/src/client/pubsub.rs b/bolt-sidecar/src/client/pubsub.rs deleted file mode 100644 index 45d127d2b..000000000 --- a/bolt-sidecar/src/client/pubsub.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{ - ops::{Deref, DerefMut}, - str::FromStr, -}; - -use alloy::{ - pubsub::PubSubFrontend, - rpc::client::{self as alloyClient, ClientBuilder, WsConnect}, - transports::{RpcError, TransportError}, -}; - -use reqwest::Url; - -/// Wrapper around an [`alloy::RpcClient`] that uses WS as the transport. Supports batching -/// JSON-RPC requests. -pub struct PubsubClient(alloyClient::RpcClient); - -#[allow(unused)] -impl PubsubClient { - /// Create a new `PubsubClient` with the given URL. - pub async fn new(url: &str) -> Result> { - let url = Url::from_str(url).unwrap(); - - let client = ClientBuilder::default().ws(WsConnect::new(url)).await?; - - Ok(Self(client)) - } -} - -impl Deref for PubsubClient { - type Target = alloyClient::RpcClient; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for PubsubClient { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} diff --git a/bolt-sidecar/src/client/rpc.rs b/bolt-sidecar/src/client/rpc.rs index ea6857394..d1894bf88 100644 --- a/bolt-sidecar/src/client/rpc.rs +++ b/bolt-sidecar/src/client/rpc.rs @@ -1,6 +1,3 @@ -//! This module contains the `RpcClient` struct, which is a wrapper around the `alloy_rpc_client`. -//! It provides a simple interface to interact with the Execution layer JSON-RPC API. - use std::ops::{Deref, DerefMut}; use alloy::{ @@ -114,6 +111,7 @@ impl RpcClient { } /// Send a raw transaction to the network. + #[allow(unused)] pub async fn send_raw_transaction(&self, raw: Bytes) -> TransportResult { self.0.request("eth_sendRawTransaction", [raw]).await } diff --git a/bolt-sidecar/src/client/test_util/deneb_get_payload_response.json b/bolt-sidecar/src/client/test_util/deneb_get_payload_response.json deleted file mode 100644 index abf2dbc5c..000000000 --- a/bolt-sidecar/src/client/test_util/deneb_get_payload_response.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "version": "deneb", - "data": { - "execution_payload": { - "parent_hash": "0xaa37d684b11d0fa6ec387322e036d2724d22e1570d8a56f5f33192bc5a6071af", - "fee_recipient": "0x614561D2d143621E126e87831AEF287678B442b8", - "state_root": "0x8be14a13156d04c96a91d44289c26e8947430a80ff70811b9802b71a63ca4587", - "receipts_root": "0x9595d62192ec5d8c9282e38f738641938d55be51fa3987f1d5a4de9f0f2a402e", - "logs_bloom": "0x00200000000000000000020080000000000000000000000000000000004000000000000000202000000000000080200080000000000000004002000040200000000080000000000000040008000008201000000000000000000000000800000000000200000000200000140000000000080400000000000080000010000000000000000000000100000000000000000000002000200010080000004000000000020000000000200000000000001004040000200000000000000000000000000000000002000001000000000000000000020400000800001000000000400000000010000000000000000000000000000000008000000000200000000000000000", - "prev_randao": "0x5c5e6cbe95e5aca976d839b8d22aecc14a9373e2e02dfc719010e54fbd1b0a59", - "block_number": "139277", - "gas_limit": "30000000", - "gas_used": "419268", - "timestamp": "1720775112", - "extra_data": "0x426f6c74204275696c646572", - "base_fee_per_gas": "7", - "block_hash": "0x7cccf27d4db8a513dc49836c1a86cd46cdff7a7fcc9f6fea3d8313624871bbef", - "transactions": [ - "0x02f87f8501a2140cff82b58385012a05f20085012a05f20e8304119094fd5f85f5ff7c61b57e404ad29f33a6fd31200f24872386f26fc1000084579f8d2ac080a05d7fed7571ff64f39cf4e48eaf56379ae5c717269455d30051851e4d41fc660fa03b1dc40f862127a8ef69bafe88336bcd04902d79b8f5711f4dfb265a6eb21d94", - "0x02f9017b8501a2140cff8303dec685012a05f2008512a05f2000830249f094843669e5220036eddbaca89d8c8b5b82268a0fc580b901040cc7326300000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000022006292538e66f0000000000000000000000005ba38f2c245618e39f6fa067bf1dec304e73ff3c00000000000000000000000092f0ee29e6e1bf0f7c668317ada78f5774a6cb7f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000003fac6482aee49bf58515be2d3fb58378a8497cc9000000000000000000000000c6cc140787b02ae479a10e41169607000c0d44f6c080a00cf74c45dbe9ee1fb923118ec5ce9db8f88cd651196ed3f9d4f8f2a65827e611a04a6bc1d49a7e18b7c92e8f3614cae116b1832ceb311c81d54b2c87de1545f68f", - "0x03f902e58501a2140cff82301985012a05f2008501a13b8600832dc6c094f70f745d5d86ae14d1be5a99954870361166ec6c8477359400b90244ef16e8450000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001670100000000000000000000000000000010001302e31382e302d64657600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004115896cebbb4cf09200bcbe8d104ff241313a6eede040b5f2c3899e9e7afa92a25a549d5bb6b7c211983262a5d3ca8ca57c50693b1fa04559a0e6c5bab9cf8c121b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0843b9aca00e1a0019101fb28118ceccaabca22a47e35b9c3f12eb2dcb25e5c543d5b75e6cd841f01a01930332dabfdb3d1104fca86c86eeb664f68c897e9b44dda2d3ccbd024ec69eea03733b5d0b2505367077f5c2324fbead4e34aa540d5d5b8b7afee7e0c70b01c07", - "0x02f8708501a2140cff82012f800782520894b6c402298fcb88039bbfde70f5ace791f18cfac88707131d70870dc880c080a03aab1b17ecf28f85de43c7733611759b87d25ba885babacb6b4c625d715415eea03fb52cb7744ccb885906e42f6b9cf82e74b47a4b4b4072af2aa52a8dc472236e" - ], - "withdrawals": [ - { - "index": "572698", - "validator_index": "2131", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "3645" - }, - { - "index": "572699", - "validator_index": "2135", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "3645" - }, - { - "index": "572700", - "validator_index": "2139", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "3645" - }, - { - "index": "572701", - "validator_index": "2143", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "3645" - }, - { - "index": "572702", - "validator_index": "2144", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "3645" - }, - { - "index": "572703", - "validator_index": "1612", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572704", - "validator_index": "1621", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572705", - "validator_index": "1623", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572706", - "validator_index": "1626", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572707", - "validator_index": "1955", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572708", - "validator_index": "1959", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572709", - "validator_index": "1961", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572710", - "validator_index": "1966", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572711", - "validator_index": "1979", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572712", - "validator_index": "1994", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - }, - { - "index": "572713", - "validator_index": "1999", - "address": "0xd4bb555d3b0d7ff17c606161b44e372689c14f4b", - "amount": "2430" - } - ], - "blob_gas_used": "131072", - "excess_blob_gas": "0" - }, - "blobs_bundle": { - "commitments": [ - "0xa20c71d1985996098aa63e8b5dc7b7fedb70de31478fe309dad3ac0e9b6d28d82be8e5e543021a0203dc785742e94b2f" - ], - "proofs": [ - "0xa2acaae116dc9dea269577cc750c4de598a387992943b18be092856c90eaa372a360aad98aa1ff88eb76423b544fca35" - ], - "blobs": [ - "0x000000000d789c3a00080000ffff00c100c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ] - } - } -} diff --git a/bolt-sidecar/src/client/test_util/mod.rs b/bolt-sidecar/src/client/test_util/mod.rs deleted file mode 100644 index 0f6639bcc..000000000 --- a/bolt-sidecar/src/client/test_util/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -#![allow(unused)] - -use beacon_api_client::VersionedValue; -use ethereum_consensus::{ - builder::SignedValidatorRegistration, - deneb::{ - self, - mainnet::{BlobsBundle, SignedBlindedBeaconBlock}, - }, - types::mainnet::ExecutionPayload, -}; -use reqwest::StatusCode; -use serde_json::Value; -use tokio::sync::watch; - -use crate::{ - api::{builder::GetHeaderParams, spec::BuilderApiError}, - primitives::{ - BatchedSignedConstraints, GetPayloadResponse, PayloadAndBlobs, SignedBuilderBid, - SignedDelegation, SignedRevocation, - }, - BuilderApi, ConstraintsApi, -}; - -/// Create a `GetPayloadResponse` with a default `Deneb` execution payload. -pub fn make_get_payload_response() -> GetPayloadResponse { - let execution_payload = ExecutionPayload::Deneb(deneb::ExecutionPayload::default()); - - let blobs_bundle = BlobsBundle::default(); - - GetPayloadResponse::Deneb(PayloadAndBlobs { execution_payload, blobs_bundle }) -} - -pub struct MockConstraintsClient { - pub response_rx: watch::Receiver, -} - -impl MockConstraintsClient { - pub fn new() -> (Self, watch::Sender) { - let (response_tx, response_rx) = watch::channel(Value::Null); - (Self { response_rx }, response_tx) - } -} - -#[async_trait::async_trait] -impl BuilderApi for MockConstraintsClient { - async fn status(&self) -> Result { - Err(BuilderApiError::Generic( - "MockConstraintsClient does not support getting status".to_string(), - )) - } - - async fn register_validators( - &self, - _registrations: Vec, - ) -> Result<(), BuilderApiError> { - Err(BuilderApiError::Generic( - "MockConstraintsClient does not support registering validators".to_string(), - )) - } - - async fn get_header( - &self, - _params: GetHeaderParams, - ) -> Result { - let response = self.response_rx.borrow().clone(); - let bid = serde_json::from_value(response)?; - Ok(bid) - } - - async fn get_payload( - &self, - _signed_block: SignedBlindedBeaconBlock, - ) -> Result { - let response = self.response_rx.borrow().clone(); - let payload = serde_json::from_value(response)?; - Ok(payload) - } -} - -#[async_trait::async_trait] -impl ConstraintsApi for MockConstraintsClient { - async fn submit_constraints( - &self, - _constraints: &BatchedSignedConstraints, - ) -> Result<(), BuilderApiError> { - Err(BuilderApiError::Generic( - "MockConstraintsClient does not support submitting constraints".to_string(), - )) - } - - async fn get_header_with_proofs( - &self, - _params: GetHeaderParams, - ) -> Result, BuilderApiError> { - let response = self.response_rx.borrow().clone(); - let bid = serde_json::from_value(response)?; - Ok(bid) - } - - async fn delegate(&self, signed_data: &[SignedDelegation]) -> Result<(), BuilderApiError> { - Err(BuilderApiError::Generic( - "MockConstraintsClient does not support delegating".to_string(), - )) - } - - async fn revoke(&self, signed_data: &[SignedRevocation]) -> Result<(), BuilderApiError> { - Err(BuilderApiError::Generic("MockConstraintsClient does not support revoking".to_string())) - } -} - -#[test] -fn test_decode_get_payload_response() { - let stringified = - std::fs::read_to_string("./src/client/test_util/deneb_get_payload_response.json") - .expect("failed to read get payload response file"); - - let parsed_response: GetPayloadResponse = - serde_json::from_str(&stringified).expect("failed to parse get payload response"); -} diff --git a/bolt-sidecar/src/common.rs b/bolt-sidecar/src/common.rs index 4287c00f8..d76e28ff2 100644 --- a/bolt-sidecar/src/common.rs +++ b/bolt-sidecar/src/common.rs @@ -326,7 +326,6 @@ mod tests { let counter = Arc::new(Mutex::new(Counter::new(3))); // Fail 3 times, succeed on 4th let result = retry_with_backoff(5, || async { - println!("attempt"); let counter = Arc::clone(&counter); let mut counter = counter.lock().await; counter.retryable_fn().await diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 20dec3ab4..2f9cc92f0 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -26,6 +26,7 @@ pub const APPLICATION_BUILDER_DOMAIN_MASK: [u8; 4] = [0, 0, 0, 1]; /// The domain mask for signing commit-boost messages. pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67]; +/// Default chain configuration for the sidecar. pub const DEFAULT_CHAIN_CONFIG: ChainConfig = ChainConfig { chain: Chain::Mainnet, commitment_deadline: DEFAULT_COMMITMENT_DEADLINE_IN_MILLIS, @@ -87,6 +88,7 @@ impl Deref for ChainConfig { /// Supported chains for the sidecar #[derive(Debug, Clone, Copy, ValueEnum, Deserialize)] #[clap(rename_all = "kebab_case")] +#[allow(missing_docs)] pub enum Chain { Mainnet, Holesky, diff --git a/bolt-sidecar/src/config/limits.rs b/bolt-sidecar/src/config/limits.rs index 7e50d24b8..8511c76bb 100644 --- a/bolt-sidecar/src/config/limits.rs +++ b/bolt-sidecar/src/config/limits.rs @@ -3,9 +3,13 @@ use std::num::NonZero; use clap::Parser; use serde::Deserialize; -// Default limit values +/// Default max commitments to accept per block. pub const DEFAULT_MAX_COMMITMENTS: usize = 128; + +/// Default max committed gas per block. pub const DEFAULT_MAX_COMMITTED_GAS: u64 = 10_000_000; + +/// Default min priority fee to accept for a commitment. pub const DEFAULT_MIN_PRIORITY_FEE: u128 = 1_000_000_000; // 1 Gwei /// Limits for the sidecar. diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 0a3797a27..4198bc949 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -3,15 +3,19 @@ use clap::Parser; use reqwest::Url; use serde::Deserialize; +/// Chain configuration options. pub mod chain; pub use chain::ChainConfig; +/// Commitment and constraint signing related options. pub mod constraint_signing; pub use constraint_signing::ConstraintSigningOpts; +/// Telemetry and metrics related options. pub mod telemetry; use telemetry::TelemetryOpts; +/// Operating limits for commitments and constraints. pub mod limits; use limits::LimitsOpts; diff --git a/bolt-sidecar/src/config/telemetry.rs b/bolt-sidecar/src/config/telemetry.rs index 01bc179d6..58cae7148 100644 --- a/bolt-sidecar/src/config/telemetry.rs +++ b/bolt-sidecar/src/config/telemetry.rs @@ -1,6 +1,7 @@ use clap::Parser; use serde::Deserialize; +/// Telemetry and metrics related options. #[derive(Parser, Debug, Clone, Deserialize)] pub struct TelemetryOpts { /// The port on which to expose Prometheus metrics diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 442e37155..d66c6e321 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -12,12 +12,14 @@ use tracing::{debug, error, info, warn}; use crate::{ builder::payload_fetcher::LocalPayloadFetcher, - chain_io::manager::BoltManager, + chain_io::BoltManager, + client::ConstraintsClient, commitments::{ server::{CommitmentEvent, CommitmentsApiServer}, spec::CommitmentError, }, common::retry_with_backoff, + config::Opts, crypto::{SignableBLS, SignerECDSA}, primitives::{ read_signed_delegations_from_file, CommitmentRequest, ConstraintsMessage, @@ -27,8 +29,7 @@ use crate::{ start_builder_proxy_server, state::{fetcher::StateFetcher, ConsensusState, ExecutionState, HeadTracker, StateClient}, telemetry::ApiMetrics, - BuilderProxyConfig, CommitBoostSigner, ConstraintsApi, ConstraintsClient, LocalBuilder, Opts, - SignerBLS, + BuilderProxyConfig, CommitBoostSigner, ConstraintsApi, LocalBuilder, SignerBLS, }; /// The driver for the sidecar, responsible for managing the main event loop. diff --git a/bolt-sidecar/src/lib.rs b/bolt-sidecar/src/lib.rs index 73868ab16..f130d0724 100644 --- a/bolt-sidecar/src/lib.rs +++ b/bolt-sidecar/src/lib.rs @@ -11,14 +11,13 @@ pub use api::{ spec::{BuilderApi, ConstraintsApi}, }; +/// Different client types for interacting with APIs mod client; -pub use client::{constraints_client::ConstraintsClient, rpc::RpcClient, BeaconClient}; /// Telemetry and metrics utilities pub mod telemetry; /// Common types and compatibility utilities -/// (To be refactored) mod common; /// Driver for the sidecar, which manages the main event loop @@ -32,8 +31,7 @@ pub mod builder; pub use builder::LocalBuilder; /// Configuration and command-line argument parsing -mod config; -pub use config::{ChainConfig, Opts}; +pub mod config; /// Crypto utilities, including BLS and ECDSA pub mod crypto; @@ -41,12 +39,14 @@ pub mod crypto; /// Primitive types and utilities pub mod primitives; -/// State management and fetching for EVM simulation +///The `state` module is responsible for keeping a local copy of relevant state that is needed +/// to simulate commitments against. It is updated on every block. +/// It consists of both execution and consensus states. pub mod state; /// The signers available to the sidecar mod signer; -pub use signer::{commit_boost::CommitBoostSigner, SignerBLS}; +pub use signer::{CommitBoostSigner, SignerBLS}; /// Utilities and contracts wrappers for interacting with the Bolt registry pub mod chain_io; diff --git a/bolt-sidecar/src/primitives/commitment.rs b/bolt-sidecar/src/primitives/commitment.rs index e89f5fde2..83d57004c 100644 --- a/bolt-sidecar/src/primitives/commitment.rs +++ b/bolt-sidecar/src/primitives/commitment.rs @@ -1,7 +1,7 @@ -use serde::{de, Deserialize, Deserializer, Serialize}; use std::str::FromStr; use alloy::primitives::{keccak256, Address, Signature, B256}; +use serde::{de, Deserialize, Deserializer, Serialize}; use crate::crypto::SignerECDSA; diff --git a/bolt-sidecar/src/signer/keystore.rs b/bolt-sidecar/src/signer/keystore.rs index e1975978b..7db4805f7 100644 --- a/bolt-sidecar/src/signer/keystore.rs +++ b/bolt-sidecar/src/signer/keystore.rs @@ -16,8 +16,8 @@ use ssz::Encode; use crate::{ builder::signature::compute_signing_root, + config::ChainConfig, crypto::bls::{cl_public_key_to_arr, BLSSig}, - ChainConfig, }; use super::SignerResult; @@ -199,7 +199,7 @@ mod tests { use blst::min_pk::SecretKey; use ethereum_consensus::crypto::PublicKey as BlsPublicKey; - use crate::{signer::local::LocalSigner, ChainConfig}; + use crate::{config::ChainConfig, signer::local::LocalSigner}; use super::KeystoreSigner; /// The str path of the root of the project diff --git a/bolt-sidecar/src/signer/local.rs b/bolt-sidecar/src/signer/local.rs index f7481b9d5..93bfdee32 100644 --- a/bolt-sidecar/src/signer/local.rs +++ b/bolt-sidecar/src/signer/local.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use blst::{min_pk::Signature, BLST_ERROR}; use ethereum_consensus::{crypto::PublicKey as ClPublicKey, deneb::compute_signing_root}; -use crate::{crypto::bls::BLSSig, ChainConfig}; +use crate::{config::ChainConfig, crypto::bls::BLSSig}; pub use blst::min_pk::SecretKey; use super::SignerResult; diff --git a/bolt-sidecar/src/signer/mod.rs b/bolt-sidecar/src/signer/mod.rs index c9ae92a1a..74c975699 100644 --- a/bolt-sidecar/src/signer/mod.rs +++ b/bolt-sidecar/src/signer/mod.rs @@ -2,14 +2,17 @@ use std::collections::HashSet; use ethereum_consensus::crypto::bls::PublicKey as BlsPublicKey; +/// Commit-Boost remote signer client wrapper. pub mod commit_boost; -use commit_boost::CommitBoostSigner; +pub use commit_boost::CommitBoostSigner; +/// EIP-2335 keystore signer implementation. pub mod keystore; -use keystore::KeystoreSigner; +pub use keystore::KeystoreSigner; +/// Local signer implementation. pub mod local; -use local::LocalSigner; +pub use local::LocalSigner; /// Error in the signer. #[derive(Debug, thiserror::Error)] diff --git a/bolt-sidecar/src/state/consensus.rs b/bolt-sidecar/src/state/consensus.rs index 3ba56860f..25fcd2ac1 100644 --- a/bolt-sidecar/src/state/consensus.rs +++ b/bolt-sidecar/src/state/consensus.rs @@ -10,9 +10,9 @@ use tracing::debug; use super::CommitmentDeadline; use crate::{ + client::BeaconClient, primitives::{CommitmentRequest, Slot}, telemetry::ApiMetrics, - BeaconClient, }; /// Consensus-related errors diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 71d3fbc2f..bca9b45d6 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -167,9 +167,9 @@ pub struct ExecutionState { /// Other values used for validation. #[derive(Debug)] pub struct ValidationParams { - block_gas_limit: u64, - max_tx_input_bytes: usize, - max_init_code_byte_size: usize, + pub block_gas_limit: u64, + pub max_tx_input_bytes: usize, + pub max_init_code_byte_size: usize, } impl Default for ValidationParams { @@ -232,7 +232,6 @@ impl ExecutionState { ) -> Result<(), ValidationError> { let CommitmentRequest::Inclusion(req) = request; - let signer = req.signer().expect("Set signer"); req.recover_signers()?; let target_slot = req.slot; @@ -352,7 +351,7 @@ impl ExecutionState { return Err(ValidationError::SlotTooLow(highest_slot_for_account)); } - trace!(?signer, nonce_diff, %balance_diff, "Applying diffs to account state"); + trace!(nonce_diff, %balance_diff, "Applying diffs to account state"); let account_state = match self.account_state(sender).copied() { Some(account) => account, @@ -464,12 +463,12 @@ impl ExecutionState { if let Some(template) = self.remove_block_template(slot) { debug!(%slot, "Removed block template for slot"); let hashes = template.transaction_hashes(); - let receipts = self.client.get_receipts(&hashes).await?; + let receipts = self.client.get_receipts_unordered(&hashes).await?; let mut receipts_len = 0; for receipt in receipts.iter().flatten() { - // Calculate the total tip revenue for this transaction: (effective_gas_price - - // basefee) * gas_used + // Calculate the total tip revenue for this transaction: + // (effective_gas_price - basefee) * gas_used let tip_per_gas = receipt.effective_gas_price - self.basefee; let total_tip = tip_per_gas * receipt.gas_used; diff --git a/bolt-sidecar/src/state/fetcher.rs b/bolt-sidecar/src/state/fetcher.rs index a8d3c4313..fc7fec755 100644 --- a/bolt-sidecar/src/state/fetcher.rs +++ b/bolt-sidecar/src/state/fetcher.rs @@ -1,7 +1,3 @@ -#![allow(missing_docs)] -#![allow(unused_variables)] -#![allow(missing_debug_implementations)] - use std::{collections::HashMap, time::Duration}; use alloy::{ @@ -15,7 +11,7 @@ use reqwest::Url; use reth_primitives::TxHash; use tracing::error; -use crate::{client::rpc::RpcClient, primitives::AccountState}; +use crate::{client::RpcClient, primitives::AccountState}; use super::execution::StateUpdate; @@ -28,29 +24,35 @@ const RETRY_BACKOFF_MS: u64 = 200; /// A trait for fetching state updates. #[async_trait::async_trait] pub trait StateFetcher { + /// Get the state updates for the specified addresses, at the specified block number. async fn get_state_update( &self, addresses: Vec<&Address>, head: Option, ) -> Result; + /// Get the head of the chain. async fn get_head(&self) -> Result; + /// Get the basefee of the latest block or the block at the specified number. async fn get_basefee(&self, block_number: Option) -> Result; + /// Get the blob basefee of the latest block or the block at the specified number. async fn get_blob_basefee(&self, block_number: Option) -> Result; + /// Get the account state for the specified address at the specified block number. async fn get_account_state( &self, address: &Address, block_number: Option, ) -> Result; + /// Get the chain ID. async fn get_chain_id(&self) -> Result; - /// Gets the receipts for the said list of transaction hashes. IMPORTANT: order is not - /// maintained! - async fn get_receipts( + /// Get the receipts for the said list of transaction hashes. + /// IMPORTANT: order is not maintained in the result. + async fn get_receipts_unordered( &self, hashes: &[TxHash], ) -> Result>, TransportError>; @@ -213,7 +215,7 @@ impl StateFetcher for StateClient { self.client.get_chain_id().await } - async fn get_receipts( + async fn get_receipts_unordered( &self, hashes: &[TxHash], ) -> Result>, TransportError> { diff --git a/bolt-sidecar/src/state/head_tracker.rs b/bolt-sidecar/src/state/head_tracker.rs index ff9c9ae13..4dbeab38a 100644 --- a/bolt-sidecar/src/state/head_tracker.rs +++ b/bolt-sidecar/src/state/head_tracker.rs @@ -5,7 +5,7 @@ use std::time::Duration; use tokio::{sync::broadcast, task::AbortHandle, time::sleep}; use tracing::warn; -use crate::BeaconClient; +use crate::client::BeaconClient; /// The delay between retries when attempting to reconnect to the beacon client const RETRY_DELAY: Duration = Duration::from_secs(1); @@ -98,9 +98,7 @@ mod tests { use reqwest::Url; use tracing::warn; - use crate::{ - state::head_tracker::HeadTracker, test_util::try_get_beacon_api_url, BeaconClient, - }; + use crate::{client::BeaconClient, state::HeadTracker, test_util::try_get_beacon_api_url}; #[tokio::test] async fn test_fetch_next_beacon_head() -> eyre::Result<()> { diff --git a/bolt-sidecar/src/state/mod.rs b/bolt-sidecar/src/state/mod.rs index 6e0e20e67..5938ae6e3 100644 --- a/bolt-sidecar/src/state/mod.rs +++ b/bolt-sidecar/src/state/mod.rs @@ -1,7 +1,3 @@ -//! The `state` module is responsible for keeping a local copy of relevant state that is needed -//! to simulate commitments against. It is updated on every block. It has both execution state and -//! consensus state. - use std::{ pin::Pin, task::{Context, Poll}, @@ -11,6 +7,7 @@ use std::{ use futures::{future::poll_fn, Future, FutureExt}; use tokio::time::Sleep; +/// Module to perform state validation. mod execution; pub use execution::{ExecutionState, ValidationError}; diff --git a/bolt-sidecar/src/test_util.rs b/bolt-sidecar/src/test_util.rs index 6eace3748..f17626c00 100644 --- a/bolt-sidecar/src/test_util.rs +++ b/bolt-sidecar/src/test_util.rs @@ -21,13 +21,13 @@ use tracing::warn; use crate::{ common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}, + config::{ChainConfig, Opts}, crypto::{ecdsa::SignableECDSA, SignableBLS}, primitives::{ CommitmentRequest, ConstraintsMessage, DelegationMessage, FullTransaction, InclusionRequest, RevocationMessage, SignedConstraints, SignedDelegation, SignedRevocation, }, signer::local::LocalSigner, - ChainConfig, Opts, }; /// The URL of the test execution client HTTP API. From 9c35be396a66ddedc82ff86163afc6f970b82c1c Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 6 Nov 2024 23:14:56 +0100 Subject: [PATCH 253/272] chore: more docs + refactor --- bolt-sidecar/src/api/builder.rs | 8 ++++++++ bolt-sidecar/src/api/commitments/handlers.rs | 2 +- bolt-sidecar/src/api/commitments/server.rs | 4 ++-- bolt-sidecar/src/builder/mod.rs | 3 +++ bolt-sidecar/src/builder/template.rs | 3 --- bolt-sidecar/src/driver.rs | 17 ++++++++++------- bolt-sidecar/src/lib.rs | 10 ++-------- bolt-sidecar/src/signer/commit_boost.rs | 2 ++ bolt-sidecar/src/signer/keystore.rs | 9 +++++++-- bolt-sidecar/src/signer/local.rs | 8 ++++++-- bolt-sidecar/src/signer/mod.rs | 2 ++ 11 files changed, 43 insertions(+), 25 deletions(-) diff --git a/bolt-sidecar/src/api/builder.rs b/bolt-sidecar/src/api/builder.rs index 855776d0f..e3302e1cc 100644 --- a/bolt-sidecar/src/api/builder.rs +++ b/bolt-sidecar/src/api/builder.rs @@ -39,6 +39,7 @@ const GET_HEADER_WITH_PROOFS_TIMEOUT: Duration = Duration::from_millis(500); /// A proxy server for the builder API. /// Forwards all requests to the target after interception. +#[derive(Debug)] pub struct BuilderProxyServer { proxy_target: T, /// INVARIANT: This will be `Some` IFF we have signed a local header for the latest slot. @@ -47,7 +48,9 @@ pub struct BuilderProxyServer { payload_fetcher: P, } +/// Parameters for the get_header request. #[derive(Debug, Deserialize)] +#[allow(missing_docs)] pub struct GetHeaderParams { pub slot: u64, pub parent_hash: Hash32, @@ -60,6 +63,7 @@ where T: ConstraintsApi, P: PayloadFetcher + Send + Sync, { + /// Create a new builder proxy server. pub fn new(proxy_target: T, payload_fetcher: P) -> Self { Self { proxy_target, local_payload: Mutex::new(None), payload_fetcher } } @@ -164,6 +168,8 @@ where Ok(Json(versioned_bid)) } + /// Gets the payload. If we have a locally built payload, we return it. + /// Otherwise, we forward the request to the constraints client. pub async fn get_payload( State(server): State>>, req: Request, @@ -259,7 +265,9 @@ async fn index() -> Html<&'static str> { Html("Hello") } +/// Errors that can occur when checking the integrity of a locally built payload. #[derive(Error, Debug, Clone)] +#[allow(missing_docs)] pub enum LocalPayloadIntegrityError { #[error( "Locally built payload does not match signed header. diff --git a/bolt-sidecar/src/api/commitments/handlers.rs b/bolt-sidecar/src/api/commitments/handlers.rs index 0c2a58f90..d9dce6e02 100644 --- a/bolt-sidecar/src/api/commitments/handlers.rs +++ b/bolt-sidecar/src/api/commitments/handlers.rs @@ -12,7 +12,7 @@ use serde_json::Value; use tracing::{debug, error, info, instrument}; use crate::{ - commitments::headers::auth_from_headers, + api::commitments::headers::auth_from_headers, common::CARGO_PKG_VERSION, primitives::{commitment::SignatureError, InclusionRequest}, }; diff --git a/bolt-sidecar/src/api/commitments/server.rs b/bolt-sidecar/src/api/commitments/server.rs index c828a52b4..db37656b6 100644 --- a/bolt-sidecar/src/api/commitments/server.rs +++ b/bolt-sidecar/src/api/commitments/server.rs @@ -21,7 +21,7 @@ use tower_http::timeout::TimeoutLayer; use tracing::{error, info}; use crate::{ - commitments::handlers, + api::commitments::handlers, primitives::{ commitment::{InclusionCommitment, SignedCommitment}, CommitmentRequest, InclusionRequest, @@ -169,7 +169,7 @@ fn make_router(state: Arc) -> Router { #[cfg(test)] mod test { - use crate::commitments::{jsonrpc::JsonResponse, spec::SIGNATURE_HEADER}; + use crate::api::commitments::{jsonrpc::JsonResponse, spec::SIGNATURE_HEADER}; use alloy::signers::{k256::SecretKey, local::PrivateKeySigner}; use serde_json::json; diff --git a/bolt-sidecar/src/builder/mod.rs b/bolt-sidecar/src/builder/mod.rs index 955990d0f..cd0151780 100644 --- a/bolt-sidecar/src/builder/mod.rs +++ b/bolt-sidecar/src/builder/mod.rs @@ -16,6 +16,9 @@ use crate::{ /// Basic block template handler that can keep track of /// the local commitments according to protocol validity rules. +/// +/// The built template can be used as a fallback block in case of no valid +/// response from all relays. pub mod template; pub use template::BlockTemplate; diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index 5a2d02f2e..24a7a0aae 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -1,6 +1,3 @@ -//! Package `template` contains the functionality for building local block templates that can -//! be used as a fallback. It's also used to keep any intermediary state that is needed to simulate -//! new commitment requests. use std::collections::HashMap; use alloy::primitives::{Address, U256}; diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index d66c6e321..9c0d78e27 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -11,13 +11,17 @@ use tokio::sync::mpsc; use tracing::{debug, error, info, warn}; use crate::{ + api::{ + builder::{start_builder_proxy_server, BuilderProxyConfig}, + commitments::{ + server::{CommitmentEvent, CommitmentsApiServer}, + spec::CommitmentError, + }, + spec::ConstraintsApi, + }, builder::payload_fetcher::LocalPayloadFetcher, chain_io::BoltManager, client::ConstraintsClient, - commitments::{ - server::{CommitmentEvent, CommitmentsApiServer}, - spec::CommitmentError, - }, common::retry_with_backoff, config::Opts, crypto::{SignableBLS, SignerECDSA}, @@ -25,11 +29,10 @@ use crate::{ read_signed_delegations_from_file, CommitmentRequest, ConstraintsMessage, FetchPayloadRequest, SignedConstraints, TransactionExt, }, - signer::{keystore::KeystoreSigner, local::LocalSigner}, - start_builder_proxy_server, + signer::{keystore::KeystoreSigner, local::LocalSigner, CommitBoostSigner, SignerBLS}, state::{fetcher::StateFetcher, ConsensusState, ExecutionState, HeadTracker, StateClient}, telemetry::ApiMetrics, - BuilderProxyConfig, CommitBoostSigner, ConstraintsApi, LocalBuilder, SignerBLS, + LocalBuilder, }; /// The driver for the sidecar, responsible for managing the main event loop. diff --git a/bolt-sidecar/src/lib.rs b/bolt-sidecar/src/lib.rs index f130d0724..9782a3494 100644 --- a/bolt-sidecar/src/lib.rs +++ b/bolt-sidecar/src/lib.rs @@ -4,12 +4,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] /// All APIs in use by the sidecar. -mod api; -pub use api::{ - builder::{start_builder_proxy_server, BuilderProxyConfig}, - commitments, - spec::{BuilderApi, ConstraintsApi}, -}; +pub mod api; /// Different client types for interacting with APIs mod client; @@ -45,8 +40,7 @@ pub mod primitives; pub mod state; /// The signers available to the sidecar -mod signer; -pub use signer::{CommitBoostSigner, SignerBLS}; +pub mod signer; /// Utilities and contracts wrappers for interacting with the Bolt registry pub mod chain_io; diff --git a/bolt-sidecar/src/signer/commit_boost.rs b/bolt-sidecar/src/signer/commit_boost.rs index f91a7c726..47b473efa 100644 --- a/bolt-sidecar/src/signer/commit_boost.rs +++ b/bolt-sidecar/src/signer/commit_boost.rs @@ -28,7 +28,9 @@ pub struct CommitBoostSigner { proxy_ecdsa: Arc>>, } +/// Error in the Commit-Boost signer. #[derive(Debug, Error)] +#[allow(missing_docs)] pub enum CommitBoostError { #[error("failed to sign constraint: {0}")] NoSignature(String), diff --git a/bolt-sidecar/src/signer/keystore.rs b/bolt-sidecar/src/signer/keystore.rs index 7db4805f7..274c4fa13 100644 --- a/bolt-sidecar/src/signer/keystore.rs +++ b/bolt-sidecar/src/signer/keystore.rs @@ -1,5 +1,3 @@ -//! An ERC-2335 keystore signer. - use std::{ collections::HashSet, ffi::OsString, @@ -22,7 +20,9 @@ use crate::{ use super::SignerResult; +/// Error in the keystore signer. #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum KeystoreError { #[error("failed to read keystore directory: {0}")] ReadFromDirectory(#[from] std::io::Error), @@ -38,6 +38,8 @@ pub enum KeystoreError { SignatureLength(String, String), } +/// A signer that can sign messages with multiple keypairs loaded from +/// ERC-2335 keystores files. #[derive(Clone)] pub struct KeystoreSigner { keypairs: Vec, @@ -46,6 +48,7 @@ pub struct KeystoreSigner { impl KeystoreSigner { /// Creates a new `KeystoreSigner` from the keystore files in the `keys_path` directory. + /// The secret is expected to be the same password for all the keystore files. pub fn from_password( keys_path: &PathBuf, password: &[u8], @@ -67,6 +70,8 @@ impl KeystoreSigner { Ok(Self { keypairs, chain }) } + /// Creates a new `KeystoreSigner` from the keystore files in the `keys_path` directory. + /// The secret files are expected to be in the `secrets_path` directory. pub fn from_secrets_directory( keys_path: &PathBuf, secrets_path: &Path, diff --git a/bolt-sidecar/src/signer/local.rs b/bolt-sidecar/src/signer/local.rs index 93bfdee32..e812b456c 100644 --- a/bolt-sidecar/src/signer/local.rs +++ b/bolt-sidecar/src/signer/local.rs @@ -1,17 +1,21 @@ use std::fmt::Debug; -use blst::{min_pk::Signature, BLST_ERROR}; +use blst::{ + min_pk::{SecretKey, Signature}, + BLST_ERROR, +}; use ethereum_consensus::{crypto::PublicKey as ClPublicKey, deneb::compute_signing_root}; use crate::{config::ChainConfig, crypto::bls::BLSSig}; -pub use blst::min_pk::SecretKey; use super::SignerResult; /// The BLS Domain Separator used in Ethereum 2.0. pub const BLS_DST_PREFIX: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +/// Error in the local signer. #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum LocalSignerError { #[error("failed to compute signing root: {0}")] SigningRootComputation(#[from] ethereum_consensus::error::Error), diff --git a/bolt-sidecar/src/signer/mod.rs b/bolt-sidecar/src/signer/mod.rs index 74c975699..5584addbd 100644 --- a/bolt-sidecar/src/signer/mod.rs +++ b/bolt-sidecar/src/signer/mod.rs @@ -16,6 +16,7 @@ pub use local::LocalSigner; /// Error in the signer. #[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] pub enum SignerError { #[error("local signer error: {0}")] LocalSigner(#[from] local::LocalSignerError), @@ -25,6 +26,7 @@ pub enum SignerError { Keystore(#[from] keystore::KeystoreError), } +/// Result type for the signer. pub type SignerResult = std::result::Result; /// Signer for BLS signatures. From be2e6b0fd51c423846566990e07614d90c9552dc Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 7 Nov 2024 16:57:08 +0100 Subject: [PATCH 254/272] feat(bolt-cli): add Dockerfile --- bolt-cli/Cargo.toml | 5 ++++ bolt-cli/Dockerfile | 55 ++++++++++++++++++++++++++++++++++++ bolt-cli/rust-toolchain.toml | 3 ++ 3 files changed, 63 insertions(+) create mode 100644 bolt-cli/Dockerfile create mode 100644 bolt-cli/rust-toolchain.toml diff --git a/bolt-cli/Cargo.toml b/bolt-cli/Cargo.toml index 64882c203..09d759a67 100644 --- a/bolt-cli/Cargo.toml +++ b/bolt-cli/Cargo.toml @@ -2,6 +2,7 @@ name = "bolt" version = "0.1.0" edition = "2021" +default-run = "bolt" [dependencies] # async @@ -40,3 +41,7 @@ tempfile = "3.13.0" [build-dependencies] tonic-build = "0.12.3" + +[[bin]] +name = "bolt" +path = "./src/main.rs" diff --git a/bolt-cli/Dockerfile b/bolt-cli/Dockerfile new file mode 100644 index 000000000..c020a6616 --- /dev/null +++ b/bolt-cli/Dockerfile @@ -0,0 +1,55 @@ +# Stage 1: Base compiler image with necessary dependencies +FROM rust:1.81.0-slim-bullseye AS base + +# Install cargo-chef for dependency caching +RUN cargo install cargo-chef + +# Set the working directory to /app +WORKDIR /app + +# Stage 2: Planner (generating the recipe) +FROM base AS planner + +# Copy only Cargo files to cache dependencies +COPY Cargo.toml Cargo.lock ./ + +# Prepare the recipe for caching dependencies (Cargo.toml/Cargo.lock) +RUN cargo chef prepare --recipe-path recipe.json + +# Stage 3: Builder with necessary dependencies for OpenSSL +FROM base AS builder + +# Install required dependencies for building Rust projects (OpenSSL, pkg-config) +RUN apt-get update && apt-get install -y \ + pkg-config \ + libssl-dev \ + build-essential \ + protobuf-compiler + +# Copy the generated recipe from the planner stage +COPY --from=planner /app/recipe.json recipe.json + +# Cache the dependencies using the cargo-chef recipe +RUN cargo chef cook --release --recipe-path recipe.json + +# Copy the source code and build the project +COPY . . +RUN cargo build --release + +# Stage 4: Final runtime image (lean image) +FROM debian:bullseye-slim AS runtime + +# Set the working directory for the final container +WORKDIR /usr/local/bin + +# Install necessary runtime dependencies (OpenSSL and CA certificates) +RUN apt-get update && apt-get install -y \ + libssl-dev \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Copy the compiled binary from the builder stage +COPY --from=builder /app/target/release/bolt /usr/local/bin/bolt + +# Define the entrypoint for the container +ENTRYPOINT ["/usr/local/bin/bolt"] diff --git a/bolt-cli/rust-toolchain.toml b/bolt-cli/rust-toolchain.toml new file mode 100644 index 000000000..bbf217f21 --- /dev/null +++ b/bolt-cli/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.81.0" +profile = "default" From 82d869d39458784eff6eea7d5ec8852c92f8a91d Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 7 Nov 2024 17:26:54 +0100 Subject: [PATCH 255/272] chore(holesky/pbs): update helix config --- testnets/holesky/helix-config.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/testnets/holesky/helix-config.yml b/testnets/holesky/helix-config.yml index 3305a335b..802b963ef 100644 --- a/testnets/holesky/helix-config.yml +++ b/testnets/holesky/helix-config.yml @@ -4,6 +4,8 @@ postgres: db_name: helixdb user: postgres password: postgres + region: 1 + region_name: "eu-central" redis: url: redis://redis:6379 @@ -14,4 +16,15 @@ simulator: beacon_clients: - url: http://beacon:4000 +builders: + - pub_key: "aa1488eae4b06a1fff840a2b6db167afc520758dc2c8af0dfb57037954df3431b747e2f900fe8805f05d635e9a29717b" + builder_info: + collateral: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + is_optimistic: false + builder_id: "Bolt Builder" + +# If empty, all routes are enabled +router_config: + enabled_routes: [] + network_config: !Holesky From 5b8c71ac1a79c5834265d60b2215452f5e73f139 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:57:22 +0100 Subject: [PATCH 256/272] fix: rm validator indexes in cb config file --- testnets/holesky/commit-boost/cb-bolt-config.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/testnets/holesky/commit-boost/cb-bolt-config.toml b/testnets/holesky/commit-boost/cb-bolt-config.toml index f78b0fc8d..cb4ed3a92 100644 --- a/testnets/holesky/commit-boost/cb-bolt-config.toml +++ b/testnets/holesky/commit-boost/cb-bolt-config.toml @@ -121,7 +121,6 @@ BOLT_SIDECAR_ENGINE_API = "http://100.85.200.41:4451" BOLT_SIDECAR_JWT_HEX = "89732cef77d7e9a20021ee8f419dbbb51bdf7f60586932c272ddef02e70cb755" # The engine JWT BOLT_SIDECAR_BUILDER_PROXY_PORT = "18551" # The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. BOLT_SIDECAR_FEE_RECIPIENT = "0x0000000000000000000000000000000000000000" # The fee recipient -BOLT_SIDECAR_VALIDATOR_INDEXES = "1..2" # The active validator indexes BOLT_SIDECAR_METRICS_PORT = "10000" From dccb14d237c1fc2909b9729455c1fbd21391853d Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 8 Nov 2024 09:38:11 +0100 Subject: [PATCH 257/272] fix: broken link in md doc --- bolt-contracts/docs/admin/deploying.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/bolt-contracts/docs/admin/deploying.md b/bolt-contracts/docs/admin/deploying.md index 215827083..42cc89da4 100644 --- a/bolt-contracts/docs/admin/deploying.md +++ b/bolt-contracts/docs/admin/deploying.md @@ -5,13 +5,14 @@ ## Configuration There are 2 JSON configuration files: -- [`config/holesky/deployments.json`](../../config/holesky/deployments.json): contains deployment addresses of EigenLayer ([here](https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/README.md#deployments)) and Symbiotic ([here](https://docs.symbiotic.fi/deployments)). -- [`config/holesky/parameters.json`](../../config/holesky/parameters.json): contains the launch parameters for `BoltParameters`. - +- [`config/holesky/deployments.json`](../../config/holesky/deployments.json): contains deployment addresses of EigenLayer ([here](https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/README.md#deployments)) and Symbiotic ([here](https://docs.symbiotic.fi/deployments/current)). +- [`config/holesky/parameters.json`](../../config/holesky/parameters.json): contains the launch parameters for `BoltParameters`. ## Deployment Guide + Make sure we have a full compilation for the Foundry Upgrades Toolkit: + ```bash forge clean && forge build ``` @@ -54,6 +55,7 @@ addresses into the [`deployments.json`](../../config/holesky/deployments.json) f ### Deployment Run the following script to deploy Bolt V1: + ```bash forge script script/holesky/admin/Deploy.s.sol --rpc-url $HOLESKY_RPC --private-key $ADMIN_PRIVATE_KEY --verify --broadcast -vvvv ``` @@ -74,17 +76,18 @@ forge script script/holesky/admin/helpers/Symbiotic.s.sol --rpc-url $HOLESKY_RPC Also set the AVS metadata in the EigenLayer AVS Directory, needs to be run with the **admin private key** used at deployment. ```bash -forge script script/holesky/admin/helpers/RegisterAVS.s.sol --rpc-url $HOLESKY_RPC --private-key $ADMIN_PRIVATE_KEY --broadcast -vvvv +forge script script/holesky/admin/helpers/RegisterAVS.s.sol --rpc-url $HOLESKY_RPC --private-key $ADMIN_PRIVATE_KEY --broadcast -vvvv ``` > [!IMPORTANT] > After the `deployments.json` file has been fully updated with the correct contract addresses, push it to Github. - ### Other Scripts #### Modifying supported Symbiotic Vaults + This script will update supported vaults according to `deployments.json`, and remove any vaults that have been whitelisted but are no longer in the `symbiotic.supportedVaults` list. + ```bash forge script script/holesky/admin/helpers/UpdateSupportedVaults.s.sol --rpc-url $HOLESKY_RPC --private-key $ADMIN_PRIVATE_KEY --broadcast -vvv -``` \ No newline at end of file +``` From 16205d2d693e486905e8ff17f20e7a5007a71af3 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 8 Nov 2024 13:49:39 +0100 Subject: [PATCH 258/272] fix!(config): remove empty environment variables set like 'VAR=' when parsing options --- bolt-sidecar/bin/sidecar.rs | 10 +++++++++- bolt-sidecar/src/config/mod.rs | 28 ++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 4615e6bc9..88b94f53e 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -2,7 +2,11 @@ use clap::Parser; use eyre::{bail, Result}; use tracing::info; -use bolt_sidecar::{config::Opts, telemetry::init_telemetry_stack, SidecarDriver}; +use bolt_sidecar::{ + config::{strip_empty_envs, Opts}, + telemetry::init_telemetry_stack, + SidecarDriver, +}; const BOLT: &str = r#" ██████╗ ██████╗ ██╗ ████████╗ @@ -14,6 +18,10 @@ const BOLT: &str = r#" #[tokio::main] async fn main() -> Result<()> { + if dotenvy::dotenv().is_ok() { + strip_empty_envs()?; + info!("Loaded .env file"); + } let opts = Opts::parse(); if let Err(err) = init_telemetry_stack(opts.telemetry.metrics_port()) { diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index 4198bc949..d9f952840 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -31,7 +31,6 @@ pub const DEFAULT_CONSTRAINTS_PROXY_PORT: u16 = 18550; /// Command-line options for the Bolt sidecar #[derive(Debug, Parser, Deserialize)] -#[clap(trailing_var_arg = true)] pub struct Opts { /// Port to listen on for incoming JSON-RPC requests of the Commitments API. /// This port should be open on your firewall in order to receive external requests! @@ -96,15 +95,40 @@ pub struct Opts { /// Additional unrecognized arguments. Useful for CI and testing /// to avoid issues on potential extra flags provided (e.g. "--exact" from cargo nextest). #[cfg(test)] - #[clap(allow_hyphen_values = true)] + #[clap(allow_hyphen_values = true, trailing_var_arg = true)] #[serde(default)] pub extra_args: Vec, } +/// It removes environment variables that are set as empty strings, i.e. like `MY_VAR=`. This is +/// useful to avoid unexpected edge cases and because we don't have options that make sense with an +/// empty string value. +pub fn strip_empty_envs() -> eyre::Result<()> { + for item in dotenvy::dotenv_iter()? { + let (key, val) = item?; + if val.is_empty() { + std::env::remove_var(key) + } + } + + Ok(()) +} + #[cfg(test)] mod tests { + use dotenvy::dotenv; + use super::*; + #[test] + #[ignore = "Doesn't need to run in CI, only for local development"] + fn test_strip_empty_envs() { + let _ = dotenv().expect("to load .env file"); + strip_empty_envs().expect("to strip empty envs"); + let opts = Opts::parse(); + println!("{:#?}", opts); + } + #[test] fn test_validate_cli_flags() { use clap::CommandFactory; From 23f726e2880b5c295793443cf4f3628d47d137ae Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:30:38 +0100 Subject: [PATCH 259/272] fix: make cargo chef detect binary --- bolt-cli/Cargo.toml | 5 ----- bolt-cli/Dockerfile | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/bolt-cli/Cargo.toml b/bolt-cli/Cargo.toml index 09d759a67..64882c203 100644 --- a/bolt-cli/Cargo.toml +++ b/bolt-cli/Cargo.toml @@ -2,7 +2,6 @@ name = "bolt" version = "0.1.0" edition = "2021" -default-run = "bolt" [dependencies] # async @@ -41,7 +40,3 @@ tempfile = "3.13.0" [build-dependencies] tonic-build = "0.12.3" - -[[bin]] -name = "bolt" -path = "./src/main.rs" diff --git a/bolt-cli/Dockerfile b/bolt-cli/Dockerfile index c020a6616..a2b7d8683 100644 --- a/bolt-cli/Dockerfile +++ b/bolt-cli/Dockerfile @@ -13,6 +13,9 @@ FROM base AS planner # Copy only Cargo files to cache dependencies COPY Cargo.toml Cargo.lock ./ +# Copy the main.rs file to allow cargo do detect a binary +COPY src/main.rs ./src/main.rs + # Prepare the recipe for caching dependencies (Cargo.toml/Cargo.lock) RUN cargo chef prepare --recipe-path recipe.json From 51a46de069f4344cdf7e83cfdd533df00805dab6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 8 Nov 2024 16:09:20 +0100 Subject: [PATCH 260/272] fix(holesky/docker): add environment variable replacement in entrypoint and not in 'environment' --- testnets/holesky/docker-compose.yml | 24 +++++++++--------------- testnets/holesky/entrypoint.sh | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 15 deletions(-) create mode 100755 testnets/holesky/entrypoint.sh diff --git a/testnets/holesky/docker-compose.yml b/testnets/holesky/docker-compose.yml index 0219e0670..f7cdd8535 100644 --- a/testnets/holesky/docker-compose.yml +++ b/testnets/holesky/docker-compose.yml @@ -4,23 +4,17 @@ services: container_name: bolt-sidecar-holesky restart: unless-stopped ports: - - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # Bolt RPC port (this should be opened on your firewall!) + # NOTE: to read these envs, it is necessary to provide them via `--env-file` or having them already set. + - "${BOLT_SIDECAR_PORT:-8017}:${BOLT_SIDECAR_PORT:-8017}" # This port should be opened on your firewall! - "${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}:${BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT:-18550}" - entrypoint: /bin/sh -c /usr/local/bin/bolt-sidecar - env_file: ./bolt-sidecar.env - environment: - # The "+" syntax replaces the environment variable with the alternate valuee only if set - # Reference: https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax - # - # NOTE: These enviroment variables take precedence over the ones provided via `env_file`. - # Reference: https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/ - BOLT_SIDECAR_DELEGATIONS_PATH: "${BOLT_SIDECAR_DELEGATIONS_PATH+/etc/delegations.json}" - BOLT_SIDECAR_KEYSTORE_PATH: "${BOLT_SIDECAR_KEYSTORE_PATH+/etc/keystore}" - BOLT_SIDECAR_KEYSTORE_SECRETS_PATH: "${BOLT_SIDECAR_KEYSTORE_SECRETS_PATH+/etc/secrets}" + entrypoint: /usr/local/bin/entrypoint.sh volumes: - - ${BOLT_SIDECAR_DELEGATIONS_PATH:-/dev/null:/etc/delegations.json} - - ${BOLT_SIDECAR_KEYSTORE_PATH:-/dev/null:/etc/keystores} - - ${BOLT_SIDECAR_KEYSTORE_SECRETS_PATH:-/dev/null:/etc/secrets} + - ./entrypoint.sh:/usr/local/bin/entrypoint.sh + - ./bolt-sidecar.env:/usr/local/bin/.env + # NOTE: to read these envs, it is necessary to provide them via `--env-file` or having them already set. + - ${BOLT_SIDECAR_DELEGATIONS_PATH:-/dev/null}:/etc/delegations.json + - ${BOLT_SIDECAR_KEYSTORE_PATH:-/dev/null}:/etc/keystores + - ${BOLT_SIDECAR_KEYSTORE_SECRETS_PATH:-/dev/null}:/etc/secrets networks: - bolt-default diff --git a/testnets/holesky/entrypoint.sh b/testnets/holesky/entrypoint.sh new file mode 100755 index 000000000..3849458c6 --- /dev/null +++ b/testnets/holesky/entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +# Override the environment variables provided by the user. +# +# The "+" syntax replaces the environment variable with the alternate valuee +# only if set. +# Reference: https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax + +# Ensure these environment variables are either empty or set with the +# alternative values, overriding what's provided with the `--env-file` flag in +# the Docker Compose file and matching the volume mounts. +export BOLT_SIDECAR_DELEGATIONS_PATH="${BOLT_SIDECAR_DELEGATIONS_PATH+/etc/delegations.json}" +export BOLT_SIDECAR_KEYSTORE_PATH="${BOLT_SIDECAR_KEYSTORE_PATH+/etc/keystore}" +export BOLT_SIDECAR_KEYSTORE_SECRETS_PATH="${BOLT_SIDECAR_KEYSTORE_SECRETS_PATH+/etc/secrets}" + +/usr/local/bin/bolt-sidecar From 478ef10ed95038e358143284a4317627a8d715e6 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 8 Nov 2024 16:09:32 +0100 Subject: [PATCH 261/272] chore(devnet): bump helix image --- scripts/kurtosis_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kurtosis_config.yaml b/scripts/kurtosis_config.yaml index 31e8e30a4..2130cac4b 100644 --- a/scripts/kurtosis_config.yaml +++ b/scripts/kurtosis_config.yaml @@ -28,7 +28,7 @@ mev_params: # instead of MEV-Boost by Flashbots bolt_boost_image: ghcr.io/chainbound/bolt-boost:0.1.0 bolt_sidecar_image: ghcr.io/chainbound/bolt-sidecar:0.1.0 - helix_relay_image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc2 + helix_relay_image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc3 mev_relay_image: ghcr.io/chainbound/bolt-relay:0.1.0 mev_builder_image: ghcr.io/chainbound/bolt-builder:0.1.0 mev_boost_image: ghcr.io/chainbound/bolt-mev-boost:0.1.0 From c2106e296f3d198671157d7d51d4f4eaa6b74679 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 8 Nov 2024 16:11:23 +0100 Subject: [PATCH 262/272] fix(sidecar): init fmt at the absolute beginning --- bolt-sidecar/bin/sidecar.rs | 46 ++++++++++++++++++++++++------- bolt-sidecar/src/config/mod.rs | 16 +++++++---- bolt-sidecar/src/telemetry/mod.rs | 15 ---------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 88b94f53e..d97e493c1 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -1,9 +1,14 @@ use clap::Parser; -use eyre::{bail, Result}; +use eyre::bail; use tracing::info; +use tracing_subscriber::{ + fmt::Layer as FmtLayer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, + Registry, +}; + use bolt_sidecar::{ - config::{strip_empty_envs, Opts}, + config::{remove_empty_envs, Opts}, telemetry::init_telemetry_stack, SidecarDriver, }; @@ -17,16 +22,13 @@ const BOLT: &str = r#" ╚═════╝ ╚═════╝ ╚══════╝╚═╝ "#; #[tokio::main] -async fn main() -> Result<()> { - if dotenvy::dotenv().is_ok() { - strip_empty_envs()?; - info!("Loaded .env file"); - } +async fn main() -> eyre::Result<()> { + read_env_file()?; + init_tracing()?; + let opts = Opts::parse(); - if let Err(err) = init_telemetry_stack(opts.telemetry.metrics_port()) { - bail!("Failed to initialize telemetry stack: {:?}", err) - } + init_telemetry_stack(opts.telemetry.metrics_port())?; println!("{BOLT}"); @@ -55,3 +57,27 @@ async fn main() -> Result<()> { } } } + +fn init_tracing() -> eyre::Result<()> { + let std_layer = FmtLayer::default().with_writer(std::io::stdout).with_filter( + EnvFilter::builder() + .with_default_directive("bolt_sidecar=info".parse()?) + .from_env_lossy() + .add_directive("reqwest=error".parse()?) + .add_directive("alloy_transport_http=error".parse()?), + ); + Registry::default().with(std_layer).try_init()?; + Ok(()) +} + +fn read_env_file() -> eyre::Result<()> { + match dotenvy::dotenv() { + // It means the .env file hasn't been found but it's okay since it's optional + Err(dotenvy::Error::Io(_)) => (), + Err(err) => bail!("Failed to load .env file: {:?}", err), + Ok(path) => println!("Loaded environment variables from path: {:?}", path), + }; + + remove_empty_envs()?; + Ok(()) +} diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index d9f952840..f55c00a84 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -1,3 +1,5 @@ +use std::env; + use alloy::primitives::Address; use clap::Parser; use reqwest::Url; @@ -18,6 +20,7 @@ use telemetry::TelemetryOpts; /// Operating limits for commitments and constraints. pub mod limits; use limits::LimitsOpts; +use tracing::debug; use crate::common::{BlsSecretKeyWrapper, EcdsaSecretKeyWrapper, JwtSecretConfig}; @@ -103,10 +106,11 @@ pub struct Opts { /// It removes environment variables that are set as empty strings, i.e. like `MY_VAR=`. This is /// useful to avoid unexpected edge cases and because we don't have options that make sense with an /// empty string value. -pub fn strip_empty_envs() -> eyre::Result<()> { - for item in dotenvy::dotenv_iter()? { - let (key, val) = item?; - if val.is_empty() { +pub fn remove_empty_envs() -> eyre::Result<()> { + for item in env::vars() { + let (key, val) = item; + if val.trim().is_empty() { + debug!("removing empty env var: {}", key); std::env::remove_var(key) } } @@ -122,9 +126,9 @@ mod tests { #[test] #[ignore = "Doesn't need to run in CI, only for local development"] - fn test_strip_empty_envs() { + fn test_remove_empty_envs() { let _ = dotenv().expect("to load .env file"); - strip_empty_envs().expect("to strip empty envs"); + remove_empty_envs().expect("to remove empty envs"); let opts = Opts::parse(); println!("{:#?}", opts); } diff --git a/bolt-sidecar/src/telemetry/mod.rs b/bolt-sidecar/src/telemetry/mod.rs index ea71a7398..3f2bf6bfe 100644 --- a/bolt-sidecar/src/telemetry/mod.rs +++ b/bolt-sidecar/src/telemetry/mod.rs @@ -3,10 +3,6 @@ use std::net::SocketAddr; use eyre::{bail, Result}; use metrics_exporter_prometheus::PrometheusBuilder; use tracing::info; -use tracing_subscriber::{ - fmt::Layer as FmtLayer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, - Registry, -}; mod metrics; pub use metrics::ApiMetrics; @@ -15,17 +11,6 @@ pub use metrics::ApiMetrics; /// /// **This function should be called at the beginning of the program.** pub fn init_telemetry_stack(metrics_port: Option) -> Result<()> { - // 1. Initialize tracing to stdout - let std_layer = FmtLayer::default().with_writer(std::io::stdout).with_filter( - EnvFilter::builder() - .with_default_directive("bolt_sidecar=info".parse()?) - .from_env_lossy() - .add_directive("reqwest=error".parse()?) - .add_directive("alloy_transport_http=error".parse()?), - ); - Registry::default().with(std_layer).try_init()?; - - // 2. Initialize metrics recorder and start the Prometheus server if let Some(metrics_port) = metrics_port { let prometheus_addr = SocketAddr::from(([0, 0, 0, 0], metrics_port)); let builder = PrometheusBuilder::new().with_http_listener(prometheus_addr); From 1e5f4f21135417baa99f94b56181254f361caf1f Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Fri, 8 Nov 2024 18:59:56 +0100 Subject: [PATCH 263/272] fix(sidecar/config): missing field in Debug impl --- bolt-sidecar/src/config/constraint_signing.rs | 1 + bolt-sidecar/src/telemetry/mod.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bolt-sidecar/src/config/constraint_signing.rs b/bolt-sidecar/src/config/constraint_signing.rs index 50356f696..f3712e9ab 100644 --- a/bolt-sidecar/src/config/constraint_signing.rs +++ b/bolt-sidecar/src/config/constraint_signing.rs @@ -49,6 +49,7 @@ impl fmt::Debug for ConstraintSigningOpts { .field("keystore_password", &"********") // Hides the actual password .field("keystore_path", &self.keystore_path) .field("keystore_secrets_path", &self.keystore_secrets_path) + .field("delegations_path", &self.delegations_path) .finish() } } diff --git a/bolt-sidecar/src/telemetry/mod.rs b/bolt-sidecar/src/telemetry/mod.rs index 3f2bf6bfe..eefcaa0a1 100644 --- a/bolt-sidecar/src/telemetry/mod.rs +++ b/bolt-sidecar/src/telemetry/mod.rs @@ -16,7 +16,7 @@ pub fn init_telemetry_stack(metrics_port: Option) -> Result<()> { let builder = PrometheusBuilder::new().with_http_listener(prometheus_addr); if let Err(e) = builder.install() { - bail!("failed to install Prometheus recorder: {:?}", e); + bail!("failed to init telemetry stack. Error installing Prometheus recorder: {:?}", e); } else { info!( "Telemetry initialized. Serving Prometheus metrics at: http://{}", From 3173638f920b215284a63f55d3c8bc22210410a3 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Sat, 9 Nov 2024 16:42:34 +0700 Subject: [PATCH 264/272] fix(sidecar): don't remove constraints before accounting --- bolt-sidecar/src/state/execution.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index bca9b45d6..079c94253 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -457,9 +457,9 @@ impl ExecutionState { let update = self.client.get_state_update(accounts, block_number).await?; trace!(%slot, ?update, "Applying execution state update"); - self.apply_state_update(update); - // Remove any block templates that are no longer valid + // NOTE: this needs to be called BEFORE applying the state update or we might remove + // constraints for which we need to get the receipts. if let Some(template) = self.remove_block_template(slot) { debug!(%slot, "Removed block template for slot"); let hashes = template.transaction_hashes(); @@ -494,6 +494,8 @@ impl ExecutionState { } } + self.apply_state_update(update); + Ok(()) } From 3cdcaf77df50e48699a8c3400402378a54a702bc Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 11 Nov 2024 11:49:00 +0100 Subject: [PATCH 265/272] chore: restore tracing in telemetry stack init --- bolt-sidecar/bin/sidecar.rs | 18 ------------------ bolt-sidecar/src/telemetry/mod.rs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index d97e493c1..3461a35b6 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -2,11 +2,6 @@ use clap::Parser; use eyre::bail; use tracing::info; -use tracing_subscriber::{ - fmt::Layer as FmtLayer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, - Registry, -}; - use bolt_sidecar::{ config::{remove_empty_envs, Opts}, telemetry::init_telemetry_stack, @@ -24,7 +19,6 @@ const BOLT: &str = r#" #[tokio::main] async fn main() -> eyre::Result<()> { read_env_file()?; - init_tracing()?; let opts = Opts::parse(); @@ -58,18 +52,6 @@ async fn main() -> eyre::Result<()> { } } -fn init_tracing() -> eyre::Result<()> { - let std_layer = FmtLayer::default().with_writer(std::io::stdout).with_filter( - EnvFilter::builder() - .with_default_directive("bolt_sidecar=info".parse()?) - .from_env_lossy() - .add_directive("reqwest=error".parse()?) - .add_directive("alloy_transport_http=error".parse()?), - ); - Registry::default().with(std_layer).try_init()?; - Ok(()) -} - fn read_env_file() -> eyre::Result<()> { match dotenvy::dotenv() { // It means the .env file hasn't been found but it's okay since it's optional diff --git a/bolt-sidecar/src/telemetry/mod.rs b/bolt-sidecar/src/telemetry/mod.rs index eefcaa0a1..1171e96b2 100644 --- a/bolt-sidecar/src/telemetry/mod.rs +++ b/bolt-sidecar/src/telemetry/mod.rs @@ -3,6 +3,10 @@ use std::net::SocketAddr; use eyre::{bail, Result}; use metrics_exporter_prometheus::PrometheusBuilder; use tracing::info; +use tracing_subscriber::{ + fmt::Layer as FmtLayer, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, + Registry, +}; mod metrics; pub use metrics::ApiMetrics; @@ -11,6 +15,15 @@ pub use metrics::ApiMetrics; /// /// **This function should be called at the beginning of the program.** pub fn init_telemetry_stack(metrics_port: Option) -> Result<()> { + let std_layer = FmtLayer::default().with_writer(std::io::stdout).with_filter( + EnvFilter::builder() + .with_default_directive("bolt_sidecar=info".parse()?) + .from_env_lossy() + .add_directive("reqwest=error".parse()?) + .add_directive("alloy_transport_http=error".parse()?), + ); + + Registry::default().with(std_layer).try_init()?; if let Some(metrics_port) = metrics_port { let prometheus_addr = SocketAddr::from(([0, 0, 0, 0], metrics_port)); let builder = PrometheusBuilder::new().with_http_listener(prometheus_addr); From 8161a72fdc81c88637e736ffd0ce121c65fc6800 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 11 Nov 2024 11:50:20 +0100 Subject: [PATCH 266/272] fix(holesky/docs): docker compose command --- testnets/holesky/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnets/holesky/README.md b/testnets/holesky/README.md index ba9df71be..66cb65d68 100644 --- a/testnets/holesky/README.md +++ b/testnets/holesky/README.md @@ -474,7 +474,7 @@ Once the configuration files are in place, make sure you are in the `testnets/holesky` directory and then run: ```bash -docker compose up -d --env-file bolt-sidecar.env +docker compose --env-file bolt-sidecar.env up -d ``` The docker compose setup comes with various observability tools, such as From 22e9eb2f2c27e1771ff92ada4247a9ac040ecbb9 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Fri, 8 Nov 2024 18:55:26 +0100 Subject: [PATCH 267/272] chore(sidecar): cleanup + remove TODOs (continue) --- bolt-sidecar/bin/sidecar.rs | 25 +--- bolt-sidecar/src/api/builder.rs | 2 +- bolt-sidecar/src/chain_io/manager.rs | 130 ++++++++---------- bolt-sidecar/src/chain_io/utils.rs | 44 +++++- bolt-sidecar/src/client/constraints_client.rs | 8 +- bolt-sidecar/src/config/chain.rs | 5 - bolt-sidecar/src/driver.rs | 65 +++++---- bolt-sidecar/src/primitives/delegation.rs | 27 +++- 8 files changed, 171 insertions(+), 135 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 3461a35b6..4e2c8696d 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -18,37 +18,22 @@ const BOLT: &str = r#" #[tokio::main] async fn main() -> eyre::Result<()> { + println!("{}", BOLT); + read_env_file()?; let opts = Opts::parse(); init_telemetry_stack(opts.telemetry.metrics_port())?; - println!("{BOLT}"); - info!(chain = opts.chain.name(), "Starting Bolt sidecar"); if opts.constraint_signing.constraint_private_key.is_some() { - match SidecarDriver::with_local_signer(&opts).await { - Ok(driver) => driver.run_forever().await, - Err(err) => { - bail!("Failed to initialize the sidecar driver with local signer: {:?}", err) - } - } + SidecarDriver::with_local_signer(&opts).await?.run_forever().await } else if opts.constraint_signing.commit_boost_signer_url.is_some() { - match SidecarDriver::with_commit_boost_signer(&opts).await { - Ok(driver) => driver.run_forever().await, - Err(err) => { - bail!("Failed to initialize the sidecar driver with commit boost: {:?}", err) - } - } + SidecarDriver::with_commit_boost_signer(&opts).await?.run_forever().await } else { - match SidecarDriver::with_keystore_signer(&opts).await { - Ok(driver) => driver.run_forever().await, - Err(err) => { - bail!("Failed to initialize the sidecar driver with keystore signer: {:?}", err) - } - } + SidecarDriver::with_keystore_signer(&opts).await?.run_forever().await } } diff --git a/bolt-sidecar/src/api/builder.rs b/bolt-sidecar/src/api/builder.rs index e3302e1cc..d5eab4e81 100644 --- a/bolt-sidecar/src/api/builder.rs +++ b/bolt-sidecar/src/api/builder.rs @@ -240,7 +240,7 @@ where { info!( port = config.server_port, - target = config.constraints_client.url.to_string(), + target = config.constraints_client.target(), "Starting builder proxy..." ); diff --git a/bolt-sidecar/src/chain_io/manager.rs b/bolt-sidecar/src/chain_io/manager.rs index 01f0d488d..9b6ef8a36 100644 --- a/bolt-sidecar/src/chain_io/manager.rs +++ b/bolt-sidecar/src/chain_io/manager.rs @@ -1,16 +1,11 @@ -use std::str::FromStr; -use tracing::error; - use alloy::{ - contract::Error as ContractError, - primitives::{Address, Bytes}, + primitives::Address, providers::{ProviderBuilder, RootProvider}, sol, - sol_types::SolInterface, - transports::{http::Http, TransportError}, + transports::http::Http, }; use ethereum_consensus::primitives::BlsPublicKey; -use eyre::bail; +use eyre::{bail, Context}; use reqwest::{Client, Url}; use serde::Serialize; @@ -23,7 +18,8 @@ use crate::config::chain::Chain; use super::utils::{self, CompressedHash}; -const CHUNK_SIZE: usize = 100; +/// Maximum number of keys to fetch from the EL node in a single query. +const MAX_CHUNK_SIZE: usize = 100; /// A wrapper over a BoltManagerContract that exposes various utility methods. #[derive(Debug, Clone)] @@ -58,91 +54,85 @@ impl BoltManager { ) -> eyre::Result> { let hashes_with_preimages = utils::pubkey_hashes(keys); let mut hashes = hashes_with_preimages.keys().cloned().collect::>(); + let total_keys = hashes.len(); + let chunk_count = total_keys.div_ceil(MAX_CHUNK_SIZE); - let mut proposers_statuses = Vec::with_capacity(hashes.len()); + let mut proposers_statuses = Vec::with_capacity(total_keys); let mut i = 0; while !hashes.is_empty() { i += 1; - // No more than CHUNK_SIZE at a time to avoid EL config limits - // - // TODO: write an unsafe function that splits a vec into owned chunks without - // allocating - let hashes_chunk = hashes.drain(..CHUNK_SIZE.min(hashes.len())).collect::>(); - - debug!( - "fetching {} proposer statuses for chunk {} of {}", - hashes_chunk.len(), - i, - total_keys.div_ceil(CHUNK_SIZE) - ); - - let returndata = self.0.getProposerStatuses(hashes_chunk).call().await; - - // TODO: clean this after https://github.com/alloy-rs/alloy/issues/787 is merged - let error = match returndata.map(|data| data.statuses) { - Ok(statuses) => { - for status in &statuses { - if !status.active { - bail!( - "validator with public key {:?} and public key hash {:?} is not active in Bolt", - hashes_with_preimages.get(&status.pubkeyHash), - status.pubkeyHash - ); - } else if status.operator != commitment_signer_pubkey { - bail!(generate_operator_keys_mismatch_error( - status.pubkeyHash, - commitment_signer_pubkey, - status.operator - )); - } - } - - proposers_statuses.extend(statuses); - - continue; + // No more than MAX_CHUNK_SIZE at a time to avoid EL config limits + let chunk_size = MAX_CHUNK_SIZE.min(hashes.len()); + let hashes_chunk = hashes.drain(..chunk_size).collect::>(); + + debug!("fetching proposer statuses for chunk {} of {}", i, chunk_count); + + let returndata = match self.0.getProposerStatuses(hashes_chunk).call().await { + Ok(returndata) => returndata, + Err(error) => { + let decoded_error = utils::try_parse_contract_error(error) + .wrap_err("Failed to fetch proposer statuses from EL client")?; + + bail!(generate_bolt_manager_error(decoded_error, commitment_signer_pubkey)); } - Err(error) => match error { - ContractError::TransportError(TransportError::ErrorResp(err)) => { - error!("error response from contract: {:?}", err); - let data = err.data.unwrap_or_default(); - let data = data.get().trim_matches('"'); - let data = Bytes::from_str(data)?; - - BoltManagerContractErrors::abi_decode(&data, true)? - } - e => return Err(e)?, - }, }; - match error { - BoltManagerContractErrors::ValidatorDoesNotExist(ValidatorDoesNotExist { - pubkeyHash: pubkey_hash, - }) => { - bail!("ValidatorDoesNotExist -- validator with public key {:?} and public key hash {:?} is not registered in Bolt", hashes_with_preimages.get(&pubkey_hash), pubkey_hash); - } - BoltManagerContractErrors::InvalidQuery(_) => { - bail!("InvalidQuery -- invalid zero public key hash"); - } - BoltManagerContractErrors::KeyNotFound(_) => { - bail!("KeyNotFound -- operator associated with commitment signer public key {:?} is not registered in Bolt", commitment_signer_pubkey); + for status in &returndata.statuses { + if !status.active { + bail!( + "validator with public key {:?} and public key hash {} is not active in Bolt", + hashes_with_preimages.get(&status.pubkeyHash), + status.pubkeyHash + ); + } else if status.operator != commitment_signer_pubkey { + bail!(generate_operator_keys_mismatch_error( + status.pubkeyHash, + commitment_signer_pubkey, + status.operator + )); } } + + proposers_statuses.extend(returndata.statuses); + + continue; } Ok(proposers_statuses) } } +fn generate_bolt_manager_error( + error: BoltManagerContractErrors, + commitment_signer_pubkey: Address, +) -> String { + match error { + BoltManagerContractErrors::ValidatorDoesNotExist(ValidatorDoesNotExist { + pubkeyHash: pubkey_hash, + }) => { + format!("BoltManager::ValidatorDoesNotExist: validator with public key hash {} is not registered in Bolt", pubkey_hash) + } + BoltManagerContractErrors::InvalidQuery(_) => { + "BoltManager::InvalidQuery: invalid zero public key hash".to_string() + } + BoltManagerContractErrors::KeyNotFound(_) => { + format!("BoltManager::KeyNotFound: operator associated with commitment signer public key {} is not registered in Bolt", commitment_signer_pubkey) + } + } +} + fn generate_operator_keys_mismatch_error( pubkey_hash: CompressedHash, commitment_signer_pubkey: Address, operator: Address, ) -> String { format!( - "mismatch between commitment signer public key and authorized operator address for validator with public key hash {:?} in Bolt.\n - commitment signer public key: {:?}\n - authorized operator address: {:?}", + "Mismatch between commitment signer public key and authorized operator address for validator\nwith public key hash {:?}. + - commitment signer public key: {} + - authorized operator address: {}", pubkey_hash, commitment_signer_pubkey, operator diff --git a/bolt-sidecar/src/chain_io/utils.rs b/bolt-sidecar/src/chain_io/utils.rs index c98a16b12..e81aa4b98 100644 --- a/bolt-sidecar/src/chain_io/utils.rs +++ b/bolt-sidecar/src/chain_io/utils.rs @@ -1,6 +1,11 @@ -use std::collections::HashMap; +use std::{collections::HashMap, str::FromStr}; -use alloy::primitives::{FixedBytes, B512}; +use alloy::{ + contract::Error as ContractError, + primitives::{Bytes, FixedBytes, B512}, + sol_types::SolInterface, + transports::TransportError, +}; use ethereum_consensus::primitives::BlsPublicKey; use reth_primitives::keccak256; @@ -39,6 +44,41 @@ fn pubkey_hash_digest(key: &BlsPublicKey) -> B512 { onchain_pubkey_repr } +/// Try to decode a contract error into a specific Solidity error interface. +/// If the error cannot be decoded or it is not a contract error, return the original error. +/// +/// Example usage: +/// +/// ```rust no_run +/// sol! { +/// library ErrorLib { +/// error SomeError(uint256 code); +/// } +/// } +/// +/// // call a contract that may return an error with the SomeError interface +/// let returndata = match myContract.call().await { +/// Ok(returndata) => returndata, +/// Err(err) => { +/// let decoded_error = try_decode_contract_error::(err)?; +/// // handle the decoded error however you want; for example, return it +/// return Err(decoded_error); +/// }, +/// } +/// ``` +pub fn try_parse_contract_error(error: ContractError) -> Result { + match error { + ContractError::TransportError(TransportError::ErrorResp(resp)) => { + let data = resp.data.unwrap_or_default(); + let data = data.get().trim_matches('"'); + let data = Bytes::from_str(data).unwrap_or_default(); + + T::abi_decode(&data, true).map_err(Into::into) + } + _ => Err(error), + } +} + #[cfg(test)] mod tests { use alloy::hex; diff --git a/bolt-sidecar/src/client/constraints_client.rs b/bolt-sidecar/src/client/constraints_client.rs index b59c519c3..f39f392fd 100644 --- a/bolt-sidecar/src/client/constraints_client.rs +++ b/bolt-sidecar/src/client/constraints_client.rs @@ -27,8 +27,7 @@ use crate::{ /// A client for interacting with the Constraints client API. #[derive(Debug, Clone)] pub struct ConstraintsClient { - /// The URL of the MEV-Boost target supporting the Constraints API. - pub url: Url, + url: Url, client: reqwest::Client, delegations: Vec, } @@ -87,6 +86,11 @@ impl ConstraintsClient { .collect::>() } + /// Returns the URL of the target client. + pub fn target(&self) -> &str { + self.url.as_str() + } + fn endpoint(&self, path: &str) -> Url { self.url.join(path).unwrap_or_else(|e| { error!(err = ?e, "Failed to join path: {} with url: {}", path, self.url); diff --git a/bolt-sidecar/src/config/chain.rs b/bolt-sidecar/src/config/chain.rs index 2f9cc92f0..0a75fc9a1 100644 --- a/bolt-sidecar/src/config/chain.rs +++ b/bolt-sidecar/src/config/chain.rs @@ -143,11 +143,6 @@ impl ChainConfig { } } - /// Get the chain name for the given chain. - pub fn name(&self) -> &'static str { - self.chain.name() - } - /// Get the slot time for the given chain in seconds. pub fn slot_time(&self) -> u64 { self.slot_time diff --git a/bolt-sidecar/src/driver.rs b/bolt-sidecar/src/driver.rs index 9c0d78e27..99b39b7dc 100644 --- a/bolt-sidecar/src/driver.rs +++ b/bolt-sidecar/src/driver.rs @@ -6,6 +6,7 @@ use ethereum_consensus::{ clock::{self, SlotStream, SystemTimeProvider}, phase0::mainnet::SLOTS_PER_EPOCH, }; +use eyre::Context; use futures::StreamExt; use tokio::sync::mpsc; use tracing::{debug, error, info, warn}; @@ -67,22 +68,6 @@ pub struct SidecarDriver { slot_stream: SlotStream, } -impl fmt::Debug for SidecarDriver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SidecarDriver") - .field("head_tracker", &self.head_tracker) - .field("execution", &self.execution) - .field("consensus", &self.consensus) - .field("constraint_signer", &self.constraint_signer) - .field("commitment_signer", &self.commitment_signer) - .field("local_builder", &self.local_builder) - .field("constraints_client", &self.constraints_client) - .field("api_events_rx", &self.api_events_rx) - .field("payload_requests_rx", &self.payload_requests_rx) - .finish() - } -} - impl SidecarDriver { /// Create a new sidecar driver with the given [Opts] and private key signer. pub async fn with_local_signer(opts: &Opts) -> eyre::Result { @@ -103,7 +88,9 @@ impl SidecarDriver { let commitment_key = opts.commitment_private_key.0.clone(); let commitment_signer = PrivateKeySigner::from_signing_key(commitment_key); - Self::from_components(opts, constraint_signer, commitment_signer, state_client).await + Self::from_components(opts, constraint_signer, commitment_signer, state_client) + .await + .wrap_err("Failed to initialize sidecar with local signer") } } @@ -133,7 +120,9 @@ impl SidecarDriver { let commitment_key = opts.commitment_private_key.0.clone(); let commitment_signer = PrivateKeySigner::from_signing_key(commitment_key); - Self::from_components(opts, keystore_signer, commitment_signer, state_client).await + Self::from_components(opts, keystore_signer, commitment_signer, state_client) + .await + .wrap_err("Failed to initialize sidecar with keystore signer") } } @@ -150,7 +139,9 @@ impl SidecarDriver { let cb_bls_signer = SignerBLS::CommitBoost(commit_boost_signer.clone()); - Self::from_components(opts, cb_bls_signer, commit_boost_signer, state_client).await + Self::from_components(opts, cb_bls_signer, commit_boost_signer, state_client) + .await + .wrap_err("Failed to initialize sidecar with commit-boost signer") } } @@ -165,15 +156,15 @@ impl SidecarDriver { let mut constraints_client = ConstraintsClient::new(opts.constraints_api_url.clone()); // read the delegations from disk if they exist and add them to the constraints client. - let validator_pubkeys = if let Some(delegations_file_path) = - opts.constraint_signing.delegations_path.as_ref() + let validator_pubkeys = if let Some(delegations_path) = + &opts.constraint_signing.delegations_path { - let delegations = read_signed_delegations_from_file(delegations_file_path)?; - let validator_public_keys = - delegations.iter().map(|d| d.message.validator_pubkey.clone()).collect::>(); + let delegations = read_signed_delegations_from_file(delegations_path)?; + let keys = delegations.iter().map(|d| d.validator_pubkey.clone()).collect::>(); constraints_client.add_delegations(delegations); - validator_public_keys + keys } else { + // If no delegations are provided, we just use the public keys from the signer. Vec::from_iter(constraint_signer.available_pubkeys()) }; @@ -182,17 +173,17 @@ impl SidecarDriver { { let commitment_signer_pubkey = commitment_signer.public_key(); info!( - validator_public_keys_len = %validator_pubkeys.len(), - commitment_signer_pubkey = ?commitment_signer_pubkey, + validator_pubkeys = %validator_pubkeys.len(), + commitment_signer = ?commitment_signer_pubkey, "Verifying validators and operator keys with Bolt Manager, this may take a while..." ); manager.verify_validator_pubkeys(validator_pubkeys, commitment_signer_pubkey).await?; - info!("Successfully verified validators and operator keys with Bolt Manager!"); + info!("Successfully verified validators and operator keys with Bolt Manager."); } else { warn!( - "No Bolt Manager contract deployed on {}, skipping validators and operator public keys verification", + "Bolt Manager is not deployed on {}, skipping validators and operator public keys verification", opts.chain.name() ); } @@ -428,3 +419,19 @@ impl SidecarDriver { } } } + +impl fmt::Debug for SidecarDriver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SidecarDriver") + .field("head_tracker", &self.head_tracker) + .field("execution", &self.execution) + .field("consensus", &self.consensus) + .field("constraint_signer", &self.constraint_signer) + .field("commitment_signer", &self.commitment_signer) + .field("local_builder", &self.local_builder) + .field("constraints_client", &self.constraints_client) + .field("api_events_rx", &self.api_events_rx) + .field("payload_requests_rx", &self.payload_requests_rx) + .finish() + } +} diff --git a/bolt-sidecar/src/primitives/delegation.rs b/bolt-sidecar/src/primitives/delegation.rs index 1b7c391e7..c67bf0663 100644 --- a/bolt-sidecar/src/primitives/delegation.rs +++ b/bolt-sidecar/src/primitives/delegation.rs @@ -1,7 +1,8 @@ -use std::{fs, path::PathBuf}; +use std::{fs, ops::Deref, path::PathBuf}; use alloy::signers::k256::sha2::{Digest, Sha256}; use ethereum_consensus::crypto::{PublicKey as BlsPublicKey, Signature as BlsSignature}; +use eyre::{bail, Result}; use crate::crypto::SignableBLS; @@ -28,6 +29,14 @@ pub struct SignedDelegation { pub signature: BlsSignature, } +impl Deref for SignedDelegation { + type Target = DelegationMessage; + + fn deref(&self) -> &Self::Target { + &self.message + } +} + /// A delegation message. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct DelegationMessage { @@ -57,15 +66,13 @@ impl SignableBLS for DelegationMessage { } /// read the delegations from disk if they exist and add them to the constraints client -pub fn read_signed_delegations_from_file( - file_path: &PathBuf, -) -> eyre::Result> { +pub fn read_signed_delegations_from_file(file_path: &PathBuf) -> Result> { match fs::read_to_string(file_path) { Ok(contents) => match serde_json::from_str::>(&contents) { Ok(delegations) => Ok(delegations), - Err(err) => Err(eyre::eyre!("Failed to parse signed delegations from disk: {:?}", err)), + Err(err) => bail!("Failed to parse signed delegations from disk: {:?}", err), }, - Err(err) => Err(eyre::eyre!("Failed to read signed delegations from disk: {:?}", err)), + Err(err) => bail!("Failed to read signed delegations from disk: {:?}", err), } } @@ -81,6 +88,14 @@ pub struct SignedRevocation { pub signature: BlsSignature, } +impl Deref for SignedRevocation { + type Target = RevocationMessage; + + fn deref(&self) -> &Self::Target { + &self.message + } +} + /// A revocation message. #[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)] pub struct RevocationMessage { From 60ec5fbca29174e1b318d292a42d333e0b2f9625 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:17:13 +0100 Subject: [PATCH 268/272] chore: eyre::Result --- bolt-sidecar/src/primitives/delegation.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/src/primitives/delegation.rs b/bolt-sidecar/src/primitives/delegation.rs index c67bf0663..677bbc5dd 100644 --- a/bolt-sidecar/src/primitives/delegation.rs +++ b/bolt-sidecar/src/primitives/delegation.rs @@ -2,7 +2,7 @@ use std::{fs, ops::Deref, path::PathBuf}; use alloy::signers::k256::sha2::{Digest, Sha256}; use ethereum_consensus::crypto::{PublicKey as BlsPublicKey, Signature as BlsSignature}; -use eyre::{bail, Result}; +use eyre::bail; use crate::crypto::SignableBLS; @@ -66,7 +66,9 @@ impl SignableBLS for DelegationMessage { } /// read the delegations from disk if they exist and add them to the constraints client -pub fn read_signed_delegations_from_file(file_path: &PathBuf) -> Result> { +pub fn read_signed_delegations_from_file( + file_path: &PathBuf, +) -> eyre::Result> { match fs::read_to_string(file_path) { Ok(contents) => match serde_json::from_str::>(&contents) { Ok(delegations) => Ok(delegations), From 77c0b1b36bc019353403f04a6b1c62c63db20eb0 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:46:04 +0100 Subject: [PATCH 269/272] fix: revert helix image tag to local --- scripts/kurtosis_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kurtosis_config.yaml b/scripts/kurtosis_config.yaml index 2130cac4b..14acc3a16 100644 --- a/scripts/kurtosis_config.yaml +++ b/scripts/kurtosis_config.yaml @@ -28,7 +28,7 @@ mev_params: # instead of MEV-Boost by Flashbots bolt_boost_image: ghcr.io/chainbound/bolt-boost:0.1.0 bolt_sidecar_image: ghcr.io/chainbound/bolt-sidecar:0.1.0 - helix_relay_image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc3 + helix_relay_image: ghcr.io/chainbound/helix:0.1.0 mev_relay_image: ghcr.io/chainbound/bolt-relay:0.1.0 mev_builder_image: ghcr.io/chainbound/bolt-builder:0.1.0 mev_boost_image: ghcr.io/chainbound/bolt-mev-boost:0.1.0 From d5a269a2e56d7ff8685fb149eaaf2735cc97b523 Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:46:29 +0100 Subject: [PATCH 270/272] chore: use alloy::hex::encode_prefixed where possible --- bolt-cli/src/commands/send.rs | 5 +++-- bolt-sidecar/src/client/constraints_client.rs | 14 +++++++++----- bolt-sidecar/src/common.rs | 6 +++--- bolt-sidecar/src/primitives/commitment.rs | 9 ++++++--- bolt-sidecar/src/primitives/transaction.rs | 7 +++++-- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/bolt-cli/src/commands/send.rs b/bolt-cli/src/commands/send.rs index 89a1663fb..796dcfb75 100644 --- a/bolt-cli/src/commands/send.rs +++ b/bolt-cli/src/commands/send.rs @@ -5,6 +5,7 @@ use alloy::{ constants::GWEI_TO_WEI, BlobTransactionSidecar, SidecarBuilder, SimpleCoder, Transaction, }, eips::eip2718::Encodable2718, + hex, network::{EthereumWallet, TransactionBuilder, TransactionBuilder4844}, primitives::{keccak256, Address, B256, U256}, providers::{ProviderBuilder, SendableTx}, @@ -227,9 +228,9 @@ async fn sign_request( keccak256(data) }; - let signature = hex::encode(wallet.sign_hash(&digest).await?.as_bytes()); + let signature = hex::encode_prefixed(wallet.sign_hash(&digest).await?.as_bytes()); - Ok(format!("{}:0x{}", wallet.address(), signature)) + Ok(format!("{}:{}", wallet.address(), signature)) } fn prepare_rpc_request(method: &str, params: Value) -> Value { diff --git a/bolt-sidecar/src/client/constraints_client.rs b/bolt-sidecar/src/client/constraints_client.rs index f39f392fd..9df26cbb9 100644 --- a/bolt-sidecar/src/client/constraints_client.rs +++ b/bolt-sidecar/src/client/constraints_client.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use alloy::hex; use axum::http::StatusCode; use beacon_api_client::VersionedValue; use ethereum_consensus::{ @@ -91,6 +92,8 @@ impl ConstraintsClient { self.url.as_str() } + /// Joins the given path with the client's URL. + /// If the path is invalid, an error is logged and the client's URL is returned. fn endpoint(&self, path: &str) -> Url { self.url.join(path).unwrap_or_else(|e| { error!(err = ?e, "Failed to join path: {} with url: {}", path, self.url); @@ -136,7 +139,8 @@ impl BuilderApi for ConstraintsClient { return Ok(()); } else { let validator_pubkeys = - registrations.iter().map(|r| r.message.public_key.clone()).collect::>(); + registrations.iter().map(|r| &r.message.public_key).collect::>(); + let filtered_delegations = self .delegations .iter() @@ -157,8 +161,8 @@ impl BuilderApi for ConstraintsClient { &self, params: GetHeaderParams, ) -> Result { - let parent_hash = format!("0x{}", hex::encode(params.parent_hash.as_ref())); - let public_key = format!("0x{}", hex::encode(params.public_key.as_ref())); + let parent_hash = hex::encode_prefixed(params.parent_hash.as_ref()); + let public_key = hex::encode_prefixed(params.public_key.as_ref()); let response = self .client @@ -230,8 +234,8 @@ impl ConstraintsApi for ConstraintsClient { &self, params: GetHeaderParams, ) -> Result, BuilderApiError> { - let parent_hash = format!("0x{}", hex::encode(params.parent_hash.as_ref())); - let public_key = format!("0x{}", hex::encode(params.public_key.as_ref())); + let parent_hash = hex::encode_prefixed(params.parent_hash.as_ref()); + let public_key = hex::encode_prefixed(params.public_key.as_ref()); let response = self .client diff --git a/bolt-sidecar/src/common.rs b/bolt-sidecar/src/common.rs index d76e28ff2..a37218839 100644 --- a/bolt-sidecar/src/common.rs +++ b/bolt-sidecar/src/common.rs @@ -7,7 +7,7 @@ use std::{ time::Duration, }; -use alloy::{primitives::U256, signers::k256::ecdsa::SigningKey}; +use alloy::{hex, primitives::U256, signers::k256::ecdsa::SigningKey}; use blst::min_pk::SecretKey; use rand::{Rng, RngCore}; use reth_primitives::PooledTransactionsElement; @@ -144,7 +144,7 @@ impl Deref for BlsSecretKeyWrapper { impl fmt::Display for BlsSecretKeyWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "0x{}", hex::encode(self.0.to_bytes())) + write!(f, "{}", hex::encode_prefixed(self.0.to_bytes())) } } @@ -180,7 +180,7 @@ impl From<&str> for EcdsaSecretKeyWrapper { impl Display for EcdsaSecretKeyWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "0x{}", hex::encode(self.0.to_bytes())) + write!(f, "{}", hex::encode_prefixed(self.0.to_bytes())) } } diff --git a/bolt-sidecar/src/primitives/commitment.rs b/bolt-sidecar/src/primitives/commitment.rs index 83d57004c..7e4a18e71 100644 --- a/bolt-sidecar/src/primitives/commitment.rs +++ b/bolt-sidecar/src/primitives/commitment.rs @@ -1,6 +1,9 @@ use std::str::FromStr; -use alloy::primitives::{keccak256, Address, Signature, B256}; +use alloy::{ + hex, + primitives::{keccak256, Address, Signature, B256}, +}; use serde::{de, Deserialize, Deserializer, Serialize}; use crate::crypto::SignerECDSA; @@ -212,7 +215,7 @@ fn serialize_sig(sig: &Signature, serializer: S) -> Result // As bytes encodes the parity as 27/28, need to change that. let mut bytes = sig.as_bytes(); bytes[bytes.len() - 1] = if parity.y_parity() { 1 } else { 0 }; - serializer.serialize_str(&format!("0x{}", hex::encode(bytes))) + serializer.serialize_str(&hex::encode_prefixed(bytes)) } impl InclusionRequest { @@ -258,7 +261,7 @@ impl ECDSASignatureExt for Signature { } fn to_hex(&self) -> String { - format!("0x{}", hex::encode(self.as_bytes_with_parity())) + hex::encode_prefixed(self.as_bytes_with_parity()) } } diff --git a/bolt-sidecar/src/primitives/transaction.rs b/bolt-sidecar/src/primitives/transaction.rs index 95be1dd1e..bca1a6167 100644 --- a/bolt-sidecar/src/primitives/transaction.rs +++ b/bolt-sidecar/src/primitives/transaction.rs @@ -1,6 +1,9 @@ use std::{borrow::Cow, fmt}; -use alloy::primitives::{Address, U256}; +use alloy::{ + hex, + primitives::{Address, U256}, +}; use reth_primitives::{BlobTransactionSidecar, Bytes, PooledTransactionsElement, TxKind, TxType}; use serde::{de, ser::SerializeSeq}; @@ -233,7 +236,7 @@ pub fn serialize_txs( let mut seq = serializer.serialize_seq(Some(txs.len()))?; for tx in txs { let encoded = tx.tx.envelope_encoded(); - seq.serialize_element(&format!("0x{}", hex::encode(encoded)))?; + seq.serialize_element(&hex::encode_prefixed(encoded))?; } seq.end() } From d2b5b9c3e3bf71dd099a98456b24647559706446 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Mon, 11 Nov 2024 17:02:15 +0100 Subject: [PATCH 271/272] fix(sidecar): remove stale templates in case of missed slots --- bolt-sidecar/src/state/execution.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 079c94253..a4bfa81aa 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -460,7 +460,7 @@ impl ExecutionState { // Remove any block templates that are no longer valid // NOTE: this needs to be called BEFORE applying the state update or we might remove // constraints for which we need to get the receipts. - if let Some(template) = self.remove_block_template(slot) { + for template in self.remove_block_templates_until(slot) { debug!(%slot, "Removed block template for slot"); let hashes = template.transaction_hashes(); let receipts = self.client.get_receipts_unordered(&hashes).await?; @@ -543,11 +543,27 @@ impl ExecutionState { self.block_templates.get(&slot) } - /// Gets the block template for the given slot number and removes it from the cache. - /// This should be called when we need to propose a block for the given slot, - /// or when a new head comes in which makes an older block template useless. - pub fn remove_block_template(&mut self, slot: u64) -> Option { - self.block_templates.remove(&slot) + /// Removes all the block templates which slot is less then or equal `slot`, and returns them. + /// + /// This should be called when we need to propose a block for the given slot, or when a new + /// head comes in which makes an older block templates useless. + /// + /// NOTE: We remove all previous block templates to ensure that, when a new head is received + /// from the beacon client, all stale template are cleared. This prevents outdated templates + /// from persisting in cases of missed slots, where such events are not emitted. + pub fn remove_block_templates_until(&mut self, slot: u64) -> Vec { + let mut slots_to_remove = + self.block_templates.keys().filter(|s| **s <= slot).copied().collect::>(); + slots_to_remove.sort(); + + let mut templates = Vec::with_capacity(slots_to_remove.len()); + for s in slots_to_remove { + if let Some(template) = self.block_templates.remove(&s) { + templates.push(template); + } + } + + templates } } From a749cf016b07b4bae2a33a0820d97e79bdf8ed3c Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Tue, 12 Nov 2024 14:04:27 +0100 Subject: [PATCH 272/272] chore(holesky): bump helix image, add helix_website=debug log directive --- testnets/holesky/docker-compose.pbs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testnets/holesky/docker-compose.pbs.yml b/testnets/holesky/docker-compose.pbs.yml index 27034a7a5..d1ee331ff 100644 --- a/testnets/holesky/docker-compose.pbs.yml +++ b/testnets/holesky/docker-compose.pbs.yml @@ -64,7 +64,7 @@ services: ["/bin/sh", "-c", "chmod +x /scripts/run-bn.sh && /scripts/run-bn.sh"] helix-relay: - image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc2 + image: ghcr.io/chainbound/helix:v0.3.0-alpha.rc4 restart: unless-stopped depends_on: - db @@ -77,6 +77,6 @@ services: - "44040:4040" environment: - RELAY_KEY=0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2 - - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug + - RUST_LOG=helix_cmd=debug,helix_api=debug,helix_common=debug,helix_datastore=debug,helix_housekeeper=debug,helix_database=debug,helix_beacon_client=debug,helix_website=debug - RUST_BACKTRACE=1 command: --config /helix-config.yml