diff --git a/.env.test b/.env.test index 38a2e490d5f9..c0628a1dc9c4 100644 --- a/.env.test +++ b/.env.test @@ -1,7 +1,5 @@ # We use these images during sim and e2e tests -# This is the last version which supports pre/post merge chains in the same network -# All newer versions only work with post merge chains -GETH_DOCKER_IMAGE=ethereum/client-go:v1.16.2 +GETH_DOCKER_IMAGE=ethereum/client-go:v1.16.7 # Use either image or local binary for the testing GETH_BINARY_DIR= LIGHTHOUSE_DOCKER_IMAGE=ethpandaops/lighthouse:unstable-d235f2c diff --git a/.github/workflows/test-sim-merge.yml b/.github/workflows/test-sim-merge.yml deleted file mode 100644 index 83cb48a5b4c8..000000000000 --- a/.github/workflows/test-sim-merge.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Sim merge execution/builder tests - -concurrency: - # If PR, cancel prev commits. head_ref = source branch name on pull_request, null if push - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -on: - push: - # We intentionally don't run push on feature branches. See PR for rational. - branches: [unstable, stable] - pull_request: - workflow_dispatch: - -env: - GETH_IMAGE: ethereum/client-go:v1.10.25 - NETHERMIND_IMAGE: nethermind/nethermind:1.14.3 - MERGEMOCK_IMAGE: g11tech/mergemock:latest - GETH_WITHDRAWALS_IMAGE: g11tech/geth:withdrawalsfeb8 - ETHEREUMJS_WITHDRAWALS_IMAGE: g11tech/ethereumjs:blobs-b6b63 - NETHERMIND_WITHDRAWALS_IMAGE: nethermindeth/nethermind:withdrawals_yolo - ETHEREUMJS_BLOBS_IMAGE: g11tech/ethereumjs:blobs-b6b63 - -jobs: - sim-merge-tests: - name: Sim merge tests - runs-on: buildjet-4vcpu-ubuntu-2204 - steps: - - uses: actions/checkout@v4 - - uses: "./.github/actions/setup-and-build" - with: - node: 24 - - - name: Pull Geth - run: docker pull $GETH_IMAGE - - - name: Pull Nethermind - run: docker pull $NETHERMIND_IMAGE - - - name: Pull mergemock - run: docker pull $MERGEMOCK_IMAGE - - - name: Test Lodestar <> mergemock relay - run: yarn test:sim:mergemock - working-directory: packages/beacon-node - env: - EL_BINARY_DIR: ${{ env.MERGEMOCK_IMAGE }} - EL_SCRIPT_DIR: mergemock - LODESTAR_PRESET: mainnet - ENGINE_PORT: 8551 - ETH_PORT: 8661 - - - name: Upload debug log test files - if: ${{ always() }} - uses: actions/upload-artifact@v4 - with: - name: debug-test-logs - path: packages/beacon-node/test-logs diff --git a/dashboards/lodestar_execution_engine.json b/dashboards/lodestar_execution_engine.json index 63c1ab636e51..434563d54eef 100644 --- a/dashboards/lodestar_execution_engine.json +++ b/dashboards/lodestar_execution_engine.json @@ -90,6 +90,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -173,6 +174,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -258,6 +260,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -343,6 +346,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -428,6 +432,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -513,6 +518,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -624,6 +630,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -637,6 +644,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 1, @@ -728,10 +736,12 @@ "fields": "", "values": false }, + "showPercentChange": false, "text": {}, - "textMode": "auto" + "textMode": "auto", + "wideLayout": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.4.1", "targets": [ { "datasource": { @@ -760,6 +770,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -773,6 +784,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -843,6 +855,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -856,6 +869,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -926,6 +940,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -939,6 +954,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 1, @@ -1010,6 +1026,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1023,6 +1040,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1105,6 +1123,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1118,6 +1137,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1185,6 +1205,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1198,6 +1219,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1277,6 +1299,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1290,6 +1313,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1382,6 +1406,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1395,6 +1420,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1487,6 +1513,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1500,6 +1527,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1618,6 +1646,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1631,6 +1660,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1759,10 +1789,12 @@ "fields": "", "values": false }, + "showPercentChange": false, "text": {}, - "textMode": "value" + "textMode": "value", + "wideLayout": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.4.1", "targets": [ { "datasource": { @@ -1809,10 +1841,12 @@ "fields": "", "values": false }, + "showPercentChange": false, "text": {}, - "textMode": "auto" + "textMode": "auto", + "wideLayout": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.4.1", "targets": [ { "datasource": { @@ -1861,10 +1895,12 @@ "fields": "", "values": false }, + "showPercentChange": false, "text": {}, - "textMode": "auto" + "textMode": "auto", + "wideLayout": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.4.1", "targets": [ { "datasource": { @@ -1913,10 +1949,12 @@ "fields": "/^Time$/", "values": false }, + "showPercentChange": false, "text": {}, - "textMode": "auto" + "textMode": "auto", + "wideLayout": true }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.4.1", "targets": [ { "datasource": { @@ -1947,6 +1985,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -1960,6 +1999,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -2054,6 +2094,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -2067,6 +2108,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 1, @@ -2138,6 +2180,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -2151,6 +2194,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -2244,6 +2288,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -2257,6 +2302,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 1, @@ -2326,6 +2372,7 @@ "mode": "palette-classic" }, "custom": { + "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", @@ -2339,6 +2386,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -2409,927 +2457,10 @@ ], "title": "Eth1 fetch rate", "type": "timeseries" - }, - { - "collapsed": false, - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 92 - }, - "id": 437, - "panels": [], - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "refId": "A" - } - ], - "title": "Merge Tracking", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "0": { - "index": 0, - "text": "STOPPED" - }, - "1": { - "index": 1, - "text": "SEARCHING" - }, - "2": { - "index": 2, - "text": "FOUND" - }, - "3": { - "index": 3, - "text": "MERGE_COMPLETE" - } - }, - "type": "value" - } - ], - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 0, - "y": 93 - }, - "id": 439, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_status", - "hide": false, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Merge Status", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 2, - "y": 93 - }, - "id": 459, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_ttd", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Target TTD", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 4, - "y": 93 - }, - "id": 475, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "(lodestar_eth1_merge_ttd - lodestar_eth1_latest_block_ttd)\n/\nrate(lodestar_eth1_latest_block_ttd[32m])", - "hide": false, - "interval": "", - "legendFormat": "", - "refId": "B" - } - ], - "title": "Time to TTD", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "unit": "dateTimeAsSystem" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 4, - "x": 8, - "y": 93 - }, - "id": 476, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "(time()\n+\n(lodestar_eth1_merge_ttd - lodestar_eth1_latest_block_ttd)\n/\nrate(lodestar_eth1_latest_block_ttd[32m])\n)*1000", - "hide": false, - "interval": "", - "legendFormat": "", - "refId": "B" - } - ], - "title": "TTD date aprox", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [], - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 12, - "y": 93 - }, - "id": 462, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_parent_blocks_fetched_total", - "hide": false, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "title": "Parents fetched", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 16, - "y": 93 - }, - "id": 456, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_block_details", - "format": "time_series", - "instant": true, - "interval": "", - "legendFormat": "{{terminalBlockNumber}}", - "refId": "A" - } - ], - "title": "Terminal Block Num", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 20, - "y": 93 - }, - "id": 464, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_block_details", - "format": "time_series", - "instant": true, - "interval": "", - "legendFormat": "{{terminalBlockNumber}}", - "refId": "A" - } - ], - "title": "Terminal Block Time", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 0, - "y": 96 - }, - "id": 460, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_td_factor", - "hide": false, - "interval": "", - "legendFormat": "td factor", - "refId": "A" - } - ], - "title": "TD factor", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 2, - "y": 96 - }, - "id": 450, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_latest_block_ttd", - "hide": false, - "interval": "", - "legendFormat": "latest block ttd", - "refId": "A" - } - ], - "title": "Latest td", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [], - "unit": "dateTimeFromNow" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 4, - "y": 96 - }, - "id": 463, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_latest_block_timestamp * 1000", - "hide": false, - "interval": "", - "legendFormat": "latest block ttd", - "refId": "A" - } - ], - "title": "Latest block time", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 6, - "y": 96 - }, - "id": 461, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_latest_block_number", - "hide": false, - "interval": "", - "legendFormat": "latest block ttd", - "refId": "A" - } - ], - "title": "Latest num", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 12, - "y": 96 - }, - "id": 467, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "last" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_get_terminal_pow_block_promise_cache_hit_total", - "hide": false, - "interval": "", - "legendFormat": "cache hits", - "refId": "A" - } - ], - "title": "Promise cache hits", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 8, - "x": 16, - "y": 96 - }, - "id": 455, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "name" - }, - "pluginVersion": "9.3.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_block_details", - "format": "time_series", - "instant": true, - "interval": "", - "legendFormat": "{{terminalBlockHash}}", - "refId": "A" - } - ], - "title": "Terminal Block Hash", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 99 - }, - "id": 458, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "8.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_latest_block_ttd", - "hide": false, - "interval": "", - "legendFormat": "lastest block td", - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "lodestar_eth1_merge_ttd", - "hide": false, - "interval": "", - "legendFormat": "merge ttd", - "refId": "B" - } - ], - "title": "Latest TD vs TTD", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "opacity", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [], - "mappings": [], - "unit": "short" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 99 - }, - "id": 444, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "8.2.2", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "${DS_PROMETHEUS}" - }, - "exemplar": false, - "expr": "rate(lodestar_eth1_poll_merge_block_errors_total[$rate_interval])", - "hide": false, - "interval": "", - "legendFormat": "polling errors", - "refId": "A" - } - ], - "title": "Error rate", - "type": "timeseries" } ], "refresh": "10s", - "schemaVersion": 38, - "style": "dark", + "schemaVersion": 39, "tags": [ "lodestar" ], diff --git a/docs/pages/contribution/advanced-topics/setting-up-a-testnet.md b/docs/pages/contribution/advanced-topics/setting-up-a-testnet.md index 04cac75c4b0a..e9cb16d1d73b 100644 --- a/docs/pages/contribution/advanced-topics/setting-up-a-testnet.md +++ b/docs/pages/contribution/advanced-topics/setting-up-a-testnet.md @@ -101,4 +101,3 @@ To set up a local testnet with a Post-Merge configuration, you may need to add t - `--params.ALTAIR_FORK_EPOCH 0` - `--params.BELLATRIX_FORK_EPOCH 0` -- `--terminal-total-difficulty-override 0` diff --git a/packages/api/test/unit/beacon/genericServerTest/config.test.ts b/packages/api/test/unit/beacon/genericServerTest/config.test.ts index 534bc69f519e..eff05ac9263a 100644 --- a/packages/api/test/unit/beacon/genericServerTest/config.test.ts +++ b/packages/api/test/unit/beacon/genericServerTest/config.test.ts @@ -16,7 +16,6 @@ describe("beacon / config", () => { PRESET_BASE: "mainnet", DEPOSIT_CONTRACT_ADDRESS: "0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b", GENESIS_FORK_VERSION: "0x00001020", - TERMINAL_TOTAL_DIFFICULTY: "115792089237316195423570985008687907853269984665640564039457584007913129639936", MIN_GENESIS_TIME: "1606824000", }; diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index 3c92b38b191b..d956c93bcc04 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -103,7 +103,6 @@ "test:unit": "vitest run --project unit --project unit-minimal", "test:e2e": "vitest run --project e2e --project e2e-mainnet", "test:sim": "vitest run test/sim/**/*.test.ts", - "test:sim:mergemock": "vitest run test/sim/mergemock.test.ts", "test:sim:blobs": "vitest run test/sim/4844-interop.test.ts", "download-spec-tests": "node --loader=ts-node/esm test/spec/downloadTests.ts", "test:spec:bls": "vitest run --project spec-minimal test/spec/bls/", diff --git a/packages/beacon-node/src/chain/blocks/verifyBlock.ts b/packages/beacon-node/src/chain/blocks/verifyBlock.ts index 7ff8f3397a9f..0ea007841b9b 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlock.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlock.ts @@ -1,4 +1,3 @@ -import {ChainForkConfig} from "@lodestar/config"; import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice"; import {ForkName, isForkPostFulu} from "@lodestar/params"; import { @@ -7,8 +6,7 @@ import { computeEpochAtSlot, isStateValidatorsNodesPopulated, } from "@lodestar/state-transition"; -import {IndexedAttestation, bellatrix, deneb} from "@lodestar/types"; -import {Logger, toRootHex} from "@lodestar/utils"; +import {IndexedAttestation, deneb} from "@lodestar/types"; import type {BeaconChain} from "../chain.js"; import {BlockError, BlockErrorCode} from "../errors/index.js"; import {BlockProcessOpts} from "../options.js"; @@ -18,7 +16,6 @@ import {ImportBlockOpts} from "./types.js"; import {DENEB_BLOWFISH_BANNER} from "./utils/blowfishBanner.js"; import {ELECTRA_GIRAFFE_BANNER} from "./utils/giraffeBanner.js"; import {CAPELLA_OWL_BANNER} from "./utils/ownBanner.js"; -import {POS_PANDA_MERGE_TRANSITION_BANNER} from "./utils/pandaMergeTransitionBanner.js"; import {FULU_ZEBRA_BANNER} from "./utils/zebraBanner.js"; import {verifyBlocksDataAvailability} from "./verifyBlocksDataAvailability.js"; import {SegmentExecStatus, verifyBlocksExecutionPayload} from "./verifyBlocksExecutionPayloads.js"; @@ -103,7 +100,6 @@ export async function verifyBlocksInEpoch( : Promise.resolve({ execAborted: null, executionStatuses: blocks.map((_blk) => ExecutionStatus.Syncing), - mergeBlockFound: null, } as SegmentExecStatus); // Store indexed attestations for each block to avoid recomputing them during import @@ -162,12 +158,6 @@ export async function verifyBlocksInEpoch( ]); if (opts.verifyOnly !== true) { - if (segmentExecStatus.execAborted === null && segmentExecStatus.mergeBlockFound !== null) { - // merge block found and is fully valid = state transition + signatures + execution payload. - // TODO: Will this banner be logged during syncing? - logOnPowBlock(this.logger, this.config, segmentExecStatus.mergeBlockFound); - } - const fromForkBoundary = this.config.getForkBoundaryAtEpoch(computeEpochAtSlot(parentBlock.slot)); const toForkBoundary = this.config.getForkBoundaryAtEpoch(computeEpochAtSlot(lastBlock.message.slot)); @@ -250,16 +240,3 @@ export async function verifyBlocksInEpoch( abortController.abort(); } } - -function logOnPowBlock(logger: Logger, config: ChainForkConfig, mergeBlock: bellatrix.BeaconBlock): void { - const mergeBlockHash = toRootHex(config.getForkTypes(mergeBlock.slot).BeaconBlock.hashTreeRoot(mergeBlock)); - const mergeExecutionHash = toRootHex(mergeBlock.body.executionPayload.blockHash); - const mergePowHash = toRootHex(mergeBlock.body.executionPayload.parentHash); - logger.info(POS_PANDA_MERGE_TRANSITION_BANNER); - logger.info("Execution transitioning from PoW to PoS!!!"); - logger.info("Importing block referencing terminal PoW block", { - blockHash: mergeBlockHash, - executionHash: mergeExecutionHash, - powHash: mergePowHash, - }); -} diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index 1183c72074c0..a74e3f033db1 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -6,19 +6,11 @@ import { LVHValidResponse, MaybeValidExecutionStatus, ProtoBlock, - assertValidTerminalPowBlock, } from "@lodestar/fork-choice"; -import {ForkSeq, SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY} from "@lodestar/params"; -import { - CachedBeaconStateAllForks, - isExecutionBlockBodyType, - isExecutionEnabled, - isExecutionStateType, - isMergeTransitionBlock as isMergeTransitionBlockFn, -} from "@lodestar/state-transition"; -import {Slot, bellatrix, electra} from "@lodestar/types"; +import {ForkSeq} from "@lodestar/params"; +import {CachedBeaconStateAllForks, isExecutionBlockBodyType, isExecutionStateType} from "@lodestar/state-transition"; +import {bellatrix, electra} from "@lodestar/types"; import {ErrorAborted, Logger, toRootHex} from "@lodestar/utils"; -import {IEth1ForBlockProduction} from "../../eth1/index.js"; import {ExecutionPayloadStatus, IExecutionEngine} from "../../execution/engine/interface.js"; import {Metrics} from "../../metrics/metrics.js"; import {IClock} from "../../util/clock.js"; @@ -29,7 +21,6 @@ import {IBlockInput} from "./blockInput/types.js"; import {ImportBlockOpts} from "./types.js"; export type VerifyBlockExecutionPayloadModules = { - eth1: IEth1ForBlockProduction; executionEngine: IExecutionEngine; clock: IClock; logger: Logger; @@ -44,9 +35,8 @@ export type SegmentExecStatus = execAborted: null; executionStatuses: MaybeValidExecutionStatus[]; executionTime: number; - mergeBlockFound: bellatrix.BeaconBlock | null; } - | {execAborted: ExecAbortType; invalidSegmentLVH?: LVHInvalidResponse; mergeBlockFound: null}; + | {execAborted: ExecAbortType; invalidSegmentLVH?: LVHInvalidResponse}; type VerifyExecutionErrorResponse = | {executionStatus: ExecutionStatus.Invalid; lvhResponse: LVHInvalidResponse; execError: BlockError} @@ -72,7 +62,6 @@ export async function verifyBlocksExecutionPayload( opts: BlockProcessOpts & ImportBlockOpts ): Promise { const executionStatuses: MaybeValidExecutionStatus[] = []; - let mergeBlockFound: bellatrix.BeaconBlock | null = null; const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); const lastBlock = blockInputs.at(-1); @@ -96,57 +85,9 @@ export async function verifyBlocksExecutionPayload( // will either validate or prune invalid blocks // // We need to track and keep updating if its safe to optimistically import these blocks. - // The following is how we determine for a block if its safe: - // - // (but we need to modify this check for this segment of blocks because it checks if the - // parent of any block imported in forkchoice is post-merge and currently we could only - // have blocks[0]'s parent imported in the chain as this is no longer one by one verify + - // import.) - // // // When to import such blocks: // From: https://github.com/ethereum/consensus-specs/pull/2844 - // A block MUST NOT be optimistically imported, unless either of the following - // conditions are met: - // - // 1. Parent of the block has execution - // - // Since with the sync optimizations, the previous block might not have been in the - // forkChoice yet, so the below check could fail for safeSlotsToImportOptimistically - // - // Luckily, we can depend on the preState0 to see if we are already post merge w.r.t - // the blocks we are importing. - // - // Or in other words if - // - block status is syncing - // - and we are not in a post merge world and is parent is not optimistically safe - // - and we are syncing close to the chain head i.e. clock slot - // - and parent is optimistically safe - // - // then throw error - // - // - // - if we haven't yet imported a post merge ancestor in forkchoice i.e. - // - and we are syncing close to the clockSlot, i.e. merge Transition could be underway - // - // - // 2. The current slot (as per the system clock) is at least - // SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY ahead of the slot of the block being - // imported. - // This means that the merge transition could be underway and we can't afford to import - // a block which is not fully validated as it could affect liveliness of the network. - // - // - // For this segment of blocks: - // We are optimistically safe with respect to this entire block segment if: - // - all the blocks are way behind the current slot - // - or we have already imported a post-merge parent of first block of this chain in forkchoice - const currentSlot = chain.clock.currentSlot; - const safeSlotsToImportOptimistically = opts.safeSlotsToImportOptimistically ?? SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY; - let isOptimisticallySafe = - parentBlock.executionStatus !== ExecutionStatus.PreMerge || - lastBlock.slot + safeSlotsToImportOptimistically < currentSlot; - for (let blockIndex = 0; blockIndex < blockInputs.length; blockIndex++) { const blockInput = blockInputs[blockIndex]; // If blocks are invalid in consensus the main promise could resolve before this loop ends. @@ -154,14 +95,7 @@ export async function verifyBlocksExecutionPayload( if (signal.aborted) { throw new ErrorAborted("verifyBlockExecutionPayloads"); } - const verifyResponse = await verifyBlockExecutionPayload( - chain, - blockInput, - preState0, - opts, - isOptimisticallySafe, - currentSlot - ); + const verifyResponse = await verifyBlockExecutionPayload(chain, blockInput, preState0); // If execError has happened, then we need to extract the segmentExecStatus and return if (verifyResponse.execError !== null) { @@ -170,75 +104,7 @@ export async function verifyBlocksExecutionPayload( // If we are here then its because executionStatus is one of MaybeValidExecutionStatus const {executionStatus} = verifyResponse; - // It becomes optimistically safe for following blocks if a post-merge block is deemed fit - // for import. If it would not have been safe verifyBlockExecutionPayload would have - // returned execError and loop would have been aborted - if (executionStatus !== ExecutionStatus.PreMerge) { - isOptimisticallySafe = true; - } executionStatuses.push(executionStatus); - - const blockBody = blockInput.getBlock().message.body; - const isMergeTransitionBlock = - // If the merge block is found, stop the search as the isMergeTransitionBlockFn condition - // will still evaluate to true for the following blocks leading to errors (while syncing) - // as the preState0 still belongs to the pre state of the first block on segment - mergeBlockFound === null && - isExecutionStateType(preState0) && - isExecutionBlockBodyType(blockBody) && - isMergeTransitionBlockFn(preState0, blockBody); - - // If this is a merge transition block, check to ensure if it references - // a valid terminal PoW block. - // - // However specs define this check to be run inside forkChoice's onBlock - // (https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/fork-choice.md#on_block) - // but we perform the check here (as inspired from the lighthouse impl) - // - // Reasons: - // 1. If the block is not valid, we should fail early and not wait till - // forkChoice import. - // 2. It makes logical sense to pair it with the block validations and - // deal it with the external services like eth1 tracker here than - // in import block - if (isMergeTransitionBlock) { - const mergeBlock = blockInput.getBlock().message as bellatrix.BeaconBlock; - const mergeBlockHash = toRootHex(chain.config.getForkTypes(mergeBlock.slot).BeaconBlock.hashTreeRoot(mergeBlock)); - const powBlockRootHex = toRootHex(mergeBlock.body.executionPayload.parentHash); - const powBlock = await chain.eth1.getPowBlock(powBlockRootHex).catch((error) => { - // Lets just warn the user here, errors if any will be reported on - // `assertValidTerminalPowBlock` checks - chain.logger.warn( - "Error fetching terminal PoW block referred in the merge transition block", - {powBlockHash: powBlockRootHex, mergeBlockHash}, - error - ); - return null; - }); - - const powBlockParent = - powBlock && - (await chain.eth1.getPowBlock(powBlock.parentHash).catch((error) => { - // Lets just warn the user here, errors if any will be reported on - // `assertValidTerminalPowBlock` checks - chain.logger.warn( - "Error fetching parent of the terminal PoW block referred in the merge transition block", - {powBlockParentHash: powBlock.parentHash, powBlock: powBlockRootHex, mergeBlockHash}, - error - ); - return null; - })); - - // executionStatus will never == ExecutionStatus.PreMerge if it's the mergeBlock. But gotta make TS happy =D - if (executionStatus === ExecutionStatus.PreMerge) { - throw Error("Merge block must not have executionStatus == PreMerge"); - } - - assertValidTerminalPowBlock(chain.config, mergeBlock, {executionStatus, powBlock, powBlockParent}); - // Valid execution payload, but may not be in a valid beacon chain block. Delay printing the POS ACTIVATED banner - // to the end of the verify block routine, which confirms that this block is fully valid. - mergeBlockFound = mergeBlock; - } } const executionTime = Date.now(); @@ -265,7 +131,6 @@ export async function verifyBlocksExecutionPayload( execAborted: null, executionStatuses, executionTime, - mergeBlockFound, }; } @@ -275,28 +140,18 @@ export async function verifyBlocksExecutionPayload( export async function verifyBlockExecutionPayload( chain: VerifyBlockExecutionPayloadModules, blockInput: IBlockInput, - preState0: CachedBeaconStateAllForks, - opts: BlockProcessOpts, - isOptimisticallySafe: boolean, - currentSlot: Slot + preState0: CachedBeaconStateAllForks ): Promise { const block = blockInput.getBlock(); /** Not null if execution is enabled */ const executionPayloadEnabled = - isExecutionStateType(preState0) && - isExecutionBlockBodyType(block.message.body) && - // Safe to use with a state previous to block's preState. isMergeComplete can only transition from false to true. - // - If preState0 is after merge block: condition is true, and will always be true - // - If preState0 is before merge block: the block could lie but then state transition function will throw above - // It is kinda safe to send non-trusted payloads to the execution client because at most it can trigger sync. - // TODO: If this becomes a problem, do some basic verification beforehand, like checking the proposer signature. - isExecutionEnabled(preState0, block.message) + isExecutionStateType(preState0) && isExecutionBlockBodyType(block.message.body) ? block.message.body.executionPayload : null; if (!executionPayloadEnabled) { - // isExecutionEnabled() -> false - return {executionStatus: ExecutionStatus.PreMerge, execError: null} as VerifyBlockExecutionResponse; + // Pre-merge block, no execution payload to verify + return {executionStatus: ExecutionStatus.PreMerge, lvhResponse: undefined, execError: null}; } // TODO: Handle better notifyNewPayload() returning error is syncing @@ -343,24 +198,10 @@ export async function verifyBlockExecutionPayload( } // Accepted and Syncing have the same treatment, as final validation of block is pending + // Post-merge, we're always safe to optimistically import case ExecutionPayloadStatus.ACCEPTED: - case ExecutionPayloadStatus.SYNCING: { - // Check if the entire segment was deemed safe or, this block specifically itself if not in - // the safeSlotsToImportOptimistically window of current slot, then we can import else - // we need to throw and not import his block - const safeSlotsToImportOptimistically = - opts.safeSlotsToImportOptimistically ?? SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY; - if (!isOptimisticallySafe && blockInput.slot + safeSlotsToImportOptimistically >= currentSlot) { - const execError = new BlockError(block, { - code: BlockErrorCode.EXECUTION_ENGINE_ERROR, - execStatus: ExecutionPayloadStatus.UNSAFE_OPTIMISTIC_STATUS, - errorMessage: `not safe to import ${execResult.status} payload within ${opts.safeSlotsToImportOptimistically} of currentSlot`, - }); - return {executionStatus: null, execError} as VerifyBlockExecutionResponse; - } - + case ExecutionPayloadStatus.SYNCING: return {executionStatus: ExecutionStatus.Syncing, execError: null}; - } // If the block has is not valid, or it referenced an invalid terminal block then the // block is invalid, however it has no bearing on any forkChoice cleanup diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index 091739b7d3da..9ba8afe541de 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -3,7 +3,7 @@ import {PrivateKey} from "@libp2p/interface"; import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map"; import {CompositeTypeAny, TreeView, Type} from "@chainsafe/ssz"; import {BeaconConfig} from "@lodestar/config"; -import {CheckpointWithHex, ExecutionStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice"; +import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice"; import {LoggerNode} from "@lodestar/logger/node"; import {EFFECTIVE_BALANCE_INCREMENT, GENESIS_SLOT, SLOTS_PER_EPOCH, isForkPostElectra} from "@lodestar/params"; import { @@ -1177,17 +1177,6 @@ export class BeaconChain implements IBeaconChain { this.seenAggregatedAttestations.prune(epoch); this.seenBlockAttesters.prune(epoch); this.beaconProposerCache.prune(epoch); - - // Poll for merge block in the background to speed-up block production. Only if: - // - after BELLATRIX_FORK_EPOCH - // - Beacon node synced - // - head state not isMergeTransitionComplete - if (this.config.BELLATRIX_FORK_EPOCH - epoch < 1) { - const head = this.forkChoice.getHead(); - if (epoch - computeEpochAtSlot(head.slot) < 5 && head.executionStatus === ExecutionStatus.PreMerge) { - this.eth1.startPollingMergeBlock(); - } - } } protected onNewHead(head: ProtoBlock): void { diff --git a/packages/beacon-node/src/chain/forkChoice/index.ts b/packages/beacon-node/src/chain/forkChoice/index.ts index d83dc9949d7e..56da740ee99b 100644 --- a/packages/beacon-node/src/chain/forkChoice/index.ts +++ b/packages/beacon-node/src/chain/forkChoice/index.ts @@ -18,7 +18,6 @@ import { getBlockRootAtSlot, getEffectiveBalanceIncrementsZeroInactive, isExecutionStateType, - isMergeTransitionComplete, } from "@lodestar/state-transition"; import {Slot, ssz} from "@lodestar/types"; import {Logger, toRootHex} from "@lodestar/utils"; @@ -135,7 +134,7 @@ export function initializeForkChoiceFromFinalizedState( unrealizedFinalizedEpoch: finalizedCheckpoint.epoch, unrealizedFinalizedRoot: toRootHex(finalizedCheckpoint.root), - ...(isExecutionStateType(state) && isMergeTransitionComplete(state) + ...(isExecutionStateType(state) ? { executionPayloadBlockHash: toRootHex(state.latestExecutionPayloadHeader.blockHash), executionPayloadNumber: state.latestExecutionPayloadHeader.blockNumber, @@ -216,7 +215,7 @@ export function initializeForkChoiceFromUnfinalizedState( unrealizedFinalizedEpoch: finalizedCheckpoint.epoch, unrealizedFinalizedRoot: toRootHex(finalizedCheckpoint.root), - ...(isExecutionStateType(unfinalizedState) && isMergeTransitionComplete(unfinalizedState) + ...(isExecutionStateType(unfinalizedState) ? { executionPayloadBlockHash: toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash), executionPayloadNumber: unfinalizedState.latestExecutionPayloadHeader.blockNumber, diff --git a/packages/beacon-node/src/chain/options.ts b/packages/beacon-node/src/chain/options.ts index 6a7ceadf7a72..5d95927232e0 100644 --- a/packages/beacon-node/src/chain/options.ts +++ b/packages/beacon-node/src/chain/options.ts @@ -1,4 +1,3 @@ -import {SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY} from "@lodestar/params"; import {defaultOptions as defaultValidatorOptions} from "@lodestar/validator"; import {DEFAULT_ARCHIVE_MODE} from "./archiveStore/constants.js"; import {ArchiveMode, ArchiveStoreOpts} from "./archiveStore/interface.js"; @@ -56,10 +55,6 @@ export type BlockProcessOpts = { * Will double processing times. Use only for debugging purposes. */ disableBlsBatchVerify?: boolean; - /** - * Override SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY - */ - safeSlotsToImportOptimistically?: number; /** * Assert progressive balances the same to EpochTransitionCache */ @@ -109,7 +104,6 @@ export const defaultChainOptions: IChainOptions = { proposerBoost: true, proposerBoostReorg: true, computeUnrealized: true, - safeSlotsToImportOptimistically: SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY, suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient, serveHistoricalState: false, assertCorrectProgressiveBalances: false, diff --git a/packages/beacon-node/src/chain/prepareNextSlot.ts b/packages/beacon-node/src/chain/prepareNextSlot.ts index 43fbb0226a48..78967f2ff293 100644 --- a/packages/beacon-node/src/chain/prepareNextSlot.ts +++ b/packages/beacon-node/src/chain/prepareNextSlot.ts @@ -197,7 +197,7 @@ export class PrepareNextSlotScheduler { this.chain.opts.emitPayloadAttributes === true && this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes) ) { - const data = await getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, { + const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, { prepareState: updatedPrepareState, prepareSlot, parentBlockRoot: fromHex(headRoot), diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 3736a141291a..556e4a42f74a 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -17,10 +17,8 @@ import { CachedBeaconStateCapella, CachedBeaconStateExecutions, computeTimeAtSlot, - getCurrentEpoch, getExpectedWithdrawals, getRandaoMix, - isMergeTransitionComplete, } from "@lodestar/state-transition"; import { BLSPubkey, @@ -44,12 +42,9 @@ import { deneb, electra, fulu, - ssz, - sszTypesFor, } from "@lodestar/types"; import {Logger, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils"; -import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js"; -import {IEth1ForBlockProduction} from "../../eth1/index.js"; +import {ZERO_HASH_HEX} from "../../constants/index.js"; import {numToQuantity} from "../../eth1/provider/utils.js"; import { IExecutionBuilder, @@ -337,14 +332,6 @@ export async function produceBlockBody( feeRecipient ); - if (prepareRes.isPremerge) { - return { - ...prepareRes, - executionPayload: sszTypesFor(fork).ExecutionPayload.defaultValue(), - executionPayloadValue: BigInt(0), - }; - } - const {prepType, payloadId} = prepareRes; Object.assign(logMeta, {executionPayloadPrepType: prepType}); @@ -366,37 +353,14 @@ export async function produceBlockBody( return {...prepareRes, ...payloadRes}; })().catch((e) => { - // catch payload fetch here, because there is still a recovery path possible if we - // are pre-merge. We don't care the same for builder segment as the execution block - // will takeover if the builder flow was activated and errors this.metrics?.blockPayload.payloadFetchErrors.inc(); - - if (!isMergeTransitionComplete(currentState as CachedBeaconStateBellatrix)) { - this.logger?.warn( - "Fetch payload from the execution failed, however since we are still pre-merge proceeding with an empty one.", - {}, - e as Error - ); - // ok we don't have an execution payload here, so we can assign an empty one - // if pre-merge - return { - isPremerge: true as const, - executionPayload: sszTypesFor(fork).ExecutionPayload.defaultValue(), - executionPayloadValue: BigInt(0), - }; - } - // since merge transition is complete, we need a valid payload even if with an - // empty (transactions) one. defaultValue isn't gonna cut it! throw e; }); const [engineRes, commonBlockBody] = await Promise.all([enginePromise, commonBlockBodyPromise]); blockBody = Object.assign({}, commonBlockBody) as AssembledBodyType; - if (engineRes.isPremerge) { - (blockBody as BeaconBlockBody).executionPayload = engineRes.executionPayload; - executionPayloadValue = engineRes.executionPayloadValue; - } else { + { const {prepType, payloadId, executionPayload, blobsBundle, executionRequests} = engineRes; shouldOverrideBuilder = engineRes.shouldOverrideBuilder; @@ -504,15 +468,10 @@ export async function produceBlockBody( } /** - * Produce ExecutionPayload for pre-merge, merge, and post-merge. - * - * Expects `eth1MergeBlockFinder` to be actively searching for blocks well in advance to being called. - * - * @returns PayloadId = pow block found, null = pow NOT found + * Produce ExecutionPayload for post-merge. */ export async function prepareExecutionPayload( chain: { - eth1: IEth1ForBlockProduction; executionEngine: IExecutionEngine; config: ChainForkConfig; }, @@ -523,14 +482,8 @@ export async function prepareExecutionPayload( finalizedBlockHash: RootHex, state: CachedBeaconStateExecutions, suggestedFeeRecipient: string -): Promise<{isPremerge: true} | {isPremerge: false; prepType: PayloadPreparationType; payloadId: PayloadId}> { - const parentHashRes = await getExecutionPayloadParentHash(chain, state); - if (parentHashRes.isPremerge) { - // Return null only if the execution is pre-merge - return {isPremerge: true}; - } - - const {parentHash} = parentHashRes; +): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> { + const parentHash = state.latestExecutionPayloadHeader.blockHash; const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime); const prevRandao = getRandaoMix(state, state.epochCtx.epoch); @@ -586,12 +539,11 @@ export async function prepareExecutionPayload( // We are only returning payloadId here because prepareExecutionPayload is also called from // prepareNextSlot, which is an advance call to execution engine to start building payload // Actual payload isn't produced till getPayload is called. - return {isPremerge: false, payloadId, prepType}; + return {payloadId, prepType}; } async function prepareExecutionPayloadHeader( chain: { - eth1: IEth1ForBlockProduction; executionBuilder?: IExecutionBuilder; config: ChainForkConfig; }, @@ -608,53 +560,13 @@ async function prepareExecutionPayloadHeader( throw Error("executionBuilder required"); } - const parentHashRes = await getExecutionPayloadParentHash(chain, state); - if (parentHashRes.isPremerge) { - throw Error("External builder disabled pre-merge"); - } - - const {parentHash} = parentHashRes; + const parentHash = state.latestExecutionPayloadHeader.blockHash; return chain.executionBuilder.getHeader(fork, state.slot, parentHash, proposerPubKey); } -export async function getExecutionPayloadParentHash( - chain: { - eth1: IEth1ForBlockProduction; - config: ChainForkConfig; - }, - state: CachedBeaconStateExecutions -): Promise<{isPremerge: true} | {isPremerge: false; parentHash: Root}> { - // Use different POW block hash parent for block production based on merge status. - // Returned value of null == using an empty ExecutionPayload value - if (isMergeTransitionComplete(state)) { - // Post-merge, normal payload - return {isPremerge: false, parentHash: state.latestExecutionPayloadHeader.blockHash}; - } - - if ( - !ssz.Root.equals(chain.config.TERMINAL_BLOCK_HASH, ZERO_HASH) && - getCurrentEpoch(state) < chain.config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - ) { - throw new Error( - `InvalidMergeTBH epoch: expected >= ${ - chain.config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH - }, actual: ${getCurrentEpoch(state)}` - ); - } - - const terminalPowBlockHash = await chain.eth1.getTerminalPowBlock(); - if (terminalPowBlockHash === null) { - // Pre-merge, no prepare payload call is needed - return {isPremerge: true}; - } - // Signify merge via producing on top of the last PoW block - return {isPremerge: false, parentHash: terminalPowBlockHash}; -} - -export async function getPayloadAttributesForSSE( +export function getPayloadAttributesForSSE( fork: ForkPostBellatrix, chain: { - eth1: IEth1ForBlockProduction; config: ChainForkConfig; }, { @@ -663,30 +575,23 @@ export async function getPayloadAttributesForSSE( parentBlockRoot, feeRecipient, }: {prepareState: CachedBeaconStateExecutions; prepareSlot: Slot; parentBlockRoot: Root; feeRecipient: string} -): Promise { - const parentHashRes = await getExecutionPayloadParentHash(chain, prepareState); - - if (!parentHashRes.isPremerge) { - const {parentHash} = parentHashRes; - const payloadAttributes = preparePayloadAttributes(fork, chain, { - prepareState, - prepareSlot, - parentBlockRoot, - feeRecipient, - }); - - const ssePayloadAttributes: SSEPayloadAttributes = { - proposerIndex: prepareState.epochCtx.getBeaconProposer(prepareSlot), - proposalSlot: prepareSlot, - parentBlockNumber: prepareState.latestExecutionPayloadHeader.blockNumber, - parentBlockRoot, - parentBlockHash: parentHash, - payloadAttributes, - }; - return ssePayloadAttributes; - } - - throw Error("The execution is still pre-merge"); +): SSEPayloadAttributes { + const parentHash = prepareState.latestExecutionPayloadHeader.blockHash; + const payloadAttributes = preparePayloadAttributes(fork, chain, { + prepareState, + prepareSlot, + parentBlockRoot, + feeRecipient, + }); + const ssePayloadAttributes: SSEPayloadAttributes = { + proposerIndex: prepareState.epochCtx.getBeaconProposer(prepareSlot), + proposalSlot: prepareSlot, + parentBlockNumber: prepareState.latestExecutionPayloadHeader.blockNumber, + parentBlockRoot, + parentBlockHash: parentHash, + payloadAttributes, + }; + return ssePayloadAttributes; } function preparePayloadAttributes( diff --git a/packages/beacon-node/src/chain/validation/block.ts b/packages/beacon-node/src/chain/validation/block.ts index cc45a2625e93..a55865f307a6 100644 --- a/packages/beacon-node/src/chain/validation/block.ts +++ b/packages/beacon-node/src/chain/validation/block.ts @@ -6,7 +6,6 @@ import { computeTimeAtSlot, getBlockProposerSignatureSet, isExecutionBlockBodyType, - isExecutionEnabled, isExecutionStateType, } from "@lodestar/state-transition"; import {SignedBeaconBlock, deneb} from "@lodestar/types"; @@ -140,7 +139,7 @@ export async function validateGossipBlock( if (fork === ForkName.bellatrix) { if (!isExecutionBlockBodyType(block.body)) throw Error("Not merge block type"); const executionPayload = block.body.executionPayload; - if (isExecutionStateType(blockState) && isExecutionEnabled(blockState, block)) { + if (isExecutionStateType(blockState)) { const expectedTimestamp = computeTimeAtSlot(config, blockSlot, chain.genesisTime); if (executionPayload.timestamp !== computeTimeAtSlot(config, blockSlot, chain.genesisTime)) { throw new BlockGossipError(GossipAction.REJECT, { diff --git a/packages/beacon-node/src/eth1/eth1MergeBlockTracker.ts b/packages/beacon-node/src/eth1/eth1MergeBlockTracker.ts deleted file mode 100644 index ea2d134fe4dd..000000000000 --- a/packages/beacon-node/src/eth1/eth1MergeBlockTracker.ts +++ /dev/null @@ -1,328 +0,0 @@ -import {ChainConfig} from "@lodestar/config"; -import {RootHex} from "@lodestar/types"; -import {Logger, pruneSetToMax, toRootHex} from "@lodestar/utils"; -import {ZERO_HASH_HEX} from "../constants/index.js"; -import {Metrics} from "../metrics/index.js"; -import {enumToIndexMap} from "../util/enum.js"; -import {EthJsonRpcBlockRaw, IEth1Provider, PowMergeBlock, PowMergeBlockTimestamp, TDProgress} from "./interface.js"; -import {dataToRootHex, quantityToBigint, quantityToNum} from "./provider/utils.js"; - -export enum StatusCode { - STOPPED = "STOPPED", - SEARCHING = "SEARCHING", - FOUND = "FOUND", -} - -type Status = - | {code: StatusCode.STOPPED} - | {code: StatusCode.SEARCHING} - | {code: StatusCode.FOUND; mergeBlock: PowMergeBlock}; - -/** For metrics, index order = declaration order of StatusCode */ -const statusCodeIdx = enumToIndexMap(StatusCode); - -/** - * Bounds `blocksByHashCache` cache, imposing a max distance between highest and lowest block numbers. - * In case of extreme forking the cache might grow unbounded. - */ -const MAX_CACHE_POW_BLOCKS = 1024; - -const MAX_TD_RENDER_VALUE = Number.MAX_SAFE_INTEGER; - -export type Eth1MergeBlockTrackerModules = { - config: ChainConfig; - logger: Logger; - signal: AbortSignal; - metrics: Metrics | null; -}; - -// get_pow_block_at_total_difficulty - -/** - * Follows the eth1 chain to find a (or multiple?) merge blocks that cross the threshold of total terminal difficulty - * - * Finding the mergeBlock could be done in demand when proposing pre-merge blocks. However, that would slow block - * production during the weeks between BELLATRIX_EPOCH and TTD. - */ -export class Eth1MergeBlockTracker { - private readonly config: ChainConfig; - private readonly logger: Logger; - private readonly metrics: Metrics | null; - - private readonly blocksByHashCache = new Map(); - private readonly intervals: NodeJS.Timeout[] = []; - - private status: Status; - private latestEth1Block: PowMergeBlockTimestamp | null = null; - private getTerminalPowBlockFromEth1Promise: Promise | null = null; - private readonly safeTDFactor: bigint; - - constructor( - {config, logger, signal, metrics}: Eth1MergeBlockTrackerModules, - private readonly eth1Provider: IEth1Provider - ) { - this.config = config; - this.logger = logger; - this.metrics = metrics; - - this.status = {code: StatusCode.STOPPED}; - - signal.addEventListener("abort", () => this.close(), {once: true}); - - this.safeTDFactor = getSafeTDFactor(this.config.TERMINAL_TOTAL_DIFFICULTY); - const scaledTTD = this.config.TERMINAL_TOTAL_DIFFICULTY / this.safeTDFactor; - - // Only run metrics if necessary - if (metrics) { - // TTD can't be dynamically changed during execution, register metric once - metrics.eth1.eth1MergeTTD.set(Number(scaledTTD as bigint)); - metrics.eth1.eth1MergeTDFactor.set(Number(this.safeTDFactor as bigint)); - - metrics.eth1.eth1MergeStatus.addCollect(() => { - // Set merge ttd, merge status and merge block status - metrics.eth1.eth1MergeStatus.set(statusCodeIdx[this.status.code]); - - if (this.latestEth1Block !== null) { - // Set latestBlock stats - metrics.eth1.eth1LatestBlockNumber.set(this.latestEth1Block.number); - metrics.eth1.eth1LatestBlockTD.set(Number(this.latestEth1Block.totalDifficulty / this.safeTDFactor)); - metrics.eth1.eth1LatestBlockTimestamp.set(this.latestEth1Block.timestamp); - } - }); - } - } - - /** - * Returns the most recent POW block that satisfies the merge block condition - */ - async getTerminalPowBlock(): Promise { - switch (this.status.code) { - case StatusCode.STOPPED: - // If not module is not polling fetch the mergeBlock explicitly - return this.getTerminalPowBlockFromEth1(); - - case StatusCode.SEARCHING: - // Assume that polling would have found the block - return null; - - case StatusCode.FOUND: - return this.status.mergeBlock; - } - } - - getTDProgress(): TDProgress | null { - if (this.latestEth1Block === null) { - return this.latestEth1Block; - } - - const tdDiff = this.config.TERMINAL_TOTAL_DIFFICULTY - this.latestEth1Block.totalDifficulty; - - if (tdDiff > BigInt(0)) { - return { - ttdHit: false, - tdFactor: this.safeTDFactor, - tdDiffScaled: Number((tdDiff / this.safeTDFactor) as bigint), - ttd: this.config.TERMINAL_TOTAL_DIFFICULTY, - td: this.latestEth1Block.totalDifficulty, - timestamp: this.latestEth1Block.timestamp, - }; - } - return { - ttdHit: true, - }; - } - - /** - * Get a POW block by hash checking the local cache first - */ - async getPowBlock(powBlockHash: string): Promise { - // Check cache first - const cachedBlock = this.blocksByHashCache.get(powBlockHash); - if (cachedBlock) { - return cachedBlock; - } - - // Fetch from node - const blockRaw = await this.eth1Provider.getBlockByHash(powBlockHash); - if (blockRaw) { - const block = toPowBlock(blockRaw); - this.cacheBlock(block); - return block; - } - - return null; - } - - /** - * Should only start polling for mergeBlock if: - * - after BELLATRIX_FORK_EPOCH - * - Beacon node synced - * - head state not isMergeTransitionComplete - */ - startPollingMergeBlock(): void { - if (this.status.code !== StatusCode.STOPPED) { - return; - } - - this.status = {code: StatusCode.SEARCHING}; - this.logger.info("Starting search for terminal POW block", { - TERMINAL_TOTAL_DIFFICULTY: this.config.TERMINAL_TOTAL_DIFFICULTY, - }); - - const interval = setInterval(() => { - // Preemptively try to find merge block and cache it if found. - // Future callers of getTerminalPowBlock() will re-use the cached found mergeBlock. - this.getTerminalPowBlockFromEth1().catch((e) => { - this.logger.error("Error on findMergeBlock", {}, e as Error); - this.metrics?.eth1.eth1PollMergeBlockErrors.inc(); - }); - }, this.config.SECONDS_PER_ETH1_BLOCK * 1000); - - this.intervals.push(interval); - } - - private close(): void { - this.intervals.forEach(clearInterval); - } - - private async getTerminalPowBlockFromEth1(): Promise { - if (!this.getTerminalPowBlockFromEth1Promise) { - this.getTerminalPowBlockFromEth1Promise = this.internalGetTerminalPowBlockFromEth1() - .then((mergeBlock) => { - // Persist found merge block here to affect both caller paths: - // - internal searcher - // - external caller if STOPPED - if (mergeBlock && this.status.code !== StatusCode.FOUND) { - if (this.status.code === StatusCode.SEARCHING) { - this.close(); - } - - this.logger.info("Terminal POW block found!", { - hash: mergeBlock.blockHash, - number: mergeBlock.number, - totalDifficulty: mergeBlock.totalDifficulty, - }); - - this.status = {code: StatusCode.FOUND, mergeBlock}; - this.metrics?.eth1.eth1MergeBlockDetails.set( - { - terminalBlockHash: mergeBlock.blockHash, - // Convert all number/bigints to string labels - terminalBlockNumber: mergeBlock.number.toString(10), - terminalBlockTD: mergeBlock.totalDifficulty.toString(10), - }, - 1 - ); - } - - return mergeBlock; - }) - .finally(() => { - this.getTerminalPowBlockFromEth1Promise = null; - }); - } else { - // This should no happen, since getTerminalPowBlockFromEth1() should resolve faster than SECONDS_PER_ETH1_BLOCK. - // else something is wrong: the el-cl comms are two slow, or the backsearch got stuck in a deep search. - this.metrics?.eth1.getTerminalPowBlockPromiseCacheHit.inc(); - } - - return this.getTerminalPowBlockFromEth1Promise; - } - - /** - * **internal** + **unsafe** since it can create multiple backward searches that overload the eth1 client. - * Must be called in a wrapper to ensure that there's only once concurrent call to this fn. - */ - private async internalGetTerminalPowBlockFromEth1(): Promise { - // Search merge block by hash - // Terminal block hash override takes precedence over terminal total difficulty - const terminalBlockHash = toRootHex(this.config.TERMINAL_BLOCK_HASH); - if (terminalBlockHash !== ZERO_HASH_HEX) { - const block = await this.getPowBlock(terminalBlockHash); - if (block) { - return block; - } - // if a TERMINAL_BLOCK_HASH other than ZERO_HASH is configured and we can't find it, return NONE - return null; - } - - // Search merge block by TTD - const latestBlockRaw = await this.eth1Provider.getBlockByNumber("latest"); - if (!latestBlockRaw) { - throw Error("getBlockByNumber('latest') returned null"); - } - - let block = toPowBlock(latestBlockRaw); - this.latestEth1Block = {...block, timestamp: quantityToNum(latestBlockRaw.timestamp)}; - this.cacheBlock(block); - - // This code path to look backwards for the merge block is only necessary if: - // - The network has not yet found the merge block - // - There are descendants of the merge block in the eth1 chain - // For the search below to require more than a few hops, multiple block proposers in a row must fail to detect - // an existing merge block. Such situation is extremely unlikely, so this search is left un-optimized. Since - // this class can start eagerly looking for the merge block when not necessary, startPollingMergeBlock() should - // only be called when there is certainty that a mergeBlock search is necessary. - - while (true) { - if (block.totalDifficulty < this.config.TERMINAL_TOTAL_DIFFICULTY) { - // TTD not reached yet - return null; - } - - // else block.totalDifficulty >= this.config.TERMINAL_TOTAL_DIFFICULTY - // Potential mergeBlock! Must find the first block that passes TTD - - // Allow genesis block to reach TTD https://github.com/ethereum/consensus-specs/pull/2719 - if (block.parentHash === ZERO_HASH_HEX) { - return block; - } - - const parent = await this.getPowBlock(block.parentHash); - if (!parent) { - throw Error(`Unknown parent of block with TD>TTD ${block.parentHash}`); - } - - this.metrics?.eth1.eth1ParentBlocksFetched.inc(); - - // block.td > TTD && parent.td < TTD => block is mergeBlock - if (parent.totalDifficulty < this.config.TERMINAL_TOTAL_DIFFICULTY) { - // Is terminal total difficulty block AND has verified block -> parent relationship - return block; - } - block = parent; - } - } - - private cacheBlock(block: PowMergeBlock): void { - this.blocksByHashCache.set(block.blockHash, block); - pruneSetToMax(this.blocksByHashCache, MAX_CACHE_POW_BLOCKS); - } -} - -export function toPowBlock(block: EthJsonRpcBlockRaw): PowMergeBlock { - // Validate untrusted data from API - return { - number: quantityToNum(block.number), - blockHash: dataToRootHex(block.hash), - parentHash: dataToRootHex(block.parentHash), - totalDifficulty: quantityToBigint(block.totalDifficulty), - }; -} - -/** - * TTD values can be very large, for xDAI > 1e45. So scale down. - * To be good, TTD should be rendered as a number < Number.MAX_TD_RENDER_VALUE ~= 9e15 - */ -export function getSafeTDFactor(ttd: bigint): bigint { - const safeIntegerMult = ttd / BigInt(MAX_TD_RENDER_VALUE); - - // TTD < MAX_TD_RENDER_VALUE, no need to scale down - if (safeIntegerMult === BigInt(0)) { - return BigInt(1); - } - - // Return closest power of 10 to ensure TD < max - const safeIntegerMultDigits = safeIntegerMult.toString(10).length; - return BigInt(10) ** BigInt(safeIntegerMultDigits); -} diff --git a/packages/beacon-node/src/eth1/index.ts b/packages/beacon-node/src/eth1/index.ts index 81b2ab6d7b91..02dffb3d4a75 100644 --- a/packages/beacon-node/src/eth1/index.ts +++ b/packages/beacon-node/src/eth1/index.ts @@ -1,9 +1,6 @@ import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; -import {Root} from "@lodestar/types"; -import {fromHex} from "@lodestar/utils"; import {Eth1DepositDataTracker, Eth1DepositDataTrackerModules} from "./eth1DepositDataTracker.js"; -import {Eth1MergeBlockTracker, Eth1MergeBlockTrackerModules} from "./eth1MergeBlockTracker.js"; -import {Eth1DataAndDeposits, IEth1ForBlockProduction, IEth1Provider, PowMergeBlock, TDProgress} from "./interface.js"; +import {Eth1DataAndDeposits, IEth1ForBlockProduction, IEth1Provider} from "./interface.js"; import {Eth1Options} from "./options.js"; import {Eth1Provider} from "./provider/eth1Provider.js"; export {Eth1Provider}; @@ -23,23 +20,6 @@ export type {IEth1ForBlockProduction, IEth1Provider}; // // - Fetch ALL deposit events from the deposit contract to build the deposit tree and validate future merkle proofs. // Then it must follow deposit events at a distance roughly similar to the `ETH1_FOLLOW_DISTANCE` parameter above. -// -// - [New bellatrix]: After BELLATRIX_FORK_EPOCH, it must fetch the block with hash -// `state.eth1_data.block_hash` to compute `terminal_total_difficulty`. Note this may change with -// https://github.com/ethereum/consensus-specs/issues/2603. -// -// - [New bellatrix]: On block production post BELLATRIX_FORK_EPOCH, pre merge, the beacon node must find the merge block -// crossing the `terminal_total_difficulty` boundary and include it in the block. After the merge block production -// will just use `execution_engine.assemble_block` without fetching individual blocks. -// -// - [New bellatrix]: Fork-choice must validate the merge block ensuring it crossed the `terminal_total_difficulty` -// boundary, so it must fetch the POW block referenced in the merge block + its POW parent block. -// -// With the merge the beacon node has to follow the eth1 chain at two distances: -// 1. At `ETH1_FOLLOW_DISTANCE` for eth1Data to be re-org safe -// 2. At the head to get the first merge block, tolerating possible re-orgs -// -// Then both streams of blocks should not be merged since it's harder to guard against re-orgs from (2) to (1). export function initializeEth1ForBlockProduction( opts: Eth1Options, @@ -59,12 +39,8 @@ export function initializeEth1ForBlockProduction( export class Eth1ForBlockProduction implements IEth1ForBlockProduction { private readonly eth1DepositDataTracker: Eth1DepositDataTracker | null; - private readonly eth1MergeBlockTracker: Eth1MergeBlockTracker; - constructor( - opts: Eth1Options, - modules: Eth1DepositDataTrackerModules & Eth1MergeBlockTrackerModules & {eth1Provider?: IEth1Provider} - ) { + constructor(opts: Eth1Options, modules: Eth1DepositDataTrackerModules & {eth1Provider?: IEth1Provider}) { const eth1Provider = modules.eth1Provider || new Eth1Provider( @@ -77,8 +53,6 @@ export class Eth1ForBlockProduction implements IEth1ForBlockProduction { this.eth1DepositDataTracker = opts.disableEth1DepositDataTracker ? null : new Eth1DepositDataTracker(opts, modules, eth1Provider); - - this.eth1MergeBlockTracker = new Eth1MergeBlockTracker(modules, eth1Provider); } async getEth1DataAndDeposits(state: CachedBeaconStateAllForks): Promise { @@ -88,23 +62,6 @@ export class Eth1ForBlockProduction implements IEth1ForBlockProduction { return this.eth1DepositDataTracker.getEth1DataAndDeposits(state); } - async getTerminalPowBlock(): Promise { - const block = await this.eth1MergeBlockTracker.getTerminalPowBlock(); - return block && fromHex(block.blockHash); - } - - getPowBlock(powBlockHash: string): Promise { - return this.eth1MergeBlockTracker.getPowBlock(powBlockHash); - } - - getTDProgress(): TDProgress | null { - return this.eth1MergeBlockTracker.getTDProgress(); - } - - startPollingMergeBlock(): void { - this.eth1MergeBlockTracker.startPollingMergeBlock(); - } - isPollingEth1Data(): boolean { return this.eth1DepositDataTracker?.isPollingEth1Data() ?? false; } @@ -127,30 +84,10 @@ export class Eth1ForBlockProductionDisabled implements IEth1ForBlockProduction { return {eth1Data: state.eth1Data, deposits: []}; } - /** - * Will miss the oportunity to propose the merge block but will still produce valid blocks - */ - async getTerminalPowBlock(): Promise { - return null; - } - - /** Will not be able to validate the merge block */ - async getPowBlock(_powBlockHash: string): Promise { - throw Error("eth1 must be enabled to verify merge block"); - } - - getTDProgress(): TDProgress | null { - return null; - } - isPollingEth1Data(): boolean { return false; } - startPollingMergeBlock(): void { - // Ignore - } - stopPollingEth1Data(): void { // Ignore } diff --git a/packages/beacon-node/src/eth1/interface.ts b/packages/beacon-node/src/eth1/interface.ts index c247e30a691c..eecec8fc7756 100644 --- a/packages/beacon-node/src/eth1/interface.ts +++ b/packages/beacon-node/src/eth1/interface.ts @@ -1,6 +1,6 @@ import {BeaconConfig} from "@lodestar/config"; import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; -import {Root, RootHex, phase0} from "@lodestar/types"; +import {phase0} from "@lodestar/types"; export type EthJsonRpcBlockRaw = { /** the block number. null when its pending block. `"0x1b4"` */ @@ -47,22 +47,6 @@ export type Eth1DataAndDeposits = { export interface IEth1ForBlockProduction { getEth1DataAndDeposits(state: CachedBeaconStateAllForks): Promise; - /** Returns the most recent POW block that satisfies the merge block condition */ - getTerminalPowBlock(): Promise; - /** Get a POW block by hash checking the local cache first */ - getPowBlock(powBlockHash: string): Promise; - - /** Get current TD progress for log notifier */ - getTDProgress(): TDProgress | null; - - /** - * Should only start polling for mergeBlock if: - * - after BELLATRIX_FORK_EPOCH - * - Beacon node synced - * - head state not isMergeTransitionComplete - */ - startPollingMergeBlock(): void; - isPollingEth1Data(): boolean; /** @@ -78,34 +62,6 @@ export type Eth1Block = { timestamp: number; }; -export type PowMergeBlock = { - number: number; - blockHash: RootHex; - parentHash: RootHex; - totalDifficulty: bigint; -}; - -export type PowMergeBlockTimestamp = PowMergeBlock & { - /** in seconds */ - timestamp: number; -}; - -export type TDProgress = - | { - ttdHit: false; - /** Power of ten by which tdDiffScaled is scaled down */ - tdFactor: bigint; - /** (TERMINAL_TOTAL_DIFFICULTY - block.totalDifficulty) / tdFactor */ - tdDiffScaled: number; - /** TERMINAL_TOTAL_DIFFICULTY */ - ttd: bigint; - /** totalDifficulty of latest fetched eth1 block */ - td: bigint; - /** timestamp in sec of latest fetched eth1 block */ - timestamp: number; - } - | {ttdHit: true}; - export type BatchDepositEvents = { depositEvents: phase0.DepositEvent[]; blockNumber: number; diff --git a/packages/beacon-node/src/execution/engine/http.ts b/packages/beacon-node/src/execution/engine/http.ts index e4e69f9d5fab..32739eba4ce9 100644 --- a/packages/beacon-node/src/execution/engine/http.ts +++ b/packages/beacon-node/src/execution/engine/http.ts @@ -194,15 +194,12 @@ export class ExecutionEngineHttp implements IExecutionEngine { * 1. {status: INVALID_BLOCK_HASH, latestValidHash: null, validationError: * errorMessage | null} if the blockHash validation has failed * - * 2. {status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: - * errorMessage | null} if terminal block conditions are not satisfied - * - * 3. {status: SYNCING, latestValidHash: null, validationError: null} if the payload + * 2. {status: SYNCING, latestValidHash: null, validationError: null} if the payload * extends the canonical chain and requisite data for its validation is missing * with the payload status obtained from the Payload validation process if the payload * has been fully validated while processing the call * - * 4. {status: ACCEPTED, latestValidHash: null, validationError: null} if the + * 3. {status: ACCEPTED, latestValidHash: null, validationError: null} if the * following conditions are met: * i) the blockHash of the payload is valid * ii) the payload doesn't extend the canonical chain @@ -330,16 +327,11 @@ export class ExecutionEngineHttp implements IExecutionEngine { * errorMessage | null}, payloadId: null} * obtained from the Payload validation process if the payload is deemed INVALID * - * 3. {payloadStatus: {status: INVALID_TERMINAL_BLOCK, latestValidHash: null, - * validationError: errorMessage | null}, payloadId: null} - * either obtained from the Payload validation process or as a result of validating a - * PoW block referenced by forkchoiceState.headBlockHash - * - * 4. {payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, + * 3. {payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, * validationError: null}, payloadId: null} * if the payload is deemed VALID and a build process hasn't been started * - * 5. {payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, + * 4. {payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, * validationError: null}, payloadId: buildProcessId} * if the payload is deemed VALID and the build process has begun. * diff --git a/packages/beacon-node/src/execution/engine/mock.ts b/packages/beacon-node/src/execution/engine/mock.ts index d28632d07a13..b6d30dece51c 100644 --- a/packages/beacon-node/src/execution/engine/mock.ts +++ b/packages/beacon-node/src/execution/engine/mock.ts @@ -9,7 +9,7 @@ import { ForkSeq, } from "@lodestar/params"; import {ExecutionPayload, RootHex, bellatrix, deneb, ssz} from "@lodestar/types"; -import {fromHex, toHex, toRootHex} from "@lodestar/utils"; +import {fromHex, toRootHex} from "@lodestar/utils"; import {ZERO_HASH_HEX} from "../../constants/index.js"; import {quantityToNum} from "../../eth1/provider/utils.js"; import {INTEROP_BLOCK_HASH} from "../../node/utils/interop/state.js"; @@ -70,7 +70,7 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { finalizedBlockHash = ZERO_HASH_HEX; readonly payloadIdCache = new PayloadIdCache(); - /** Known valid blocks, both pre-merge and post-merge */ + /** Known valid blocks */ private readonly validBlocks = new Map(); /** Preparing payloads to be retrieved via engine_getPayloadV1 */ private readonly preparingPayloads = new Map(); @@ -135,18 +135,6 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { return [] as ExecutionPayloadBodyRpc[]; } - /** - * Mock manipulator to add more known blocks to this mock. - */ - addPowBlock(powBlock: bellatrix.PowBlock): void { - this.validBlocks.set(toHex(powBlock.blockHash), { - parentHash: toHex(powBlock.parentHash), - blockHash: toHex(powBlock.blockHash), - timestamp: 0, - blockNumber: 0, - }); - } - /** * Mock manipulator to add predefined responses before execution engine client calls */ @@ -258,7 +246,7 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend { // section of the EIP. Additionally, if this validation fails, client software MUST NOT update the forkchoice // state and MUST NOT begin a payload build process. // - // > TODO + // > N/A: All networks have completed the merge transition // 4. Before updating the forkchoice state, client software MUST ensure the validity of the payload referenced by // forkchoiceState.headBlockHash, and MAY validate the payload while processing the call. The validation process diff --git a/packages/beacon-node/src/metrics/metrics/lodestar.ts b/packages/beacon-node/src/metrics/metrics/lodestar.ts index 721b6ffbcbac..7e352e318ec6 100644 --- a/packages/beacon-node/src/metrics/metrics/lodestar.ts +++ b/packages/beacon-node/src/metrics/metrics/lodestar.ts @@ -1668,58 +1668,6 @@ export function createLodestarMetrics( name: "lodestar_eth1_logs_batch_size_dynamic", help: "Dynamic batch size to fetch deposit logs", }), - - // Merge Search info - eth1MergeStatus: register.gauge({ - name: "lodestar_eth1_merge_status", - help: "Eth1 Merge Status 0 PRE_MERGE 1 SEARCHING 2 FOUND 3 POST_MERGE", - }), - eth1MergeTDFactor: register.gauge({ - name: "lodestar_eth1_merge_td_factor", - help: "TTD set for the merge", - }), - eth1MergeTTD: register.gauge({ - name: "lodestar_eth1_merge_ttd", - help: "TTD set for the merge scaled down by td_factor", - }), - - eth1PollMergeBlockErrors: register.gauge({ - name: "lodestar_eth1_poll_merge_block_errors_total", - help: "Total count of errors polling merge block", - }), - getTerminalPowBlockPromiseCacheHit: register.gauge({ - name: "lodestar_eth1_get_terminal_pow_block_promise_cache_hit_total", - help: "Total count of skipped runs in poll merge block, because a previous promise existed", - }), - eth1ParentBlocksFetched: register.gauge({ - name: "lodestar_eth1_parent_blocks_fetched_total", - help: "Total count of parent blocks fetched searching for merge block", - }), - - // Latest block details - eth1LatestBlockTD: register.gauge({ - name: "lodestar_eth1_latest_block_ttd", - help: "Eth1 latest Block td scaled down by td_factor", - }), - eth1LatestBlockNumber: register.gauge({ - name: "lodestar_eth1_latest_block_number", - help: "Eth1 latest block number", - }), - eth1LatestBlockTimestamp: register.gauge({ - name: "lodestar_eth1_latest_block_timestamp", - help: "Eth1 latest block timestamp", - }), - - // Merge details - eth1MergeBlockDetails: register.gauge<{ - terminalBlockHash: string; - terminalBlockNumber: string; - terminalBlockTD: string; - }>({ - name: "lodestar_eth1_merge_block_details", - help: "If found then 1 with terminal block details", - labelNames: ["terminalBlockHash", "terminalBlockNumber", "terminalBlockTD"], - }), }, eth1HttpClient: { diff --git a/packages/beacon-node/src/node/notifier.ts b/packages/beacon-node/src/node/notifier.ts index 36ed384656e1..aabb1850a060 100644 --- a/packages/beacon-node/src/node/notifier.ts +++ b/packages/beacon-node/src/node/notifier.ts @@ -6,7 +6,6 @@ import { computeEpochAtSlot, computeStartSlotAtEpoch, isExecutionCachedStateType, - isMergeTransitionComplete, } from "@lodestar/state-transition"; import {Epoch} from "@lodestar/types"; import {ErrorAborted, Logger, prettyBytes, prettyBytesShort, sleep} from "@lodestar/utils"; @@ -36,7 +35,6 @@ export async function runNodeNotifier(modules: NodeNotifierModules): Promise { - const logger = testLogger(); - - function getConfig(ttd: bigint): ChainConfig { - return { - // Set time units to 1s to make the test faster - SECONDS_PER_ETH1_BLOCK: 1, - SLOT_DURATION_MS: 1000, - DEPOSIT_CONTRACT_ADDRESS: Buffer.alloc(32, 0), - TERMINAL_TOTAL_DIFFICULTY: ttd, - TERMINAL_BLOCK_HASH: ZERO_HASH, - } as Partial as ChainConfig; - } - const eth1Config = {DEPOSIT_CONTRACT_ADDRESS: ZERO_HASH}; - - // Compute lazily since getGoerliRpcUrl() throws if GOERLI_RPC_URL is not set - let eth1Options: Eth1Options; - beforeAll(() => { - eth1Options = { - enabled: true, - providerUrls: [getGoerliRpcUrl()], - depositContractDeployBlock: 0, - unsafeAllowDepositDataOverwrite: false, - }; - }); - - let controller: AbortController; - beforeEach(() => { - controller = new AbortController(); - }); - afterEach(() => controller.abort()); - - it("Should find terminal pow block through TERMINAL_BLOCK_HASH", async () => { - const eth1Provider = new Eth1Provider(eth1Config, eth1Options, controller.signal); - const latestBlock = await eth1Provider.getBlockByNumber("latest"); - if (!latestBlock) throw Error("No latestBlock"); - const terminalTotalDifficulty = quantityToBigint(latestBlock.totalDifficulty) - BigInt(1000); - const config = getConfig(terminalTotalDifficulty); - config.TERMINAL_BLOCK_HASH = fromHexString(latestBlock.hash); - const eth1MergeBlockTracker = new Eth1MergeBlockTracker( - { - config, - logger, - signal: controller.signal, - metrics: null, - }, - eth1Provider as IEth1Provider - ); - - // Wait for Eth1MergeBlockTracker to find at least one merge block - while (!controller.signal.aborted) { - if (await eth1MergeBlockTracker.getTerminalPowBlock()) break; - await sleep(500, controller.signal); - } - - // Status should acknowlege merge block is found - expect(eth1MergeBlockTracker["status"]).toBe(StatusCode.FOUND); - - // Given the total difficulty offset the block that has TTD is the `difficultyOffset`nth block - const mergeBlock = await eth1MergeBlockTracker.getTerminalPowBlock(); - if (!mergeBlock) throw Error("terminal pow block not found"); - expect(mergeBlock.totalDifficulty).toBe(quantityToBigint(latestBlock.totalDifficulty)); - }); - - it("Should find merge block polling future 'latest' blocks", async () => { - const eth1Provider = new Eth1Provider(eth1Config, eth1Options, controller.signal); - const latestBlock = await eth1Provider.getBlockByNumber("latest"); - if (!latestBlock) throw Error("No latestBlock"); - - // Set TTD to current totalDifficulty + 1, so the next block is the merge block - const terminalTotalDifficulty = quantityToBigint(latestBlock.totalDifficulty) + BigInt(1); - - const eth1MergeBlockTracker = new Eth1MergeBlockTracker( - { - config: getConfig(terminalTotalDifficulty), - logger, - signal: controller.signal, - metrics: null, - }, - eth1Provider as IEth1Provider - ); - - // Wait for Eth1MergeBlockTracker to find at least one merge block - while (!controller.signal.aborted) { - if (await eth1MergeBlockTracker.getTerminalPowBlock()) break; - await sleep(500, controller.signal); - } - - // Status should acknowlege merge block is found - expect(eth1MergeBlockTracker["status"]).toBe(StatusCode.FOUND); - - // Given the total difficulty offset the block that has TTD is the `difficultyOffset`nth block - const mergeBlock = await eth1MergeBlockTracker.getTerminalPowBlock(); - if (!mergeBlock) throw Error("mergeBlock not found"); - // "mergeBlock.totalDifficulty is not >= TTD" - expect(mergeBlock.totalDifficulty).toBeGreaterThanOrEqual(terminalTotalDifficulty); - }); - - it("Should find merge block fetching past blocks", async () => { - const eth1Provider = new Eth1Provider(eth1Config, eth1Options, controller.signal); - const latestBlock = await eth1Provider.getBlockByNumber("latest"); - if (!latestBlock) throw Error("No latestBlock"); - - // Set TTD to current totalDifficulty + 1, so the previous block is the merge block - const terminalTotalDifficulty = quantityToBigint(latestBlock.totalDifficulty) - BigInt(1); - - const eth1MergeBlockTracker = new Eth1MergeBlockTracker( - { - config: getConfig(terminalTotalDifficulty), - logger, - signal: controller.signal, - metrics: null, - }, - eth1Provider as IEth1Provider - ); - - // Wait for Eth1MergeBlockTracker to find at least one merge block - while (!controller.signal.aborted) { - if (await eth1MergeBlockTracker.getTerminalPowBlock()) break; - await sleep(500, controller.signal); - } - - // Status should acknowlege merge block is found - expect(eth1MergeBlockTracker["status"]).toBe(StatusCode.FOUND); - - // Given the total difficulty offset the block that has TTD is the `difficultyOffset`nth block - const mergeBlock = await eth1MergeBlockTracker.getTerminalPowBlock(); - if (!mergeBlock) throw Error("mergeBlock not found"); - // "mergeBlock.totalDifficulty is not >= TTD" - expect(mergeBlock.totalDifficulty).toBeGreaterThanOrEqual(terminalTotalDifficulty); - }); -}); diff --git a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts index 14dd293e150e..4d52b4a64d5c 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -3,7 +3,6 @@ import {afterAll, beforeAll, bench, describe} from "@chainsafe/benchmark"; import {fromHexString} from "@chainsafe/ssz"; import {config} from "@lodestar/config/default"; import {LevelDbController} from "@lodestar/db/controller/level"; -import {SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY} from "@lodestar/params"; import {CachedBeaconStateAltair} from "@lodestar/state-transition"; import {defaultOptions as defaultValidatorOptions} from "@lodestar/validator"; import {generatePerfTestCachedStateAltair} from "../../../../../state-transition/test/perf/util.js"; @@ -31,7 +30,6 @@ describe("produceBlockBody", () => { proposerBoost: true, proposerBoostReorg: true, computeUnrealized: false, - safeSlotsToImportOptimistically: SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY, disableArchiveOnCheckpoint: true, suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient, skipCreateStateCacheIfAvailable: true, diff --git a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts index 7d9b21ff94cf..e7cd6a1b2c9c 100644 --- a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts +++ b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts @@ -2,7 +2,7 @@ import {generateKeyPair} from "@libp2p/crypto/keys"; import {afterAll, beforeAll, bench, describe, setBenchOpts} from "@chainsafe/benchmark"; import {config} from "@lodestar/config/default"; import {LevelDbController} from "@lodestar/db/controller/level"; -import {SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY, SLOTS_PER_EPOCH} from "@lodestar/params"; +import {SLOTS_PER_EPOCH} from "@lodestar/params"; import {sleep, toHex} from "@lodestar/utils"; import {defaultOptions as defaultValidatorOptions} from "@lodestar/validator"; import {rangeSyncTest} from "../../../../state-transition/test/perf/params.js"; @@ -82,7 +82,6 @@ describe.skip("verify+import blocks - range sync perf test", () => { proposerBoost: true, proposerBoostReorg: true, computeUnrealized: false, - safeSlotsToImportOptimistically: SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY, disableArchiveOnCheckpoint: true, suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient, skipCreateStateCacheIfAvailable: true, diff --git a/packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/besu.sh similarity index 88% rename from packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/besu.sh index d864814ece7a..1f54d8646793 100755 --- a/packages/beacon-node/test/scripts/el-interop/besu/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/besu.sh @@ -2,7 +2,8 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/besu -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh $EL_BINARY_DIR/besu --engine-rpc-enabled --rpc-http-enabled --rpc-http-api ADMIN,ETH,MINER,NET --rpc-http-port $ETH_PORT --engine-rpc-port $ENGINE_PORT --engine-jwt-secret $currentDir/$DATA_DIR/jwtsecret --data-path $DATA_DIR --data-storage-format BONSAI --genesis-file $DATA_DIR/genesis.json diff --git a/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh index f211f5d0714b..46e6cc6fda40 100755 --- a/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh +++ b/packages/beacon-node/test/scripts/el-interop/besu/common-setup.sh @@ -6,11 +6,11 @@ echo $EL_BINARY_DIR echo $JWT_SECRET_HEX echo $TEMPLATE_FILE -echo $scriptDir +echo $elDir echo $currentDir -env TTD=$TTD envsubst < $scriptDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json +env TTD=$TTD envsubst < $elDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json echo "12345678" > $DATA_DIR/password.txt pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" diff --git a/packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/besudocker.sh similarity index 90% rename from packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/besudocker.sh index d26307ee3d24..30117834d468 100755 --- a/packages/beacon-node/test/scripts/el-interop/besudocker/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/besudocker.sh @@ -2,7 +2,8 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/besudocker -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution -p $ETH_PORT:$ETH_PORT -p $ENGINE_PORT:$ENGINE_PORT -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --engine-rpc-enabled --rpc-http-enabled --rpc-http-api ADMIN,ETH,MINER,NET --rpc-http-port $ETH_PORT --engine-rpc-port $ENGINE_PORT --engine-jwt-secret /data/jwtsecret --data-path /data/besu --data-storage-format BONSAI --genesis-file /data/genesis.json diff --git a/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh index b3d93190ef2d..cde122a65686 100644 --- a/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh +++ b/packages/beacon-node/test/scripts/el-interop/besudocker/common-setup.sh @@ -6,11 +6,11 @@ echo $EL_BINARY_DIR echo $JWT_SECRET_HEX echo $TEMPLATE_FILE -echo $scriptDir +echo $elDir echo $currentDir -env TTD=$TTD envsubst < $scriptDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json +env TTD=$TTD envsubst < $elDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json echo "12345678" > $DATA_DIR/password.txt pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" diff --git a/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker.sh similarity index 86% rename from packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/ethereumjsdocker.sh index fbf9dcaaf929..42293061e1c0 100755 --- a/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker.sh @@ -2,7 +2,8 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/ethereumjsdocker -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution --network host -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --dataDir /data/ethereumjs --gethGenesis /data/genesis.json --rpc --rpcEngineAddr 0.0.0.0 --rpcAddr 0.0.0.0 --rpcEngine --jwt-secret /data/jwtsecret --logLevel debug --isSingleNode diff --git a/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/common-setup.sh index f5e64eadf26e..46741823b8a1 100755 --- a/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/common-setup.sh +++ b/packages/beacon-node/test/scripts/el-interop/ethereumjsdocker/common-setup.sh @@ -6,11 +6,11 @@ echo $EL_BINARY_DIR echo $JWT_SECRET_HEX echo $TEMPLATE_FILE -echo $scriptDir +echo $elDir echo $currentDir -env TTD=$TTD envsubst < $scriptDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json +env TTD=$TTD envsubst < $elDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json echo "12345678" > $DATA_DIR/password.txt pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" diff --git a/packages/beacon-node/test/scripts/el-interop/geth/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/geth.sh similarity index 87% rename from packages/beacon-node/test/scripts/el-interop/geth/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/geth.sh index 7f2ce0c96fa0..b9e6923df40f 100755 --- a/packages/beacon-node/test/scripts/el-interop/geth/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/geth.sh @@ -2,7 +2,8 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/geth -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh $EL_BINARY_DIR/geth --http -http.api "engine,net,eth,miner" --http.port $ETH_PORT --authrpc.port $ENGINE_PORT --authrpc.jwtsecret $currentDir/$DATA_DIR/jwtsecret --datadir $DATA_DIR --allow-insecure-unlock --unlock $pubKey --password $DATA_DIR/password.txt --syncmode full \ No newline at end of file diff --git a/packages/beacon-node/test/scripts/el-interop/geth/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/geth/common-setup.sh index b91a1a49ba61..aac1ec4c20be 100755 --- a/packages/beacon-node/test/scripts/el-interop/geth/common-setup.sh +++ b/packages/beacon-node/test/scripts/el-interop/geth/common-setup.sh @@ -5,11 +5,11 @@ echo $DATA_DIR echo $EL_BINARY_DIR echo $JWT_SECRET_HEX -echo $scriptDir +echo $elDir echo $currentDir -env TTD=$TTD envsubst < $scriptDir/genesisPre.tmpl > $DATA_DIR/genesis.json +env TTD=$TTD envsubst < $elDir/genesisPre.tmpl > $DATA_DIR/genesis.json echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json echo "12345678" > $DATA_DIR/password.txt pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" diff --git a/packages/beacon-node/test/scripts/el-interop/geth/pre-merge.sh b/packages/beacon-node/test/scripts/el-interop/geth/pre-merge.sh deleted file mode 100755 index fccf432e145c..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/geth/pre-merge.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -x - -scriptDir=$(dirname $0) -currentDir=$(pwd) - -. $scriptDir/common-setup.sh - -$EL_BINARY_DIR/geth --http -http.api "engine,net,eth,miner" --http.port $ETH_PORT --authrpc.port $ENGINE_PORT --authrpc.jwtsecret $currentDir/$DATA_DIR/jwtsecret --datadir $DATA_DIR --allow-insecure-unlock --unlock $pubKey --password $DATA_DIR/password.txt --nodiscover --mine --syncmode full diff --git a/packages/beacon-node/test/scripts/el-interop/gethdocker/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/gethdocker.sh similarity index 89% rename from packages/beacon-node/test/scripts/el-interop/gethdocker/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/gethdocker.sh index 177d0dcb7e3b..a7f8fcb208a3 100755 --- a/packages/beacon-node/test/scripts/el-interop/gethdocker/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/gethdocker.sh @@ -2,7 +2,8 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/gethdocker -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution --network host -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --http -http.api "engine,net,eth,miner" --http.port $ETH_PORT --authrpc.port $ENGINE_PORT --authrpc.jwtsecret /data/jwtsecret --allow-insecure-unlock --unlock $pubKey --password /data/password.txt --datadir /data/geth --syncmode full diff --git a/packages/beacon-node/test/scripts/el-interop/gethdocker/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/gethdocker/common-setup.sh index 4e77c3c10842..0183c470644b 100755 --- a/packages/beacon-node/test/scripts/el-interop/gethdocker/common-setup.sh +++ b/packages/beacon-node/test/scripts/el-interop/gethdocker/common-setup.sh @@ -6,11 +6,11 @@ echo $EL_BINARY_DIR echo $JWT_SECRET_HEX echo $TEMPLATE_FILE -echo $scriptDir +echo $elDir echo $currentDir -env TTD=$TTD envsubst < $scriptDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json +env TTD=$TTD envsubst < $elDir/$TEMPLATE_FILE > $DATA_DIR/genesis.json echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json echo "12345678" > $DATA_DIR/password.txt pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" diff --git a/packages/beacon-node/test/scripts/el-interop/gethdocker/pre-merge.sh b/packages/beacon-node/test/scripts/el-interop/gethdocker/pre-merge.sh deleted file mode 100755 index a760fec65b30..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/gethdocker/pre-merge.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -x - -scriptDir=$(dirname $0) -currentDir=$(pwd) - -. $scriptDir/common-setup.sh - -# EL_BINARY_DIR refers to the local docker image build from kiln/gethdocker folder -docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution --network host -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --http -http.api "engine,net,eth,miner" --http.port $ETH_PORT --authrpc.port $ENGINE_PORT --authrpc.jwtsecret /data/jwtsecret --allow-insecure-unlock --unlock $pubKey --password /data/password.txt --datadir /data/geth --nodiscover --mine --syncmode full diff --git a/packages/beacon-node/test/scripts/el-interop/mergemock/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/mergemock/common-setup.sh deleted file mode 100755 index aea73c4c7a40..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/mergemock/common-setup.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -x - -echo $TTD -echo $DATA_DIR -echo $EL_BINARY_DIR -echo $JWT_SECRET_HEX - -echo $scriptDir -echo $currentDir - - -env TTD=$TTD envsubst < $scriptDir/genesisPre.tmpl > $DATA_DIR/genesis.json -echo "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" > $DATA_DIR/sk.json -echo "12345678" > $DATA_DIR/password.txt -pubKey="0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" - -# echo a hex encoded 256 bit secret into a file, however remove leading 0x as mergemock doesnt like it -echo $JWT_SECRET_HEX | sed 's/0x//' > $DATA_DIR/jwtsecret -docker rm -f custom-execution diff --git a/packages/beacon-node/test/scripts/el-interop/mergemock/genesisPre.tmpl b/packages/beacon-node/test/scripts/el-interop/mergemock/genesisPre.tmpl deleted file mode 100644 index e3e1fa4141be..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/mergemock/genesisPre.tmpl +++ /dev/null @@ -1,36 +0,0 @@ -{ - "config": { - "chainId": 1, - "homesteadBlock": 0, - "eip150Block": 0, - "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "muirGlacierBlock": 0, - "berlinBlock": 0, - "londonBlock": 0, - "clique": { - "period": 5, - "epoch": 30000 - }, - "terminalTotalDifficulty": ${TTD} - }, - "nonce": "0x42", - "timestamp": "0x0", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x1c9c380", - "difficulty": "0x0", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "alloc": { - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {"balance": "0x6d6172697573766477000000"} - }, - "number": "0x0", - "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "baseFeePerGas": "0x7" -} diff --git a/packages/beacon-node/test/scripts/el-interop/mergemock/jwt.hex b/packages/beacon-node/test/scripts/el-interop/mergemock/jwt.hex deleted file mode 100644 index ed20b10a08a6..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/mergemock/jwt.hex +++ /dev/null @@ -1 +0,0 @@ -0xdc6457099f127cf0bac78de8b297df04951281909db4f58b43def7c7151e765d diff --git a/packages/beacon-node/test/scripts/el-interop/mergemock/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/mergemock/post-merge.sh deleted file mode 100755 index 902b868cc8c2..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/mergemock/post-merge.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -x - -scriptDir=$(dirname $0) -currentDir=$(pwd) - -. $scriptDir/common-setup.sh - -# if we don't provide any datadir merge mock stores data in memory which is fine by us -docker run --rm -u $(id -u ${USER}):$(id -g ${USER}) --name custom-execution --network host -v $currentDir/$DATA_DIR/genesis.json:/usr/app/genesis.json -v $currentDir/$DATA_DIR/jwtsecret:/usr/app/jwt.hex $EL_BINARY_DIR relay --listen-addr 127.0.0.1:$ETH_PORT --engine-listen-addr 127.0.0.1:$ENGINE_PORT --log.level debug --genesis-validators-root 0x3e8bd71d9925794b4f5e8623e15094ea6edc0fd206e3551e13dd2d10e08fbaba diff --git a/packages/beacon-node/test/scripts/el-interop/nethermind/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/nethermind.sh similarity index 89% rename from packages/beacon-node/test/scripts/el-interop/nethermind/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/nethermind.sh index d379ff40324c..6c0323ed814a 100755 --- a/packages/beacon-node/test/scripts/el-interop/nethermind/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/nethermind.sh @@ -2,8 +2,9 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/nethermind -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh cd $EL_BINARY_DIR dotnet run -c Release -- --config themerge_kiln_testvectors --Merge.TerminalTotalDifficulty $TTD --JsonRpc.JwtSecretFile $currentDir/$DATA_DIR/jwtsecret --JsonRpc.Enabled true --JsonRpc.Host 0.0.0.0 --JsonRpc.AdditionalRpcUrls "http://localhost:$ETH_PORT|http|net;eth;subscribe;engine;web3;client|no-auth,http://localhost:$ENGINE_PORT|http|eth;engine" --Sync.SnapSync false diff --git a/packages/beacon-node/test/scripts/el-interop/nethermind/pre-merge.sh b/packages/beacon-node/test/scripts/el-interop/nethermind/pre-merge.sh deleted file mode 100755 index 72cf36efa649..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/nethermind/pre-merge.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -x - -scriptDir=$(dirname $0) -currentDir=$(pwd) - -. $scriptDir/common-setup.sh - -cd $EL_BINARY_DIR -dotnet run -c Release -- --config themerge_kiln_m2 --Merge.TerminalTotalDifficulty $TTD --JsonRpc.JwtSecretFile $currentDir/$DATA_DIR/jwtsecret --Merge.Enabled true --Init.DiagnosticMode=None --JsonRpc.Enabled true --JsonRpc.Host 0.0.0.0 --JsonRpc.AdditionalRpcUrls "http://localhost:$ETH_PORT|http|net;eth;subscribe;engine;web3;client|no-auth,http://localhost:$ENGINE_PORT|http|eth;engine" --Sync.SnapSync false diff --git a/packages/beacon-node/test/scripts/el-interop/netherminddocker/post-merge.sh b/packages/beacon-node/test/scripts/el-interop/netherminddocker.sh similarity index 91% rename from packages/beacon-node/test/scripts/el-interop/netherminddocker/post-merge.sh rename to packages/beacon-node/test/scripts/el-interop/netherminddocker.sh index 2a41fe8979eb..fa3a2b5ab15e 100755 --- a/packages/beacon-node/test/scripts/el-interop/netherminddocker/post-merge.sh +++ b/packages/beacon-node/test/scripts/el-interop/netherminddocker.sh @@ -2,8 +2,9 @@ scriptDir=$(dirname $0) currentDir=$(pwd) +elDir=$scriptDir/netherminddocker -. $scriptDir/common-setup.sh +. $elDir/common-setup.sh if [ "$TEMPLATE_FILE" == "genesisPostWithdraw.tmpl" ] then diff --git a/packages/beacon-node/test/scripts/el-interop/netherminddocker/common-setup.sh b/packages/beacon-node/test/scripts/el-interop/netherminddocker/common-setup.sh index 8892332be47a..bde72a2c058e 100755 --- a/packages/beacon-node/test/scripts/el-interop/netherminddocker/common-setup.sh +++ b/packages/beacon-node/test/scripts/el-interop/netherminddocker/common-setup.sh @@ -5,7 +5,7 @@ echo $DATA_DIR echo $EL_BINARY_DIR echo $JWT_SECRET_HEX -echo $scriptDir +echo $elDir echo $currentDir # echo a hex encoded 256 bit secret into a file diff --git a/packages/beacon-node/test/scripts/el-interop/netherminddocker/pre-merge.sh b/packages/beacon-node/test/scripts/el-interop/netherminddocker/pre-merge.sh deleted file mode 100755 index 468e00933576..000000000000 --- a/packages/beacon-node/test/scripts/el-interop/netherminddocker/pre-merge.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -x - -scriptDir=$(dirname $0) -currentDir=$(pwd) - -. $scriptDir/common-setup.sh - -echo "sleeping for 10 seconds..." - -docker run --rm --network host --name custom-execution -v $currentDir/$DATA_DIR:/data $EL_BINARY_DIR --datadir /data/nethermind --config themerge_kiln_m2 --Merge.TerminalTotalDifficulty $TTD --JsonRpc.JwtSecretFile /data/jwtsecret --Merge.Enabled true --Init.DiagnosticMode=None --JsonRpc.Enabled true --JsonRpc.Host 0.0.0.0 --JsonRpc.AdditionalRpcUrls "http://localhost:$ETH_PORT|http|net;eth;subscribe;engine;web3;client|no-auth,http://localhost:$ENGINE_PORT|http|eth;engine" --Sync.SnapSync false diff --git a/packages/beacon-node/test/sim/electra-interop.test.ts b/packages/beacon-node/test/sim/electra-interop.test.ts index d4dfa5b5d73c..6166df8cd213 100644 --- a/packages/beacon-node/test/sim/electra-interop.test.ts +++ b/packages/beacon-node/test/sim/electra-interop.test.ts @@ -20,7 +20,7 @@ import {TestLoggerOpts, testLogger} from "../utils/logger.js"; import {getDevBeaconNode} from "../utils/node/beacon.js"; import {simTestInfoTracker} from "../utils/node/simTest.js"; import {getAndInitDevValidators} from "../utils/node/validator.js"; -import {ELClient, ELStartMode, runEL, sendRawTransactionBig} from "../utils/runEl.js"; +import {ELClient, runEL, sendRawTransactionBig} from "../utils/runEl.js"; import {logFilesDir} from "./params.js"; import {shell} from "./shell.js"; @@ -67,7 +67,7 @@ describe("executionEngine / ExecutionEngineHttp", () => { it("Send and get payloads with depositRequests to/from EL", async () => { const {elClient, tearDownCallBack} = await runEL( - {...elSetupConfig, mode: ELStartMode.PostMerge, genesisTemplate: "electra.tmpl"}, + {...elSetupConfig, genesisTemplate: "electra.tmpl"}, {...elRunOptions, ttd: BigInt(0)}, controller.signal ); @@ -232,7 +232,7 @@ describe("executionEngine / ExecutionEngineHttp", () => { it.skip("Post-merge, run for a few blocks", async () => { console.log("\n\nPost-merge, run for a few blocks\n\n"); const {elClient, tearDownCallBack} = await runEL( - {...elSetupConfig, mode: ELStartMode.PostMerge, genesisTemplate: "electra.tmpl"}, + {...elSetupConfig, genesisTemplate: "electra.tmpl"}, {...elRunOptions, ttd: BigInt(0)}, controller.signal ); @@ -259,7 +259,7 @@ describe("executionEngine / ExecutionEngineHttp", () => { electraEpoch: Epoch; testName: string; }): Promise { - const {genesisBlockHash, ttd, engineRpcUrl, ethRpcUrl} = elClient; + const {genesisBlockHash, engineRpcUrl, ethRpcUrl} = elClient; const validatorClientCount = 1; const validatorsPerClient = 32; @@ -306,7 +306,6 @@ describe("executionEngine / ExecutionEngineHttp", () => { CAPELLA_FORK_EPOCH: 0, DENEB_FORK_EPOCH: 0, ELECTRA_FORK_EPOCH: electraEpoch, - TERMINAL_TOTAL_DIFFICULTY: ttd, }, options: { api: {rest: {enabled: true} as BeaconRestApiServerOpts}, diff --git a/packages/beacon-node/test/sim/mergemock.test.ts b/packages/beacon-node/test/sim/mergemock.test.ts deleted file mode 100644 index 1fe15518f76a..000000000000 --- a/packages/beacon-node/test/sim/mergemock.test.ts +++ /dev/null @@ -1,273 +0,0 @@ -import fs from "node:fs"; -import {afterAll, afterEach, describe, it, vi} from "vitest"; -import {fromHexString, toHexString} from "@chainsafe/ssz"; -import {routes} from "@lodestar/api"; -import {ChainConfig} from "@lodestar/config"; -import {TimestampFormatCode} from "@lodestar/logger"; -import {SLOTS_PER_EPOCH} from "@lodestar/params"; -import {Epoch, SignedBeaconBlock, bellatrix} from "@lodestar/types"; -import {LogLevel, sleep} from "@lodestar/utils"; -import {ValidatorProposerConfig} from "@lodestar/validator"; -import {BeaconRestApiServerOpts} from "../../src/api/index.js"; -import {ZERO_HASH} from "../../src/constants/index.js"; -import {BuilderStatus} from "../../src/execution/builder/http.js"; -import {Eth1Provider} from "../../src/index.js"; -import {ClockEvent} from "../../src/util/clock.js"; -import {TestLoggerOpts, testLogger} from "../utils/logger.js"; -import {getDevBeaconNode} from "../utils/node/beacon.js"; -import {simTestInfoTracker} from "../utils/node/simTest.js"; -import {getAndInitDevValidators} from "../utils/node/validator.js"; -import {ELClient, ELStartMode, runEL} from "../utils/runEl.js"; -import {logFilesDir} from "./params.js"; -import {shell} from "./shell.js"; - -// NOTE: How to run -// EL_BINARY_DIR=g11tech/mergemock:latest EL_SCRIPT_DIR=mergemock LODESTAR_PRESET=mainnet ETH_PORT=8661 ENGINE_PORT=8551 yarn vitest run test/sim/mergemock.test.ts -// ``` - -const jwtSecretHex = "0xdc6457099f127cf0bac78de8b297df04951281909db4f58b43def7c7151e765d"; - -describe("executionEngine / ExecutionEngineHttp", () => { - if (!process.env.EL_BINARY_DIR || !process.env.EL_SCRIPT_DIR) { - throw Error( - `EL ENV must be provided, EL_BINARY_DIR: ${process.env.EL_BINARY_DIR}, EL_SCRIPT_DIR: ${process.env.EL_SCRIPT_DIR}` - ); - } - vi.setConfig({testTimeout: 10 * 60 * 1000}); - - const dataPath = fs.mkdtempSync("lodestar-test-mergemock"); - const elSetupConfig = { - elScriptDir: process.env.EL_SCRIPT_DIR, - elBinaryDir: process.env.EL_BINARY_DIR, - }; - const elRunOptions = { - dataPath, - jwtSecretHex, - enginePort: parseInt(process.env.ENGINE_PORT ?? "8551"), - ethPort: parseInt(process.env.ETH_PORT ?? "8545"), - }; - - const controller = new AbortController(); - afterAll(async () => { - controller?.abort(); - await shell(`sudo rm -rf ${dataPath}`); - }); - - const afterEachCallbacks: (() => Promise | void)[] = []; - afterEach(async () => { - while (afterEachCallbacks.length > 0) { - const callback = afterEachCallbacks.pop(); - if (callback) await callback(); - } - }); - - it("Test builder flow", async () => { - console.log("\n\nPost-merge, run for a few blocks\n\n"); - const {elClient, tearDownCallBack} = await runEL( - {...elSetupConfig, mode: ELStartMode.PostMerge}, - {...elRunOptions, ttd: BigInt(0)}, - controller.signal - ); - afterEachCallbacks.push(() => tearDownCallBack()); - - await runNodeWithEL({ - elClient, - bellatrixEpoch: 0, - testName: "post-merge", - }); - }); - - type RunOpts = {elClient: ELClient; bellatrixEpoch: Epoch; testName: string}; - - async function runNodeWithEL({elClient, bellatrixEpoch, testName}: RunOpts): Promise { - const {genesisBlockHash, ttd, engineRpcUrl, ethRpcUrl} = elClient; - const validatorClientCount = 1; - const validatorsPerClient = 32; - - const testParams: Pick = { - SLOT_DURATION_MS: 2000, - }; - - // Should reach justification in 6 epochs max. - // Merge block happens at epoch 2 slot 4. Then 4 epochs to finalize - const expectedEpochsToFinish = 1; - // 1 epoch of margin of error - const epochsOfMargin = 1; - const timeoutSetupMargin = 30 * 1000; // Give extra 30 seconds of margin - - // We only expect builder blocks since `builderalways` is configured - // In a perfect run expected builder = 32, expected engine = 0 - // keeping 4 missed slots and 4 engine blocks due to fallback as margin - const expectedBuilderBlocks = 28; - const maximumEngineBlocks = 4; - - // All assertions are tracked w.r.t. fee recipient by attaching different fee recipient to - // execution and builder - const feeRecipientLocal = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - const feeRecipientEngine = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; - const feeRecipientMevBoost = "0xcccccccccccccccccccccccccccccccccccccccc"; - - // delay a bit so regular sync sees it's up to date and sync is completed from the beginning - const genesisSlotsDelay = 8; - - const timeout = - ((epochsOfMargin + expectedEpochsToFinish) * SLOTS_PER_EPOCH + genesisSlotsDelay) * testParams.SLOT_DURATION_MS; - - vi.setConfig({testTimeout: timeout + 2 * timeoutSetupMargin}); - - const genesisTime = Math.floor(Date.now() / 1000) + genesisSlotsDelay * (testParams.SLOT_DURATION_MS / 1000); - - const testLoggerOpts: TestLoggerOpts = { - level: LogLevel.info, - file: { - filepath: `${logFilesDir}/mergemock-${testName}.log`, - level: LogLevel.debug, - }, - timestampFormat: { - format: TimestampFormatCode.EpochSlot, - genesisTime, - slotsPerEpoch: SLOTS_PER_EPOCH, - secondsPerSlot: testParams.SLOT_DURATION_MS / 1000, - }, - }; - const loggerNodeA = testLogger("Node-A", testLoggerOpts); - - const bn = await getDevBeaconNode({ - params: { - ...testParams, - ALTAIR_FORK_EPOCH: 0, - BELLATRIX_FORK_EPOCH: bellatrixEpoch, - TERMINAL_TOTAL_DIFFICULTY: ttd, - }, - options: { - api: {rest: {enabled: true} as BeaconRestApiServerOpts}, - sync: {isSingleNode: true}, - network: {allowPublishToZeroPeers: true, discv5: null}, - // Now eth deposit/merge tracker methods directly available on engine endpoints - eth1: {enabled: false, providerUrls: [engineRpcUrl], jwtSecretHex}, - executionEngine: {urls: [engineRpcUrl], jwtSecretHex}, - executionBuilder: { - url: ethRpcUrl, - enabled: true, - issueLocalFcUWithFeeRecipient: feeRecipientMevBoost, - allowedFaults: 8, - faultInspectionWindow: 32, - }, - chain: {suggestedFeeRecipient: feeRecipientLocal}, - }, - validatorCount: validatorClientCount * validatorsPerClient, - logger: loggerNodeA, - genesisTime, - eth1BlockHash: fromHexString(genesisBlockHash), - }); - if (!bn.chain.executionBuilder) { - throw Error("executionBuilder should have been initialized"); - } - // Enable builder by default, else because of circuit breaker we always start it with disabled - bn.chain.executionBuilder.updateStatus(BuilderStatus.enabled); - - afterEachCallbacks.push(async () => { - await bn.close(); - await sleep(1000); - }); - - const stopInfoTracker = simTestInfoTracker(bn, loggerNodeA); - const valProposerConfig = { - defaultConfig: { - graffiti: "default graffiti", - strictFeeRecipientCheck: true, - feeRecipient: feeRecipientEngine, - builder: { - gasLimit: 60000000, - selection: routes.validator.BuilderSelection.BuilderAlways, - }, - }, - } as ValidatorProposerConfig; - - const {validators} = await getAndInitDevValidators({ - logPrefix: "mergemock", - node: bn, - validatorsPerClient, - validatorClientCount, - startIndex: 0, - // At least one sim test must use the REST API for beacon <-> validator comms - useRestApi: true, - testLoggerOpts, - valProposerConfig, - }); - - afterEachCallbacks.push(async () => { - await Promise.all(validators.map((v) => v.close())); - }); - - let engineBlocks = 0; - let builderBlocks = 0; - await new Promise((resolve, _reject) => { - bn.chain.emitter.on(routes.events.EventType.block, async (blockData) => { - const {data: fullOrBlindedBlock} = (await bn.api.beacon.getBlockV2({blockId: blockData.block})) as { - data: SignedBeaconBlock; - }; - if (fullOrBlindedBlock !== undefined) { - const blockFeeRecipient = toHexString( - (fullOrBlindedBlock as bellatrix.SignedBeaconBlock).message.body.executionPayload.feeRecipient - ); - if (blockFeeRecipient === feeRecipientMevBoost) { - builderBlocks++; - } else { - engineBlocks++; - } - } - }); - bn.chain.clock.on(ClockEvent.epoch, (epoch) => { - // Resolve only if the finalized checkpoint includes execution payload - if (epoch >= expectedEpochsToFinish) { - console.log("\nGot event epoch, stopping validators and nodes\n"); - resolve(); - } - }); - }); - - // Stop chain and un-subscribe events so the execution engine won't update it's head - // Allow some time to broadcast finalized events and complete the importBlock routine - await Promise.all(validators.map((v) => v.close())); - await bn.close(); - await sleep(500); - - if (bn.chain.beaconProposerCache.get(1) !== feeRecipientEngine) { - throw Error("Invalid feeRecipient set at BN"); - } - - // Assertions to make sure the end state is good - // 1. The proper head is set - const rpc = new Eth1Provider({DEPOSIT_CONTRACT_ADDRESS: ZERO_HASH}, {providerUrls: [engineRpcUrl], jwtSecretHex}); - const consensusHead = bn.chain.forkChoice.getHead(); - const executionHeadBlock = await rpc.getBlockByNumber("latest"); - - if (!executionHeadBlock) throw Error("Execution has not head block"); - if (consensusHead.executionPayloadBlockHash !== executionHeadBlock.hash) { - throw Error( - "Consensus head not equal to execution head: " + - JSON.stringify({ - executionHeadBlockHash: executionHeadBlock.hash, - consensusHeadExecutionPayloadBlockHash: consensusHead.executionPayloadBlockHash, - consensusHeadSlot: consensusHead.slot, - }) - ); - } - - // 2. builder blocks are as expected - if (builderBlocks < expectedBuilderBlocks) { - throw Error(`Incorrect builderBlocks=${builderBlocks} (expected=${expectedBuilderBlocks})`); - } - - // 3. engine blocks do not exceed max limit - if (engineBlocks > maximumEngineBlocks) { - throw Error(`Incorrect engineBlocks=${engineBlocks} (limit=${maximumEngineBlocks})`); - } - - // wait for 1 slot to print current epoch stats - await sleep(1 * bn.config.SLOT_DURATION_MS); - stopInfoTracker(); - console.log("\n\nDone\n\n"); - } -}); diff --git a/packages/beacon-node/test/spec/presets/fork_choice.test.ts b/packages/beacon-node/test/spec/presets/fork_choice.test.ts index a10ddeb438c9..33bfb100c7f4 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -21,7 +21,6 @@ import { BeaconBlock, RootHex, SignedBeaconBlock, - bellatrix, deneb, fulu, ssz, @@ -40,7 +39,6 @@ import {defaultChainOptions} from "../../../src/chain/options.js"; import {validateBlockDataColumnSidecars} from "../../../src/chain/validation/dataColumnSidecar.js"; import {ZERO_HASH_HEX} from "../../../src/constants/constants.js"; import {Eth1ForBlockProductionDisabled} from "../../../src/eth1/index.js"; -import {PowMergeBlock} from "../../../src/eth1/interface.js"; import {ExecutionPayloadStatus} from "../../../src/execution/engine/interface.js"; import {ExecutionEngineMockBackend} from "../../../src/execution/engine/mock.js"; import {getExecutionEngineFromBackend} from "../../../src/execution/index.js"; @@ -61,7 +59,6 @@ const ANCHOR_BLOCK_FILE_NAME = "anchor_block"; const BLOCK_FILE_NAME = "^(block)_([0-9a-zA-Z]+)$"; const BLOBS_FILE_NAME = "^(blobs)_([0-9a-zA-Z]+)$"; const COLUMN_FILE_NAME = "^(column)_([0-9a-zA-Z]+)$"; -const POW_BLOCK_FILE_NAME = "^(pow_block)_([0-9a-zA-Z]+)$"; const ATTESTATION_FILE_NAME = "^(attestation)_([0-9a-zA-Z])+$"; const ATTESTER_SLASHING_FILE_NAME = "^(attester_slashing)_([0-9a-zA-Z])+$"; @@ -80,7 +77,7 @@ const forkChoiceTest = /** This is to track test's tickTime to be used in proposer boost */ let tickTime = 0; const clock = new ClockStopped(currentSlot); - const eth1 = new Eth1ForBlockProductionMock(); + const eth1 = new Eth1ForBlockProductionDisabled(); const executionEngineBackend = new ExecutionEngineMockBackend({ onlyPredefinedResponses: opts.onlyPredefinedResponses, genesisBlockHash: isExecutionStateType(anchorState) @@ -333,23 +330,6 @@ const forkChoiceTest = } } - // **on_merge_block execution** - // Adds PowBlock data which is required for executing on_block(store, block). - // The file is located in the same folder (see below). PowBlocks should be used as return values for - // get_pow_block(hash: Hash32) -> PowBlock function if hashes match. - else if (isPowBlock(step)) { - const powBlock = testcase.powBlocks.get(step.pow_block); - if (!powBlock) throw Error(`pow_block ${step.pow_block} not found`); - logger.debug(`Step ${i}/${stepsLen} pow_block`, { - blockHash: toHexString(powBlock.blockHash), - parentHash: toHexString(powBlock.parentHash), - }); - // Register PowBlock for `get_pow_block(hash: Hash32)` calls in verifyBlock - eth1.addPowBlock(powBlock); - // Register PowBlock to allow validation in execution engine - executionEngineBackend.addPowBlock(powBlock); - } - // Optional step for optimistic sync tests. else if (isOnPayloadInfoStep(step)) { logger.debug(`Step ${i}/${stepsLen} payload_status`, {blockHash: step.block_hash}); @@ -455,7 +435,6 @@ const forkChoiceTest = [BLOCK_FILE_NAME]: ssz[fork].SignedBeaconBlock, [BLOBS_FILE_NAME]: ssz.deneb.Blobs, [COLUMN_FILE_NAME]: ssz.fulu.DataColumnSidecar, - [POW_BLOCK_FILE_NAME]: ssz.bellatrix.PowBlock, [ATTESTATION_FILE_NAME]: sszTypesFor(fork).Attestation, [ATTESTER_SLASHING_FILE_NAME]: sszTypesFor(fork).AttesterSlashing, }, @@ -464,7 +443,6 @@ const forkChoiceTest = const blocks = new Map(); const blobs = new Map(); const columns = new Map(); - const powBlocks = new Map(); const attestations = new Map(); const attesterSlashings = new Map(); for (const key in t) { @@ -482,10 +460,6 @@ const forkChoiceTest = if (columnMatch) { columns.set(key, t[key]); } - const powBlockMatch = key.match(POW_BLOCK_FILE_NAME); - if (powBlockMatch) { - powBlocks.set(key, t[key]); - } const attMatch = key.match(ATTESTATION_FILE_NAME); if (attMatch) { attestations.set(key, t[key]); @@ -503,7 +477,6 @@ const forkChoiceTest = blocks, blobs, columns, - powBlocks, attestations, attesterSlashings, }; @@ -530,7 +503,7 @@ function toSpecTestCheckpoint(checkpoint: CheckpointWithHex): SpecTestCheckpoint }; } -type Step = OnTick | OnAttestation | OnAttesterSlashing | OnBlock | OnPowBlock | OnPayloadInfo | Checks; +type Step = OnTick | OnAttestation | OnAttesterSlashing | OnBlock | OnPayloadInfo | Checks; type SpecTestCheckpoint = {epoch: bigint; root: string}; @@ -571,15 +544,6 @@ type OnBlock = { valid?: number; }; -/** Optional step for optimistic sync tests. */ -type OnPowBlock = { - /** - * the name of the `pow_block_<32-byte-root>.ssz_snappy` file. To - * execute `on_pow_block(store, block)` - */ - pow_block: string; -}; - type OnPayloadInfo = { /** Encoded 32-byte value of payload's block hash. */ block_hash: string; @@ -622,7 +586,6 @@ type ForkChoiceTestCase = { blocks: Map; blobs: Map; columns: Map; - powBlocks: Map; attestations: Map; attesterSlashings: Map; }; @@ -643,10 +606,6 @@ function isBlock(step: Step): step is OnBlock { return typeof (step as OnBlock).block === "string"; } -function isPowBlock(step: Step): step is OnPowBlock { - return typeof (step as OnPowBlock).pow_block === "string"; -} - function isOnPayloadInfoStep(step: Step): step is OnPayloadInfo { return typeof (step as OnPayloadInfo).block_hash === "string"; } @@ -655,25 +614,6 @@ function isCheck(step: Step): step is Checks { return typeof (step as Checks).checks === "object"; } -// Extend Eth1ForBlockProductionDisabled to not have to re-implement new methods -class Eth1ForBlockProductionMock extends Eth1ForBlockProductionDisabled { - private items = new Map(); - - async getPowBlock(powBlockHash: string): Promise { - return this.items.get(powBlockHash) ?? null; - } - - addPowBlock(powBlock: bellatrix.PowBlock): void { - this.items.set(toHexString(powBlock.blockHash), { - // not used by verifyBlock() - number: 0, - blockHash: toHexString(powBlock.blockHash), - parentHash: toHexString(powBlock.parentHash), - totalDifficulty: powBlock.totalDifficulty, - }); - } -} - specTestIterator(path.join(ethereumConsensusSpecsTests.outputDir, "tests", ACTIVE_PRESET), { fork_choice: {type: RunnerType.default, fn: forkChoiceTest({onlyPredefinedResponses: false})}, sync: {type: RunnerType.default, fn: forkChoiceTest({onlyPredefinedResponses: true})}, diff --git a/packages/beacon-node/test/spec/utils/specTestIterator.ts b/packages/beacon-node/test/spec/utils/specTestIterator.ts index b83d3726e1c5..428b6602d4f2 100644 --- a/packages/beacon-node/test/spec/utils/specTestIterator.ts +++ b/packages/beacon-node/test/spec/utils/specTestIterator.ts @@ -60,10 +60,13 @@ const coveredTestRunners = [ // ``` export const defaultSkipOpts: SkipOpts = { skippedForks: ["eip7805"], - // TODO: capella - // BeaconBlockBody proof in lightclient is the new addition in v1.3.0-rc.2-hotfix - // Skip them for now to enable subsequently skippedTestSuites: [ + // Merge transition tests are skipped because we no longer support performing the merge transition. + // All networks have already completed the merge, so this code path is no longer needed. + /^bellatrix\/fork_choice\/on_merge_block\/.*/, + // TODO: capella + // BeaconBlockBody proof in lightclient is the new addition in v1.3.0-rc.2-hotfix + // Skip them for now to enable subsequently /^capella\/light_client\/single_merkle_proof\/BeaconBlockBody.*/, /^deneb\/light_client\/single_merkle_proof\/BeaconBlockBody.*/, /^electra\/light_client\/single_merkle_proof\/BeaconBlockBody.*/, @@ -72,7 +75,12 @@ export const defaultSkipOpts: SkipOpts = { /^gloas\/(finality|fork_choice|networking|sanity|transition)\/.*$/, /^gloas\/ssz_static\/ForkChoiceNode.*$/, ], - skippedTests: [], + skippedTests: [ + // These tests validate "first payload" scenarios where is_execution_enabled was false pre-merge. + // Since we removed merge transition support, these code paths no longer exist. + /^bellatrix\/operations\/execution_payload\/.+\/bad_parent_hash_first_payload$/, + /^bellatrix\/sanity\/blocks\/.+\/is_execution_enabled_false$/, + ], skippedRunners: [], }; diff --git a/packages/beacon-node/test/unit/eth1/eth1MergeBlockTracker.test.ts b/packages/beacon-node/test/unit/eth1/eth1MergeBlockTracker.test.ts deleted file mode 100644 index 5f19c5c4f57e..000000000000 --- a/packages/beacon-node/test/unit/eth1/eth1MergeBlockTracker.test.ts +++ /dev/null @@ -1,256 +0,0 @@ -import {afterEach, beforeEach, describe, expect, it} from "vitest"; -import {toHexString} from "@chainsafe/ssz"; -import {ChainConfig} from "@lodestar/config"; -import {sleep} from "@lodestar/utils"; -import {ZERO_HASH} from "../../../src/constants/index.js"; -import {Eth1MergeBlockTracker, StatusCode, toPowBlock} from "../../../src/eth1/eth1MergeBlockTracker.js"; -import {Eth1ProviderState, EthJsonRpcBlockRaw} from "../../../src/eth1/interface.js"; -import {IEth1Provider} from "../../../src/index.js"; -import {testLogger} from "../../utils/logger.js"; - -describe("eth1 / Eth1MergeBlockTracker", () => { - const logger = testLogger(); - - const terminalTotalDifficulty = 1000; - let config: ChainConfig; - let controller: AbortController; - beforeEach(() => { - controller = new AbortController(); - - config = { - // Set time units to 0 to make the test as fast as possible - SECONDS_PER_ETH1_BLOCK: 0, - SLOT_DURATION_MS: 0, - // Hardcode TTD to a low value - TERMINAL_TOTAL_DIFFICULTY: BigInt(terminalTotalDifficulty), - TERMINAL_BLOCK_HASH: ZERO_HASH, - } as Partial as ChainConfig; - }); - - afterEach(() => controller.abort()); - - it("Should find terminal pow block through TERMINAL_BLOCK_HASH", async () => { - config.TERMINAL_BLOCK_HASH = Buffer.alloc(32, 1); - const block: EthJsonRpcBlockRaw = { - number: toHex(10), - hash: toRootHex(11), - parentHash: toRootHex(10), - totalDifficulty: toHex(100), - timestamp: "0x0", - }; - const terminalPowBlock = toPowBlock(block); - const eth1Provider: IEth1Provider = { - deployBlock: 0, - getBlockNumber: async () => 0, - getBlockByNumber: async () => { - throw Error("Not implemented"); - }, - getBlockByHash: async (blockHashHex): Promise => { - return blockHashHex === toHexString(config.TERMINAL_BLOCK_HASH) ? block : null; - }, - getBlocksByNumber: async (): Promise => { - throw Error("Not implemented"); - }, - getDepositEvents: async (): Promise => { - throw Error("Not implemented"); - }, - validateContract: async (): Promise => { - throw Error("Not implemented"); - }, - getState: () => Eth1ProviderState.ONLINE, - }; - - const eth1MergeBlockTracker = new Eth1MergeBlockTracker( - { - config, - logger, - signal: controller.signal, - metrics: null, - }, - eth1Provider - ); - eth1MergeBlockTracker.startPollingMergeBlock(); - - // Wait for Eth1MergeBlockTracker to find at least one merge block - while (!controller.signal.aborted) { - if (await eth1MergeBlockTracker.getTerminalPowBlock()) break; - await sleep(10, controller.signal); - } - - // Status should acknowlege merge block is found - expect(eth1MergeBlockTracker["status"].code).toBe(StatusCode.FOUND); - - // Given the total difficulty offset the block that has TTD is the `difficultyOffset`nth block - expect(await eth1MergeBlockTracker.getTerminalPowBlock()).toEqual(terminalPowBlock); - }); - - it("Should find terminal pow block polling future 'latest' blocks", async () => { - // Set current network totalDifficulty to behind terminalTotalDifficulty by 5. - // Then on each call to getBlockByNumber("latest") increase totalDifficulty by 1. - const numOfBlocks = 5; - const difficulty = 1; - - let latestBlockPointer = 0; - - const blocks: EthJsonRpcBlockRaw[] = []; - const blocksByHash = new Map(); - - for (let i = 0; i < numOfBlocks + 1; i++) { - const block: EthJsonRpcBlockRaw = { - number: toHex(i), - hash: toRootHex(i + 1), - parentHash: toRootHex(i), - // Latest block is under TTD, so past block search is stopped - totalDifficulty: toHex(terminalTotalDifficulty - numOfBlocks * difficulty + i * difficulty), - timestamp: "0x0", - }; - blocks.push(block); - } - - const eth1Provider: IEth1Provider = { - deployBlock: 0, - getBlockNumber: async () => 0, - getBlockByNumber: async (blockNumber) => { - // On each call simulate that the eth1 chain advances 1 block with +1 totalDifficulty - if (blockNumber === "latest") { - if (latestBlockPointer >= blocks.length) { - throw Error("Fetched too many blocks"); - } - return blocks[latestBlockPointer++]; - } - return blocks[blockNumber]; - }, - getBlockByHash: async (blockHashHex) => blocksByHash.get(blockHashHex) ?? null, - getBlocksByNumber: async (): Promise => { - throw Error("Not implemented"); - }, - getDepositEvents: async (): Promise => { - throw Error("Not implemented"); - }, - validateContract: async (): Promise => { - throw Error("Not implemented"); - }, - getState: () => Eth1ProviderState.ONLINE, - }; - - await runFindMergeBlockTest(eth1Provider, blocks.at(-1) as EthJsonRpcBlockRaw); - }); - - it("Should find terminal pow block fetching past blocks", async () => { - // Set current network totalDifficulty to behind terminalTotalDifficulty by 5. - // Then on each call to getBlockByNumber("latest") increase totalDifficulty by 1. - - const numOfBlocks = 5; - const difficulty = 1; - const ttdOffset = 1 * difficulty; - const hashOffset = 100; - const blocks: EthJsonRpcBlockRaw[] = []; - - for (let i = 0; i < numOfBlocks * 2; i++) { - const block: EthJsonRpcBlockRaw = { - number: toHex(hashOffset + i), - hash: toRootHex(hashOffset + i + 1), - parentHash: toRootHex(hashOffset + i), - // Latest block is under TTD, so past block search is stopped - totalDifficulty: toHex(terminalTotalDifficulty + i * difficulty - ttdOffset), - timestamp: "0x0", - }; - blocks.push(block); - } - - // Before last block (with ttdOffset = 1) is the merge block - const expectedMergeBlock = blocks[ttdOffset]; - - const eth1Provider = mockEth1ProviderFromBlocks(blocks); - await runFindMergeBlockTest(eth1Provider, expectedMergeBlock); - }); - - it("Should find terminal pow block fetching past blocks till genesis", async () => { - // There's no block with TD < TTD, searcher should stop at genesis block - - const numOfBlocks = 5; - const difficulty = 1; - const blocks: EthJsonRpcBlockRaw[] = []; - - for (let i = 0; i < numOfBlocks * 2; i++) { - const block: EthJsonRpcBlockRaw = { - number: toHex(i), - hash: toRootHex(i + 1), - parentHash: toRootHex(i), - // Latest block is under TTD, so past block search is stopped - totalDifficulty: toHex(terminalTotalDifficulty + i * difficulty + 1), - timestamp: "0x0", - }; - blocks.push(block); - } - - // Merge block must be genesis block - const expectedMergeBlock = blocks[0]; - - const eth1Provider = mockEth1ProviderFromBlocks(blocks); - await runFindMergeBlockTest(eth1Provider, expectedMergeBlock); - }); - - function mockEth1ProviderFromBlocks(blocks: EthJsonRpcBlockRaw[]): IEth1Provider { - const blocksByHash = new Map(); - - for (const block of blocks) { - blocksByHash.set(block.hash, block); - } - - return { - deployBlock: 0, - getBlockNumber: async () => 0, - getBlockByNumber: async (blockNumber) => { - // Always return the same block with totalDifficulty > TTD and unknown parent - if (blockNumber === "latest") return blocks.at(-1) as EthJsonRpcBlockRaw; - return blocks[blockNumber]; - }, - getBlockByHash: async (blockHashHex) => blocksByHash.get(blockHashHex) ?? null, - getBlocksByNumber: async (from, to) => blocks.slice(from, to), - getDepositEvents: async (): Promise => { - throw Error("Not implemented"); - }, - validateContract: async (): Promise => { - throw Error("Not implemented"); - }, - getState: () => Eth1ProviderState.ONLINE, - }; - } - - async function runFindMergeBlockTest( - eth1Provider: IEth1Provider, - expectedMergeBlock: EthJsonRpcBlockRaw - ): Promise { - const eth1MergeBlockTracker = new Eth1MergeBlockTracker( - { - config, - logger, - signal: controller.signal, - metrics: null, - }, - eth1Provider - ); - eth1MergeBlockTracker.startPollingMergeBlock(); - - // Wait for Eth1MergeBlockTracker to find at least one merge block - while (!controller.signal.aborted) { - if (await eth1MergeBlockTracker.getTerminalPowBlock()) break; - await sleep(10, controller.signal); - } - - // Status should acknowlege merge block is found - expect(eth1MergeBlockTracker["status"].code).toBe(StatusCode.FOUND); - - // Given the total difficulty offset the block that has TTD is the `difficultyOffset`nth block - expect(await eth1MergeBlockTracker.getTerminalPowBlock()).toEqual(toPowBlock(expectedMergeBlock)); - } -}); - -function toHex(num: number | bigint): string { - return "0x" + num.toString(16); -} - -function toRootHex(num: number): string { - return "0x" + num.toString(16).padStart(64, "0"); -} diff --git a/packages/beacon-node/test/utils/networkWithMockDb.ts b/packages/beacon-node/test/utils/networkWithMockDb.ts index 5572a7cba992..5194713fd383 100644 --- a/packages/beacon-node/test/utils/networkWithMockDb.ts +++ b/packages/beacon-node/test/utils/networkWithMockDb.ts @@ -48,7 +48,6 @@ export async function getNetworkForTest( const chain = new BeaconChain( { - safeSlotsToImportOptimistically: 0, archiveStateEpochFrequency: 0, suggestedFeeRecipient: "", blsVerifyAllMainThread: true, diff --git a/packages/beacon-node/test/utils/runEl.ts b/packages/beacon-node/test/utils/runEl.ts index ae5ad992ae16..1614a899b0d7 100644 --- a/packages/beacon-node/test/utils/runEl.ts +++ b/packages/beacon-node/test/utils/runEl.ts @@ -9,16 +9,10 @@ import {shell} from "../sim/shell.js"; let txRpcId = 1; -export enum ELStartMode { - PreMerge = "pre-merge", - PostMerge = "post-merge", -} - -export type ELSetupConfig = {mode: ELStartMode; elScriptDir: string; elBinaryDir: string; genesisTemplate?: string}; +export type ELSetupConfig = {elScriptDir: string; elBinaryDir: string; genesisTemplate?: string}; export type ELRunOptions = {ttd: bigint; dataPath: string; jwtSecretHex: string; enginePort: number; ethPort: number}; export type ELClient = { genesisBlockHash: string; - ttd: bigint; engineRpcUrl: string; ethRpcUrl: string; network: string; @@ -26,7 +20,7 @@ export type ELClient = { }; /** - * A util function to start an EL in a "pre-merge" or "post-merge" mode using an `elScriptDir` setup + * A util function to start an EL using an `elScriptDir` setup * scripts folder in packages/beacon-node/test/scripts/el-interop. * * Returns an ELRunConfig after starting the EL, which can be used to initialize the genesis @@ -34,11 +28,11 @@ export type ELClient = { */ export async function runEL( - {mode, elScriptDir, elBinaryDir, genesisTemplate: template}: ELSetupConfig, + {elScriptDir, elBinaryDir, genesisTemplate: template}: ELSetupConfig, {ttd, dataPath, jwtSecretHex, enginePort, ethPort}: ELRunOptions, signal: AbortSignal ): Promise<{elClient: ELClient; tearDownCallBack: () => Promise}> { - const network = `${elScriptDir}/${mode}`; + const network = `${elScriptDir}`; const ethRpcUrl = `http://127.0.0.1:${ethPort}`; const engineRpcUrl = `http://127.0.0.1:${enginePort}`; const genesisTemplate = template ?? "genesisPre.tmpl"; diff --git a/packages/cli/src/config/beaconParams.ts b/packages/cli/src/config/beaconParams.ts index 7abb0e9a5537..db01dfc57f3a 100644 --- a/packages/cli/src/config/beaconParams.ts +++ b/packages/cli/src/config/beaconParams.ts @@ -6,13 +6,7 @@ import { createChainForkConfig, } from "@lodestar/config"; import {NetworkName, getNetworkBeaconParams} from "../networks/index.js"; -import { - GlobalArgs, - ITerminalPowArgs, - defaultNetwork, - parseBeaconParamsArgs, - parseTerminalPowArgs, -} from "../options/index.js"; +import {GlobalArgs, defaultNetwork, parseBeaconParamsArgs} from "../options/index.js"; import {readFile} from "../util/index.js"; import {IBeaconParamsUnparsed} from "./types.js"; @@ -44,7 +38,6 @@ export function getBeaconParamsFromArgs(args: GlobalArgs): ChainConfig { paramsFile: args.paramsFile, additionalParamsCli: { ...parseBeaconParamsArgs(args as IBeaconParamsUnparsed), - ...parseTerminalPowArgs(args as ITerminalPowArgs), }, }); } diff --git a/packages/cli/src/options/beaconNodeOptions/chain.ts b/packages/cli/src/options/beaconNodeOptions/chain.ts index 9987883a63a0..04af60b9086b 100644 --- a/packages/cli/src/options/beaconNodeOptions/chain.ts +++ b/packages/cli/src/options/beaconNodeOptions/chain.ts @@ -23,7 +23,6 @@ export type ChainArgs = { "chain.computeUnrealized"?: boolean; "chain.assertCorrectProgressiveBalances"?: boolean; "chain.maxSkipSlots"?: number; - "safe-slots-to-import-optimistically": number; emitPayloadAttributes?: boolean; broadcastValidationStrictness?: string; "chain.minSameMessageSignatureSetsToBatch"?: number; @@ -63,7 +62,6 @@ export function parseArgs(args: ChainArgs): IBeaconNodeOptions["chain"] { computeUnrealized: args["chain.computeUnrealized"], assertCorrectProgressiveBalances: args["chain.assertCorrectProgressiveBalances"], maxSkipSlots: args["chain.maxSkipSlots"], - safeSlotsToImportOptimistically: args["safe-slots-to-import-optimistically"], emitPayloadAttributes: args.emitPayloadAttributes, broadcastValidationStrictness: args.broadcastValidationStrictness, minSameMessageSignatureSetsToBatch: @@ -227,15 +225,6 @@ Will double processing times. Use only for debugging purposes.", group: "chain", }, - "safe-slots-to-import-optimistically": { - hidden: true, - type: "number", - description: - "Slots from current (clock) slot till which its safe to import a block optimistically if the merge is not justified yet.", - default: defaultOptions.chain.safeSlotsToImportOptimistically, - group: "chain", - }, - "chain.archiveStateEpochFrequency": { description: "Minimum number of epochs between archived states", default: defaultOptions.chain.archiveStateEpochFrequency, diff --git a/packages/cli/src/options/paramsOptions.ts b/packages/cli/src/options/paramsOptions.ts index bd89ffa77d9e..787cbdaae1e7 100644 --- a/packages/cli/src/options/paramsOptions.ts +++ b/packages/cli/src/options/paramsOptions.ts @@ -1,4 +1,4 @@ -import {ChainConfig, chainConfigTypes} from "@lodestar/config"; +import {chainConfigTypes} from "@lodestar/config"; import {CliCommandOptions, CliOptionDefinition} from "@lodestar/utils"; import {IBeaconParamsUnparsed} from "../config/types.js"; import {ObjectKeys} from "../util/index.js"; @@ -7,12 +7,7 @@ import {ObjectKeys} from "../util/index.js"; // If an arbitrary key notation is used, it removes type safety on most of this CLI arg parsing code. // Params will be parsed from an args object assuming to contain the required keys -export type ITerminalPowArgs = { - "terminal-total-difficulty-override"?: string; - "terminal-block-hash-override"?: string; - "terminal-block-hash-epoch-override"?: string; -}; -export type IParamsArgs = Record & ITerminalPowArgs; +export type IParamsArgs = Record; const getArgKey = (key: keyof IBeaconParamsUnparsed): string => `params.${key}`; @@ -24,7 +19,7 @@ export function parseBeaconParamsArgs(args: Record): IB }, {}); } -const paramsOptionsByName = ObjectKeys(chainConfigTypes).reduce( +export const paramsOptions: CliCommandOptions = ObjectKeys(chainConfigTypes).reduce( (options: Record, key): Record => { options[getArgKey(key)] = { hidden: true, @@ -35,38 +30,3 @@ const paramsOptionsByName = ObjectKeys(chainConfigTypes).reduce( }, {} ); - -const terminalArgsToParamsMap: {[K in keyof ITerminalPowArgs]: keyof ChainConfig} = { - "terminal-total-difficulty-override": "TERMINAL_TOTAL_DIFFICULTY", - "terminal-block-hash-override": "TERMINAL_BLOCK_HASH", - "terminal-block-hash-epoch-override": "TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH", -}; - -export function parseTerminalPowArgs(args: ITerminalPowArgs): IBeaconParamsUnparsed { - const parsedArgs = ObjectKeys(terminalArgsToParamsMap).reduce((beaconParams: Partial, key) => { - const paramOption = terminalArgsToParamsMap[key]; - const value = args[key]; - if (paramOption != null && value != null) beaconParams[paramOption] = value; - return beaconParams; - }, {}); - return parsedArgs; -} - -export const paramsOptions: CliCommandOptions = { - ...paramsOptionsByName, - - "terminal-total-difficulty-override": { - description: "Terminal PoW block TTD override", - type: "string", - }, - - "terminal-block-hash-override": { - description: "Terminal PoW block hash override", - type: "string", - }, - - "terminal-block-hash-epoch-override": { - description: "Terminal PoW block hash override activation epoch", - type: "string", - }, -}; diff --git a/packages/cli/test/sim/multiFork.test.ts b/packages/cli/test/sim/multiFork.test.ts index ce227db84860..6974fe135b71 100644 --- a/packages/cli/test/sim/multiFork.test.ts +++ b/packages/cli/test/sim/multiFork.test.ts @@ -2,7 +2,6 @@ import path from "node:path"; import {createAccountBalanceAssertion} from "../utils/crucible/assertions/accountBalanceAssertion.js"; import {createExecutionHeadAssertion} from "../utils/crucible/assertions/executionHeadAssertion.js"; import {createForkAssertion} from "../utils/crucible/assertions/forkAssertion.js"; -import {mergeAssertion} from "../utils/crucible/assertions/mergeAssertion.js"; import {nodeAssertion} from "../utils/crucible/assertions/nodeAssertion.js"; import {createWithdrawalAssertions} from "../utils/crucible/assertions/withdrawalsAssertion.js"; import {BeaconClient, ExecutionClient, Match, ValidatorClient} from "../utils/crucible/interfaces.js"; @@ -116,14 +115,6 @@ env.tracker.register({ }, }); -env.tracker.register({ - ...mergeAssertion, - match: ({slot}) => { - // Check at the end of bellatrix fork, merge should happen by then - return slot === env.clock.getLastSlotOfEpoch(bellatrixForkEpoch) ? Match.Assert | Match.Remove : Match.None; - }, -}); - env.tracker.register( createAccountBalanceAssertion({ address: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", diff --git a/packages/cli/test/unit/options/beaconNodeOptions.test.ts b/packages/cli/test/unit/options/beaconNodeOptions.test.ts index ac02498621b9..c32a35132fa0 100644 --- a/packages/cli/test/unit/options/beaconNodeOptions.test.ts +++ b/packages/cli/test/unit/options/beaconNodeOptions.test.ts @@ -33,7 +33,6 @@ describe("options / beaconNodeOptions", () => { suggestedFeeRecipient: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "chain.assertCorrectProgressiveBalances": true, "chain.maxSkipSlots": 100, - "safe-slots-to-import-optimistically": 256, "chain.archiveStateEpochFrequency": 1024, "chain.minSameMessageSignatureSetsToBatch": 32, "chain.maxShufflingCacheEpochs": 100, @@ -139,7 +138,6 @@ describe("options / beaconNodeOptions", () => { preaggregateSlotDistance: 1, attDataCacheSlotDistance: 2, computeUnrealized: true, - safeSlotsToImportOptimistically: 256, suggestedFeeRecipient: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", assertCorrectProgressiveBalances: true, maxSkipSlots: 100, diff --git a/packages/cli/test/utils/crucible/assertions/mergeAssertion.ts b/packages/cli/test/utils/crucible/assertions/mergeAssertion.ts deleted file mode 100644 index 6e0c1a63d7af..000000000000 --- a/packages/cli/test/utils/crucible/assertions/mergeAssertion.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {BeaconStateAllForks, isExecutionStateType, isMergeTransitionComplete} from "@lodestar/state-transition"; -import {Assertion, AssertionResult} from "../interfaces.js"; -import {neverMatcher} from "./matchers.js"; - -export const mergeAssertion: Assertion<"merge", string> = { - id: "merge", - // Include into particular test with custom condition - match: neverMatcher, - async assert({node}) { - const errors: AssertionResult[] = []; - - const res = await node.beacon.api.debug.getStateV2({stateId: "head"}); - const state = res.value() as unknown as BeaconStateAllForks; - - if (!(isExecutionStateType(state) && isMergeTransitionComplete(state))) { - errors.push("Node has not yet completed the merged transition"); - } - - return errors; - }, -}; diff --git a/packages/cli/test/utils/crucible/clients/execution/geth.ts b/packages/cli/test/utils/crucible/clients/execution/geth.ts index 6de53f9543ec..0b59273e3280 100644 --- a/packages/cli/test/utils/crucible/clients/execution/geth.ts +++ b/packages/cli/test/utils/crucible/clients/execution/geth.ts @@ -3,7 +3,7 @@ import path from "node:path"; import {Web3} from "web3"; import {fetch} from "@lodestar/utils"; import {EL_GENESIS_PASSWORD, EL_GENESIS_SECRET_KEY, SHARED_JWT_SECRET, SIM_ENV_NETWORK_ID} from "../../constants.js"; -import {ExecutionClient, ExecutionNodeGenerator, ExecutionStartMode, JobOptions, RunnerType} from "../../interfaces.js"; +import {ExecutionClient, ExecutionNodeGenerator, JobOptions, RunnerType} from "../../interfaces.js"; import {getNodeMountedPaths} from "../../utils/paths.js"; import {getNodePorts} from "../../utils/ports.js"; import {registerWeb3JsPlugins} from "../../web3js/plugins/index.js"; @@ -13,7 +13,7 @@ export const generateGethNode: ExecutionNodeGenerator = (o throw new Error("GETH_BINARY_DIR or GETH_DOCKER_IMAGE must be provided"); } - const {id, mode, ttd, address, mining, clientOptions, nodeIndex} = opts; + const {id, ttd, address, mining, clientOptions, nodeIndex} = opts; const ports = getNodePorts(nodeIndex); const isDocker = !!process.env.GETH_DOCKER_IMAGE; @@ -136,7 +136,6 @@ export const generateGethNode: ExecutionNodeGenerator = (o "--verbosity", "5", ...(mining ? ["--mine"] : []), - ...(mode === ExecutionStartMode.PreMerge ? ["--nodiscover"] : []), ...clientOptions, ], env: {}, diff --git a/packages/cli/test/utils/crucible/clients/execution/index.ts b/packages/cli/test/utils/crucible/clients/execution/index.ts index 14ba093ed66b..b40a0a1d29e9 100644 --- a/packages/cli/test/utils/crucible/clients/execution/index.ts +++ b/packages/cli/test/utils/crucible/clients/execution/index.ts @@ -6,7 +6,6 @@ import { ExecutionGeneratorOptions, ExecutionGenesisOptions, ExecutionNode, - ExecutionStartMode, } from "../../interfaces.js"; import {getGethGenesisBlock} from "../../utils/executionGenesis.js"; import {getEstimatedForkTime} from "../../utils/index.js"; @@ -57,16 +56,13 @@ export async function createExecutionNode( ...options, ...genesisOptions, id: elId, - mode: - options.mode ?? - (forkConfig.BELLATRIX_FORK_EPOCH > 0 ? ExecutionStartMode.PreMerge : ExecutionStartMode.PostMerge), address: runner.getNextIp(), mining: options.mining ?? false, }; await ensureDirectories(opts.paths); await writeFile(opts.paths.jwtsecretFilePath, SHARED_JWT_SECRET); - await writeFile(opts.paths.genesisFilePath, JSON.stringify(getGethGenesisBlock(opts.mode, genesisOptions))); + await writeFile(opts.paths.genesisFilePath, JSON.stringify(getGethGenesisBlock(genesisOptions))); switch (client) { case ExecutionClient.Mock: { diff --git a/packages/cli/test/utils/crucible/clients/execution/nethermind.ts b/packages/cli/test/utils/crucible/clients/execution/nethermind.ts index 92615aeb4cc5..7a85584c154f 100644 --- a/packages/cli/test/utils/crucible/clients/execution/nethermind.ts +++ b/packages/cli/test/utils/crucible/clients/execution/nethermind.ts @@ -16,7 +16,6 @@ export const generateNethermindNode: ExecutionNodeGenerator; [BeaconClient.Lighthouse]: Record; @@ -137,7 +132,6 @@ export interface ExecutionGenesisOptions extends ExecutionGenesisOptions, GeneratorOptions { - mode: ExecutionStartMode; mining: boolean; paths: ExecutionPaths; clientOptions: ExecutionClientsOptions[E]; diff --git a/packages/cli/test/utils/crucible/utils/executionGenesis.ts b/packages/cli/test/utils/crucible/utils/executionGenesis.ts index 7250c147e2c1..82c8036c7160 100644 --- a/packages/cli/test/utils/crucible/utils/executionGenesis.ts +++ b/packages/cli/test/utils/crucible/utils/executionGenesis.ts @@ -1,10 +1,7 @@ import {SIM_ENV_CHAIN_ID, SIM_ENV_NETWORK_ID} from "../constants.js"; -import {Eth1GenesisBlock, ExecutionGenesisOptions, ExecutionStartMode} from "../interfaces.js"; +import {Eth1GenesisBlock, ExecutionGenesisOptions} from "../interfaces.js"; -export const getGethGenesisBlock = ( - mode: ExecutionStartMode, - options: ExecutionGenesisOptions -): Record => { +export const getGethGenesisBlock = (options: ExecutionGenesisOptions): Record => { const {ttd, cliqueSealingPeriod, shanghaiTime, genesisTime, cancunTime, pragueTime} = options; const genesis = { @@ -63,20 +60,12 @@ export const getGethGenesisBlock = ( baseFeePerGas: "0x0", }; - if (mode === ExecutionStartMode.PreMerge) { - return genesis; - } - - // TODO: Figure out PostMerge genesis later return genesis; }; -export const getNethermindChainSpec = ( - mode: ExecutionStartMode, - options: ExecutionGenesisOptions -): Record => { +export const getNethermindChainSpec = (options: ExecutionGenesisOptions): Record => { const {ttd, shanghaiTime} = options; - const genesis = getGethGenesisBlock(mode, options) as Eth1GenesisBlock; + const genesis = getGethGenesisBlock(options) as Eth1GenesisBlock; return { name: "simulation-dev", diff --git a/packages/config/src/chainConfig/types.ts b/packages/config/src/chainConfig/types.ts index 5cf0d79549e8..41e33cf84a7b 100644 --- a/packages/config/src/chainConfig/types.ts +++ b/packages/config/src/chainConfig/types.ts @@ -15,8 +15,11 @@ export type ChainConfig = { CONFIG_NAME: string; // Transition + /** @deprecated All networks have completed the merge transition */ TERMINAL_TOTAL_DIFFICULTY: bigint; + /** @deprecated All networks have completed the merge transition */ TERMINAL_BLOCK_HASH: Uint8Array; + /** @deprecated All networks have completed the merge transition */ TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: number; // Genesis diff --git a/packages/fork-choice/src/forkChoice/forkChoice.ts b/packages/fork-choice/src/forkChoice/forkChoice.ts index f6c405e4b42d..02234e8f7367 100644 --- a/packages/fork-choice/src/forkChoice/forkChoice.ts +++ b/packages/fork-choice/src/forkChoice/forkChoice.ts @@ -1,4 +1,4 @@ -import {ChainConfig, ChainForkConfig} from "@lodestar/config"; +import {ChainForkConfig} from "@lodestar/config"; import {SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params"; import { CachedBeaconStateAllForks, @@ -10,7 +10,6 @@ import { computeStartSlotAtEpoch, getAttesterSlashableIndices, isExecutionBlockBodyType, - isExecutionEnabled, isExecutionStateType, } from "@lodestar/state-transition"; import {computeUnrealizedCheckpoints} from "@lodestar/state-transition/epoch"; @@ -23,7 +22,6 @@ import { RootHex, Slot, ValidatorIndex, - bellatrix, phase0, ssz, } from "@lodestar/types"; @@ -49,7 +47,6 @@ import { EpochDifference, IForkChoice, NotReorgedReason, - PowBlockHex, ShouldOverrideForkChoiceUpdateResult, } from "./interface.js"; import {CheckpointWithHex, IForkChoiceStore, JustifiedBalances, toCheckpointWithHex} from "./store.js"; @@ -656,16 +653,6 @@ export class ForkChoice implements IForkChoice { this.proposerBoostRoot = blockRootHex; } - // As per specs, we should be validating here the terminal conditions of - // the PoW if this were a merge transition block. - // (https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/fork-choice.md#on_block) - // - // However this check has been moved to the `verifyBlockStateTransition` in - // `packages/beacon-node/src/chain/blocks/verifyBlock.ts` as: - // - // 1. Its prudent to fail fast and not try importing a block in forkChoice. - // 2. Also the data to run such a validation is readily available there. - const justifiedCheckpoint = toCheckpointWithHex(state.currentJustifiedCheckpoint); const finalizedCheckpoint = toCheckpointWithHex(state.finalizedCheckpoint); const stateJustifiedEpoch = justifiedCheckpoint.epoch; @@ -754,7 +741,7 @@ export class ForkChoice implements IForkChoice { unrealizedFinalizedEpoch: unrealizedFinalizedCheckpoint.epoch, unrealizedFinalizedRoot: unrealizedFinalizedCheckpoint.rootHex, - ...(isExecutionBlockBodyType(block.body) && isExecutionStateType(state) && isExecutionEnabled(state, block) + ...(isExecutionBlockBodyType(block.body) && isExecutionStateType(state) ? { executionPayloadBlockHash: toRootHex(block.body.executionPayload.blockHash), executionPayloadNumber: block.body.executionPayload.blockNumber, @@ -1609,65 +1596,6 @@ export class ForkChoice implements IForkChoice { } } -/** - * This function checks the terminal pow conditions on the merge block as - * specified in the config either via TTD or TBH. This function is part of - * forkChoice because if the merge block was previously imported as syncing - * and the EL eventually signals it catching up via validateLatestHash - * the specs mandates validating terminal conditions on the previously - * imported merge block. - */ -export function assertValidTerminalPowBlock( - config: ChainConfig, - block: bellatrix.BeaconBlock, - preCachedData: { - executionStatus: ExecutionStatus.Syncing | ExecutionStatus.Valid; - powBlock?: PowBlockHex | null; - powBlockParent?: PowBlockHex | null; - } -): void { - if (!ssz.Root.equals(config.TERMINAL_BLOCK_HASH, ZERO_HASH)) { - if (computeEpochAtSlot(block.slot) < config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH) - throw Error(`Terminal block activation epoch ${config.TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH} not reached`); - - // powBock.blockHash is hex, so we just pick the corresponding root - if (!ssz.Root.equals(block.body.executionPayload.parentHash, config.TERMINAL_BLOCK_HASH)) - throw new Error( - `Invalid terminal block hash, expected: ${toRootHex(config.TERMINAL_BLOCK_HASH)}, actual: ${toRootHex( - block.body.executionPayload.parentHash - )}` - ); - } else { - // If no TERMINAL_BLOCK_HASH override, check ttd - - // Delay powBlock checks if the payload execution status is unknown because of - // syncing response in notifyNewPayload call while verifying - if (preCachedData?.executionStatus === ExecutionStatus.Syncing) return; - - const {powBlock, powBlockParent} = preCachedData; - if (!powBlock) throw Error("onBlock preCachedData must include powBlock"); - // if powBlock is genesis don't assert powBlockParent - if (!powBlockParent && powBlock.parentHash !== HEX_ZERO_HASH) - throw Error("onBlock preCachedData must include powBlockParent"); - - const isTotalDifficultyReached = powBlock.totalDifficulty >= config.TERMINAL_TOTAL_DIFFICULTY; - // If we don't have powBlockParent here, powBlock is the genesis and as we would have errored above - // we can mark isParentTotalDifficultyValid as valid - const isParentTotalDifficultyValid = - !powBlockParent || powBlockParent.totalDifficulty < config.TERMINAL_TOTAL_DIFFICULTY; - if (!isTotalDifficultyReached) { - throw Error( - `Invalid terminal POW block: total difficulty not reached expected >= ${config.TERMINAL_TOTAL_DIFFICULTY}, actual = ${powBlock.totalDifficulty}` - ); - } - - if (!isParentTotalDifficultyValid) { - throw Error( - `Invalid terminal POW block parent: expected < ${config.TERMINAL_TOTAL_DIFFICULTY}, actual = ${powBlockParent.totalDifficulty}` - ); - } - } -} // Approximate https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/fork-choice.md#calculate_committee_fraction // Calculates proposer boost score when committeePercent = config.PROPOSER_SCORE_BOOST export function getCommitteeFraction( diff --git a/packages/fork-choice/src/forkChoice/interface.ts b/packages/fork-choice/src/forkChoice/interface.ts index 51ee5bb28fd5..32d9b6c68f78 100644 --- a/packages/fork-choice/src/forkChoice/interface.ts +++ b/packages/fork-choice/src/forkChoice/interface.ts @@ -246,10 +246,3 @@ export interface IForkChoice { */ getDependentRoot(block: ProtoBlock, atEpochDiff: EpochDifference): RootHex; } - -/** Same to the PowBlock but we want RootHex to work with forkchoice conveniently */ -export type PowBlockHex = { - blockHash: RootHex; - parentHash: RootHex; - totalDifficulty: bigint; -}; diff --git a/packages/fork-choice/src/index.ts b/packages/fork-choice/src/index.ts index 59c7553a19c0..e476660ee9ad 100644 --- a/packages/fork-choice/src/index.ts +++ b/packages/fork-choice/src/index.ts @@ -6,14 +6,13 @@ export { type InvalidBlock, InvalidBlockCode, } from "./forkChoice/errors.js"; -export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt, assertValidTerminalPowBlock} from "./forkChoice/forkChoice.js"; +export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js"; export { type AncestorResult, AncestorStatus, EpochDifference, type IForkChoice, NotReorgedReason, - type PowBlockHex, } from "./forkChoice/interface.js"; export * from "./forkChoice/safeBlocks.js"; export { diff --git a/packages/fork-choice/test/unit/forkChoice/utils.test.ts b/packages/fork-choice/test/unit/forkChoice/utils.test.ts deleted file mode 100644 index c029f82eb837..000000000000 --- a/packages/fork-choice/test/unit/forkChoice/utils.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {describe, expect, it} from "vitest"; -import {createChainForkConfig} from "@lodestar/config"; -import {ssz} from "@lodestar/types"; -import {ExecutionStatus, assertValidTerminalPowBlock} from "../../../src/index.js"; - -describe("assertValidTerminalPowBlock", () => { - const config = createChainForkConfig({TERMINAL_TOTAL_DIFFICULTY: BigInt(10)}); - const block = ssz.bellatrix.BeaconBlock.defaultValue(); - const executionStatus = ExecutionStatus.Valid; - it("should accept ttd >= genesis block as terminal without powBlockParent", () => { - const powBlock = { - blockHash: "0x" + "ab".repeat(32), - // genesis powBlock will have zero parent hash - parentHash: "0x" + "00".repeat(32), - totalDifficulty: BigInt(10), - }; - expect(() => - assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: null, powBlock}) - ).not.toThrow(); - }); - - it("should require powBlockParent if powBlock not genesis", () => { - const powBlock = { - blockHash: "0x" + "ab".repeat(32), - // genesis powBlock will have non zero parent hash - parentHash: "0x" + "01".repeat(32), - totalDifficulty: BigInt(10), - }; - expect(() => - assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: null, powBlock}) - ).toThrow(); - }); - - it("should require powBlock >= ttd", () => { - const powBlock = { - blockHash: "0x" + "ab".repeat(32), - // genesis powBlock will have non zero parent hash - parentHash: "0x" + "01".repeat(32), - totalDifficulty: BigInt(9), - }; - expect(() => - assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: powBlock, powBlock}) - ).toThrow(); - }); - - it("should require powBlockParent < ttd", () => { - const powBlock = { - blockHash: "0x" + "ab".repeat(32), - // genesis powBlock will have non zero parent hash - parentHash: "0x" + "01".repeat(32), - totalDifficulty: BigInt(10), - }; - expect(() => - assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent: powBlock, powBlock}) - ).toThrow(); - }); - - it("should accept powBlockParent < ttd and powBlock >= ttd", () => { - const powBlock = { - blockHash: "0x" + "ab".repeat(32), - // genesis powBlock will have non zero parent hash - parentHash: "0x" + "01".repeat(32), - totalDifficulty: BigInt(10), - }; - const powBlockParent = { - ...powBlock, - totalDifficulty: BigInt(9), - }; - expect(() => assertValidTerminalPowBlock(config, block, {executionStatus, powBlockParent, powBlock})).not.toThrow(); - }); -}); diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index bcee6d82e129..abecfb024368 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -271,6 +271,7 @@ export const MAX_REQUEST_LIGHT_CLIENT_COMMITTEE_HASHES = 128; /** * Optimistic sync + * @deprecated All networks have completed the merge transition, blocks are always safe to import optimistically. */ export const SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY = 128; /** @deprecated */ diff --git a/packages/state-transition/src/block/index.ts b/packages/state-transition/src/block/index.ts index b2a2ad06f640..a5d67e4c75aa 100644 --- a/packages/state-transition/src/block/index.ts +++ b/packages/state-transition/src/block/index.ts @@ -7,7 +7,7 @@ import { CachedBeaconStateCapella, CachedBeaconStateGloas, } from "../types.js"; -import {getFullOrBlindedPayload, isExecutionEnabled} from "../util/execution.js"; +import {getFullOrBlindedPayload} from "../util/execution.js"; import {BlockExternalData, DataAvailabilityStatus} from "./externalData.js"; import {processBlobKzgCommitments} from "./processBlobKzgCommitments.js"; import {processBlockHeader} from "./processBlockHeader.js"; @@ -67,11 +67,7 @@ export function processBlock( // The call to the process_execution_payload must happen before the call to the process_randao as the former depends // on the randao_mix computed with the reveal of the previous block. // TODO GLOAS: We call processExecutionPayload somewhere else post-gloas - if ( - fork >= ForkSeq.bellatrix && - fork < ForkSeq.gloas && - isExecutionEnabled(state as CachedBeaconStateBellatrix, block) - ) { + if (fork >= ForkSeq.bellatrix && fork < ForkSeq.gloas) { processExecutionPayload(fork, state as CachedBeaconStateBellatrix, block.body, externalData); } diff --git a/packages/state-transition/src/block/processExecutionPayload.ts b/packages/state-transition/src/block/processExecutionPayload.ts index 0af784074cdb..65f28822a022 100644 --- a/packages/state-transition/src/block/processExecutionPayload.ts +++ b/packages/state-transition/src/block/processExecutionPayload.ts @@ -3,11 +3,7 @@ import {ForkName, ForkSeq, isForkPostDeneb} from "@lodestar/params"; import {BeaconBlockBody, BlindedBeaconBlockBody, deneb, isExecutionPayload} from "@lodestar/types"; import {toHex, toRootHex} from "@lodestar/utils"; import {CachedBeaconStateBellatrix, CachedBeaconStateCapella} from "../types.js"; -import { - executionPayloadToPayloadHeader, - getFullOrBlindedPayloadFromBody, - isMergeTransitionComplete, -} from "../util/execution.js"; +import {executionPayloadToPayloadHeader, getFullOrBlindedPayloadFromBody} from "../util/execution.js"; import {computeEpochAtSlot, computeTimeAtSlot, getRandaoMix} from "../util/index.js"; import {BlockExternalData, ExecutionPayloadStatus} from "./externalData.js"; @@ -21,15 +17,13 @@ export function processExecutionPayload( const forkName = ForkName[ForkSeq[fork] as ForkName]; // Verify consistency of the parent hash, block number, base fee per gas and gas limit // with respect to the previous execution payload header - if (isMergeTransitionComplete(state)) { - const {latestExecutionPayloadHeader} = state; - if (!byteArrayEquals(payload.parentHash, latestExecutionPayloadHeader.blockHash)) { - throw Error( - `Invalid execution payload parentHash ${toRootHex(payload.parentHash)} latest blockHash ${toRootHex( - latestExecutionPayloadHeader.blockHash - )}` - ); - } + const {latestExecutionPayloadHeader} = state; + if (!byteArrayEquals(payload.parentHash, latestExecutionPayloadHeader.blockHash)) { + throw Error( + `Invalid execution payload parentHash ${toRootHex(payload.parentHash)} latest blockHash ${toRootHex( + latestExecutionPayloadHeader.blockHash + )}` + ); } // Verify random diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index a5ad0be8163f..a64f21a08699 100644 --- a/packages/state-transition/src/util/execution.ts +++ b/packages/state-transition/src/util/execution.ts @@ -2,7 +2,6 @@ import {ForkName, ForkPostBellatrix, ForkPreGloas, ForkSeq} from "@lodestar/para import { BeaconBlock, BeaconBlockBody, - BlindedBeaconBlock, BlindedBeaconBlockBody, ExecutionPayload, ExecutionPayloadHeader, @@ -10,75 +9,16 @@ import { capella, deneb, isBlindedBeaconBlockBody, - isExecutionPayload, ssz, } from "@lodestar/types"; import { BeaconStateAllForks, - BeaconStateBellatrix, BeaconStateCapella, BeaconStateExecutions, CachedBeaconStateAllForks, CachedBeaconStateExecutions, } from "../types.js"; -/** - * Execution enabled = merge is done. - * When (A) state has execution data OR (B) block has execution data - */ -export function isExecutionEnabled(state: BeaconStateExecutions, block: BeaconBlock | BlindedBeaconBlock): boolean { - if (isMergeTransitionComplete(state)) { - return true; - } - - // Throws if not post-bellatrix block. A fork-guard in isExecutionEnabled() prevents this from happening - const payload = getFullOrBlindedPayload(block); - // Note: spec says to check all payload is zero-ed. However a state-root cannot be zero for any non-empty payload - // TODO: Consider comparing with the payload root if this assumption is not correct. - // return !byteArrayEquals(payload.stateRoot, ZERO_HASH); - - // UPDATE: stateRoot comparision should have been enough with zero hash, but spec tests were failing - // Revisit this later to fix specs and make this efficient - return isExecutionPayload(payload) - ? !ssz.bellatrix.ExecutionPayload.equals(payload, ssz.bellatrix.ExecutionPayload.defaultValue()) - : !ssz.bellatrix.ExecutionPayloadHeader.equals( - state.latestExecutionPayloadHeader, - // TODO: Performance - ssz.bellatrix.ExecutionPayloadHeader.defaultValue() - ); -} - -/** - * Merge block is the SINGLE block that transitions from POW to POS. - * state has no execution data AND this block has execution data - */ -export function isMergeTransitionBlock(state: BeaconStateExecutions, body: bellatrix.BeaconBlockBody): boolean { - return ( - !isMergeTransitionComplete(state) && - !ssz.bellatrix.ExecutionPayload.equals(body.executionPayload, ssz.bellatrix.ExecutionPayload.defaultValue()) - ); -} - -/** - * Merge is complete when the state includes execution layer data: - * state.latestExecutionPayloadHeader NOT EMPTY - */ -export function isMergeTransitionComplete(state: BeaconStateExecutions): boolean { - if (!isCapellaStateType(state)) { - return !ssz.bellatrix.ExecutionPayloadHeader.equals( - (state as BeaconStateBellatrix).latestExecutionPayloadHeader, - // TODO: Performance - ssz.bellatrix.ExecutionPayloadHeader.defaultValue() - ); - } - - return !ssz.capella.ExecutionPayloadHeader.equals( - state.latestExecutionPayloadHeader, - // TODO: Performance - ssz.capella.ExecutionPayloadHeader.defaultValue() - ); -} - /** Type guard for bellatrix.BeaconState */ export function isExecutionStateType(state: BeaconStateAllForks): state is BeaconStateExecutions { return (state as BeaconStateExecutions).latestExecutionPayloadHeader !== undefined; diff --git a/packages/types/src/bellatrix/types.ts b/packages/types/src/bellatrix/types.ts index dcd45d7c97ea..02ee66ed1003 100644 --- a/packages/types/src/bellatrix/types.ts +++ b/packages/types/src/bellatrix/types.ts @@ -9,6 +9,7 @@ export type BeaconBlockBody = ValueOf; export type BeaconBlock = ValueOf; export type SignedBeaconBlock = ValueOf; export type BeaconState = ValueOf; +/** @deprecated */ export type PowBlock = ValueOf; export type BlindedBeaconBlockBody = ValueOf; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index bd6cd2a5358c..0f3efaefcc68 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -110,8 +110,8 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record