diff --git a/.codecov.yml b/.codecov.yml index 45b353124178..f9d25d52769e 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -34,3 +34,9 @@ ignore: - "supporting-docs" - "docker" - ".github" + +flags: + unit: + paths: + - ".*" + carryforward: true diff --git a/.env.test b/.env.test new file mode 100644 index 000000000000..31222f7549cb --- /dev/null +++ b/.env.test @@ -0,0 +1,13 @@ +# We use these images during sim and e2e tests +# TODO: Upgrade Geth once the Nethermind issue is resolved else it's causing following error +# Rejected peer id=134e2c1a76745626 addr=192.168.0.3:9052 conn=staticdial err="useless peer" +GETH_DOCKER_IMAGE=ethereum/client-go:v1.11.6 +# Use either image or local binary for the testing +GETH_BINARY_DIR= +LIGHTHOUSE_DOCKER_IMAGE=sigp/lighthouse:v4.6.0-amd64-modern-dev +# We can't upgrade nethermind further due to genesis hash mismatch with the geth +# https://github.com/NethermindEth/nethermind/issues/6683 +NETHERMIND_DOCKER_IMAGE=nethermind/nethermind:1.18.2 +# We mostly use mainnet for unit testing +# Changing this value may impact the tests which are written with mainnet in mind +LODESTAR_PRESET=mainnet diff --git a/.eslintrc.js b/.eslintrc.js index 8eeda9a0446f..c4f0d45fc42d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -64,6 +64,10 @@ module.exports = { }, //ignore rules on destructured params {selector: "variable", modifiers: ["destructured"], format: null}, + { + selector: "import", + format: ["camelCase", "PascalCase"], + }, ], "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-floating-promises": "error", @@ -94,6 +98,7 @@ module.exports = { "func-call-spacing": "off", // Force to add names to all functions to ease CPU profiling "func-names": ["error", "always"], + "import/namespace": "off", //if --fix is run it messes imports like /lib/presets/minimal & /lib/presets/mainnet "import/no-duplicates": "off", "import/no-extraneous-dependencies": [ diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 709ceff687ac..989f4bc9c0b5 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: https://gitcoin.co/grants/6034/lodestar-typescript-eth-consensus-client-by-chains +custom: https://etherscan.io/address/0xb4da52336092db22fe8e036866d59c6488604f89 diff --git a/.github/actions/core-dump/action.yml b/.github/actions/core-dump/action.yml index eae37c2101b8..e2e0b1224912 100644 --- a/.github/actions/core-dump/action.yml +++ b/.github/actions/core-dump/action.yml @@ -10,7 +10,7 @@ runs: shell: sh - name: Backup core dump - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: core-dump path: /cores/* diff --git a/.github/actions/dotenv/action.yml b/.github/actions/dotenv/action.yml new file mode 100644 index 000000000000..88c0f80b02d3 --- /dev/null +++ b/.github/actions/dotenv/action.yml @@ -0,0 +1,5 @@ +name: "Setup env variables using .env file" +description: "Load .env file from root of repo and setup for CI runner" +runs: + using: "node20" + main: index.js diff --git a/.github/actions/dotenv/index.js b/.github/actions/dotenv/index.js new file mode 100644 index 000000000000..fd7ce8ec2527 --- /dev/null +++ b/.github/actions/dotenv/index.js @@ -0,0 +1,29 @@ +const fs = require("fs"); +const core = require("@actions/core"); +const dotEnv = require("dotenv"); +const envFile = ".env.test"; + +if (!fs.existsSync(envFile)) { + core.setFailed("File .env not found"); +} + +const result = dotEnv.config({path: envFile}); +if (result.error) { + core.setFailed(result.error.message); +} else { + core.setOutput("env", result.parsed); + core.info("Env file loaded"); + core.info("Populating env variables..."); + + for (const key in result.parsed) { + const value = result.parsed[key]; + core.info(`${key}=${value}`); + + // Export variable + core.exportVariable(key, value); + + // Set to output so it can be used in as the input for the next job/step + core.setOutput(key, value); + + } +} diff --git a/.github/actions/setup-and-build/action.yml b/.github/actions/setup-and-build/action.yml new file mode 100644 index 000000000000..f2575ea04565 --- /dev/null +++ b/.github/actions/setup-and-build/action.yml @@ -0,0 +1,58 @@ +name: "Setup and build the repo" +description: "A task to reuse setup steps during multiple jobs" +inputs: + node: + description: Node version + required: true + +runs: + using: "composite" + steps: + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{inputs.node}} + check-latest: true + cache: yarn + + - name: Node.js version + id: node + shell: bash + run: echo "v8CppApiVersion=$(node --print "process.versions.modules")" >> $GITHUB_OUTPUT + + - name: Restore build + uses: actions/cache/restore@v4 + id: cache-build-restore + with: + path: | + node_modules + packages/*/node_modules + lib/ + packages/*/lib + packages/*/.git-data.json + key: ${{ runner.os }}-node-${{ inputs.node }}-${{ github.sha }} + + - name: Install & build + if: steps.cache-build-restore.outputs.cache-hit != 'true' + shell: bash + run: yarn install --frozen-lockfile && yarn build + + - name: Build + if: steps.cache-build-restore.outputs.cache-hit == 'true' + shell: bash + run: yarn build + + - name: Check Build + shell: bash + run: yarn check-build + + - name: Cache build artifacts + uses: actions/cache@master + with: + path: | + node_modules + packages/*/node_modules + lib/ + packages/*/lib + packages/*/.git-data.json + key: ${{ runner.os }}-node-${{ inputs.node }}-${{ github.sha }} diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b5fa586c7a8b..efb9e0231e20 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -30,8 +30,8 @@ jobs: steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20 check-latest: true diff --git a/.github/workflows/build-debug-node.yml b/.github/workflows/build-debug-node.yml index 9e7c1ac66fab..da97e10d8e97 100644 --- a/.github/workflows/build-debug-node.yml +++ b/.github/workflows/build-debug-node.yml @@ -44,7 +44,7 @@ jobs: working-directory: 'nodejs' - name: Upload build to artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: nodejs-debug-build-${{ github.event.inputs.version }} path: nodejs-debug-build-${{ github.event.inputs.version }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 726199e68549..2d635afe2688 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml index 4dbbcdce5f5c..9b20732c2a56 100644 --- a/.github/workflows/docs-check.yml +++ b/.github/workflows/docs-check.yml @@ -12,8 +12,8 @@ jobs: runs-on: ubuntu-latest steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20 cache: yarn diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a47f236b3470..123ec596f1a2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,11 +25,11 @@ jobs: echo "Deploying ref: $DEPLOY_REF" # Checkout the correct ref being deployed - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ env.DEPLOY_REF }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 20 check-latest: true diff --git a/.github/workflows/publish-dev.yml b/.github/workflows/publish-dev.yml index 2e71cc86c33c..e38abd9e68dc 100644 --- a/.github/workflows/publish-dev.yml +++ b/.github/workflows/publish-dev.yml @@ -15,10 +15,10 @@ jobs: runs-on: buildjet-4vcpu-ubuntu-2204 steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 20 registry-url: "https://registry.npmjs.org" @@ -109,7 +109,7 @@ jobs: runs-on: buildjet-4vcpu-ubuntu-2204 needs: npm steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # https://github.com/docker/setup-qemu-action - name: Set up QEMU uses: docker/setup-qemu-action@v1 diff --git a/.github/workflows/publish-rc.yml b/.github/workflows/publish-rc.yml index c0dfe3b513dd..214c3497db1a 100644 --- a/.github/workflows/publish-rc.yml +++ b/.github/workflows/publish-rc.yml @@ -15,7 +15,7 @@ jobs: runs-on: buildjet-4vcpu-ubuntu-2204 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -49,10 +49,10 @@ jobs: if: needs.tag.outputs.is_rc == 'true' steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Needs full depth for changelog generation - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 20 check-latest: true @@ -132,7 +132,7 @@ jobs: needs: [tag, npm] if: needs.tag.outputs.is_rc == 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: scripts/await-release.sh ${{ needs.tag.outputs.tag }} rc 900 # https://github.com/docker/setup-qemu-action - name: Set up QEMU diff --git a/.github/workflows/publish-stable.yml b/.github/workflows/publish-stable.yml index c0d046891bdf..933dc0b0ca4b 100644 --- a/.github/workflows/publish-stable.yml +++ b/.github/workflows/publish-stable.yml @@ -15,7 +15,7 @@ jobs: runs-on: buildjet-4vcpu-ubuntu-2204 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -55,10 +55,10 @@ jobs: if: needs.tag.outputs.is_stable == 'true' steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Needs full depth for changelog generation - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 20 check-latest: true @@ -132,7 +132,7 @@ jobs: needs: [tag, npm] if: needs.tag.outputs.is_stable == 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: scripts/await-release.sh ${{ needs.tag.outputs.tag }} latest 900 # https://github.com/docker/setup-qemu-action - name: Set up QEMU diff --git a/.github/workflows/test-sim-merge.yml b/.github/workflows/test-sim-merge.yml index 268df5620559..641d5ab2732e 100644 --- a/.github/workflows/test-sim-merge.yml +++ b/.github/workflows/test-sim-merge.yml @@ -27,8 +27,8 @@ jobs: runs-on: buildjet-4vcpu-ubuntu-2204 steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20 check-latest: true @@ -92,7 +92,7 @@ jobs: - name: Upload debug log test files if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: debug-test-logs path: packages/beacon-node/test-logs @@ -144,7 +144,7 @@ jobs: - name: Upload debug log test files if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: debug-test-logs path: packages/beacon-node/test-logs diff --git a/.github/workflows/test-sim.yml b/.github/workflows/test-sim.yml index 65260fc38937..6895c74c016e 100644 --- a/.github/workflows/test-sim.yml +++ b/.github/workflows/test-sim.yml @@ -17,72 +17,163 @@ on: required: false default: "" genesisDelaySlots: - description: 'Number of slots delay before genesis' + description: "Number of slots delay before genesis" required: true type: number default: 40 -env: - GETH_DOCKER_IMAGE: ethereum/client-go:v1.11.6 - LIGHTHOUSE_DOCKER_IMAGE: sigp/lighthouse:latest-amd64-modern-dev - NETHERMIND_DOCKER_IMAGE: nethermind/nethermind:1.18.0 - jobs: - tests-sim: - name: Sim tests + build: + name: Build runs-on: buildjet-4vcpu-ubuntu-2204 steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 20 - check-latest: true - cache: yarn - - name: Node.js version - id: node - run: echo "v8CppApiVersion=$(node --print "process.versions.modules")" >> $GITHUB_OUTPUT - - name: Restore dependencies - uses: actions/cache@master - id: cache-deps + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" with: - path: | - node_modules - packages/*/node_modules - key: ${{ runner.os }}-${{ steps.node.outputs.v8CppApiVersion }}-${{ hashFiles('**/yarn.lock', '**/package.json') }} - - name: Install & build - if: steps.cache-deps.outputs.cache-hit != 'true' - run: yarn install --frozen-lockfile && yarn build - - name: Build - run: yarn build - if: steps.cache-deps.outputs.cache-hit == 'true' - # + node: 20 + sim-test-multifork: + name: Multifork sim test + needs: build + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + # - Uses YAML anchors in the future + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" + with: + node: 20 + - name: Load env variables + uses: ./.github/actions/dotenv + - name: Download required docker images before running tests + run: | + docker pull ${{env.GETH_DOCKER_IMAGE}} + docker pull ${{env.LIGHTHOUSE_DOCKER_IMAGE}} + docker pull ${{env.NETHERMIND_DOCKER_IMAGE}} - name: Sim tests multifork run: DEBUG='${{github.event.inputs.debug}}' yarn test:sim:multifork working-directory: packages/cli - env: + env: GENESIS_DELAY_SLOTS: ${{github.event.inputs.genesisDelaySlots}} + - name: Upload debug log test files for "packages/cli" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: sim-test-multifork-logs + path: packages/cli/test-logs + sim-test-endpoints: + name: Endpoint sim tests + needs: build + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + # - Uses YAML anchors in the future + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" + with: + node: 20 + - name: Load env variables + uses: ./.github/actions/dotenv + - name: Download required docker images before running tests + run: | + docker pull ${{env.GETH_DOCKER_IMAGE}} + docker pull ${{env.LIGHTHOUSE_DOCKER_IMAGE}} + docker pull ${{env.NETHERMIND_DOCKER_IMAGE}} - name: Sim tests endpoints - run: yarn test:sim:endpoints + run: DEBUG='${{github.event.inputs.debug}}' yarn test:sim:endpoints working-directory: packages/cli + env: + GENESIS_DELAY_SLOTS: ${{github.event.inputs.genesisDelaySlots}} + - name: Upload debug log test files for "packages/cli" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: sim-test-endpoints-logs + path: packages/cli/test-logs + sim-test-deneb: + name: Deneb sim tests + needs: build + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + # - Uses YAML anchors in the future + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" + with: + node: 20 + - name: Load env variables + uses: ./.github/actions/dotenv + - name: Download required docker images before running tests + run: | + docker pull ${{env.GETH_DOCKER_IMAGE}} + docker pull ${{env.LIGHTHOUSE_DOCKER_IMAGE}} + docker pull ${{env.NETHERMIND_DOCKER_IMAGE}} - name: Sim tests deneb - run: yarn test:sim:deneb + run: DEBUG='${{github.event.inputs.debug}}' yarn test:sim:deneb working-directory: packages/cli + env: + GENESIS_DELAY_SLOTS: ${{github.event.inputs.genesisDelaySlots}} + - name: Upload debug log test files for "packages/cli" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: sim-test-deneb-logs + path: packages/cli/test-logs + sim-test-eth-backup-provider: + name: Eth backup provider sim tests + needs: build + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + # - Uses YAML anchors in the future + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" + with: + node: 20 + - name: Load env variables + uses: ./.github/actions/dotenv + - name: Download required docker images before running tests + run: | + docker pull ${{env.GETH_DOCKER_IMAGE}} + docker pull ${{env.LIGHTHOUSE_DOCKER_IMAGE}} + docker pull ${{env.NETHERMIND_DOCKER_IMAGE}} - name: Sim tests backup eth provider - run: yarn test:sim:backup_eth_provider + run: DEBUG='${{github.event.inputs.debug}}' yarn test:sim:backup_eth_provider working-directory: packages/cli + env: + GENESIS_DELAY_SLOTS: ${{github.event.inputs.genesisDelaySlots}} + - name: Upload debug log test files for "packages/cli" + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: sim-test-eth-backup-provider-logs + path: packages/cli/test-logs + sim-test-mixed-clients: + name: Mixed clients sim tests + needs: build + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + # - Uses YAML anchors in the future + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" + with: + node: 20 + - name: Load env variables + uses: ./.github/actions/dotenv + - name: Download required docker images before running tests + run: | + docker pull ${{env.GETH_DOCKER_IMAGE}} + docker pull ${{env.LIGHTHOUSE_DOCKER_IMAGE}} + docker pull ${{env.NETHERMIND_DOCKER_IMAGE}} - name: Sim tests mixed client run: DEBUG='${{github.event.inputs.debug}}' yarn test:sim:mixedclient working-directory: packages/cli - + env: + GENESIS_DELAY_SLOTS: ${{github.event.inputs.genesisDelaySlots}} - name: Upload debug log test files for "packages/cli" if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: debug-test-logs-cli + name: sim-test-mixed-clients-logs path: packages/cli/test-logs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d865045c4f3a..046cbe1f3cce 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,10 +12,6 @@ on: pull_request: workflow_dispatch: -env: - GETH_DOCKER_IMAGE: ethereum/client-go:v1.11.6 - NETHERMIND_DOCKER_IMAGE: nethermind/nethermind:1.18.0 - jobs: build: name: Build @@ -26,51 +22,18 @@ jobs: node: [20] steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{matrix.node}} - check-latest: true - cache: yarn - - name: Node.js version - id: node - run: echo "v8CppApiVersion=$(node --print "process.versions.modules")" >> $GITHUB_OUTPUT - - name: Restore build - uses: actions/cache/restore@v3 - id: cache-build-restore + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - - name: Install & build - if: steps.cache-build-restore.outputs.cache-hit != 'true' - run: yarn install --frozen-lockfile && yarn build - - name: Build - if: steps.cache-build-restore.outputs.cache-hit == 'true' - run: yarn build - - name: Check Build - run: yarn check-build + node: ${{ matrix.node }} + - name: Test root binary exists run: ./lodestar --version + - name: Reject yarn.lock changes run: .github/workflows/scripts/reject_yarn_lock_changes.sh # Run only on forks if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} - - name: Cache build artifacts - uses: actions/cache@master - id: cache-build - with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} lint: name: Lint @@ -81,32 +44,26 @@ jobs: matrix: node: [20] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - check-latest: true - cache: yarn - - name: Restore build cache - id: cache-primes-restore - uses: actions/cache/restore@v3 + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - fail-on-cache-miss: true + node: ${{ matrix.node }} + - name: Assert yarn prints no warnings run: scripts/assert_no_yarn_warnings.sh + + - name: Lint yarn lockfile + run: npx lockfile-lint --path yarn.lock --allowed-hosts npm yarn --validate-https + - name: Lint Code run: yarn lint + - name: Lint Grafana dashboards run: scripts/validate-grafana-dashboards.sh + - name: Assert ESM module exports run: node scripts/assert_exports.mjs + - name: Assert eslintrc rules sorted run: scripts/assert_eslintrc_sorted.mjs @@ -119,24 +76,11 @@ jobs: matrix: node: [20] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - check-latest: true - cache: yarn - - name: Restore build cache - id: cache-primes-restore - uses: actions/cache/restore@v3 + - uses: actions/checkout@v4 + + - uses: "./.github/actions/setup-and-build" with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - fail-on-cache-miss: true + node: ${{ matrix.node }} - name: Check Types run: yarn check-types @@ -153,28 +97,10 @@ jobs: matrix: node: [20] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" with: - node-version: ${{ matrix.node }} - check-latest: true - cache: yarn - - # # Remove when finished debugging core dumps - # - uses: './.github/actions/setup-debug-node' - - - name: Restore build cache - id: cache-primes-restore - uses: actions/cache/restore@v3 - with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - fail-on-cache-miss: true + node: ${{ matrix.node }} # Cache validator slashing protection data tests - name: Restore spec tests cache @@ -188,13 +114,18 @@ jobs: # Rever to "yarn test:unit" when finished debugging core dumps # run: sudo sh -c "ulimit -c unlimited && /usr/bin/node-with-debug $(which yarn) test:unit" run: yarn test:unit - + # # Remove when finished debugging core dumps # - uses: './.github/actions/core-dump' # if: ${{ failure() && steps.unit_tests.conclusion == 'failure' }} - name: Upload coverage data - run: yarn coverage + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + verbose: true # optional (default = false) + flags: "unit" e2e-tests: name: E2E Tests @@ -206,24 +137,14 @@ jobs: node: [20] steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{matrix.node}} - check-latest: true - cache: yarn - - name: Restore build cache - id: cache-primes-restore - uses: actions/cache/restore@v3 + - uses: actions/checkout@v4 + + - uses: "./.github/actions/setup-and-build" with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - fail-on-cache-miss: true + node: ${{ matrix.node }} + + - name: Load env variables + uses: ./.github/actions/dotenv - name: Run the e2e test environment run: scripts/run_e2e_env.sh start @@ -238,7 +159,7 @@ jobs: - name: Upload debug log test for test env if: ${{ always() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: debug-e2e-test-logs-node-${{matrix.node}} path: test-logs/e2e-test-env @@ -253,26 +174,12 @@ jobs: node: [20] steps: # - Uses YAML anchors in the future - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" with: - node-version: ${{matrix.node}} - check-latest: true - cache: yarn - - name: Restore build cache - id: cache-primes-restore - uses: actions/cache/restore@v3 - with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - fail-on-cache-miss: true + node: ${{ matrix.node }} - name: Install Chrome browser - run: npx @puppeteer/browsers install chromedriver@latest --path /tmp + run: npx @puppeteer/browsers install chromedriver@latest --path /tmp - name: Install Firefox browser run: npx @puppeteer/browsers install firefox@latest --path /tmp - name: Browser tests @@ -290,24 +197,10 @@ jobs: matrix: node: [20] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{matrix.node}} - check-latest: true - cache: yarn - - name: Restore build cache - id: cache-primes-restore - uses: actions/cache/restore@v3 + - uses: actions/checkout@v4 + - uses: "./.github/actions/setup-and-build" with: - path: | - node_modules - packages/*/node_modules - lib/ - packages/*/lib - packages/*/.git-data.json - key: ${{ runner.os }}-node-${{ matrix.node }}-${{ github.sha }} - fail-on-cache-miss: true + node: ${{ matrix.node }} # Download spec tests with cache - name: Restore spec tests cache @@ -326,7 +219,7 @@ jobs: working-directory: packages/beacon-node - name: Spec tests bls run: yarn test:spec:bls - working-directory: packages/beacon-node + working-directory: packages/beacon-node - name: Spec tests minimal run: yarn test:spec:minimal working-directory: packages/beacon-node diff --git a/.wordlist.txt b/.wordlist.txt index 9e9b2773bd66..f56de5f92c82 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -48,6 +48,7 @@ JSON JSObjects JWT KDE +Kubernetes LGPL LGPLv LMD diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4e67c4d226b..009eaa850367 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,14 @@ To run tests: - :test_tube: Run `yarn check-types` to check TypeScript types. - :test_tube: Run `yarn lint` to run the linter (ESLint). +Note that to run `test:e2e`, first ensure that the environment is correctly setup by running the `run_e2e_env.sh` script. + +```sh +./scripts/run_e2e_env.sh start +``` + +Similarly, run `yarn download-spec-tests` before running `yarn test:spec`. + Contributing to tests: - Test must not depend on external live resources, such that running tests for a commit must be deterministic: @@ -53,11 +61,12 @@ If you observe following error running any of the test files that means you are - To fix errors always focus on passing all minimal tests first without running mainnet tests. - Spec tests often compare full expected vs actual states in JSON format. -- A single logical error can cause many spec tests to fail. To focus on a single test at a time you can use vitest's option `--bail` to stop at the first failed test -- To then run only that failed test you can run against a specific file as use vitest's filters to run only one case +- A single logical error can cause many spec tests to fail. To focus on a single test at a time you can use vitest's option `--bail 1` to stop at the first failed test +- To then run only that failed test you can run against a specific file as use vitest's filters option `-t ` to run only one case +- Before running the tests, make sure to switch to the package directory (e.g. `packages/beacon-node`) to speed up test execution ```sh -LODESTAR_PRESET=minimal yarn vitest --run --config vitest.config.spec.ts test/spec/phase0/sanity.test.ts +LODESTAR_PRESET=minimal yarn vitest --run --bail 1 --config vitest.spec.config.ts test/spec/presets/sanity.test.ts -t attester_slashing ``` ## Docker diff --git a/README.md b/README.md index 52c671ae6f1d..b5accf7a2c61 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,23 @@ # Lodestar Ethereum Consensus Implementation [![GitHub release (latest by date)](https://img.shields.io/github/v/release/chainsafe/lodestar?label=Github)](https://github.com/ChainSafe/lodestar/releases/latest) -[![npm](https://img.shields.io/npm/v/@chainsafe/lodestar)](https://www.npmjs.com/package/@chainsafe/lodestar) [![Docker Image Version (latest by date)](https://img.shields.io/docker/v/chainsafe/lodestar?color=blue&label=Docker&sort=semver)](https://hub.docker.com/r/chainsafe/lodestar) [![Ethereum Consensus Spec v1.1.10](https://img.shields.io/badge/ETH%20consensus--spec-1.1.10-blue)](https://github.com/ethereum/consensus-specs/releases/tag/v1.1.10) ![ES Version](https://img.shields.io/badge/ES-2021-yellow) ![Node Version](https://img.shields.io/badge/node-20.x-green) +[![codecov](https://codecov.io/gh/ChainSafe/lodestar/graph/badge.svg)](https://codecov.io/gh/ChainSafe/lodestar) [![gitpoap badge](https://public-api.gitpoap.io/v1/repo/ChainSafe/lodestar/badge)](https://www.gitpoap.io/gh/ChainSafe/lodestar) [Lodestar](https://lodestar.chainsafe.io) is a TypeScript implementation of the [Ethereum Consensus specification](https://github.com/ethereum/consensus-specs) developed by [ChainSafe Systems](https://chainsafe.io). ## Getting started -- :gear: Follow the installation method for [source install](https://chainsafe.github.io/lodestar/install/source/), [NPM install](https://chainsafe.github.io/lodestar/install/npm/), or [Docker install](https://chainsafe.github.io/lodestar/install/docker/) to install Lodestar. Or use our [Lodestar Quickstart scripts](https://github.com/ChainSafe/lodestar-quickstart). -- :books: Use [Lodestar libraries](https://chainsafe.github.io/lodestar/libraries) in your next Ethereum Typescript project. -- :globe_with_meridians: Run a beacon node on [mainnet or a public testnet](https://chainsafe.github.io/lodestar/usage/beacon-management). -- :computer: Utilize the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/usage/local). +- :gear: Follow the installation method for [source install](https://chainsafe.github.io/lodestar/getting-started/installation/#build-from-source), [NPM install](https://chainsafe.github.io/lodestar/getting-started/installation/#install-from-npm-not-recommended), or [Docker install](https://chainsafe.github.io/lodestar/getting-started/installation/#docker-installation) to install Lodestar. Or use our [Lodestar Quickstart scripts](https://github.com/ChainSafe/lodestar-quickstart). +- :books: Use [Lodestar libraries](https://chainsafe.github.io/lodestar/supporting-libraries/libraries/) in your next Ethereum Typescript project. +- :globe_with_meridians: Run a beacon node on [mainnet or a public testnet](https://chainsafe.github.io/lodestar/getting-started/starting-a-node/). +- :computer: Utilize the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/advanced-topics/setting-up-a-testnet/). - :spiral_notepad: View the Lodestar [CLI commands and options](https://chainsafe.github.io/lodestar/reference/cli/). -- :nerd_face: View the [Package and dependency structure](https://chainsafe.github.io/lodestar/design/depgraph/). +- :nerd_face: View the [Package and dependency structure](https://chainsafe.github.io/lodestar/contribution/depgraph/). - :memo: Prospective contributors can read the [contributing section](./CONTRIBUTING.md) to understand how we develop and test on Lodestar. - :writing_hand: If you have questions [submit an issue](https://github.com/ChainSafe/lodestar/issues/new) or join us on [Discord](https://discord.gg/yjyvFRP)! [![Discord](https://img.shields.io/discord/593655374469660673.svg?label=Discord&logo=discord)](https://discord.gg/aMxzVcr) @@ -46,7 +46,7 @@ yarn build | Package | Version | License | Docs | Description | | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -| [`@lodestar/beacon-node`](./packages/beacon-node) | [![npm](https://img.shields.io/npm/v/@chainsafe/lodestar)](https://www.npmjs.com/package/@chainsafe/lodestar) | [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) | [![documentation](https://img.shields.io/badge/readme-blue)](./packages/beacon-node) | :rotating_light: Beacon-chain client | +| [`@lodestar/beacon-node`](./packages/beacon-node) | [![npm](https://img.shields.io/npm/v/@lodestar/beacon-node)](https://www.npmjs.com/package/@lodestar/beacon-node) | [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) | [![documentation](https://img.shields.io/badge/readme-blue)](./packages/beacon-node) | :rotating_light: Beacon-chain client | | [`@lodestar/validator`](./packages/validator) | [![npm](https://img.shields.io/npm/v/@lodestar/validator)](https://www.npmjs.com/package/@lodestar/validator) | [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) | [![documentation](https://img.shields.io/badge/readme-blue)](./packages/validator) | :bank: Validator client | | [`@lodestar/light-client`](./packages/light-client) | [![npm](https://img.shields.io/npm/v/@lodestar/light-client)](https://www.npmjs.com/package/@lodestar/light-client) | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) | [![documentation](https://img.shields.io/badge/readme-blue)](./packages/light-client) | :bird: Ethereum Light client | | [`@lodestar/api`](./packages/api) | [![npm](https://img.shields.io/npm/v/@lodestar/api)](https://www.npmjs.com/package/@lodestar/api) | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) | [![documentation](https://img.shields.io/badge/readme-blue)](./packages/api) | :clipboard: REST Client for the Ethereum Beacon API | diff --git a/dashboards/lodestar_validator_client.json b/dashboards/lodestar_validator_client.json index 7cc41cffdb34..8ec6a04437b1 100644 --- a/dashboards/lodestar_validator_client.json +++ b/dashboards/lodestar_validator_client.json @@ -506,21 +506,121 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "description": "A list of beacon nodes the validator client is connected to, with their last known health status. For fallback nodes, this status might be inaccurate as it is only updated if primary node is unhealthy.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [] + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "urlIndex" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "0": { + "index": 0, + "text": "Primary" + } + }, + "type": "value" + }, + { + "options": { + "from": 1, + "result": { + "index": 1, + "text": "Fallback" + }, + "to": 999 + }, + "type": "range" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "options": { + "0": { + "color": "red", + "index": 2, + "text": "Unhealthy" + }, + "10": { + "color": "green", + "index": 0, + "text": "Healthy" + } + }, + "type": "value" + }, + { + "options": { + "from": 1, + "result": { + "color": "yellow", + "index": 1, + "text": "Degraded" + }, + "to": 9 + }, + "type": "range" + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + } + ] + }, "gridPos": { "h": 4, "w": 12, "x": 12, "y": 5 }, - "id": 45, + "id": 44, "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false }, - "content": "_Validator metrics =D_", - "mode": "markdown" + "showHeader": false }, "pluginVersion": "10.1.1", "targets": [ @@ -529,10 +629,66 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "editorMode": "code", + "exemplar": false, + "expr": "vc_rest_api_client_urls_score", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false, "refId": "A" } ], - "type": "text" + "title": "Connected beacon nodes", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "client_name": true, + "group": true, + "host_type": true, + "instance": true, + "job": true, + "network": true, + "scrape_location": true + }, + "indexByName": { + "Time": 3, + "Value": 2, + "__name__": 4, + "baseUrl": 0, + "client_name": 5, + "group": 6, + "host_type": 7, + "instance": 8, + "job": 9, + "network": 10, + "scrape_location": 11, + "urlIndex": 1 + }, + "renameByName": { + "Value": "Status", + "baseUrl": "URL", + "urlIndex": "Type" + } + } + }, + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "field": "Type" + } + ] + } + } + ], + "type": "table" }, { "datasource": { @@ -1100,7 +1256,7 @@ "unit": "s" } }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.1.1", "reverseYBuckets": false, "targets": [ { @@ -1212,7 +1368,7 @@ "unit": "s" } }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.1.1", "reverseYBuckets": false, "targets": [ { @@ -1269,6 +1425,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1349,6 +1506,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1429,6 +1587,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineStyle": { "fill": "solid" @@ -1535,6 +1694,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1615,6 +1775,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1750,7 +1911,7 @@ "unit": "s" } }, - "pluginVersion": "9.3.2", + "pluginVersion": "10.1.1", "reverseYBuckets": false, "targets": [ { @@ -1806,6 +1967,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -1935,6 +2097,7 @@ "tooltip": false, "viz": false }, + "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, @@ -2013,6 +2176,170 @@ ], "title": "Duties reorgs / sec", "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-YlRd" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 73 + }, + "id": 46, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "60 * sum(rate(vc_rest_api_client_request_errors_total[$rate_interval])) by (baseUrl) > 0", + "instant": false, + "legendFormat": "{{baseUrl}}", + "range": true, + "refId": "A" + } + ], + "title": "REST API request errors (rate / min)", + "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": 20, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 73 + }, + "id": 45, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "60 * sum(rate(vc_rest_api_client_request_to_fallbacks_total[$rate_interval])) by (baseUrl) > 0", + "instant": false, + "legendFormat": "{{baseUrl}}", + "range": true, + "refId": "A" + } + ], + "title": "Requests to fallback nodes (rate / min)", + "type": "timeseries" } ], "refresh": "10s", diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index c26b849e98b9..21a56df6c160 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -42,6 +42,7 @@ markdown_extensions: - codehilite: guess_lang: false - admonition + - pymdownx.details - toc: permalink: true - pymdownx.superfences: @@ -60,9 +61,9 @@ extra_css: # Socials extra: social: - - icon: fontawesome/brands/github-alt + - icon: fontawesome/brands/github link: https://github.com/ChainSafe/lodestar - - icon: fontawesome/brands/twitter + - icon: fontawesome/brands/x-twitter link: https://twitter.com/lodestar_eth - icon: fontawesome/brands/discord link: https://discord.gg/yjyvFRP @@ -122,6 +123,7 @@ nav: # - Bug Reports: contribution/bug-reports.md - Dependency Graph: contribution/depgraph.md # - Repo: contribution/repo.md + - Dev CLI Reference: contribution/dev-cli.md - Testing: - Overview: contribution/testing/index.md # - Unit Tests: contribution/testing/unit-tests.md @@ -142,4 +144,5 @@ nav: # - Block Exploration: advanced-topics/block-exploration.md # - Slashing Protection: advanced-topics/slashing-protection.md - Setting Up a Testnet: advanced-topics/setting-up-a-testnet.md - # - Doppelganger Detection: advanced-topics/doppelganger-detection.md \ No newline at end of file + # - Doppelganger Detection: advanced-topics/doppelganger-detection.md + - Frequently Asked Questions: faqs.md diff --git a/docs/pages/advanced-topics/setting-up-a-testnet.md b/docs/pages/advanced-topics/setting-up-a-testnet.md index a6350b3a03de..4a57f6db423e 100644 --- a/docs/pages/advanced-topics/setting-up-a-testnet.md +++ b/docs/pages/advanced-topics/setting-up-a-testnet.md @@ -58,7 +58,7 @@ The exact ENR of node to connect to is then supplied via the `--bootnodes` flag. Once the second node starts, you should see an output similar to the following in either of the terminals: -``` +```txt Eph 167991/6 6.007 [] info: Searching peers - peers: 1 - slot: 5375718 (skipped 5375718) - head: 0 0xcc67…3345 - finalized: 0x0000…0000:0 ``` @@ -70,7 +70,7 @@ For example, making the request on the first node via the following command: will give a result similar to the following: -``` +```json { "data": [ { diff --git a/docs/pages/beacon-management/networking.md b/docs/pages/beacon-management/networking.md index 1afd80c9cea3..579fbdef8fe0 100644 --- a/docs/pages/beacon-management/networking.md +++ b/docs/pages/beacon-management/networking.md @@ -44,7 +44,7 @@ Note that bootnodes are announced via ENR. Lodestar prints out its own ENR on startup, the logs will show something similar to the following -``` +```txt info: discv5 worker started peerId=16Uiu...t9LQ3, initialENR=enr:-Iu4QGE...WRwgiMo, bindAddr4=/ip4/0.0.0.0/udp/9000 ``` diff --git a/docs/pages/contribution/testing/simulation-tests.md b/docs/pages/contribution/testing/simulation-tests.md index c1059e5c4177..e8e26ad83468 100644 --- a/docs/pages/contribution/testing/simulation-tests.md +++ b/docs/pages/contribution/testing/simulation-tests.md @@ -12,7 +12,7 @@ There are a number of sim tests that are available and each has a slightly diffe ### Environment Variables -To see what typical values for these are check out the `test-sim.yaml` workflow file in the `.github/workflows` directory. +To see what typical values for these are check out the `.env.test` file in the root directory. - `GETH_DOCKER_IMAGE`: The geth docker image that will be used - `NETHERMIND_IMAGE`: The nethermind docker image that will be used @@ -23,10 +23,7 @@ To see what typical values for these are check out the `test-sim.yaml` workflow The multi-fork sim test checks most of the functionality Lodestar provides. Is verifies that Lodestar is capable of peering, moving through all of the forks and using various sync methods in a testnet environment. Lodestar is tested with both Geth and Nethermind as the execution client. It also checks a Lighthouse/Geth node for cross client compatibility. ```sh -GETH_DOCKER_IMAGE=ethereum/client-go:v1.11.6 \ - LIGHTHOUSE_DOCKER_IMAGE=sigp/lighthouse:latest-amd64-modern-dev \ - NETHERMIND_DOCKER_IMAGE=nethermind/nethermind:1.18.0 \ - yarn workspace @chainsafe/lodestar test:sim:multifork +yarn workspace @chainsafe/lodestar test:sim:multifork ``` ### `test:sim:endpoints` @@ -34,8 +31,7 @@ GETH_DOCKER_IMAGE=ethereum/client-go:v1.11.6 \ This tests that various endpoints of the beacon node and validator client are working as expected. ```sh -GETH_DOCKER_IMAGE=ethereum/client-go:v1.11.6 \ - yarn workspace @chainsafe/lodestar test:sim:endpoints +yarn workspace @chainsafe/lodestar test:sim:endpoints ``` ### `test:sim:deneb` @@ -47,9 +43,7 @@ This test is still included in our CI but is no longer as important as it once w Checks that Lodestar is compatible with other consensus validators and vice-versa. All tests use Geth as the EL. ```sh -GETH_DOCKER_IMAGE=ethereum/client-go:v1.11.6 \ - LIGHTHOUSE_DOCKER_IMAGE=sigp/lighthouse:latest-amd64-modern-dev \ - yarn workspace @chainsafe/lodestar test:sim:mixedclient +yarn workspace @chainsafe/lodestar test:sim:mixedclient ``` ## Sim Test Infrastructure diff --git a/docs/pages/faqs.md b/docs/pages/faqs.md new file mode 100644 index 000000000000..65d719251bba --- /dev/null +++ b/docs/pages/faqs.md @@ -0,0 +1,23 @@ +# Frequently Asked Questions + +This section of the documentation will cover common questions and encounters often asked by users and developers. + +## Troubleshooting Lodestar + +### Using Kubernetes + + +???+ note "Unknown arguments error" + Lodestar reads all environment variables prefixed with `LODESTAR` and will try to parse + them similar to command line arguments, meaning any unknown argument will cause an error. + ``` + ✖ Unknown arguments: servicePort, servicePortEthConsensusP2p, + port9000Tcp, port9000TcpPort, port9000TcpProto, port9000TcpAddr, serviceHost + ``` + The extra arguments are present because Kubernetes automatically + [adds environment variables](https://kubernetes.io/docs/concepts/services-networking/service/#environment-variables) + to the Pod based on the name (`metadata.name`) defined in the associated `Service`. + To resolve the issue, this name has to be changed to something that does not start with `lodestar`. + + Reference Issue: [#6045](https://github.com/ChainSafe/lodestar/issues/6045) + diff --git a/docs/pages/getting-started/starting-a-node.md b/docs/pages/getting-started/starting-a-node.md index dd11381bde10..1641cd7d9115 100644 --- a/docs/pages/getting-started/starting-a-node.md +++ b/docs/pages/getting-started/starting-a-node.md @@ -47,7 +47,7 @@ Use the `--JsonRpc.JwtSecretFile /data/jwtsecret` flag to configure the secret. Use the `--engine-jwt-secret=` flag to configure the secret. Use their documentation [here](https://besu.hyperledger.org/en/stable/Reference/CLI/CLI-Syntax/#engine-jwt-secret). **For Erigon:** -Use the `--authrpc.jwtsecret` flag to configure the secret. Use their documentation [here](https://github.com/ledgerwatch/erigon#authentication-api). +Use the `--authrpc.jwtsecret` flag to configure the secret. Use their documentation [here](https://github.com/ledgerwatch/erigon?tab=readme-ov-file#beacon-chain-consensus-layer). ## Run a beacon node @@ -68,7 +68,7 @@ In case execution-layer clients are available at different locations, use `--exe Immediately you should see confirmation that the node has started -```bash +```txt pr-20 15:12:45.274[] info: Lodestar network=mainnet, version=v1.7.2, commit= Apr-20 15:12:45.327[] info: Connected to LevelDB database path=/data/mt1/chain-db Apr-20 15:12:57.747[] info: Initializing beacon from a valid db state slot=6264480, epoch=195765, stateRoot=0x8133cd4d0be59c3e94405f902fe0ad68ffaa5013b525dddb6285b91ad79716f6, isWithinWeakSubjectivityPeriod=true @@ -136,35 +136,35 @@ Lodestar beacon sync log aims to provide information of utmost importance about See the following example of different kinds of sync log: -``` +```txt Apr-20 15:24:08.034[] info: Searching peers - peers: 0 - slot: 6265018 - head: 6264018 0xed93…7b0a - exec-block: syncing(17088476 0x9649…) - finalized: 0xbf30…7e7c:195777 Apr-20 15:24:17.000[] info: Searching peers - peers: 0 - slot: 6265019 - head: 6264018 0xed93…7b0a - exec-block: syncing(17088476 0x9649…) - finalized: 0xbf30…7e7c:195777 ``` -``` +```txt Apr-20 15:13:41.298[] info: Syncing - 2.5 minutes left - 2.78 slots/s - slot: 6264966 - head: 6262966 0x5cec…f5b8 - exec-block: valid(17088105 0x6f74…) - finalized: 0x5cc0…3874:195764 - peers: 1 Apr-20 15:13:41.298[] info: Syncing - 2 minutes left - 2.78 slots/s - slot: 6264967 - head: 6263965 0x5cec…f5b8 - exec-block: valid(17088105 0x6f74…) - finalized: 0x5cc0…3874:195764 - peers: 1 ``` -``` +```txt Apr-20 15:13:53.151[] info: Syncing - 1.6 minutes left - 3.82 slots/s - slot: 6264967 - head: (slot -360) 0xe0cf…9f3c - exec-block: valid(17088167 0x2d6a…) - finalized: 0x8f3f…2f81:195766 - peers: 5 Apr-20 15:14:05.425[] info: Syncing - 1.1 minutes left - 4.33 slots/s - slot: 6264968 - head: (slot -297) 0x3655…1658 - exec-block: valid(17088231 0xdafd…) - finalized: 0x9475…425a:195769 - peers: 2 Apr-20 15:14:53.001[] info: Syncing - 9 seconds left - 5.00 slots/s - slot: 6264972 - head: (slot -45) 0x44e4…20a4 - exec-block: valid(17088475 0xca61…) - finalized: 0x9cbd…ba83:195776 - peers: 8 ``` -``` +```txt Apr-20 15:15:01.443[network] info: Subscribed gossip core topics Apr-20 15:15:01.446[sync] info: Subscribed gossip core topics Apr-20 15:15:05.000[] info: Synced - slot: 6264973 - head: 0x90ea…c655 - exec-block: valid(17088521 0xca9b…) - finalized: 0x6981…682f:195778 - peers: 6 Apr-20 15:15:17.003[] info: Synced - slot: 6264974 - head: 0x4f7e…0e3a - exec-block: valid(17088522 0x08b1…) - finalized: 0x6981…682f:195778 - peers: 6 ``` -``` +```txt Apr-20 15:15:41.001[] info: Synced - slot: 6264976 - head: (slot -1) 0x17c6…71a7 - exec-block: valid(17088524 0x5bc1…) - finalized: 0x6981…682f:195778 - peers: 8 Apr-20 15:15:53.001[] info: Synced - slot: 6264977 - head: (slot -2) 0x17c6…71a7 - exec-block: valid(17088524 0x5bc1…) - finalized: 0x6981…682f:195778 - peers: 8 ``` -``` +```txt Apr-20 15:16:05.000[] info: Synced - slot: 6264978 - head: 0xc9fd…28c5 - exec-block: valid(17088526 0xb5bf…) - finalized: 0x6981…682f:195778 - peers: 8 Apr-20 15:16:17.017[] info: Synced - slot: 6264979 - head: 0xde91…d4cb - exec-block: valid(17088527 0xa488…) - finalized: 0x6981…682f:195778 - peers: 7 ``` diff --git a/docs/pages/logging-and-metrics/prometheus-grafana.md b/docs/pages/logging-and-metrics/prometheus-grafana.md index 681e15d91ede..44f4fb6bd3c1 100644 --- a/docs/pages/logging-and-metrics/prometheus-grafana.md +++ b/docs/pages/logging-and-metrics/prometheus-grafana.md @@ -7,7 +7,7 @@ Prometheus is an open-source monitoring system with efficient time series databa To start, download Prometheus from https://prometheus.io/download/. Unzip the downloaded .zip file and run Prometheus from its installed location with the lodestar `prometheus.yml` passed in as the configuration file -``` +```sh ./prometheus --config.file=$dataDir/prometheus.yml ``` @@ -18,7 +18,7 @@ Unzip the downloaded .zip file and run Prometheus from its installed location wi Then run the Lodestar beacon node with -``` +```sh lodestar --metrics=true --metrics.port=8008 ``` diff --git a/docs/pages/reference/cli.md b/docs/pages/reference/cli.md index 1b57913b99fc..9bfd266aa51b 100644 --- a/docs/pages/reference/cli.md +++ b/docs/pages/reference/cli.md @@ -6,3 +6,4 @@ _**Welcome! This page has been moved. Please checkout our new docs layout from t - [Validator CLI](../validator-management/validator-cli.md) - [Bootnode CLI](../bootnode/bootnode-cli.md) - [Light Client CLI](../lightclient-prover/lightclient-cli.md) +- [Dev CLI](../contribution/dev-cli.md) diff --git a/docs/pages/validator-management/vc-configuration.md b/docs/pages/validator-management/vc-configuration.md index a1077997cd77..4853bca5bfa6 100644 --- a/docs/pages/validator-management/vc-configuration.md +++ b/docs/pages/validator-management/vc-configuration.md @@ -97,6 +97,7 @@ With produceBlockV3 (enabled automatically after the Deneb hard fork), the `--bu With Lodestar's `--builder.selection` validator options, you can select: - `maxprofit`: Default setting for Lodestar set at `--builder.boostFactor=100`. This default setting will always choose the more profitable block. Using this option, you may customize your `--builder.boostFactor` to your preference. Examples of its usage are below. +- `executionalways`: An alias of `--builder.boostFactor=0`, which will select the local execution block, unless it fails to produce due to an error or a delay in the response from the execution client. - `executiononly`: Beacon node will be requested to produce local execution block even if builder relays are configured. This option will always select the local execution block and will error if it couldn't produce one. - `builderalways`: An alias of `--builder.boostFactor=18446744073709551615` (2**64 - 1), which will select the builder block, unless the builder block fails to produce. The builder block may fail to produce if it's not available, not timely or there is an indication of censorship via `shouldOverrideBuilder` from the execution payload response. - `builderonly`: Generally used for distributed validators (DVs). No execution block production will be triggered. Therefore, if a builder block is not produced, the API will fail and _no block will be produced_. @@ -131,7 +132,7 @@ To start a Lodestar validator run the command: You should see confirmation that modules have started. -``` +```txt Nov-29 10:47:13.647[] info: Lodestar network=sepolia, version=v1.2.2/f093b46, commit=f093b468ec3ab0dbbe8e2d2c8175f52ad88aa35f Nov-29 10:47:13.649[] info: Connecting to LevelDB database path=/home/user/.local/share/lodestar/sepolia/validator-db Nov-29 10:47:51.732[] info: 3 local keystores diff --git a/docs/requirements.txt b/docs/requirements.txt index 18befdfcbe5e..755759c0ace3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ -mkdocs>=1.2 -mkdocs-material>=7.0 -Pygments>=2.4 -markdown>=3.2 -pymdown-extensions>=7.0 -mkdocs-mermaid2-plugin>=0.4 \ No newline at end of file +mkdocs==1.5.3 +mkdocs-material==9.5.9 +Pygments==2.17.2 +markdown==3.5.2 +pymdown-extensions==10.7 +mkdocs-mermaid2-plugin==1.1.1 diff --git a/lerna.json b/lerna.json index 5362d02551d8..a79413a21c37 100644 --- a/lerna.json +++ b/lerna.json @@ -4,7 +4,7 @@ ], "npmClient": "yarn", "useNx": true, - "version": "1.15.1", + "version": "1.16.0", "stream": true, "command": { "version": { diff --git a/lodestar b/lodestar index 0b20ba4c9bcd..7cf5301e4de4 100755 --- a/lodestar +++ b/lodestar @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # Convenience script to run the lodestar binary from built source # diff --git a/package.json b/package.json index 93b364359a38..38c78a7456f7 100644 --- a/package.json +++ b/package.json @@ -13,14 +13,13 @@ "build": "lerna run build", "build:watch": "lerna exec --parallel -- 'yarn run build:watch'", "build:ifchanged": "lerna exec -- ../../scripts/build_if_changed.sh", - "lint": "eslint --color --ext .ts packages/*/src packages/*/test", + "lint": "eslint --report-unused-disable-directives --color --ext .ts packages/*/src packages/*/test", "lint:fix": "yarn lint --fix", "lint-dashboards": "node scripts/lint-grafana-dashboards.mjs ./dashboards", "check-build": "lerna run check-build", "check-readme": "lerna run check-readme", "check-types": "lerna run check-types", "check-spelling": "pyspelling -c .pyspelling.yml -v", - "coverage": "lerna run coverage", "docs:install": "pip install --user -r docs/requirements.txt", "docs:build": "lerna run check-readme && lerna run docs:build && ./scripts/prepare-docs.sh", "docs:lint": "prettier '**/*.md' --check", @@ -31,6 +30,7 @@ "test:browsers": "lerna run test:browsers", "test:e2e": "lerna run test:e2e --concurrency 1", "test:e2e:sim": "lerna run test:e2e:sim", + "download-spec-tests": "lerna run download-spec-tests", "test:spec": "lerna run test:spec", "test-coverage:unit": "c8 --config .c8rc.json --report-dir coverage/unit/ --all npm run test:unit", "test-coverage:browsers": "c8 --config .c8rc.json --report-dir coverage/browsers/ --all npm run test:browsers", @@ -38,29 +38,30 @@ "test-coverage:e2e-sim": "c8 --config .c8rc.json --report-dir coverage/e2e-sim/ --all npm run test:e2e:sim", "test-coverage:spec": "c8 --config .c8rc.json --report-dir coverage/spec/ --all npm run test:spec", "benchmark": "yarn benchmark:files 'packages/*/test/perf/**/*.test.ts'", - "benchmark:files": "LODESTAR_PRESET=mainnet NODE_OPTIONS='--max-old-space-size=4096 --loader=ts-node/esm' benchmark --config .benchrc.yaml --defaultBranch unstable", + "benchmark:files": "NODE_OPTIONS='--max-old-space-size=4096 --loader=ts-node/esm' benchmark --config .benchrc.yaml --defaultBranch unstable", "release:create-rc": "node scripts/release/create_rc.mjs", "release:tag-rc": "node scripts/release/tag_rc.mjs", "release:tag-stable": "node scripts/release/tag_stable.mjs", "release:publish": "lerna publish from-package --yes --no-verify-access" }, "devDependencies": { + "@actions/core": "^1.10.1", "@chainsafe/eslint-plugin-node": "^11.2.3", "@dapplion/benchmark": "^0.2.4", "@types/mocha": "^10.0.6", "@types/node": "^20.6.5", - "@typescript-eslint/eslint-plugin": "6.7.2", - "@typescript-eslint/parser": "6.7.2", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "@vitest/coverage-v8": "^1.2.1", "@vitest/browser": "^1.2.1", - "codecov": "^3.8.3", "crypto-browserify": "^3.12.0", + "dotenv": "^16.4.1", "electron": "^26.2.2", - "eslint": "^8.50.0", + "eslint": "^8.56.0", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-vitest": "^0.3.20", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-vitest": "^0.3.22", "https-browserify": "^1.0.0", "jsdom": "^23.0.1", "lerna": "^7.3.0", diff --git a/packages/api/README.md b/packages/api/README.md index 79658fda65f7..e8837961159b 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -45,7 +45,7 @@ You will need to go over the [specification](https://github.com/ethereum/beacon- ## Getting started - Follow the [installation guide](https://chainsafe.github.io/lodestar/) to install Lodestar. -- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/usage/local). +- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/advanced-topics/setting-up-a-testnet/). ## Contributors diff --git a/packages/api/package.json b/packages/api/package.json index e9fa0936c3f8..db84e9618ad6 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -60,21 +60,19 @@ "build:release": "yarn clean && yarn run build", "check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"", "check-types": "tsc", - "coverage": "codecov -F lodestar-api", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", - "test": "yarn test:unit && yarn test:e2e", + "test": "yarn test:unit", "test:unit": "vitest --run --dir test/unit/", "check-readme": "typescript-docs-verifier" }, "dependencies": { "@chainsafe/persistent-merkle-tree": "^0.6.1", "@chainsafe/ssz": "^0.14.0", - "@lodestar/config": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/types": "^1.15.1", - "@lodestar/utils": "^1.15.1", + "@lodestar/config": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/types": "^1.16.0", + "@lodestar/utils": "^1.16.0", "eventsource": "^2.0.2", "qs": "^6.11.1" }, diff --git a/packages/api/src/beacon/client/beacon.ts b/packages/api/src/beacon/client/beacon.ts index 7a92afe15c6f..ef1e1983577d 100644 --- a/packages/api/src/beacon/client/beacon.ts +++ b/packages/api/src/beacon/client/beacon.ts @@ -11,7 +11,7 @@ export function getClient(config: ChainForkConfig, httpClient: IHttpClient): Api const reqSerializers = getReqSerializers(config); const returnTypes = getReturnTypes(); // Some routes return JSON, use a client auto-generator - const client = generateGenericJsonClient(routesData, reqSerializers, returnTypes, httpClient); + const client = generateGenericJsonClient(routesData, reqSerializers, returnTypes, httpClient) as Api; const fetchOptsSerializer = getFetchOptsSerializers(routesData, reqSerializers); return { diff --git a/packages/api/src/beacon/routes/beacon/block.ts b/packages/api/src/beacon/routes/beacon/block.ts index 95a32097028d..ae150ca6b3e8 100644 --- a/packages/api/src/beacon/routes/beacon/block.ts +++ b/packages/api/src/beacon/routes/beacon/block.ts @@ -229,8 +229,12 @@ export type Api = { * Retrieves BlobSidecar included in requested block. * @param blockId Block identifier. * Can be one of: "head" (canonical head in node's view), "genesis", "finalized", \, \. + * @param indices Array of indices for blob sidecars to request for in the specified block. Returns all blob sidecars in the block if not specified. */ - getBlobSidecars(blockId: BlockId): Promise< + getBlobSidecars( + blockId: BlockId, + indices?: number[] + ): Promise< ApiClientResponse<{ [HttpStatusCode.OK]: {executionOptimistic: ExecutionOptimistic; data: deneb.BlobSidecars}; }> @@ -270,7 +274,7 @@ export type ReqTypes = { publishBlockV2: {body: unknown; query: {broadcast_validation?: string}}; publishBlindedBlock: {body: unknown}; publishBlindedBlockV2: {body: unknown; query: {broadcast_validation?: string}}; - getBlobSidecars: BlockIdOnlyReq; + getBlobSidecars: {params: {block_id: string}; query: {indices?: number[]}}; }; export function getReqSerializers(config: ChainForkConfig): ReqSerializers { @@ -356,7 +360,17 @@ export function getReqSerializers(config: ChainForkConfig): ReqSerializers ({ + params: {block_id: String(block_id)}, + query: {indices}, + }), + parseReq: ({params, query}) => [params.block_id, query.indices], + schema: { + params: {block_id: Schema.StringRequired}, + query: {indices: Schema.UintArray}, + }, + }, }; } diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 41a8e2e9cad9..ddcc57104523 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -35,6 +35,10 @@ export enum EventType { attestation = "attestation", /** The node has received a valid voluntary exit (from P2P or API) */ voluntaryExit = "voluntary_exit", + /** The node has received a valid proposer slashing (from P2P or API) */ + proposerSlashing = "proposer_slashing", + /** The node has received a valid attester slashing (from P2P or API) */ + attesterSlashing = "attester_slashing", /** The node has received a valid blsToExecutionChange (from P2P or API) */ blsToExecutionChange = "bls_to_execution_change", /** Finalized checkpoint has been updated */ @@ -58,6 +62,8 @@ export const eventTypes: {[K in EventType]: K} = { [EventType.block]: EventType.block, [EventType.attestation]: EventType.attestation, [EventType.voluntaryExit]: EventType.voluntaryExit, + [EventType.proposerSlashing]: EventType.proposerSlashing, + [EventType.attesterSlashing]: EventType.attesterSlashing, [EventType.blsToExecutionChange]: EventType.blsToExecutionChange, [EventType.finalizedCheckpoint]: EventType.finalizedCheckpoint, [EventType.chainReorg]: EventType.chainReorg, @@ -85,6 +91,8 @@ export type EventData = { }; [EventType.attestation]: phase0.Attestation; [EventType.voluntaryExit]: phase0.SignedVoluntaryExit; + [EventType.proposerSlashing]: phase0.ProposerSlashing; + [EventType.attesterSlashing]: phase0.AttesterSlashing; [EventType.blsToExecutionChange]: capella.SignedBLSToExecutionChange; [EventType.finalizedCheckpoint]: { block: RootHex; @@ -174,6 +182,8 @@ export function getTypeByEvent(): {[K in EventType]: TypeJson} { [EventType.attestation]: ssz.phase0.Attestation, [EventType.voluntaryExit]: ssz.phase0.SignedVoluntaryExit, + [EventType.proposerSlashing]: ssz.phase0.ProposerSlashing, + [EventType.attesterSlashing]: ssz.phase0.AttesterSlashing, [EventType.blsToExecutionChange]: ssz.capella.SignedBLSToExecutionChange, [EventType.finalizedCheckpoint]: new ContainerType( diff --git a/packages/api/src/beacon/routes/validator.ts b/packages/api/src/beacon/routes/validator.ts index 09fbd9de3162..5b6fa7797d88 100644 --- a/packages/api/src/beacon/routes/validator.ts +++ b/packages/api/src/beacon/routes/validator.ts @@ -43,6 +43,7 @@ import {ExecutionOptimistic} from "./beacon/block.js"; export enum BuilderSelection { BuilderAlways = "builderalways", + ExecutionAlways = "executionalways", MaxProfit = "maxprofit", /** Only activate builder flow for DVT block proposal protocols */ BuilderOnly = "builderonly", diff --git a/packages/api/src/builder/client.ts b/packages/api/src/builder/client.ts index ed344b9c0e7f..381732b81433 100644 --- a/packages/api/src/builder/client.ts +++ b/packages/api/src/builder/client.ts @@ -1,11 +1,11 @@ import {ChainForkConfig} from "@lodestar/config"; -import {IHttpClient, generateGenericJsonClient} from "../utils/client/index.js"; +import {IHttpClient, generateGenericJsonClient, ApiWithExtraOpts} from "../utils/client/index.js"; import {Api, ReqTypes, routesData, getReqSerializers, getReturnTypes} from "./routes.js"; /** * REST HTTP client for builder routes */ -export function getClient(config: ChainForkConfig, httpClient: IHttpClient): Api { +export function getClient(config: ChainForkConfig, httpClient: IHttpClient): ApiWithExtraOpts { const reqSerializers = getReqSerializers(config); const returnTypes = getReturnTypes(); // All routes return JSON, use a client auto-generator diff --git a/packages/api/src/builder/index.ts b/packages/api/src/builder/index.ts index 76100fba2057..531ceac1b624 100644 --- a/packages/api/src/builder/index.ts +++ b/packages/api/src/builder/index.ts @@ -1,14 +1,19 @@ import {ChainForkConfig} from "@lodestar/config"; -import {HttpClient, HttpClientModules, HttpClientOptions, IHttpClient} from "../utils/client/httpClient.js"; -import {Api} from "./routes.js"; +import { + HttpClient, + HttpClientModules, + HttpClientOptions, + IHttpClient, + ApiWithExtraOpts, +} from "../utils/client/index.js"; +import {Api as BuilderApi} from "../builder/routes.js"; import * as builder from "./client.js"; // NOTE: Don't export server here so it's not bundled to all consumers -export type {Api}; - // Note: build API does not have namespaces as routes are declared at the "root" namespace +export type Api = ApiWithExtraOpts; type ClientModules = HttpClientModules & { config: ChainForkConfig; httpClient?: IHttpClient; diff --git a/packages/api/src/utils/client/client.ts b/packages/api/src/utils/client/client.ts index a88b68553898..430d302a070c 100644 --- a/packages/api/src/utils/client/client.ts +++ b/packages/api/src/utils/client/client.ts @@ -5,10 +5,17 @@ import {APIClientHandler} from "../../interfaces.js"; import {FetchOpts, HttpError, IHttpClient} from "./httpClient.js"; import {HttpStatusCode} from "./httpStatusCode.js"; -// See /packages/api/src/routes/index.ts for reasoning - /* eslint-disable @typescript-eslint/no-explicit-any */ +type ExtraOpts = {retryAttempts?: number}; +type ParametersWithOptionalExtraOpts any> = [...Parameters, ExtraOpts] | Parameters; + +export type ApiWithExtraOpts> = { + [K in keyof T]: (...args: ParametersWithOptionalExtraOpts) => ReturnType; +}; + +// See /packages/api/src/routes/index.ts for reasoning + /** * Format FetchFn opts from Fn arguments given a route definition and request serializer. * For routes that return only JSOn use @see getGenericJsonClient @@ -58,22 +65,38 @@ export function generateGenericJsonClient< reqSerializers: ReqSerializers, returnTypes: ReturnTypes, fetchFn: IHttpClient -): Api { +): ApiWithExtraOpts { return mapValues(routesData, (routeDef, routeId) => { const fetchOptsSerializer = getFetchOptsSerializer(routeDef, reqSerializers[routeId], routeId as string); const returnType = returnTypes[routeId as keyof ReturnTypes] as TypeJson | null; - return async function request(...args: Parameters): Promise> { + return async function request( + ...args: ParametersWithOptionalExtraOpts + ): Promise> { try { + // extract the extraOpts if provided + // + const argLen = (args as any[])?.length ?? 0; + const lastArg = (args as any[])[argLen] as ExtraOpts | undefined; + const retryAttempts = lastArg?.retryAttempts; + const extraOpts = {retryAttempts}; + if (returnType) { - const res = await fetchFn.json(fetchOptsSerializer(...args)); + // open extraOpts first if some serializer wants to add some overriding param + const res = await fetchFn.json({ + ...extraOpts, + ...fetchOptsSerializer(...(args as Parameters)), + }); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/return-await return {ok: true, response: returnType.fromJson(res.body), status: res.status} as ReturnType; } else { // We need to avoid parsing the response as the servers might just // response status 200 and close the request instead of writing an // empty json response. We return the status code. - const res = await fetchFn.request(fetchOptsSerializer(...args)); + const res = await fetchFn.request({ + ...extraOpts, + ...fetchOptsSerializer(...(args as Parameters)), + }); // eslint-disable-next-line @typescript-eslint/return-await return {ok: true, response: undefined, status: res.status} as ReturnType; @@ -98,5 +121,5 @@ export function generateGenericJsonClient< throw err; } }; - }) as unknown as Api; + }) as unknown as ApiWithExtraOpts; } diff --git a/packages/api/src/utils/client/format.ts b/packages/api/src/utils/client/format.ts index 7f12631f6877..141be4139eb0 100644 --- a/packages/api/src/utils/client/format.ts +++ b/packages/api/src/utils/client/format.ts @@ -1,11 +1,11 @@ -import qs from "qs"; +import {stringify as queryStringStringify} from "qs"; /** * Ethereum Beacon API requires the query with format: * - arrayFormat: repeat `topic=topic1&topic=topic2` */ export function stringifyQuery(query: unknown): string { - return qs.stringify(query, {arrayFormat: "repeat"}); + return queryStringStringify(query, {arrayFormat: "repeat"}); } /** diff --git a/packages/api/src/utils/client/httpClient.ts b/packages/api/src/utils/client/httpClient.ts index 674c6ddadbe8..ba2ea0c15498 100644 --- a/packages/api/src/utils/client/httpClient.ts +++ b/packages/api/src/utils/client/httpClient.ts @@ -1,4 +1,4 @@ -import {ErrorAborted, Logger, TimeoutError, isValidHttpUrl, toBase64} from "@lodestar/utils"; +import {ErrorAborted, Logger, TimeoutError, isValidHttpUrl, toBase64, retry} from "@lodestar/utils"; import {ReqGeneric, RouteDef} from "../index.js"; import {ApiClientResponse, ApiClientSuccessResponse} from "../../interfaces.js"; import {fetch, isFetchError} from "./fetch.js"; @@ -70,6 +70,7 @@ export type FetchOpts = { /** Optional, for metrics */ routeId?: string; timeoutMs?: number; + retryAttempts?: number; }; export interface IHttpClient { @@ -160,7 +161,7 @@ export class HttpClient implements IHttpClient { if (metrics) { metrics.urlsScore.addCollect(() => { for (let i = 0; i < this.urlsScore.length; i++) { - metrics.urlsScore.set({urlIndex: i}, this.urlsScore[i]); + metrics.urlsScore.set({urlIndex: i, baseUrl: this.urlsOpts[i].baseUrl}, this.urlsScore[i]); } }); } @@ -181,6 +182,30 @@ export class HttpClient implements IHttpClient { private async requestWithBodyWithRetries( opts: FetchOpts, getBody: (res: Response) => Promise + ): Promise<{status: HttpStatusCode; body: T}> { + if (opts.retryAttempts !== undefined) { + const routeId = opts.routeId ?? DEFAULT_ROUTE_ID; + + return retry( + async (_attempt) => { + return this.requestWithBodyWithFallbacks(opts, getBody); + }, + { + retries: opts.retryAttempts, + retryDelay: 200, + onRetry: (e, attempt) => { + this.logger?.debug("Retrying request", {routeId, attempt, lastError: e.message}); + }, + } + ); + } else { + return this.requestWithBodyWithFallbacks(opts, getBody); + } + } + + private async requestWithBodyWithFallbacks( + opts: FetchOpts, + getBody: (res: Response) => Promise ): Promise<{status: HttpStatusCode; body: T}> { // Early return when no fallback URLs are setup if (this.urlsOpts.length === 1) { @@ -211,7 +236,7 @@ export class HttpClient implements IHttpClient { const routeId = opts.routeId ?? DEFAULT_ROUTE_ID; if (i > 0) { - this.metrics?.requestToFallbacks.inc({routeId}); + this.metrics?.requestToFallbacks.inc({routeId, baseUrl}); this.logger?.debug("Requesting fallback URL", {routeId, baseUrl, score: this.urlsScore[i]}); } @@ -319,7 +344,7 @@ export class HttpClient implements IHttpClient { this.logger?.debug("HttpClient response", {routeId}); return {status: res.status, body}; } catch (e) { - this.metrics?.requestErrors.inc({routeId}); + this.metrics?.requestErrors.inc({routeId, baseUrl}); if (isAbortedError(e as Error)) { if (signalGlobal?.aborted) { diff --git a/packages/api/src/utils/client/metrics.ts b/packages/api/src/utils/client/metrics.ts index 65089e92e7ec..b326bdf3abb3 100644 --- a/packages/api/src/utils/client/metrics.ts +++ b/packages/api/src/utils/client/metrics.ts @@ -3,7 +3,7 @@ import {Gauge, GaugeExtra, Histogram} from "@lodestar/utils"; export type Metrics = { requestTime: Histogram<{routeId: string}>; streamTime: Histogram<{routeId: string}>; - requestErrors: Gauge<{routeId: string}>; - requestToFallbacks: Gauge<{routeId: string}>; - urlsScore: GaugeExtra<{urlIndex: number}>; + requestErrors: Gauge<{routeId: string; baseUrl: string}>; + requestToFallbacks: Gauge<{routeId: string; baseUrl: string}>; + urlsScore: GaugeExtra<{urlIndex: number; baseUrl: string}>; }; diff --git a/packages/api/src/utils/server/types.ts b/packages/api/src/utils/server/types.ts index 0e2d5201f51c..ba5f54cc96a3 100644 --- a/packages/api/src/utils/server/types.ts +++ b/packages/api/src/utils/server/types.ts @@ -1,6 +1,4 @@ -// eslint-disable-next-line import/no-extraneous-dependencies import type {FastifyInstance, FastifyContextConfig} from "fastify"; -// eslint-disable-next-line import/no-extraneous-dependencies import type * as fastify from "fastify"; import {ReqGeneric} from "../types.js"; diff --git a/packages/api/test/unit/beacon/genericServerTest/config.test.ts b/packages/api/test/unit/beacon/genericServerTest/config.test.ts index e11e4cbff6cb..3e9c001bffbf 100644 --- a/packages/api/test/unit/beacon/genericServerTest/config.test.ts +++ b/packages/api/test/unit/beacon/genericServerTest/config.test.ts @@ -9,9 +9,7 @@ import {testData} from "../testData/config.js"; /* eslint-disable @typescript-eslint/naming-convention */ describe("beacon / config", () => { - describe("Run generic server test", () => { - runGenericServerTest(config, getClient, getRoutes, testData); - }); + runGenericServerTest(config, getClient, getRoutes, testData); it("Serialize Partial Spec object", () => { const returnTypes = getReturnTypes(); diff --git a/packages/api/test/unit/beacon/genericServerTest/debug.test.ts b/packages/api/test/unit/beacon/genericServerTest/debug.test.ts index 6f7889677ec6..9a4c4bd71ef1 100644 --- a/packages/api/test/unit/beacon/genericServerTest/debug.test.ts +++ b/packages/api/test/unit/beacon/genericServerTest/debug.test.ts @@ -1,5 +1,6 @@ -import {describe, it, expect, MockInstance} from "vitest"; +import {describe, it, expect, MockInstance, beforeAll, afterAll} from "vitest"; import {toHexString} from "@chainsafe/ssz"; +import {FastifyInstance} from "fastify"; import {ssz} from "@lodestar/types"; import {config} from "@lodestar/config/default"; import {Api, ReqTypes, routesData} from "../../../../src/beacon/routes/debug.js"; @@ -13,19 +14,28 @@ import {testData} from "../testData/debug.js"; describe( "beacon / debug", - function () { - describe("Run generic server test", () => { - runGenericServerTest(config, getClient, getRoutes, testData); - }); + () => { + runGenericServerTest(config, getClient, getRoutes, testData); // Get state by SSZ describe("getState() in SSZ format", () => { - const {baseUrl, server} = getTestServer(); const mockApi = getMockApi(routesData); - for (const route of Object.values(getRoutes(config, mockApi))) { - registerRoute(server, route); - } + let baseUrl: string; + let server: FastifyInstance; + + beforeAll(async () => { + const res = getTestServer(); + server = res.server; + for (const route of Object.values(getRoutes(config, mockApi))) { + registerRoute(server, route); + } + baseUrl = await res.start(); + }); + + afterAll(async () => { + if (server !== undefined) await server.close(); + }); for (const method of ["getState" as const, "getStateV2" as const]) { it(method, async () => { diff --git a/packages/api/test/unit/beacon/genericServerTest/events.test.ts b/packages/api/test/unit/beacon/genericServerTest/events.test.ts index 3e1c02396710..2d6c9462c1fd 100644 --- a/packages/api/test/unit/beacon/genericServerTest/events.test.ts +++ b/packages/api/test/unit/beacon/genericServerTest/events.test.ts @@ -1,4 +1,5 @@ -import {describe, it, expect, beforeEach, afterEach} from "vitest"; +import {describe, it, expect, beforeEach, afterEach, beforeAll, afterAll} from "vitest"; +import {FastifyInstance} from "fastify"; import {sleep} from "@lodestar/utils"; import {Api, routesData, EventType, BeaconEvent} from "../../../../src/beacon/routes/events.js"; import {getClient} from "../../../../src/beacon/client/events.js"; @@ -8,11 +9,23 @@ import {getMockApi, getTestServer} from "../../../utils/utils.js"; import {eventTestData} from "../testData/events.js"; describe("beacon / events", () => { - const {baseUrl, server} = getTestServer(); const mockApi = getMockApi(routesData); - for (const route of Object.values(getRoutes(mockApi))) { - registerRoute(server, route); - } + let server: FastifyInstance; + let baseUrl: string; + + beforeAll(async () => { + const res = getTestServer(); + server = res.server; + for (const route of Object.values(getRoutes(mockApi))) { + registerRoute(server, route); + } + + baseUrl = await res.start(); + }); + + afterAll(async () => { + if (server !== undefined) await server.close(); + }); let controller: AbortController; beforeEach(() => { diff --git a/packages/api/test/unit/beacon/oapiSpec.test.ts b/packages/api/test/unit/beacon/oapiSpec.test.ts index 15a10bfeb6f7..f7d6cb9a077c 100644 --- a/packages/api/test/unit/beacon/oapiSpec.test.ts +++ b/packages/api/test/unit/beacon/oapiSpec.test.ts @@ -130,12 +130,6 @@ const ignoredProperties: Record = { */ getHealth: {request: ["query.syncing_status"]}, - /** - * https://github.com/ChainSafe/lodestar/issues/6185 - * - must have required property 'query' - */ - getBlobSidecars: {request: ["query"]}, - /* https://github.com/ChainSafe/lodestar/issues/4638 /query - must have required property 'skip_randao_verification' diff --git a/packages/api/test/unit/beacon/testData/beacon.ts b/packages/api/test/unit/beacon/testData/beacon.ts index 7fa8368c590b..6d6bc6576f56 100644 --- a/packages/api/test/unit/beacon/testData/beacon.ts +++ b/packages/api/test/unit/beacon/testData/beacon.ts @@ -71,7 +71,7 @@ export const testData: GenericServerTestCases = { res: undefined, }, getBlobSidecars: { - args: ["head"], + args: ["head", [0]], res: {executionOptimistic: true, data: ssz.deneb.BlobSidecars.defaultValue()}, }, diff --git a/packages/api/test/unit/beacon/testData/events.ts b/packages/api/test/unit/beacon/testData/events.ts index 7bfac9a59a88..f4962bc1827a 100644 --- a/packages/api/test/unit/beacon/testData/events.ts +++ b/packages/api/test/unit/beacon/testData/events.ts @@ -5,7 +5,7 @@ import {GenericServerTestCases} from "../../../utils/genericServerTest.js"; const abortController = new AbortController(); -/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/naming-convention */ +/* eslint-disable @typescript-eslint/naming-convention */ export const testData: GenericServerTestCases = { eventstream: { @@ -48,6 +48,68 @@ export const eventTestData: EventData = { signature: "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505", }), + [EventType.proposerSlashing]: ssz.phase0.ProposerSlashing.fromJson({ + signed_header_1: { + message: { + slot: "0", + proposer_index: "0", + parent_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + state_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + body_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + signed_header_2: { + message: { + slot: "0", + proposer_index: "0", + parent_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + state_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + body_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + }), + [EventType.attesterSlashing]: ssz.phase0.AttesterSlashing.fromJson({ + attestation_1: { + attesting_indices: ["0", "1"], + data: { + slot: "0", + index: "0", + beacon_block_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + source: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + target: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + }, + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + attestation_2: { + attesting_indices: ["0", "1"], + data: { + slot: "0", + index: "0", + beacon_block_root: "0x0000000000000000000000000000000000000000000000000000000000000000", + source: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + target: { + epoch: "0", + root: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, + }, + signature: + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + }, + }), [EventType.blsToExecutionChange]: ssz.capella.SignedBLSToExecutionChange.fromJson({ message: { validator_index: "1", diff --git a/packages/api/test/unit/client/httpClient.test.ts b/packages/api/test/unit/client/httpClient.test.ts index b22727d6a22b..c82b879bdcf5 100644 --- a/packages/api/test/unit/client/httpClient.test.ts +++ b/packages/api/test/unit/client/httpClient.test.ts @@ -1,6 +1,6 @@ import {IncomingMessage} from "node:http"; import {describe, it, afterEach, expect} from "vitest"; -import fastify, {RouteOptions} from "fastify"; +import {RouteOptions, fastify} from "fastify"; import {ErrorAborted, TimeoutError, toBase64} from "@lodestar/utils"; import {HttpClient, HttpError} from "../../../src/utils/client/index.js"; import {HttpStatusCode} from "../../../src/utils/client/httpStatusCode.js"; diff --git a/packages/api/test/utils/checkAgainstSpec.ts b/packages/api/test/utils/checkAgainstSpec.ts index ed65279bca22..c887f66e95e6 100644 --- a/packages/api/test/utils/checkAgainstSpec.ts +++ b/packages/api/test/utils/checkAgainstSpec.ts @@ -199,12 +199,21 @@ function prettyAjvErrors(errors: ErrorObject[] | null | undefined): string { return errors.map((e) => `${e.instancePath ?? "."} - ${e.message}`).join("\n"); } +type StringifiedProperty = string | StringifiedProperty[]; + +function stringifyProperty(value: unknown): StringifiedProperty { + if (typeof value === "number") { + return value.toString(10); + } else if (Array.isArray(value)) { + return value.map(stringifyProperty); + } + return String(value); +} + function stringifyProperties(obj: Record): Record { for (const key of Object.keys(obj)) { const value = obj[key]; - if (typeof value === "number") { - obj[key] = value.toString(10); - } + obj[key] = stringifyProperty(value); } return obj; diff --git a/packages/api/test/utils/genericServerTest.ts b/packages/api/test/utils/genericServerTest.ts index f0f805b7469a..4cd0263aaea8 100644 --- a/packages/api/test/utils/genericServerTest.ts +++ b/packages/api/test/utils/genericServerTest.ts @@ -1,4 +1,5 @@ -import {it, expect, MockInstance} from "vitest"; +import {it, expect, MockInstance, describe, beforeAll, afterAll} from "vitest"; +import {FastifyInstance} from "fastify"; import {ChainForkConfig} from "@lodestar/config"; import {ReqGeneric, Resolves} from "../../src/utils/index.js"; import {FetchOpts, HttpClient, IHttpClient} from "../../src/utils/client/index.js"; @@ -28,26 +29,39 @@ export function runGenericServerTest< testCases: GenericServerTestCases ): void { const mockApi = getMockApi(testCases); - const {baseUrl, server} = getTestServer(); + let server: FastifyInstance; - const httpClient = new HttpClientSpy({baseUrl}); - const client = getClient(config, httpClient); + let client: Api; + let httpClient: HttpClientSpy; - for (const route of Object.values(getRoutes(config, mockApi))) { - registerRoute(server, route); - } + beforeAll(async () => { + const res = getTestServer(); + server = res.server; + + for (const route of Object.values(getRoutes(config, mockApi))) { + registerRoute(server, route); + } + + const baseUrl = await res.start(); + httpClient = new HttpClientSpy({baseUrl}); + client = getClient(config, httpClient); + }); - for (const key of Object.keys(testCases)) { - const routeId = key as keyof Api; - const testCase = testCases[routeId]; + afterAll(async () => { + if (server !== undefined) await server.close(); + }); + + describe("run generic server tests", () => { + it.each(Object.keys(testCases))("%s", async (key) => { + const routeId = key as keyof Api; + const testCase = testCases[routeId]; - it(routeId as string, async () => { // Register mock data for this route // TODO: Look for the type error (mockApi[routeId] as MockInstance).mockResolvedValue(testCases[routeId].res); // Do the call - const res = await (client[routeId] as APIClientHandler)(...(testCase.args as any[])); + const res = await client[routeId](...(testCase.args as any[])); // Use spy to assert argument serialization if (testCase.query) { @@ -64,7 +78,7 @@ export function runGenericServerTest< // Assert returned value is correct expect(res.response).toEqual(testCase.res); }); - } + }); } class HttpClientSpy extends HttpClient { diff --git a/packages/api/test/utils/utils.ts b/packages/api/test/utils/utils.ts index cca89c8e4fd5..b261ae54920f 100644 --- a/packages/api/test/utils/utils.ts +++ b/packages/api/test/utils/utils.ts @@ -1,16 +1,13 @@ -import {beforeAll, afterAll, MockedObject, vi} from "vitest"; -import qs from "qs"; -import fastify, {FastifyInstance} from "fastify"; +import {MockedObject, vi} from "vitest"; +import {parse as parseQueryString} from "qs"; +import {FastifyInstance, fastify} from "fastify"; import {mapValues} from "@lodestar/utils"; import {ServerApi} from "../../src/interfaces.js"; -export function getTestServer(): {baseUrl: string; server: FastifyInstance} { - const port = Math.floor(Math.random() * (65535 - 49152)) + 49152; - const baseUrl = `http://localhost:${port}`; - +export function getTestServer(): {server: FastifyInstance; start: () => Promise} { const server = fastify({ ajv: {customOptions: {coerceTypes: "array"}}, - querystringParser: (str) => qs.parse(str, {comma: true, parseArrays: false}), + querystringParser: (str) => parseQueryString(str, {comma: true, parseArrays: false}), }); server.addHook("onError", (request, reply, error, done) => { @@ -19,9 +16,9 @@ export function getTestServer(): {baseUrl: string; server: FastifyInstance} { done(); }); - beforeAll(async () => { - await new Promise((resolve, reject) => { - server.listen({port}, function (err, address) { + const start = (): Promise => + new Promise((resolve, reject) => { + server.listen({port: 0}, function (err, address) { if (err !== null && err != undefined) { reject(err); } else { @@ -29,13 +26,8 @@ export function getTestServer(): {baseUrl: string; server: FastifyInstance} { } }); }); - }); - - afterAll(async () => { - await server.close(); - }); - return {baseUrl, server}; + return {start, server}; } export function getMockApi>( diff --git a/packages/beacon-node/README.md b/packages/beacon-node/README.md index 2459cd5699be..12d1aca05e95 100644 --- a/packages/beacon-node/README.md +++ b/packages/beacon-node/README.md @@ -18,7 +18,7 @@ You will need to go over the [specification](https://github.com/ethereum/consens ## Getting started - Follow the [installation guide](https://chainsafe.github.io/lodestar/) to install Lodestar. -- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/usage/local). +- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/advanced-topics/setting-up-a-testnet/). ## Contributors diff --git a/packages/beacon-node/package.json b/packages/beacon-node/package.json index e2366bb86051..7608035cb440 100644 --- a/packages/beacon-node/package.json +++ b/packages/beacon-node/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -72,36 +72,35 @@ "build:release": "yarn clean && yarn run build", "check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"", "check-types": "tsc", - "coverage": "codecov -F lodestar", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "test": "yarn test:unit && yarn test:e2e", - "test:unit:minimal": "vitest --run --segfaultRetry 3 --dir test/unit/", - "test:unit:mainnet": "LODESTAR_PRESET=mainnet vitest --run --dir test/unit-mainnet", + "test:unit:minimal": "LODESTAR_PRESET=minimal vitest --run --segfaultRetry 3 --dir test/unit/", + "test:unit:mainnet": "LODESTAR_PRESET=mainnet vitest --run --segfaultRetry 3 --dir test/unit-mainnet", "test:unit": "wrapper() { yarn test:unit:minimal $@ && yarn test:unit:mainnet $@; }; wrapper", - "test:e2e": "LODESTAR_PRESET=minimal vitest --run --segfaultRetry 3 --config vitest.config.e2e.ts --dir test/e2e", + "test:e2e": "LODESTAR_PRESET=minimal vitest --run --segfaultRetry 3 --config vitest.e2e.config.ts --dir test/e2e", "test:sim": "vitest --run test/sim/**/*.test.ts", "test:sim:merge-interop": "vitest --run test/sim/merge-interop.test.ts", "test:sim:mergemock": "vitest --run test/sim/mergemock.test.ts", "test:sim:withdrawals": "vitest --run test/sim/withdrawal-interop.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 --config vitest.config.spec.ts --dir test/spec/bls/", - "test:spec:general": "vitest --run --config vitest.config.spec.ts --dir test/spec/general/", - "test:spec:minimal": "LODESTAR_PRESET=minimal vitest --run --config vitest.config.spec.ts --dir test/spec/presets/", - "test:spec:mainnet": "LODESTAR_PRESET=mainnet vitest --run --config vitest.config.spec.ts --dir test/spec/presets/", + "test:spec:bls": "vitest --run --config vitest.spec.config.ts --dir test/spec/bls/", + "test:spec:general": "vitest --run --config vitest.spec.config.ts --dir test/spec/general/", + "test:spec:minimal": "LODESTAR_PRESET=minimal vitest --run --config vitest.spec.config.ts --dir test/spec/presets/", + "test:spec:mainnet": "LODESTAR_PRESET=mainnet vitest --run --config vitest.spec.config.ts --dir test/spec/presets/", "test:spec": "yarn test:spec:bls && yarn test:spec:general && yarn test:spec:minimal && yarn test:spec:mainnet", "check-readme": "typescript-docs-verifier" }, "dependencies": { "@chainsafe/as-chacha20poly1305": "^0.1.0", - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/bls": "7.1.1", + "@chainsafe/as-sha256": "^0.4.1", + "@chainsafe/bls": "7.1.3", "@chainsafe/blst": "^0.2.9", - "@chainsafe/discv5": "^7.1.0", - "@chainsafe/enr": "^2.0.2", - "@chainsafe/libp2p-gossipsub": "^11.1.0", + "@chainsafe/discv5": "^9.0.0", + "@chainsafe/enr": "^3.0.0", + "@chainsafe/libp2p-gossipsub": "^11.2.1", + "@chainsafe/libp2p-identify": "^1.0.0", "@chainsafe/libp2p-noise": "^14.1.0", "@chainsafe/persistent-merkle-tree": "^0.6.1", "@chainsafe/prometheus-gc-stats": "^1.0.0", @@ -121,18 +120,18 @@ "@libp2p/peer-id-factory": "^4.0.3", "@libp2p/prometheus-metrics": "^3.0.10", "@libp2p/tcp": "9.0.10", - "@lodestar/api": "^1.15.1", - "@lodestar/config": "^1.15.1", - "@lodestar/db": "^1.15.1", - "@lodestar/fork-choice": "^1.15.1", - "@lodestar/light-client": "^1.15.1", - "@lodestar/logger": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/reqresp": "^1.15.1", - "@lodestar/state-transition": "^1.15.1", - "@lodestar/types": "^1.15.1", - "@lodestar/utils": "^1.15.1", - "@lodestar/validator": "^1.15.1", + "@lodestar/api": "^1.16.0", + "@lodestar/config": "^1.16.0", + "@lodestar/db": "^1.16.0", + "@lodestar/fork-choice": "^1.16.0", + "@lodestar/light-client": "^1.16.0", + "@lodestar/logger": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/reqresp": "^1.16.0", + "@lodestar/state-transition": "^1.16.0", + "@lodestar/types": "^1.16.0", + "@lodestar/utils": "^1.16.0", + "@lodestar/validator": "^1.16.0", "@multiformats/multiaddr": "^12.1.3", "@types/datastore-level": "^3.0.0", "buffer-xor": "^2.0.2", @@ -154,7 +153,7 @@ "systeminformation": "^5.17.12", "uint8-varint": "^2.0.2", "uint8arraylist": "^2.4.7", - "uint8arrays": "^4.0.9", + "uint8arrays": "^5.0.1", "xxhash-wasm": "1.0.2" }, "devDependencies": { diff --git a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts index f2e29f00fe57..79e258960b2c 100644 --- a/packages/beacon-node/src/api/impl/beacon/blocks/index.ts +++ b/packages/beacon-node/src/api/impl/beacon/blocks/index.ts @@ -8,7 +8,7 @@ import {BlockSource, getBlockInput, ImportBlockOpts, BlockInput} from "../../../ import {promiseAllMaybeAsync} from "../../../../util/promises.js"; import {isOptimisticBlock} from "../../../../util/forkChoice.js"; import {computeBlobSidecars} from "../../../../util/blobs.js"; -import {BlockError, BlockErrorCode} from "../../../../chain/errors/index.js"; +import {BlockError, BlockErrorCode, BlockGossipError} from "../../../../chain/errors/index.js"; import {OpSource} from "../../../../metrics/validatorMonitor.js"; import {NetworkEvent} from "../../../../network/index.js"; import {ApiModules} from "../../types.js"; @@ -83,6 +83,13 @@ export function getBeaconBlockApi({ try { await validateGossipBlock(config, chain, signedBlock, fork); } catch (error) { + if (error instanceof BlockGossipError && error.type.code === BlockErrorCode.ALREADY_KNOWN) { + chain.logger.debug("Ignoring known block during publishing", valLogMeta); + // Blocks might already be published by another node as part of a fallback setup or DVT cluster + // and can reach our node by gossip before the api. The error can be ignored and should not result in a 500 response. + return; + } + chain.logger.error("Gossip validations failed while publishing the block", valLogMeta, error as Error); chain.persistInvalidSszValue( chain.config.getForkTypes(slot).SignedBeaconBlock, @@ -179,6 +186,13 @@ export function getBeaconBlockApi({ const publishPromises = [ // Send the block, regardless of whether or not it is valid. The API // specification is very clear that this is the desired behaviour. + // + // i) Publish blobs and block before importing so that network can see them asap + // ii) publish blobs first because + // a) by the times nodes see block, they might decide to pull blobs + // b) they might require more hops to reach recipients in peerDAS kind of setup where + // blobs might need to hop between nodes because of partial subnet subscription + ...blobSidecars.map((blobSidecar) => () => network.publishBlobSidecar(blobSidecar)), () => network.publishBeaconBlock(signedBlock) as Promise, () => // there is no rush to persist block since we published it to gossip anyway @@ -191,7 +205,6 @@ export function getBeaconBlockApi({ } throw e; }), - ...blobSidecars.map((blobSidecar) => () => network.publishBlobSidecar(blobSidecar)), ]; await promiseAllMaybeAsync(publishPromises); }; @@ -406,7 +419,7 @@ export function getBeaconBlockApi({ await publishBlock(signedBlockOrContents, opts); }, - async getBlobSidecars(blockId) { + async getBlobSidecars(blockId, indices) { const {block, executionOptimistic} = await resolveBlockId(chain, blockId); const blockRoot = config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message); @@ -418,9 +431,10 @@ export function getBeaconBlockApi({ if (!blobSidecars) { throw Error(`blobSidecars not found in db for slot=${block.message.slot} root=${toHexString(blockRoot)}`); } + return { executionOptimistic, - data: blobSidecars, + data: indices ? blobSidecars.filter(({index}) => indices.includes(index)) : blobSidecars, }; }, }; diff --git a/packages/beacon-node/src/api/impl/validator/index.ts b/packages/beacon-node/src/api/impl/validator/index.ts index e795d1c5afad..553146a6cc58 100644 --- a/packages/beacon-node/src/api/impl/validator/index.ts +++ b/packages/beacon-node/src/api/impl/validator/index.ts @@ -35,7 +35,7 @@ import { phase0, } from "@lodestar/types"; import {ExecutionStatus} from "@lodestar/fork-choice"; -import {toHex, racePromisesWithCutoff, RaceEvent} from "@lodestar/utils"; +import {toHex, resolveOrRacePromises, prettyWeiToEth} from "@lodestar/utils"; import { AttestationError, AttestationErrorCode, @@ -57,7 +57,7 @@ import {getValidatorStatus} from "../beacon/state/utils.js"; import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js"; import {SCHEDULER_LOOKAHEAD_FACTOR} from "../../../chain/prepareNextSlot.js"; import {ChainEvent, CheckpointHex, CommonBlockBody} from "../../../chain/index.js"; -import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices} from "./utils.js"; +import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource} from "./utils.js"; /** * If the node is within this many epochs from the head, we declare it to be synced regardless of @@ -177,6 +177,35 @@ export function getValidatorApi({ return computeEpochAtSlot(getCurrentSlot(config, chain.genesisTime - MAX_API_CLOCK_DISPARITY_SEC)); } + function getBlockValueLogInfo( + block: {executionPayloadValue: bigint; consensusBlockValue: bigint}, + source?: ProducedBlockSource + ): Record { + const executionValue = block.executionPayloadValue; + const consensusValue = block.consensusBlockValue; + const totalValue = executionValue + consensusValue; + + if (source == null) { + return { + executionPayloadValue: prettyWeiToEth(executionValue), + consensusBlockValue: prettyWeiToEth(consensusValue), + blockTotalValue: prettyWeiToEth(totalValue), + }; + } else if (source === ProducedBlockSource.builder) { + return { + builderExecutionPayloadValue: prettyWeiToEth(executionValue), + builderConsensusBlockValue: prettyWeiToEth(consensusValue), + builderBlockTotalValue: prettyWeiToEth(totalValue), + }; + } else { + return { + engineExecutionPayloadValue: prettyWeiToEth(executionValue), + engineConsensusBlockValue: prettyWeiToEth(consensusValue), + engineBlockTotalValue: prettyWeiToEth(totalValue), + }; + } + } + /** * This function is called 1s before next epoch, usually at that time PrepareNextSlotScheduler finishes * so we should have checkpoint state, otherwise wait for up to the slot 1 of epoch. @@ -472,11 +501,28 @@ export function getValidatorApi({ chain.executionBuilder !== undefined && builderSelection !== routes.validator.BuilderSelection.ExecutionOnly; + // At any point either the builder or execution or both flows should be active. + // + // Ideally such a scenario should be prevented on startup, but proposerSettingsFile or keymanager + // configurations could cause a validator pubkey to have builder disabled with builder selection builder only + // (TODO: independently make sure such an options update is not successful for a validator pubkey) + // + // So if builder is disabled ignore builder selection of builder only if caused by user mistake + // https://github.com/ChainSafe/lodestar/issues/6338 + const isEngineEnabled = !isBuilderEnabled || builderSelection !== routes.validator.BuilderSelection.BuilderOnly; + + if (!isEngineEnabled && !isBuilderEnabled) { + throw Error( + `Internal Error: Neither builder nor execution proposal flow activated isBuilderEnabled=${isBuilderEnabled} builderSelection=${builderSelection}` + ); + } + const loggerContext = { fork, builderSelection, slot, isBuilderEnabled, + isEngineEnabled, strictFeeRecipientCheck, // winston logger doesn't like bigint builderBoostFactor: `${builderBoostFactor}`, @@ -490,196 +536,151 @@ export function getValidatorApi({ }); logger.debug("Produced common block body", loggerContext); + logger.verbose("Block production race (builder vs execution) starting", { + ...loggerContext, + cutoffMs: BLOCK_PRODUCTION_RACE_CUTOFF_MS, + timeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS, + }); + + // use abort controller to stop waiting for both block sources + const controller = new AbortController(); + // Start calls for building execution and builder blocks - const blindedBlockPromise = isBuilderEnabled - ? // can't do fee recipient checks as builder bid doesn't return feeRecipient as of now - produceBuilderBlindedBlock(slot, randaoReveal, graffiti, { + + const builderPromise = isBuilderEnabled + ? produceBuilderBlindedBlock(slot, randaoReveal, graffiti, { feeRecipient, + // can't do fee recipient checks as builder bid doesn't return feeRecipient as of now + strictFeeRecipientCheck: false, // skip checking and recomputing head in these individual produce calls skipHeadChecksAndUpdate: true, commonBlockBody, - }).catch((e) => { - logger.error("produceBuilderBlindedBlock failed to produce block", {slot}, e); - return null; }) - : null; - - const fullBlockPromise = - // At any point either the builder or execution or both flows should be active. - // - // Ideally such a scenario should be prevented on startup, but proposerSettingsFile or keymanager - // configurations could cause a validator pubkey to have builder disabled with builder selection builder only - // (TODO: independently make sure such an options update is not successful for a validator pubkey) - // - // So if builder is disabled ignore builder selection of builderonly if caused by user mistake - !isBuilderEnabled || builderSelection !== routes.validator.BuilderSelection.BuilderOnly - ? // TODO deneb: builderSelection needs to be figured out if to be done beacon side - // || builderSelection !== BuilderSelection.BuilderOnly - produceEngineFullBlockOrContents(slot, randaoReveal, graffiti, { - feeRecipient, - strictFeeRecipientCheck, - // skip checking and recomputing head in these individual produce calls - skipHeadChecksAndUpdate: true, - commonBlockBody, - }).catch((e) => { - logger.error("produceEngineFullBlockOrContents failed to produce block", {slot}, e); - return null; - }) - : null; - - let blindedBlock, fullBlock; - if (blindedBlockPromise !== null && fullBlockPromise !== null) { - // reference index of promises in the race - const promisesOrder = [ProducedBlockSource.builder, ProducedBlockSource.engine]; - [blindedBlock, fullBlock] = await racePromisesWithCutoff< - | ((routes.validator.ProduceBlockOrContentsRes | routes.validator.ProduceBlindedBlockRes) & { - shouldOverrideBuilder?: boolean; - }) - | null - >( - [blindedBlockPromise, fullBlockPromise], - BLOCK_PRODUCTION_RACE_CUTOFF_MS, - BLOCK_PRODUCTION_RACE_TIMEOUT_MS, - // Callback to log the race events for better debugging capability - (event: RaceEvent, delayMs: number, index?: number) => { - const eventRef = index !== undefined ? {source: promisesOrder[index]} : {}; - logger.verbose("Block production race (builder vs execution)", { - event, - ...eventRef, - delayMs, - cutoffMs: BLOCK_PRODUCTION_RACE_CUTOFF_MS, - timeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS, - slot, - }); - } - ); - if (blindedBlock instanceof Error) { - // error here means race cutoff exceeded - logger.error("Failed to produce builder block", {slot}, blindedBlock); - blindedBlock = null; - } - if (fullBlock instanceof Error) { - logger.error("Failed to produce execution block", {slot}, fullBlock); - fullBlock = null; - } - } else if (blindedBlockPromise !== null && fullBlockPromise === null) { - blindedBlock = await blindedBlockPromise; - fullBlock = null; - } else if (blindedBlockPromise === null && fullBlockPromise !== null) { - blindedBlock = null; - fullBlock = await fullBlockPromise; - } else { - throw Error( - `Internal Error: Neither builder nor execution proposal flow activated isBuilderEnabled=${isBuilderEnabled} builderSelection=${builderSelection}` - ); - } + : Promise.reject(new Error("Builder disabled")); - const builderPayloadValue = blindedBlock?.executionPayloadValue ?? BigInt(0); - const enginePayloadValue = fullBlock?.executionPayloadValue ?? BigInt(0); - const consensusBlockValueBuilder = blindedBlock?.consensusBlockValue ?? BigInt(0); - const consensusBlockValueEngine = fullBlock?.consensusBlockValue ?? BigInt(0); - - const blockValueBuilder = builderPayloadValue + consensusBlockValueBuilder; // Total block value is in wei - const blockValueEngine = enginePayloadValue + consensusBlockValueEngine; // Total block value is in wei - - let executionPayloadSource: ProducedBlockSource | null = null; - const shouldOverrideBuilder = fullBlock?.shouldOverrideBuilder ?? false; - - // handle the builder override case separately - if (shouldOverrideBuilder === true) { - executionPayloadSource = ProducedBlockSource.engine; - logger.info("Selected engine block as censorship suspected in builder blocks", { - // winston logger doesn't like bigint - enginePayloadValue: `${enginePayloadValue}`, - consensusBlockValueEngine: `${consensusBlockValueEngine}`, - blockValueEngine: `${blockValueEngine}`, - shouldOverrideBuilder, - slot, - }); - } else if (fullBlock && blindedBlock) { - switch (builderSelection) { - case routes.validator.BuilderSelection.MaxProfit: { + const enginePromise = isEngineEnabled + ? produceEngineFullBlockOrContents(slot, randaoReveal, graffiti, { + feeRecipient, + strictFeeRecipientCheck, + // skip checking and recomputing head in these individual produce calls + skipHeadChecksAndUpdate: true, + commonBlockBody, + }).then((engineBlock) => { + // Once the engine returns a block, in the event of either: + // - suspected builder censorship + // - builder boost factor set to 0 or builder selection `executionalways` + // we don't need to wait for builder block as engine block will always be selected if ( - // explicitly handle the two special values mentioned in spec for builder preferred / engine preferred - builderBoostFactor !== MAX_BUILDER_BOOST_FACTOR && - (builderBoostFactor === BigInt(0) || - blockValueEngine >= (blockValueBuilder * builderBoostFactor) / BigInt(100)) + engineBlock.shouldOverrideBuilder || + builderBoostFactor === BigInt(0) || + builderSelection === routes.validator.BuilderSelection.ExecutionAlways ) { - executionPayloadSource = ProducedBlockSource.engine; - } else { - executionPayloadSource = ProducedBlockSource.builder; + controller.abort(); } - break; - } + return engineBlock; + }) + : Promise.reject(new Error("Engine disabled")); - case routes.validator.BuilderSelection.ExecutionOnly: { - executionPayloadSource = ProducedBlockSource.engine; - break; - } + const [builder, engine] = await resolveOrRacePromises([builderPromise, enginePromise], { + resolveTimeoutMs: BLOCK_PRODUCTION_RACE_CUTOFF_MS, + raceTimeoutMs: BLOCK_PRODUCTION_RACE_TIMEOUT_MS, + signal: controller.signal, + }); - // For everything else just select the builder - default: { - executionPayloadSource = ProducedBlockSource.builder; - } - } - logger.info(`Selected executionPayloadSource=${executionPayloadSource} block`, { - builderSelection, - // winston logger doesn't like bigint - builderBoostFactor: `${builderBoostFactor}`, - enginePayloadValue: `${enginePayloadValue}`, - builderPayloadValue: `${builderPayloadValue}`, - consensusBlockValueEngine: `${consensusBlockValueEngine}`, - consensusBlockValueBuilder: `${consensusBlockValueBuilder}`, - blockValueEngine: `${blockValueEngine}`, - blockValueBuilder: `${blockValueBuilder}`, - shouldOverrideBuilder, - slot, - }); - } else if (fullBlock && !blindedBlock) { - executionPayloadSource = ProducedBlockSource.engine; - logger.info("Selected engine block: no builder block produced", { - // winston logger doesn't like bigint - enginePayloadValue: `${enginePayloadValue}`, - consensusBlockValueEngine: `${consensusBlockValueEngine}`, - blockValueEngine: `${blockValueEngine}`, - shouldOverrideBuilder, - slot, + if (builder.status === "pending" && engine.status === "pending") { + throw Error("Builder and engine both failed to produce the block within timeout"); + } + + if (builder.status === "rejected" && engine.status === "rejected") { + throw Error("Builder and engine both failed to produce the block"); + } + + if (engine.status === "rejected" && isEngineEnabled) { + logger.warn( + "Engine failed to produce the block", + { + ...loggerContext, + durationMs: engine.durationMs, + }, + engine.reason + ); + } + + if (builder.status === "rejected" && isBuilderEnabled) { + logger.warn( + "Builder failed to produce the block", + { + ...loggerContext, + durationMs: builder.durationMs, + }, + builder.reason + ); + } + + // handle shouldOverrideBuilder separately + if (engine.status === "fulfilled" && engine.value.shouldOverrideBuilder) { + logger.info("Selected engine block: censorship suspected in builder blocks", { + ...loggerContext, + durationMs: engine.durationMs, + shouldOverrideBuilder: engine.value.shouldOverrideBuilder, + ...getBlockValueLogInfo(engine.value), }); - } else if (blindedBlock && !fullBlock) { - executionPayloadSource = ProducedBlockSource.builder; + + return {...engine.value, executionPayloadBlinded: false, executionPayloadSource: ProducedBlockSource.engine}; + } + + if (builder.status === "fulfilled" && engine.status !== "fulfilled") { logger.info("Selected builder block: no engine block produced", { - // winston logger doesn't like bigint - builderPayloadValue: `${builderPayloadValue}`, - consensusBlockValueBuilder: `${consensusBlockValueBuilder}`, - blockValueBuilder: `${blockValueBuilder}`, - shouldOverrideBuilder, - slot, + ...loggerContext, + durationMs: builder.durationMs, + ...getBlockValueLogInfo(builder.value), }); + + return {...builder.value, executionPayloadBlinded: true, executionPayloadSource: ProducedBlockSource.builder}; } - if (executionPayloadSource === null) { - throw Error(`Failed to produce engine or builder block for slot=${slot}`); + if (engine.status === "fulfilled" && builder.status !== "fulfilled") { + logger.info("Selected engine block: no builder block produced", { + ...loggerContext, + durationMs: engine.durationMs, + ...getBlockValueLogInfo(engine.value), + }); + + return {...engine.value, executionPayloadBlinded: false, executionPayloadSource: ProducedBlockSource.engine}; } - if (executionPayloadSource === ProducedBlockSource.engine) { - return { - ...fullBlock, - executionPayloadBlinded: false, - executionPayloadSource, - } as routes.validator.ProduceBlockOrContentsRes & { - executionPayloadBlinded: false; - executionPayloadSource: ProducedBlockSource; - }; - } else { - return { - ...blindedBlock, - executionPayloadBlinded: true, - executionPayloadSource, - } as routes.validator.ProduceBlindedBlockRes & { - executionPayloadBlinded: true; - executionPayloadSource: ProducedBlockSource; - }; + if (engine.status === "fulfilled" && builder.status === "fulfilled") { + const executionPayloadSource = selectBlockProductionSource({ + builderBlockValue: builder.value.executionPayloadValue + builder.value.consensusBlockValue, + engineBlockValue: engine.value.executionPayloadValue + engine.value.consensusBlockValue, + builderBoostFactor, + builderSelection, + }); + + logger.info(`Selected ${executionPayloadSource} block`, { + ...loggerContext, + engineDurationMs: engine.durationMs, + ...getBlockValueLogInfo(engine.value, ProducedBlockSource.engine), + builderDurationMs: builder.durationMs, + ...getBlockValueLogInfo(builder.value, ProducedBlockSource.builder), + }); + + if (executionPayloadSource === ProducedBlockSource.engine) { + return { + ...engine.value, + executionPayloadBlinded: false, + executionPayloadSource, + }; + } else { + return { + ...builder.value, + executionPayloadBlinded: true, + executionPayloadSource, + }; + } } + + throw Error("Unreachable error occurred during the builder and execution block production"); }; const produceBlock: ServerApi["produceBlock"] = async function produceBlock( diff --git a/packages/beacon-node/src/api/impl/validator/utils.ts b/packages/beacon-node/src/api/impl/validator/utils.ts index 86ea223d59ab..8932f8951cf4 100644 --- a/packages/beacon-node/src/api/impl/validator/utils.ts +++ b/packages/beacon-node/src/api/impl/validator/utils.ts @@ -1,6 +1,8 @@ import {BeaconStateAllForks, computeSlotsSinceEpochStart} from "@lodestar/state-transition"; import {ATTESTATION_SUBNET_COUNT} from "@lodestar/params"; -import {BLSPubkey, CommitteeIndex, Slot, ValidatorIndex} from "@lodestar/types"; +import {routes} from "@lodestar/api"; +import {BLSPubkey, CommitteeIndex, ProducedBlockSource, Slot, ValidatorIndex} from "@lodestar/types"; +import {MAX_BUILDER_BOOST_FACTOR} from "@lodestar/validator"; export function computeSubnetForCommitteesAtSlot( slot: Slot, @@ -41,3 +43,31 @@ export function getPubkeysForIndices( return pubkeys; } + +export function selectBlockProductionSource({ + builderSelection, + engineBlockValue, + builderBlockValue, + builderBoostFactor, +}: { + builderSelection: routes.validator.BuilderSelection; + engineBlockValue: bigint; + builderBlockValue: bigint; + builderBoostFactor: bigint; +}): ProducedBlockSource { + switch (builderSelection) { + case routes.validator.BuilderSelection.ExecutionAlways: + case routes.validator.BuilderSelection.ExecutionOnly: + return ProducedBlockSource.engine; + + case routes.validator.BuilderSelection.MaxProfit: + return builderBoostFactor !== MAX_BUILDER_BOOST_FACTOR && + (builderBoostFactor === BigInt(0) || engineBlockValue >= (builderBlockValue * builderBoostFactor) / BigInt(100)) + ? ProducedBlockSource.engine + : ProducedBlockSource.builder; + + case routes.validator.BuilderSelection.BuilderAlways: + case routes.validator.BuilderSelection.BuilderOnly: + return ProducedBlockSource.builder; + } +} diff --git a/packages/beacon-node/src/api/rest/base.ts b/packages/beacon-node/src/api/rest/base.ts index 3ddb5354a897..f14937a49fa4 100644 --- a/packages/beacon-node/src/api/rest/base.ts +++ b/packages/beacon-node/src/api/rest/base.ts @@ -1,6 +1,6 @@ -import qs from "qs"; -import fastify, {FastifyInstance} from "fastify"; -import fastifyCors from "@fastify/cors"; +import {parse as parseQueryString} from "qs"; +import {FastifyInstance, fastify} from "fastify"; +import {fastifyCors} from "@fastify/cors"; import bearerAuthPlugin from "@fastify/bearer-auth"; import {RouteConfig} from "@lodestar/api/beacon/server"; import {ErrorAborted, Gauge, Histogram, Logger} from "@lodestar/utils"; @@ -48,7 +48,7 @@ export class RestApiServer { logger: false, ajv: {customOptions: {coerceTypes: "array"}}, querystringParser: (str) => - qs.parse(str, { + parseQueryString(str, { // Array as comma-separated values must be supported to be OpenAPI spec compliant comma: true, // Drop support for array query strings like `id[0]=1&id[1]=2&id[2]=3` as those are not required to diff --git a/packages/beacon-node/src/chain/archiver/archiveBlocks.ts b/packages/beacon-node/src/chain/archiver/archiveBlocks.ts index 27934a6bae1f..71fa9fb61b2e 100644 --- a/packages/beacon-node/src/chain/archiver/archiveBlocks.ts +++ b/packages/beacon-node/src/chain/archiver/archiveBlocks.ts @@ -35,7 +35,8 @@ export async function archiveBlocks( lightclientServer: LightClientServer, logger: Logger, finalizedCheckpoint: CheckpointHex, - currentEpoch: Epoch + currentEpoch: Epoch, + archiveBlobEpochs?: number ): Promise { // Use fork choice to determine the blocks to archive and delete // getAllAncestorBlocks response includes the finalized block, so it's also moved to the cold db @@ -81,19 +82,25 @@ export async function archiveBlocks( } // Delete expired blobs - // Keep only `[max(GENESIS_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS), current_epoch]` + // Keep only `[current_epoch - max(MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, archiveBlobEpochs)] + // if archiveBlobEpochs set to Infinity do not prune` if (finalizedPostDeneb) { - const blobSidecarsMinEpoch = currentEpoch - config.MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS; - if (blobSidecarsMinEpoch >= config.DENEB_FORK_EPOCH) { - const slotsToDelete = await db.blobSidecarsArchive.keys({lt: computeStartSlotAtEpoch(blobSidecarsMinEpoch)}); - if (slotsToDelete.length > 0) { - await db.blobSidecarsArchive.batchDelete(slotsToDelete); - logger.verbose( - `blobSidecars prune: batchDelete range ${slotsToDelete[0]}..${slotsToDelete[slotsToDelete.length - 1]}` - ); - } else { - logger.verbose(`blobSidecars prune: no entries before epoch ${blobSidecarsMinEpoch}`); + if (archiveBlobEpochs !== Infinity) { + const blobsArchiveWindow = Math.max(config.MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, archiveBlobEpochs ?? 0); + const blobSidecarsMinEpoch = currentEpoch - blobsArchiveWindow; + if (blobSidecarsMinEpoch >= config.DENEB_FORK_EPOCH) { + const slotsToDelete = await db.blobSidecarsArchive.keys({lt: computeStartSlotAtEpoch(blobSidecarsMinEpoch)}); + if (slotsToDelete.length > 0) { + await db.blobSidecarsArchive.batchDelete(slotsToDelete); + logger.verbose( + `blobSidecars prune: batchDelete range ${slotsToDelete[0]}..${slotsToDelete[slotsToDelete.length - 1]}` + ); + } else { + logger.verbose(`blobSidecars prune: no entries before epoch ${blobSidecarsMinEpoch}`); + } } + } else { + logger.verbose("blobSidecars pruning skipped: archiveBlobEpochs set to Infinity"); } } diff --git a/packages/beacon-node/src/chain/archiver/index.ts b/packages/beacon-node/src/chain/archiver/index.ts index 9c0290bfd8c4..ee0711e05e4b 100644 --- a/packages/beacon-node/src/chain/archiver/index.ts +++ b/packages/beacon-node/src/chain/archiver/index.ts @@ -11,6 +11,7 @@ const PROCESS_FINALIZED_CHECKPOINT_QUEUE_LEN = 256; export type ArchiverOpts = StatesArchiverOpts & { disableArchiveOnCheckpoint?: boolean; + archiveBlobEpochs?: number; }; type ProposalStats = { @@ -37,6 +38,7 @@ export class Archiver { private prevFinalized: CheckpointWithHex; private readonly statesArchiver: StatesArchiver; + private archiveBlobEpochs?: number; constructor( private readonly db: IBeaconDb, @@ -45,6 +47,7 @@ export class Archiver { signal: AbortSignal, opts: ArchiverOpts ) { + this.archiveBlobEpochs = opts.archiveBlobEpochs; this.statesArchiver = new StatesArchiver(chain.regen, db, logger, opts); this.prevFinalized = chain.forkChoice.getFinalizedCheckpoint(); this.jobQueue = new JobItemQueue<[CheckpointWithHex], void>(this.processFinalizedCheckpoint, { @@ -96,7 +99,8 @@ export class Archiver { this.chain.lightClientServer, this.logger, finalized, - this.chain.clock.currentEpoch + this.chain.clock.currentEpoch, + this.archiveBlobEpochs ); this.prevFinalized = finalized; diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 89ed52b66750..0f1f2a7890d9 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -67,6 +67,7 @@ export async function importBlock( const parentEpoch = computeEpochAtSlot(parentBlockSlot); const prevFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch; const blockDelaySec = (fullyVerifiedBlock.seenTimestampSec - postState.genesisTime) % this.config.SECONDS_PER_SLOT; + const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); // 1. Persist block to hot DB (pre-emptively) // If eagerPersistBlock = true we do that in verifyBlocksInEpoch to batch all I/O operations to save block time to head @@ -414,6 +415,16 @@ export async function importBlock( this.emitter.emit(routes.events.EventType.attestation, attestation); } } + if (this.emitter.listenerCount(routes.events.EventType.attesterSlashing)) { + for (const attesterSlashing of block.message.body.attesterSlashings) { + this.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing); + } + } + if (this.emitter.listenerCount(routes.events.EventType.proposerSlashing)) { + for (const proposerSlashing of block.message.body.proposerSlashings) { + this.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing); + } + } } // Register stat metrics about the block after importing it @@ -437,9 +448,13 @@ export async function importBlock( }, 0); if (opts.seenTimestampSec !== undefined) { - const recvToImportedBlock = Date.now() / 1000 - opts.seenTimestampSec; - this.metrics?.gossipBlock.receivedToBlockImport.observe(recvToImportedBlock); - this.logger.verbose("Imported block", {slot: blockSlot, recvToImportedBlock}); + const recvToValidation = Date.now() / 1000 - opts.seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + this.metrics?.gossipBlock.blockImport.recvToValidation.observe(recvToValidation); + this.metrics?.gossipBlock.blockImport.validationTime.observe(validationTime); + + this.logger.debug("Imported block", {slot: blockSlot, recvToValLatency, recvToValidation, validationTime}); } this.logger.verbose("Block processed", { diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts index 91242d879f85..8f386ef191d2 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksExecutionPayloads.ts @@ -75,6 +75,7 @@ export async function verifyBlocksExecutionPayload( ): Promise { const executionStatuses: MaybeValidExecutionStatus[] = []; let mergeBlockFound: bellatrix.BeaconBlock | null = null; + const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); // Error in the same way as verifyBlocksSanityChecks if empty blocks if (blocks.length === 0) { @@ -246,11 +247,17 @@ export async function verifyBlocksExecutionPayload( const executionTime = Date.now(); if (blocks.length === 1 && opts.seenTimestampSec !== undefined && executionStatuses[0] === ExecutionStatus.Valid) { - const recvToVerifiedExecPayload = executionTime / 1000 - opts.seenTimestampSec; - chain.metrics?.gossipBlock.receivedToExecutionPayloadVerification.observe(recvToVerifiedExecPayload); - chain.logger.verbose("Verified execution payload", { + const recvToValidation = executionTime / 1000 - opts.seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + chain.metrics?.gossipBlock.executionPayload.recvToValidation.observe(recvToValidation); + chain.metrics?.gossipBlock.executionPayload.validationTime.observe(validationTime); + + chain.logger.debug("Verified execution payload", { slot: blocks[0].message.slot, - recvToVerifiedExecPayload, + recvToValLatency, + recvToValidation, + validationTime, }); } diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts index 14ad46a35c1e..ff0b19ee3edc 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksSignatures.ts @@ -22,6 +22,7 @@ export async function verifyBlocksSignatures( opts: ImportBlockOpts ): Promise<{verifySignaturesTime: number}> { const isValidPromises: Promise[] = []; + const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); // Verifies signatures after running state transition, so all SyncCommittee signed roots are known at this point. // We must ensure block.slot <= state.slot before running getAllBlockSignatureSets(). @@ -54,9 +55,18 @@ export async function verifyBlocksSignatures( const verifySignaturesTime = Date.now(); if (blocks.length === 1 && opts.seenTimestampSec !== undefined) { - const recvToSigVer = verifySignaturesTime / 1000 - opts.seenTimestampSec; - metrics?.gossipBlock.receivedToSignaturesVerification.observe(recvToSigVer); - logger.verbose("Verified block signatures", {slot: blocks[0].message.slot, recvToSigVer}); + const recvToValidation = verifySignaturesTime / 1000 - opts.seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + metrics?.gossipBlock.signatureVerification.recvToValidation.observe(recvToValidation); + metrics?.gossipBlock.signatureVerification.validationTime.observe(validationTime); + + logger.debug("Verified block signatures", { + slot: blocks[0].message.slot, + recvToValLatency, + recvToValidation, + validationTime, + }); } return {verifySignaturesTime}; diff --git a/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts b/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts index 7d15d4e4f6ce..62d5f6802533 100644 --- a/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +++ b/packages/beacon-node/src/chain/blocks/verifyBlocksStateTransitionOnly.ts @@ -31,6 +31,7 @@ export async function verifyBlocksStateTransitionOnly( ): Promise<{postStates: CachedBeaconStateAllForks[]; proposerBalanceDeltas: number[]; verifyStateTime: number}> { const postStates: CachedBeaconStateAllForks[] = []; const proposerBalanceDeltas: number[] = []; + const recvToValLatency = Date.now() / 1000 - (opts.seenTimestampSec ?? Date.now() / 1000); for (let i = 0; i < blocks.length; i++) { const {validProposerSignature, validSignatures} = opts; @@ -96,9 +97,13 @@ export async function verifyBlocksStateTransitionOnly( const verifyStateTime = Date.now(); if (blocks.length === 1 && opts.seenTimestampSec !== undefined) { const slot = blocks[0].block.message.slot; - const recvToTransition = verifyStateTime / 1000 - opts.seenTimestampSec; - metrics?.gossipBlock.receivedToStateTransition.observe(recvToTransition); - logger.verbose("Verified block state transition", {slot, recvToTransition}); + const recvToValidation = verifyStateTime / 1000 - opts.seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + metrics?.gossipBlock.stateTransition.recvToValidation.observe(recvToValidation); + metrics?.gossipBlock.stateTransition.validationTime.observe(validationTime); + + logger.debug("Verified block state transition", {slot, recvToValLatency, recvToValidation, validationTime}); } return {postStates, proposerBalanceDeltas, verifyStateTime}; diff --git a/packages/beacon-node/src/chain/emitter.ts b/packages/beacon-node/src/chain/emitter.ts index 8c02064b4a92..9c3ea4a9bf52 100644 --- a/packages/beacon-node/src/chain/emitter.ts +++ b/packages/beacon-node/src/chain/emitter.ts @@ -1,5 +1,5 @@ import {EventEmitter} from "events"; -import StrictEventEmitter from "strict-event-emitter-types"; +import {StrictEventEmitter} from "strict-event-emitter-types"; import {routes} from "@lodestar/api"; import {phase0} from "@lodestar/types"; diff --git a/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts b/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts index 00309d322a11..db7922758732 100644 --- a/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts +++ b/packages/beacon-node/src/chain/opPools/aggregatedAttestationPool.ts @@ -1,12 +1,6 @@ import bls from "@chainsafe/bls"; import {toHexString} from "@chainsafe/ssz"; -import { - ForkName, - MAX_ATTESTATIONS, - MIN_ATTESTATION_INCLUSION_DELAY, - SLOTS_PER_EPOCH, - TIMELY_SOURCE_FLAG_INDEX, -} from "@lodestar/params"; +import {ForkName, MAX_ATTESTATIONS, MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params"; import {phase0, Epoch, Slot, ssz, ValidatorIndex, RootHex} from "@lodestar/types"; import { CachedBeaconStateAllForks, @@ -24,9 +18,17 @@ import {InsertOutcome} from "./types.js"; type DataRootHex = string; +type CommitteeIndex = number; + type AttestationWithScore = {attestation: phase0.Attestation; score: number}; -type GetParticipationFn = (epoch: Epoch, committee: number[]) => Set | null; +/** + * This function returns not seen participation for a given epoch and committee. + * Return null if all validators are seen or no info to check. + */ +type GetNotSeenValidatorsFn = (epoch: Epoch, committee: number[]) => Set | null; + +type ValidateAttestationDataFn = (attData: phase0.AttestationData) => boolean; /** * Limit the max attestations with the same AttestationData. @@ -37,13 +39,13 @@ type GetParticipationFn = (epoch: Epoch, committee: number[]) => Set | n const MAX_RETAINED_ATTESTATIONS_PER_GROUP = 4; /** - * On mainnet, each slot has 64 committees, and each block has 128 attestations max so we don't - * want to store more than 2 per group. + * On mainnet, each slot has 64 committees, and each block has 128 attestations max so in average + * we get 2 attestation per groups. + * Starting from Jan 2024, we have a performance issue getting attestations for a block. Based on the + * fact that lot of groups will have only 1 attestation since it's full of participation increase this number + * a bit higher than average. This also help decrease number of slots to search for attestations. */ -const MAX_ATTESTATIONS_PER_GROUP = 2; - -/** Same to https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#has_flag */ -const TIMELY_SOURCE = 1 << TIMELY_SOURCE_FLAG_INDEX; +const MAX_ATTESTATIONS_PER_GROUP = 3; /** * Maintain a pool of aggregated attestations. Attestations can be retrieved for inclusion in a block @@ -52,19 +54,22 @@ const TIMELY_SOURCE = 1 << TIMELY_SOURCE_FLAG_INDEX; * Note that we want to remove attestations with attesters that were included in the chain. */ export class AggregatedAttestationPool { - private readonly attestationGroupByDataHashBySlot = new MapDef>( - () => new Map() - ); + private readonly attestationGroupByDataHashByIndexBySlot = new MapDef< + Slot, + Map> + >(() => new Map>()); private lowestPermissibleSlot = 0; /** For metrics to track size of the pool */ getAttestationCount(): {attestationCount: number; attestationDataCount: number} { let attestationCount = 0; let attestationDataCount = 0; - for (const attestationGroupByData of this.attestationGroupByDataHashBySlot.values()) { - attestationDataCount += attestationGroupByData.size; - for (const attestationGroup of attestationGroupByData.values()) { - attestationCount += attestationGroup.getAttestationCount(); + for (const attestationGroupByDataByIndex of this.attestationGroupByDataHashByIndexBySlot.values()) { + for (const attestationGroupByData of attestationGroupByDataByIndex.values()) { + attestationDataCount += attestationGroupByData.size; + for (const attestationGroup of attestationGroupByData.values()) { + attestationCount += attestationGroup.getAttestationCount(); + } } } return {attestationCount, attestationDataCount}; @@ -84,7 +89,12 @@ export class AggregatedAttestationPool { return InsertOutcome.Old; } - const attestationGroupByDataHash = this.attestationGroupByDataHashBySlot.getOrDefault(slot); + const attestationGroupByDataHashByIndex = this.attestationGroupByDataHashByIndexBySlot.getOrDefault(slot); + let attestationGroupByDataHash = attestationGroupByDataHashByIndex.get(attestation.data.index); + if (!attestationGroupByDataHash) { + attestationGroupByDataHash = new Map(); + attestationGroupByDataHashByIndex.set(attestation.data.index, attestationGroupByDataHash); + } let attestationGroup = attestationGroupByDataHash.get(dataRootHex); if (!attestationGroup) { attestationGroup = new MatchingDataAttestationGroup(committee, attestation.data); @@ -100,7 +110,7 @@ export class AggregatedAttestationPool { /** Remove attestations which are too old to be included in a block. */ prune(clockSlot: Slot): void { // Only retain SLOTS_PER_EPOCH slots - pruneBySlot(this.attestationGroupByDataHashBySlot, clockSlot, SLOTS_PER_EPOCH); + pruneBySlot(this.attestationGroupByDataHashByIndexBySlot, clockSlot, SLOTS_PER_EPOCH); this.lowestPermissibleSlot = Math.max(clockSlot - SLOTS_PER_EPOCH, 0); } @@ -112,15 +122,19 @@ export class AggregatedAttestationPool { const stateEpoch = state.epochCtx.epoch; const statePrevEpoch = stateEpoch - 1; - const getParticipation = getParticipationFn(state); + const notSeenValidatorsFn = getNotSeenValidatorsFn(state); + const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state); const attestationsByScore: AttestationWithScore[] = []; - const slots = Array.from(this.attestationGroupByDataHashBySlot.keys()).sort((a, b) => b - a); + const slots = Array.from(this.attestationGroupByDataHashByIndexBySlot.keys()).sort((a, b) => b - a); + let minScore = Number.MAX_SAFE_INTEGER; + let slotCount = 0; slot: for (const slot of slots) { - const attestationGroupByDataHash = this.attestationGroupByDataHashBySlot.get(slot); + slotCount++; + const attestationGroupByDataHashByIndex = this.attestationGroupByDataHashByIndexBySlot.get(slot); // should not happen - if (!attestationGroupByDataHash) { + if (!attestationGroupByDataHashByIndex) { throw Error(`No aggregated attestation pool for slot=${slot}`); } @@ -134,34 +148,64 @@ export class AggregatedAttestationPool { continue; // Invalid attestations } - const attestationGroups = Array.from(attestationGroupByDataHash.values()); - for (const attestationGroup of attestationGroups) { - if (!isValidAttestationData(forkChoice, state, attestationGroup.data)) { + const slotDelta = stateSlot - slot; + const shuffling = state.epochCtx.getShufflingAtEpoch(epoch); + const slotCommittees = shuffling.committees[slot % SLOTS_PER_EPOCH]; + for (const [committeeIndex, attestationGroupByData] of attestationGroupByDataHashByIndex.entries()) { + // all attestations will be validated against the state in next step so we can get committee from the state + // this is an improvement to save the notSeenValidatorsFn call for the same slot/index instead of the same attestation data + if (committeeIndex > slotCommittees.length) { + // invalid index, should not happen continue; } - const participation = getParticipation(epoch, attestationGroup.committee); - if (participation === null) { + + const committee = slotCommittees[committeeIndex]; + const notSeenAttestingIndices = notSeenValidatorsFn(epoch, committee); + if (notSeenAttestingIndices === null || notSeenAttestingIndices.size === 0) { + continue; + } + + if ( + slotCount > 2 && + attestationsByScore.length >= MAX_ATTESTATIONS && + notSeenAttestingIndices.size / slotDelta < minScore + ) { + // after 2 slots, there are a good chance that we have 2 * MAX_ATTESTATIONS attestations and break the for loop early + // if not, we may have to scan all slots in the pool + // if we have enough attestations and the max possible score is lower than scores of `attestationsByScore`, we should skip + // otherwise it takes time to check attestation, add it and remove it later after the sort by score continue; } - // TODO: Is it necessary to validateAttestation for: - // - Attestation committee index not within current committee count - // - Attestation aggregation bits length does not match committee length - // - // These properties should not change after being validate in gossip - // IF they have to be validated, do it only with one attestation per group since same data - // The committeeCountPerSlot can be precomputed once per slot - - attestationsByScore.push( - ...attestationGroup.getAttestationsForBlock(participation).map((attestation) => ({ - attestation: attestation.attestation, - score: attestation.notSeenAttesterCount / (stateSlot - slot), - })) - ); - - // Stop accumulating attestations there are enough that may have good scoring - if (attestationsByScore.length > MAX_ATTESTATIONS * 2) { - break slot; + for (const attestationGroup of attestationGroupByData.values()) { + if (!validateAttestationDataFn(attestationGroup.data)) { + continue; + } + + // TODO: Is it necessary to validateAttestation for: + // - Attestation committee index not within current committee count + // - Attestation aggregation bits length does not match committee length + // + // These properties should not change after being validate in gossip + // IF they have to be validated, do it only with one attestation per group since same data + // The committeeCountPerSlot can be precomputed once per slot + for (const {attestation, notSeenAttesterCount} of attestationGroup.getAttestationsForBlock( + notSeenAttestingIndices + )) { + const score = notSeenAttesterCount / slotDelta; + if (score < minScore) { + minScore = score; + } + attestationsByScore.push({ + attestation, + score, + }); + } + + // Stop accumulating attestations there are enough that may have good scoring + if (attestationsByScore.length >= MAX_ATTESTATIONS * 2) { + break slot; + } } } } @@ -183,13 +227,15 @@ export class AggregatedAttestationPool { * @param bySlot slot to filter, `bySlot === attestation.data.slot` */ getAll(bySlot?: Slot): phase0.Attestation[] { - let attestationGroupsArr: Map[]; + let attestationGroupsArr: Map[]; if (bySlot === undefined) { - attestationGroupsArr = Array.from(this.attestationGroupByDataHashBySlot.values()); + attestationGroupsArr = Array.from(this.attestationGroupByDataHashByIndexBySlot.values()).flatMap((byIndex) => + Array.from(byIndex.values()) + ); } else { - const attestationGroups = this.attestationGroupByDataHashBySlot.get(bySlot); - if (!attestationGroups) throw Error(`No attestations for slot ${bySlot}`); - attestationGroupsArr = [attestationGroups]; + const attestationGroupsByIndex = this.attestationGroupByDataHashByIndexBySlot.get(bySlot); + if (!attestationGroupsByIndex) throw Error(`No attestations for slot ${bySlot}`); + attestationGroupsArr = Array.from(attestationGroupsByIndex.values()); } const attestations: phase0.Attestation[] = []; @@ -224,6 +270,7 @@ export class MatchingDataAttestationGroup { private readonly attestations: AttestationWithIndex[] = []; constructor( + // TODO: no need committee here readonly committee: ValidatorIndex[], readonly data: phase0.AttestationData ) {} @@ -284,24 +331,18 @@ export class MatchingDataAttestationGroup { return InsertOutcome.NewData; } - getAttestationsForBlock(seenAttestingIndices: Set): AttestationNonParticipant[] { + /** + * Get AttestationNonParticipant for this groups of same attestation data. + * @param notSeenAttestingIndices not seen attestting indices, i.e. indices in the same committee + * @returns an array of AttestationNonParticipant + */ + getAttestationsForBlock(notSeenAttestingIndices: Set): AttestationNonParticipant[] { const attestations: AttestationNonParticipant[] = []; - - const committeeLen = this.committee.length; - const committeeSeenAttesting = new Array(committeeLen); - - // Intersect committee with participation only once for all attestations - for (let i = 0; i < committeeLen; i++) { - committeeSeenAttesting[i] = seenAttestingIndices.has(this.committee[i]); - } - for (const {attestation} of this.attestations) { - const {aggregationBits} = attestation; let notSeenAttesterCount = 0; - - for (let i = 0; i < committeeLen; i++) { - // TODO: Optimize aggregationBits.get() in bulk for the entire BitArray - if (!committeeSeenAttesting[i] && aggregationBits.get(i)) { + const {aggregationBits} = attestation; + for (const notSeenIndex of notSeenAttestingIndices) { + if (aggregationBits.get(notSeenIndex)) { notSeenAttesterCount++; } } @@ -311,9 +352,13 @@ export class MatchingDataAttestationGroup { } } - return attestations - .sort((a, b) => b.notSeenAttesterCount - a.notSeenAttesterCount) - .slice(0, MAX_ATTESTATIONS_PER_GROUP); + if (attestations.length <= MAX_ATTESTATIONS_PER_GROUP) { + return attestations; + } else { + return attestations + .sort((a, b) => b.notSeenAttesterCount - a.notSeenAttesterCount) + .slice(0, MAX_ATTESTATIONS_PER_GROUP); + } } /** Get attestations for API. */ @@ -335,7 +380,7 @@ export function aggregateInto(attestation1: AttestationWithIndex, attestation2: * Pre-compute participation from a CachedBeaconStateAllForks, for use to check if an attestation's committee * has already attested or not. */ -export function getParticipationFn(state: CachedBeaconStateAllForks): GetParticipationFn { +export function getNotSeenValidatorsFn(state: CachedBeaconStateAllForks): GetNotSeenValidatorsFn { if (state.config.getForkName(state.slot) === ForkName.phase0) { // Get attestations to be included in a phase0 block. // As we are close to altair, this is not really important, it's mainly for e2e. @@ -344,18 +389,29 @@ export function getParticipationFn(state: CachedBeaconStateAllForks): GetPartici const phase0State = state as CachedBeaconStatePhase0; const stateEpoch = computeEpochAtSlot(state.slot); - const previousEpochParticipants = extractParticipation( + const previousEpochParticipants = extractParticipationPhase0( phase0State.previousEpochAttestations.getAllReadonly(), state ); - const currentEpochParticipants = extractParticipation(phase0State.currentEpochAttestations.getAllReadonly(), state); - - return (epoch: Epoch) => { - return epoch === stateEpoch - ? currentEpochParticipants - : epoch === stateEpoch - 1 - ? previousEpochParticipants - : null; + const currentEpochParticipants = extractParticipationPhase0( + phase0State.currentEpochAttestations.getAllReadonly(), + state + ); + + return (epoch: Epoch, committee: number[]) => { + const participants = + epoch === stateEpoch ? currentEpochParticipants : epoch === stateEpoch - 1 ? previousEpochParticipants : null; + if (participants === null) { + return null; + } + + const notSeenAttestingIndices = new Set(); + for (const [i, validatorIndex] of committee.entries()) { + if (!participants.has(validatorIndex)) { + notSeenAttestingIndices.add(i); + } + } + return notSeenAttestingIndices.size === 0 ? null : notSeenAttestingIndices; }; } @@ -374,20 +430,24 @@ export function getParticipationFn(state: CachedBeaconStateAllForks): GetPartici const participationStatus = epoch === stateEpoch ? currentParticipation : epoch === stateEpoch - 1 ? previousParticipation : null; - if (participationStatus === null) return null; + if (participationStatus === null) { + return null; + } - const seenValidatorIndices = new Set(); - for (const validatorIndex of committee) { - if (flagIsTimelySource(participationStatus[validatorIndex])) { - seenValidatorIndices.add(validatorIndex); + const notSeenAttestingIndices = new Set(); + for (const [i, validatorIndex] of committee.entries()) { + // no need to check flagIsTimelySource as if validator is not seen, it's participation status is 0 + if (participationStatus[validatorIndex] === 0) { + notSeenAttestingIndices.add(i); } } - return seenValidatorIndices; + // if all validators are seen then return null, we don't need to check for any attestations of same committee again + return notSeenAttestingIndices.size === 0 ? null : notSeenAttestingIndices; }; } } -export function extractParticipation( +export function extractParticipationPhase0( attestations: phase0.PendingAttestation[], state: CachedBeaconStateAllForks ): Set { @@ -408,7 +468,56 @@ export function extractParticipation( } /** - * Do those validations: + * This returns a function to validate if an attestation data is compatible to a state, + * it's an optimized version of isValidAttestationData(). + * Atttestation data is validated by: + * - Validate the source checkpoint + * - Validate shuffling using beacon block root and target epoch + * + * Here we always validate the source checkpoint, and cache beacon block root + target epoch + * to avoid running the same shuffling validation multiple times. + */ +export function getValidateAttestationDataFn( + forkChoice: IForkChoice, + state: CachedBeaconStateAllForks +): ValidateAttestationDataFn { + const cachedValidatedAttestationData = new Map(); + const {previousJustifiedCheckpoint, currentJustifiedCheckpoint} = state; + const stateEpoch = state.epochCtx.epoch; + return (attData: phase0.AttestationData) => { + const targetEpoch = attData.target.epoch; + let justifiedCheckpoint; + // simple check first + if (targetEpoch === stateEpoch) { + justifiedCheckpoint = currentJustifiedCheckpoint; + } else if (targetEpoch === stateEpoch - 1) { + justifiedCheckpoint = previousJustifiedCheckpoint; + } else { + return false; + } + + if (!ssz.phase0.Checkpoint.equals(attData.source, justifiedCheckpoint)) return false; + + // Shuffling can't have changed if we're in the first few epochs + // Also we can't look back 2 epochs if target epoch is 1 or less + if (stateEpoch < 2 || targetEpoch < 2) { + return true; + } + + // the isValidAttestationData does not depend on slot and index + const beaconBlockRootHex = toHex(attData.beaconBlockRoot); + const cacheKey = beaconBlockRootHex + targetEpoch; + let isValid = cachedValidatedAttestationData.get(cacheKey); + if (isValid === undefined) { + isValid = isValidShuffling(forkChoice, state, beaconBlockRootHex, targetEpoch); + cachedValidatedAttestationData.set(cacheKey, isValid); + } + return isValid; + }; +} + +/** + * A straight forward version to validate attestation data. We don't use it, but keep it here for reference. * - Validate the source checkpoint * - Since we validated attestation's signature in gossip validation function, * we only need to validate the shuffling of attestation @@ -441,6 +550,16 @@ export function isValidAttestationData( if (stateEpoch < 2 || targetEpoch < 2) { return true; } + const beaconBlockRootHex = toHex(data.beaconBlockRoot); + return isValidShuffling(forkChoice, state, beaconBlockRootHex, targetEpoch); +} + +function isValidShuffling( + forkChoice: IForkChoice, + state: CachedBeaconStateAllForks, + blockRootHex: RootHex, + targetEpoch: Epoch +): boolean { // Otherwise the shuffling is determined by the block at the end of the target epoch // minus the shuffling lookahead (usually 2). We call this the "pivot". const pivotSlot = computeStartSlotAtEpoch(targetEpoch - 1) - 1; @@ -450,7 +569,7 @@ export function isValidAttestationData( // pivot block is the same as the current state's pivot block. If it is, then the // attestation's shuffling is the same as the current state's. // To account for skipped slots, find the first block at *or before* the pivot slot. - const beaconBlockRootHex = toHex(data.beaconBlockRoot); + const beaconBlockRootHex = blockRootHex; const beaconBlock = forkChoice.getBlockHex(beaconBlockRootHex); if (!beaconBlock) { throw Error(`Attestation data.beaconBlockRoot ${beaconBlockRootHex} not found in forkchoice`); @@ -467,7 +586,3 @@ export function isValidAttestationData( } return attestationDependentRoot === stateDependentRoot; } - -function flagIsTimelySource(flag: number): boolean { - return (flag & TIMELY_SOURCE) === TIMELY_SOURCE; -} diff --git a/packages/beacon-node/src/chain/options.ts b/packages/beacon-node/src/chain/options.ts index cc7795ade0a1..e687099a0cb4 100644 --- a/packages/beacon-node/src/chain/options.ts +++ b/packages/beacon-node/src/chain/options.ts @@ -30,6 +30,7 @@ export type IChainOptions = BlockProcessOpts & trustedSetup?: string; broadcastValidationStrictness?: string; minSameMessageSignatureSetsToBatch: number; + archiveBlobEpochs?: number; }; export type BlockProcessOpts = { diff --git a/packages/beacon-node/src/eth1/provider/jsonRpcHttpClient.ts b/packages/beacon-node/src/eth1/provider/jsonRpcHttpClient.ts index faa4e310e10a..9655aba5fc6b 100644 --- a/packages/beacon-node/src/eth1/provider/jsonRpcHttpClient.ts +++ b/packages/beacon-node/src/eth1/provider/jsonRpcHttpClient.ts @@ -1,5 +1,5 @@ import {EventEmitter} from "events"; -import StrictEventEmitter from "strict-event-emitter-types"; +import {StrictEventEmitter} from "strict-event-emitter-types"; import {fetch} from "@lodestar/api"; import {ErrorAborted, Gauge, Histogram, TimeoutError, isValidHttpUrl, retry} from "@lodestar/utils"; import {IJson, RpcPayload} from "../interface.js"; @@ -158,11 +158,7 @@ export class JsonRpcHttpClient implements IJsonRpcHttpClient { const routeId = opts?.routeId ?? "unknown"; const res = await retry>( - async (attempt) => { - /** If this is a retry, increment the retry counter for this method */ - if (attempt > 1) { - this.opts?.metrics?.retryCount.inc({routeId}); - } + async (_attempt) => { return this.fetchJson({jsonrpc: "2.0", id: this.id++, ...payload}, opts); }, { @@ -170,6 +166,9 @@ export class JsonRpcHttpClient implements IJsonRpcHttpClient { retryDelay: opts?.retryDelay ?? this.opts?.retryDelay ?? 0, shouldRetry: opts?.shouldRetry, signal: this.opts?.signal, + onRetry: () => { + this.opts?.metrics?.retryCount.inc({routeId}); + }, } ); return parseRpcResponse(res, payload); diff --git a/packages/beacon-node/src/eth1/provider/jwt.ts b/packages/beacon-node/src/eth1/provider/jwt.ts index 1e267120957f..da1fc1827cdd 100644 --- a/packages/beacon-node/src/eth1/provider/jwt.ts +++ b/packages/beacon-node/src/eth1/provider/jwt.ts @@ -2,6 +2,7 @@ import type {TAlgorithm} from "jwt-simple"; // TODO: fix jwt-simple types import jwt from "jwt-simple"; +// eslint-disable-next-line import/no-named-as-default-member const {encode, decode} = jwt; /** diff --git a/packages/beacon-node/src/execution/builder/http.ts b/packages/beacon-node/src/execution/builder/http.ts index c47e8471f199..b4013c384f81 100644 --- a/packages/beacon-node/src/execution/builder/http.ts +++ b/packages/beacon-node/src/execution/builder/http.ts @@ -118,7 +118,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder { async submitBlindedBlock( signedBlindedBlock: allForks.SignedBlindedBeaconBlock ): Promise { - const res = await this.api.submitBlindedBlock(signedBlindedBlock); + const res = await this.api.submitBlindedBlock(signedBlindedBlock, {retryAttempts: 3}); ApiError.assert(res, "execution.builder.submitBlindedBlock"); const {data} = res.response; diff --git a/packages/beacon-node/src/metrics/metrics/lodestar.ts b/packages/beacon-node/src/metrics/metrics/lodestar.ts index 284f4e75c064..c42dc4a747b3 100644 --- a/packages/beacon-node/src/metrics/metrics/lodestar.ts +++ b/packages/beacon-node/src/metrics/metrics/lodestar.ts @@ -668,26 +668,68 @@ export function createLodestarMetrics( help: "Time elapsed between block slot time and the time block processed", buckets: [0.5, 1, 2, 4, 6, 12], }), - receivedToGossipValidate: register.histogram({ - name: "lodestar_gossip_block_received_to_gossip_validate", - help: "Time elapsed between block received and block validated", - buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], - }), - receivedToStateTransition: register.histogram({ - name: "lodestar_gossip_block_received_to_state_transition", - help: "Time elapsed between block received and block state transition", - buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], - }), - receivedToSignaturesVerification: register.histogram({ - name: "lodestar_gossip_block_received_to_signatures_verification", - help: "Time elapsed between block received and block signatures verification", - buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], - }), - receivedToExecutionPayloadVerification: register.histogram({ - name: "lodestar_gossip_block_received_to_execution_payload_verification", - help: "Time elapsed between block received and execution payload verification", - buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], - }), + + gossipValidation: { + recvToValidation: register.histogram({ + name: "lodestar_gossip_block_received_to_gossip_validate", + help: "Time elapsed between block received and block validated", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + validationTime: register.histogram({ + name: "lodestar_gossip_block_gossip_validate_time", + help: "Time to apply gossip validations", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + }, + stateTransition: { + recvToValidation: register.histogram({ + name: "lodestar_gossip_block_received_to_state_transition", + help: "Time elapsed between block received and block state transition", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + validationTime: register.histogram({ + name: "lodestar_gossip_block_state_transition_time", + help: "Time to validate block state transition", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + }, + signatureVerification: { + recvToValidation: register.histogram({ + name: "lodestar_gossip_block_received_to_signatures_verification", + help: "Time elapsed between block received and block signatures verification", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + validationTime: register.histogram({ + name: "lodestar_gossip_block_signatures_verification_time", + help: "Time elapsed for block signatures verification", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + }, + executionPayload: { + recvToValidation: register.histogram({ + name: "lodestar_gossip_block_received_to_execution_payload_verification", + help: "Time elapsed between block received and execution payload verification", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + validationTime: register.histogram({ + name: "lodestar_gossip_execution_payload_verification_time", + help: "Time elapsed for execution payload verification", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + }, + blockImport: { + recvToValidation: register.histogram({ + name: "lodestar_gossip_block_received_to_block_import", + help: "Time elapsed between block received and block import", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + validationTime: register.histogram({ + name: "lodestar_gossip_block_block_import_time", + help: "Time elapsed for block import", + buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], + }), + }, + receivedToBlobsAvailabilityTime: register.histogram<{numBlobs: number}>({ name: "lodestar_gossip_block_received_to_blobs_availability_time", help: "Time elapsed between block received and blobs became available", @@ -705,11 +747,7 @@ export function createLodestarMetrics( buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], labelNames: ["numBlobs"], }), - receivedToBlockImport: register.histogram({ - name: "lodestar_gossip_block_received_to_block_import", - help: "Time elapsed between block received and block import", - buckets: [0.05, 0.1, 0.3, 0.5, 0.7, 1, 1.3, 1.6, 2, 2.5, 3, 3.5, 4], - }), + processBlockErrors: register.gauge<{error: BlockErrorCode | "NOT_BLOCK_ERROR"}>({ name: "lodestar_gossip_block_process_block_errors", help: "Count of errors, by error type, while processing blocks", @@ -717,9 +755,14 @@ export function createLodestarMetrics( }), }, gossipBlob: { - receivedToGossipValidate: register.histogram({ + recvToValidation: register.histogram({ name: "lodestar_gossip_blob_received_to_gossip_validate", - help: "Time elapsed between blob received and blob validated", + help: "Time elapsed between blob received and blob validation", + buckets: [0.05, 0.1, 0.2, 0.5, 1, 1.5, 2, 4], + }), + validationTime: register.histogram({ + name: "lodestar_gossip_blob_gossip_validate_time", + help: "Time elapsed for blob validation", buckets: [0.05, 0.1, 0.2, 0.5, 1, 1.5, 2, 4], }), }, @@ -1622,21 +1665,21 @@ export function createLodestarMetrics( // Provide max resolution on problematic values around 1 second buckets: [0.1, 0.5, 1, 2, 5, 15], }), - requestErrors: register.gauge<{routeId: string}>({ + requestErrors: register.gauge<{routeId: string; baseUrl: string}>({ name: "lodestar_builder_http_client_request_errors_total", help: "Total count of errors on builder http client requests by routeId", - labelNames: ["routeId"], + labelNames: ["routeId", "baseUrl"], }), - requestToFallbacks: register.gauge<{routeId: string}>({ + requestToFallbacks: register.gauge<{routeId: string; baseUrl: string}>({ name: "lodestar_builder_http_client_request_to_fallbacks_total", help: "Total count of requests to fallback URLs on builder http API by routeId", - labelNames: ["routeId"], + labelNames: ["routeId", "baseUrl"], }), - urlsScore: register.gauge<{urlIndex: number}>({ + urlsScore: register.gauge<{urlIndex: number; baseUrl: string}>({ name: "lodestar_builder_http_client_urls_score", help: "Current score of builder http URLs by url index", - labelNames: ["urlIndex"], + labelNames: ["urlIndex", "baseUrl"], }), }, diff --git a/packages/beacon-node/src/monitoring/system.ts b/packages/beacon-node/src/monitoring/system.ts index ee85e9ae5088..a1a79b1bd277 100644 --- a/packages/beacon-node/src/monitoring/system.ts +++ b/packages/beacon-node/src/monitoring/system.ts @@ -1,5 +1,9 @@ +// We want to keep `system` export to be used as namespace +/* eslint-disable import/no-named-as-default-member */ import os from "node:os"; import path from "node:path"; +// We want to keep `system` export as it's more readable and easier to understand +// eslint-disable-next-line import/no-named-as-default import system from "systeminformation"; import {Logger} from "@lodestar/utils"; diff --git a/packages/beacon-node/src/network/core/networkCoreWorker.ts b/packages/beacon-node/src/network/core/networkCoreWorker.ts index 2cbef57a16a2..1df348335582 100644 --- a/packages/beacon-node/src/network/core/networkCoreWorker.ts +++ b/packages/beacon-node/src/network/core/networkCoreWorker.ts @@ -30,7 +30,6 @@ const workerData = worker.workerData as NetworkWorkerData; const parentPort = worker.parentPort; // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!workerData) throw Error("workerData must be defined"); -// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!parentPort) throw Error("parentPort must be defined"); const config = createBeaconConfig(chainConfigFromJson(workerData.chainConfigJson), workerData.genesisValidatorsRoot); diff --git a/packages/beacon-node/src/network/core/networkCoreWorkerHandler.ts b/packages/beacon-node/src/network/core/networkCoreWorkerHandler.ts index 8370d3687635..2b6c85afa1f8 100644 --- a/packages/beacon-node/src/network/core/networkCoreWorkerHandler.ts +++ b/packages/beacon-node/src/network/core/networkCoreWorkerHandler.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import worker_threads from "node:worker_threads"; +import workerThreads from "node:worker_threads"; import {PeerScoreStatsDump} from "@chainsafe/libp2p-gossipsub/dist/src/score/peer-score.js"; import {PublishOpts} from "@chainsafe/libp2p-gossipsub/types"; import {ModuleThread, Thread, Worker, spawn} from "@chainsafe/threads"; @@ -78,14 +78,14 @@ export class WorkerNetworkCore implements INetworkCore { wireEventsOnMainThread( NetworkWorkerThreadEventType.networkEvent, modules.events, - modules.worker as unknown as worker_threads.Worker, + modules.worker as unknown as workerThreads.Worker, modules.metrics, networkEventDirection ); wireEventsOnMainThread( NetworkWorkerThreadEventType.reqRespBridgeEvents, this.reqRespBridgeEventBus, - modules.worker as unknown as worker_threads.Worker, + modules.worker as unknown as workerThreads.Worker, modules.metrics, reqRespBridgeEventDirection ); diff --git a/packages/beacon-node/src/network/discv5/index.ts b/packages/beacon-node/src/network/discv5/index.ts index 5a06545b98af..97d07ed774d5 100644 --- a/packages/beacon-node/src/network/discv5/index.ts +++ b/packages/beacon-node/src/network/discv5/index.ts @@ -1,6 +1,6 @@ import EventEmitter from "events"; import {PeerId} from "@libp2p/interface"; -import StrictEventEmitter from "strict-event-emitter-types"; +import {StrictEventEmitter} from "strict-event-emitter-types"; import {exportToProtobuf} from "@libp2p/peer-id-factory"; import {createPrivateKeyFromPeerId, ENR, ENRData, SignableENR} from "@chainsafe/enr"; import {spawn, Thread, Worker} from "@chainsafe/threads"; diff --git a/packages/beacon-node/src/network/interface.ts b/packages/beacon-node/src/network/interface.ts index 573698c13ded..aeeb61f1feb2 100644 --- a/packages/beacon-node/src/network/interface.ts +++ b/packages/beacon-node/src/network/interface.ts @@ -15,6 +15,7 @@ import { } from "@libp2p/interface"; import type {AddressManager, ConnectionManager, Registrar, TransportManager} from "@libp2p/interface-internal"; import type {Datastore} from "interface-datastore"; +import {Identify} from "@chainsafe/libp2p-identify"; import {Slot, SlotRootHex, allForks, altair, capella, deneb, phase0} from "@lodestar/types"; import {PeerIdStr} from "../util/peerId.js"; import {INetworkEventBus} from "./events.js"; @@ -98,4 +99,4 @@ export type LodestarComponents = { metrics?: Metrics; }; -export type Libp2p = ILibp2p<{components: LodestarComponents}>; +export type Libp2p = ILibp2p<{components: LodestarComponents; identify: Identify}>; diff --git a/packages/beacon-node/src/network/libp2p/index.ts b/packages/beacon-node/src/network/libp2p/index.ts index ccbd07355c32..a0d58033cf2f 100644 --- a/packages/beacon-node/src/network/libp2p/index.ts +++ b/packages/beacon-node/src/network/libp2p/index.ts @@ -1,7 +1,8 @@ import {PeerId} from "@libp2p/interface"; import {Registry} from "prom-client"; import {ENR} from "@chainsafe/enr"; -import {identify} from "@libp2p/identify"; +// TODO: We should use this fork until https://github.com/libp2p/js-libp2p/pull/2387 +import {identify} from "@chainsafe/libp2p-identify"; import {bootstrap} from "@libp2p/bootstrap"; import {mdns} from "@libp2p/mdns"; import {createLibp2p} from "libp2p"; @@ -111,6 +112,7 @@ export async function createNodeJsLibp2p( services: { identify: identify({ agentVersion: networkOpts.private ? "" : networkOpts.version ? `lodestar/${networkOpts.version}` : "lodestar", + runOnConnectionOpen: false, }), // individual components are specified because the components object is a Proxy // and passing it here directly causes problems downstream, not to mention is slowwww diff --git a/packages/beacon-node/src/network/peers/peerManager.ts b/packages/beacon-node/src/network/peers/peerManager.ts index 94af8f06ea03..a5f330603593 100644 --- a/packages/beacon-node/src/network/peers/peerManager.ts +++ b/packages/beacon-node/src/network/peers/peerManager.ts @@ -3,7 +3,7 @@ import {BitArray} from "@chainsafe/ssz"; import {SYNC_COMMITTEE_SUBNET_COUNT} from "@lodestar/params"; import {BeaconConfig} from "@lodestar/config"; import {allForks, altair, phase0} from "@lodestar/types"; -import {retry, withTimeout} from "@lodestar/utils"; +import {withTimeout} from "@lodestar/utils"; import {LoggerNode} from "@lodestar/logger/node"; import {GoodByeReasonCode, GOODBYE_KNOWN_CODES, Libp2pEvent} from "../../constants/index.js"; import {IClock} from "../../util/clock.js"; @@ -606,22 +606,18 @@ export class PeerManager { void this.requestStatus(remotePeer, this.statusCache.get()); } - // AgentVersion was set in libp2p IdentifyService, 'peer:connect' event handler - // since it's not possible to handle it async, we have to wait for a while to set AgentVersion - // See https://github.com/libp2p/js-libp2p/pull/1168 - retry( - async () => { - const agentVersionBytes = (await this.libp2p.peerStore.get(peerData.peerId)).metadata.get("AgentVersion"); - if (agentVersionBytes) { - const agentVersion = new TextDecoder().decode(agentVersionBytes) || "N/A"; + this.libp2p.services.identify + .identify(evt.detail) + .then((result) => { + const agentVersion = result.agentVersion; + if (agentVersion) { peerData.agentVersion = agentVersion; peerData.agentClient = getKnownClientFromAgentVersion(agentVersion); } - }, - {retries: 3, retryDelay: 1000} - ).catch((err) => { - this.logger.error("Error setting agentVersion for the peer", {peerId: peerData.peerId.toString()}, err); - }); + }) + .catch((err) => { + this.logger.debug("Error setting agentVersion for the peer", {peerId: peerData.peerId.toString()}, err); + }); }; /** diff --git a/packages/beacon-node/src/network/peers/score/score.ts b/packages/beacon-node/src/network/peers/score/score.ts index a5617ab9fea5..a949bf5d43d1 100644 --- a/packages/beacon-node/src/network/peers/score/score.ts +++ b/packages/beacon-node/src/network/peers/score/score.ts @@ -139,14 +139,12 @@ export class MaxScore implements IPeerScore { return DEFAULT_SCORE; } - // eslint-disable-next-line @typescript-eslint/no-empty-function add(): void {} update(): number { return MAX_SCORE; } - // eslint-disable-next-line @typescript-eslint/no-empty-function updateGossipsubScore(): void {} getStat(): PeerScoreStat { diff --git a/packages/beacon-node/src/network/processor/gossipHandlers.ts b/packages/beacon-node/src/network/processor/gossipHandlers.ts index 9073a204b785..60186f8fb79f 100644 --- a/packages/beacon-node/src/network/processor/gossipHandlers.ts +++ b/packages/beacon-node/src/network/processor/gossipHandlers.ts @@ -1,6 +1,6 @@ import {toHexString} from "@chainsafe/ssz"; import {BeaconConfig} from "@lodestar/config"; -import {Logger, prettyBytes} from "@lodestar/utils"; +import {LogLevel, Logger, prettyBytes} from "@lodestar/utils"; import {Root, Slot, ssz, allForks, deneb} from "@lodestar/types"; import {ForkName, ForkSeq} from "@lodestar/params"; import {routes} from "@lodestar/api"; @@ -115,7 +115,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler const forkTypes = config.getForkTypes(slot); const blockHex = prettyBytes(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message)); const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec); - const recvToVal = Date.now() / 1000 - seenTimestampSec; + const recvToValLatency = Date.now() / 1000 - seenTimestampSec; // always set block to seen cache for all forks so that we don't need to download it const blockInputRes = chain.seenGossipBlockInput.getGossipBlockInput(config, { @@ -133,19 +133,27 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler const blockInputMeta = config.getForkSeq(signedBlock.message.slot) >= ForkSeq.deneb ? blockInputRes.blockInputMeta : {}; - metrics?.gossipBlock.receivedToGossipValidate.observe(recvToVal); - logger.verbose("Received gossip block", { - slot: slot, - root: blockHex, - curentSlot: chain.clock.currentSlot, - peerId: peerIdStr, - delaySec, - recvToVal, - ...blockInputMeta, - }); - try { await validateGossipBlock(config, chain, signedBlock, fork); + + const recvToValidation = Date.now() / 1000 - seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + metrics?.gossipBlock.gossipValidation.recvToValidation.observe(recvToValidation); + metrics?.gossipBlock.gossipValidation.validationTime.observe(validationTime); + + logger.debug("Received gossip block", { + slot: slot, + root: blockHex, + curentSlot: chain.clock.currentSlot, + peerId: peerIdStr, + delaySec, + ...blockInputMeta, + recvToValLatency, + recvToValidation, + validationTime, + }); + return blockInput; } catch (e) { if (e instanceof BlockGossipError) { @@ -177,7 +185,7 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler const blockHex = prettyBytes(blockRoot); const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec); - const recvToVal = Date.now() / 1000 - seenTimestampSec; + const recvToValLatency = Date.now() / 1000 - seenTimestampSec; const {blockInput, blockInputMeta} = chain.seenGossipBlockInput.getGossipBlockInput(config, { type: GossipedInputType.blob, @@ -185,20 +193,27 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler blobBytes, }); - metrics?.gossipBlob.receivedToGossipValidate.observe(recvToVal); - logger.verbose("Received gossip blob", { - slot: slot, - root: blockHex, - curentSlot: chain.clock.currentSlot, - peerId: peerIdStr, - delaySec, - recvToVal, - gossipIndex, - ...blockInputMeta, - }); - try { await validateGossipBlobSidecar(chain, blobSidecar, gossipIndex); + const recvToValidation = Date.now() / 1000 - seenTimestampSec; + const validationTime = recvToValidation - recvToValLatency; + + metrics?.gossipBlob.recvToValidation.observe(recvToValidation); + metrics?.gossipBlob.validationTime.observe(validationTime); + + logger.debug("Received gossip blob", { + slot: slot, + root: blockHex, + curentSlot: chain.clock.currentSlot, + peerId: peerIdStr, + delaySec, + gossipIndex, + ...blockInputMeta, + recvToValLatency, + recvToValidation, + validationTime, + }); + return blockInput; } catch (e) { if (e instanceof BlobSidecarGossipError) { @@ -255,6 +270,9 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler chain.seenGossipBlockInput.prune(); }) .catch((e) => { + // Adjust verbosity based on error type + let logLevel: LogLevel; + if (e instanceof BlockError) { switch (e.type.code) { case BlockErrorCode.DATA_UNAVAILABLE: { @@ -264,6 +282,9 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler const rootHex = toHexString(forkTypes.BeaconBlock.hashTreeRoot(signedBlock.message)); events.emit(NetworkEvent.unknownBlock, {rootHex, peer: peerIdStr}); + + // Error is quite frequent and not critical + logLevel = LogLevel.debug; break; } // ALREADY_KNOWN should not happen with ignoreIfKnown=true above @@ -272,14 +293,21 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler case BlockErrorCode.PARENT_UNKNOWN: case BlockErrorCode.PRESTATE_MISSING: case BlockErrorCode.EXECUTION_ENGINE_ERROR: + // Errors might indicate an issue with our node or the connected EL client + logLevel = LogLevel.error; break; default: // TODO: Should it use PeerId or string? core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipBlock"); + // Misbehaving peer, but could highlight an issue in another client + logLevel = LogLevel.warn; } + } else { + // Any unexpected error + logLevel = LogLevel.error; } metrics?.gossipBlock.processBlockErrors.inc({error: e instanceof BlockError ? e.type.code : "NOT_BLOCK_ERROR"}); - logger.error("Error receiving block", {slot: signedBlock.message.slot, peer: peerIdStr}, e as Error); + logger[logLevel]("Error receiving block", {slot: signedBlock.message.slot, peer: peerIdStr}, e as Error); chain.seenGossipBlockInput.prune(); }); } @@ -453,6 +481,8 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } catch (e) { logger.error("Error adding attesterSlashing to pool", {}, e as Error); } + + chain.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing); }, [GossipType.proposer_slashing]: async ({ @@ -470,6 +500,8 @@ function getDefaultHandlers(modules: ValidatorFnsModules, options: GossipHandler } catch (e) { logger.error("Error adding attesterSlashing to pool", {}, e as Error); } + + chain.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing); }, [GossipType.voluntary_exit]: async ({gossipData, topic}: GossipHandlerParamGeneric) => { diff --git a/packages/beacon-node/src/network/processor/index.ts b/packages/beacon-node/src/network/processor/index.ts index ba84c9b12a60..420360920518 100644 --- a/packages/beacon-node/src/network/processor/index.ts +++ b/packages/beacon-node/src/network/processor/index.ts @@ -2,6 +2,8 @@ import {Logger, MapDef, mapValues, sleep} from "@lodestar/utils"; import {RootHex, Slot, SlotRootHex} from "@lodestar/types"; import {routes} from "@lodestar/api"; import {pruneSetToMax} from "@lodestar/utils"; +import {ForkSeq} from "@lodestar/params"; +import {computeStartSlotAtEpoch} from "@lodestar/state-transition"; import {IBeaconChain} from "../../chain/interface.js"; import {GossipErrorCode} from "../../chain/errors/gossipValidation.js"; import {Metrics} from "../../metrics/metrics.js"; @@ -47,10 +49,10 @@ const MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE = 3; /** * This is respective to gossipsub seenTTL (which is 550 * 0.7 = 385s), also it's respective * to beacon_attestation ATTESTATION_PROPAGATION_SLOT_RANGE (32 slots). - * If message slots are withint this window, it'll likely to be filtered by gossipsub seenCache. + * If message slots are within this window, it'll likely to be filtered by gossipsub seenCache. * This is mainly for DOS protection, see https://github.com/ChainSafe/lodestar/issues/5393 */ -const EARLIEST_PERMISSIBLE_SLOT_DISTANCE = 32; +const DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE = 32; type WorkOpts = { bypassQueue?: boolean; @@ -250,7 +252,14 @@ export class NetworkProcessor { if (slotRoot) { // DOS protection: avoid processing messages that are too old const {slot, root} = slotRoot; - if (slot < this.chain.clock.currentSlot - EARLIEST_PERMISSIBLE_SLOT_DISTANCE) { + const clockSlot = this.chain.clock.currentSlot; + const {fork} = message.topic; + let earliestPermissableSlot = clockSlot - DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE; + if (ForkSeq[fork] >= ForkSeq.deneb && topicType === GossipType.beacon_attestation) { + // post deneb, the attestations could be in current or previous epoch + earliestPermissableSlot = computeStartSlotAtEpoch(this.chain.clock.currentEpoch - 1); + } + if (slot < earliestPermissableSlot) { // TODO: Should report the dropped job to gossip? It will be eventually pruned from the mcache this.metrics?.networkProcessor.gossipValidationError.inc({ topic: topicType, @@ -258,10 +267,7 @@ export class NetworkProcessor { }); return; } - if ( - slot === this.chain.clock.currentSlot && - (topicType === GossipType.beacon_block || topicType === GossipType.blob_sidecar) - ) { + if (slot === clockSlot && (topicType === GossipType.beacon_block || topicType === GossipType.blob_sidecar)) { // in the worse case if the current slot block is not valid, this will be reset in the next slot this.isProcessingCurrentSlotBlock = true; } diff --git a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts index 5aea63fb9fe3..2a20a3f1f7fd 100644 --- a/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts +++ b/packages/beacon-node/src/network/reqresp/ReqRespBeaconNode.ts @@ -212,7 +212,6 @@ export class ReqRespBeaconNode extends ReqResp { * Any protocol not in this list must be un-subscribed. */ private getProtocolsAtFork(fork: ForkName): [ProtocolNoHandler, ProtocolHandler][] { - // eslint-disable-next-line @typescript-eslint/no-explicit-any const protocolsAtFork: [ProtocolNoHandler, ProtocolHandler][] = [ [protocols.Ping(this.config), this.onPing.bind(this)], [protocols.Status(this.config), this.onStatus.bind(this)], diff --git a/packages/beacon-node/src/network/reqresp/score.ts b/packages/beacon-node/src/network/reqresp/score.ts index 6aca573a9ca9..c74b645c9909 100644 --- a/packages/beacon-node/src/network/reqresp/score.ts +++ b/packages/beacon-node/src/network/reqresp/score.ts @@ -24,6 +24,7 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod): switch (e.type.code) { case RequestErrorCode.INVALID_REQUEST: case RequestErrorCode.INVALID_RESPONSE_SSZ: + case RequestErrorCode.SSZ_OVER_MAX_SIZE: return PeerAction.LowToleranceError; case RequestErrorCode.SERVER_ERROR: diff --git a/packages/beacon-node/src/network/subnets/attnetsService.ts b/packages/beacon-node/src/network/subnets/attnetsService.ts index 7eabc2e4114c..ab24714e5da9 100644 --- a/packages/beacon-node/src/network/subnets/attnetsService.ts +++ b/packages/beacon-node/src/network/subnets/attnetsService.ts @@ -147,7 +147,7 @@ export class AttnetsService implements IAttnetsService { /** Call ONLY ONCE: Two epoch before the fork, re-subscribe all existing random subscriptions to the new fork */ subscribeSubnetsToNextFork(nextFork: ForkName): void { - this.logger.info("Suscribing to random attnets to next fork", {nextFork}); + this.logger.info("Subscribing to random attnets to next fork", {nextFork}); for (const subnet of this.subscriptionsRandom.getAll()) { this.gossip.subscribeTopic({type: gossipType, fork: nextFork, subnet}); } @@ -155,7 +155,7 @@ export class AttnetsService implements IAttnetsService { /** Call ONLY ONCE: Two epochs after the fork, un-subscribe all subnets from the old fork */ unsubscribeSubnetsFromPrevFork(prevFork: ForkName): void { - this.logger.info("Unsuscribing to random attnets from prev fork", {prevFork}); + this.logger.info("Unsubscribing to random attnets from prev fork", {prevFork}); for (let subnet = 0; subnet < ATTESTATION_SUBNET_COUNT; subnet++) { if (!this.opts.subscribeAllSubnets) { this.gossip.unsubscribeTopic({type: gossipType, fork: prevFork, subnet}); diff --git a/packages/beacon-node/src/network/subnets/dllAttnetsService.ts b/packages/beacon-node/src/network/subnets/dllAttnetsService.ts index 7236695cb11a..9de0bef68357 100644 --- a/packages/beacon-node/src/network/subnets/dllAttnetsService.ts +++ b/packages/beacon-node/src/network/subnets/dllAttnetsService.ts @@ -138,7 +138,7 @@ export class DLLAttnetsService implements IAttnetsService { * Call ONLY ONCE: Two epoch before the fork, re-subscribe all existing random subscriptions to the new fork **/ subscribeSubnetsToNextFork(nextFork: ForkName): void { - this.logger.info("Suscribing to long lived attnets to next fork", { + this.logger.info("Subscribing to long lived attnets to next fork", { nextFork, subnets: Array.from(this.longLivedSubscriptions).join(","), }); @@ -152,7 +152,7 @@ export class DLLAttnetsService implements IAttnetsService { * Call ONLY ONCE: Two epochs after the fork, un-subscribe all subnets from the old fork **/ unsubscribeSubnetsFromPrevFork(prevFork: ForkName): void { - this.logger.info("Unsuscribing to long lived attnets from prev fork", {prevFork}); + this.logger.info("Unsubscribing to long lived attnets from prev fork", {prevFork}); for (let subnet = 0; subnet < ATTESTATION_SUBNET_COUNT; subnet++) { if (!this.opts.subscribeAllSubnets) { this.gossip.unsubscribeTopic({type: gossipType, fork: prevFork, subnet}); diff --git a/packages/beacon-node/src/network/subnets/syncnetsService.ts b/packages/beacon-node/src/network/subnets/syncnetsService.ts index 147c4d0b5fb8..c61b1060899a 100644 --- a/packages/beacon-node/src/network/subnets/syncnetsService.ts +++ b/packages/beacon-node/src/network/subnets/syncnetsService.ts @@ -74,7 +74,7 @@ export class SyncnetsService implements SubnetsService { /** Call ONLY ONCE: Two epoch before the fork, re-subscribe all existing random subscriptions to the new fork */ subscribeSubnetsToNextFork(nextFork: ForkName): void { - this.logger.info("Suscribing to random attnets to next fork", {nextFork}); + this.logger.info("Subscribing to random attnets to next fork", {nextFork}); for (const subnet of this.subscriptionsCommittee.getAll()) { this.gossip.subscribeTopic({type: gossipType, fork: nextFork, subnet}); } @@ -82,7 +82,7 @@ export class SyncnetsService implements SubnetsService { /** Call ONLY ONCE: Two epochs after the fork, un-subscribe all subnets from the old fork */ unsubscribeSubnetsFromPrevFork(prevFork: ForkName): void { - this.logger.info("Unsuscribing to random attnets from prev fork", {prevFork}); + this.logger.info("Unsubscribing to random attnets from prev fork", {prevFork}); for (let subnet = 0; subnet < SYNC_COMMITTEE_SUBNET_COUNT; subnet++) { if (!this.opts?.subscribeAllSubnets) { this.gossip.unsubscribeTopic({type: gossipType, fork: prevFork, subnet}); diff --git a/packages/beacon-node/src/sync/range/range.ts b/packages/beacon-node/src/sync/range/range.ts index 7739b6950ecb..d20e0c3690cd 100644 --- a/packages/beacon-node/src/sync/range/range.ts +++ b/packages/beacon-node/src/sync/range/range.ts @@ -1,5 +1,5 @@ import {EventEmitter} from "events"; -import StrictEventEmitter from "strict-event-emitter-types"; +import {StrictEventEmitter} from "strict-event-emitter-types"; import {computeStartSlotAtEpoch} from "@lodestar/state-transition"; import {BeaconConfig} from "@lodestar/config"; import {Epoch, phase0} from "@lodestar/types"; diff --git a/packages/beacon-node/src/util/blobs.ts b/packages/beacon-node/src/util/blobs.ts index bbad27f684ed..13d935ba29da 100644 --- a/packages/beacon-node/src/util/blobs.ts +++ b/packages/beacon-node/src/util/blobs.ts @@ -1,4 +1,4 @@ -import SHA256 from "@chainsafe/as-sha256"; +import {digest as sha256Digest} from "@chainsafe/as-sha256"; import {Tree} from "@chainsafe/persistent-merkle-tree"; import {VERSIONED_HASH_VERSION_KZG, KZG_COMMITMENT_GINDEX0, ForkName} from "@lodestar/params"; import {deneb, ssz, allForks} from "@lodestar/types"; @@ -8,7 +8,7 @@ import {signedBlockToSignedHeader} from "@lodestar/state-transition"; type VersionHash = Uint8Array; export function kzgCommitmentToVersionedHash(kzgCommitment: deneb.KZGCommitment): VersionHash { - const hash = SHA256.digest(kzgCommitment); + const hash = sha256Digest(kzgCommitment); // Equivalent to `VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:]` hash[0] = VERSIONED_HASH_VERSION_KZG; return hash; diff --git a/packages/beacon-node/src/util/clock.ts b/packages/beacon-node/src/util/clock.ts index 0be3b706a5ec..197d17f38281 100644 --- a/packages/beacon-node/src/util/clock.ts +++ b/packages/beacon-node/src/util/clock.ts @@ -1,5 +1,5 @@ import EventEmitter from "node:events"; -import type StrictEventEmitter from "strict-event-emitter-types"; +import type {StrictEventEmitter} from "strict-event-emitter-types"; import type {Epoch, Slot} from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {ErrorAborted} from "@lodestar/utils"; diff --git a/packages/beacon-node/src/util/kzg.ts b/packages/beacon-node/src/util/kzg.ts index d366a00ccc3a..e20a379d62ff 100644 --- a/packages/beacon-node/src/util/kzg.ts +++ b/packages/beacon-node/src/util/kzg.ts @@ -43,10 +43,10 @@ const G2POINT_COUNT = 65; const TOTAL_SIZE = 2 * POINT_COUNT_BYTES + G1POINT_BYTES * G1POINT_COUNT + G2POINT_BYTES * G2POINT_COUNT; export async function initCKZG(): Promise { - /* eslint-disable import/no-extraneous-dependencies, @typescript-eslint/ban-ts-comment */ + /* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-ignore ckzg = (await import("c-kzg")).default as typeof ckzg; - /* eslint-enable import/no-extraneous-dependencies, @typescript-eslint/ban-ts-comment */ + /* eslint-enable @typescript-eslint/ban-ts-comment */ } export enum TrustedFileMode { diff --git a/packages/beacon-node/test/e2e/network/gossipsub.test.ts b/packages/beacon-node/test/e2e/network/gossipsub.test.ts index 0ffab720598c..fd1794bf549d 100644 --- a/packages/beacon-node/test/e2e/network/gossipsub.test.ts +++ b/packages/beacon-node/test/e2e/network/gossipsub.test.ts @@ -16,7 +16,12 @@ describe( {timeout: 3000} ); -describe( +/** + * This is nice to have to investigate networking issue in local environment. + * Since we use vitest to run tests in parallel, including this causes the test to be unstable. + * See https://github.com/ChainSafe/lodestar/issues/6358 + */ +describe.skip( "gossipsub / worker", function () { runTests({useWorker: true}); @@ -133,6 +138,70 @@ function runTests({useWorker}: {useWorker: boolean}): void { ); }); + it("Publish and receive an attesterSlashing", async function () { + let onAttesterSlashingChange: (payload: Uint8Array) => void; + const onAttesterSlashingChangePromise = new Promise((resolve) => (onAttesterSlashingChange = resolve)); + + const {netA, netB} = await mockModules({ + [GossipType.attester_slashing]: async ({gossipData}: GossipHandlerParamGeneric) => { + onAttesterSlashingChange(gossipData.serializedData); + }, + }); + + await Promise.all([onPeerConnect(netA), onPeerConnect(netB), connect(netA, netB)]); + expect(netA.getConnectedPeerCount()).toBe(1); + expect(netB.getConnectedPeerCount()).toBe(1); + + await netA.subscribeGossipCoreTopics(); + await netB.subscribeGossipCoreTopics(); + + // Wait to have a peer connected to a topic + while (!netA.closed) { + await sleep(500); + if (await hasSomeMeshPeer(netA)) { + break; + } + } + + const attesterSlashing = ssz.phase0.AttesterSlashing.defaultValue(); + await netA.publishAttesterSlashing(attesterSlashing); + + const received = await onAttesterSlashingChangePromise; + expect(Buffer.from(received)).toEqual(Buffer.from(ssz.phase0.AttesterSlashing.serialize(attesterSlashing))); + }); + + it("Publish and receive a proposerSlashing", async function () { + let onProposerSlashingChange: (payload: Uint8Array) => void; + const onProposerSlashingChangePromise = new Promise((resolve) => (onProposerSlashingChange = resolve)); + + const {netA, netB} = await mockModules({ + [GossipType.proposer_slashing]: async ({gossipData}: GossipHandlerParamGeneric) => { + onProposerSlashingChange(gossipData.serializedData); + }, + }); + + await Promise.all([onPeerConnect(netA), onPeerConnect(netB), connect(netA, netB)]); + expect(netA.getConnectedPeerCount()).toBe(1); + expect(netB.getConnectedPeerCount()).toBe(1); + + await netA.subscribeGossipCoreTopics(); + await netB.subscribeGossipCoreTopics(); + + // Wait to have a peer connected to a topic + while (!netA.closed) { + await sleep(500); + if (await hasSomeMeshPeer(netA)) { + break; + } + } + + const proposerSlashing = ssz.phase0.ProposerSlashing.defaultValue(); + await netA.publishProposerSlashing(proposerSlashing); + + const received = await onProposerSlashingChangePromise; + expect(Buffer.from(received)).toEqual(Buffer.from(ssz.phase0.ProposerSlashing.serialize(proposerSlashing))); + }); + it("Publish and receive a LightClientOptimisticUpdate", async function () { let onLightClientOptimisticUpdate: (ou: Uint8Array) => void; const onLightClientOptimisticUpdatePromise = new Promise( diff --git a/packages/beacon-node/test/e2e/network/onWorker/workerEchoHandler.ts b/packages/beacon-node/test/e2e/network/onWorker/workerEchoHandler.ts index 0b84ace618f1..b2c0f6b56aa0 100644 --- a/packages/beacon-node/test/e2e/network/onWorker/workerEchoHandler.ts +++ b/packages/beacon-node/test/e2e/network/onWorker/workerEchoHandler.ts @@ -1,4 +1,4 @@ -import worker_threads from "node:worker_threads"; +import workerThreads from "node:worker_threads"; import {spawn, Worker} from "@chainsafe/threads"; export type EchoWorker = { @@ -8,9 +8,8 @@ export type EchoWorker = { export async function getEchoWorker(): Promise { const workerThreadjs = new Worker("./workerEcho.js"); - const worker = workerThreadjs as unknown as worker_threads.Worker; + const worker = workerThreadjs as unknown as workerThreads.Worker; - // eslint-disable-next-line @typescript-eslint/no-explicit-any await spawn(workerThreadjs, { // A Lodestar Node may do very expensive task at start blocking the event loop and causing // the initialization to timeout. The number below is big enough to almost disable the timeout diff --git a/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts b/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts index 8eb0ecdd1ba4..795a4bf67ecc 100644 --- a/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts +++ b/packages/beacon-node/test/e2e/network/peers/peerManager.test.ts @@ -58,7 +58,6 @@ describe("network / peers / PeerManager", function () { const reqResp = new ReqRespFake(); const peerRpcScores = new PeerRpcScoreStore(); const networkEventBus = new NetworkEventBus(); - /* eslint-disable @typescript-eslint/no-empty-function */ const mockSubnetsService: IAttnetsService = { getActiveSubnets: () => [], shouldProcess: () => true, diff --git a/packages/beacon-node/test/e2e/network/reqresp.test.ts b/packages/beacon-node/test/e2e/network/reqresp.test.ts index 855dc7046203..38b5cda4db5f 100644 --- a/packages/beacon-node/test/e2e/network/reqresp.test.ts +++ b/packages/beacon-node/test/e2e/network/reqresp.test.ts @@ -13,11 +13,7 @@ import {arrToSource} from "../../unit/network/reqresp/utils.js"; import {GetReqRespHandlerFn, ReqRespMethod} from "../../../src/network/reqresp/types.js"; import {PeerIdStr} from "../../../src/util/peerId.js"; -/* eslint-disable - require-yield, - @typescript-eslint/naming-convention, - @typescript-eslint/explicit-function-return-type -*/ +/* eslint-disable require-yield, @typescript-eslint/naming-convention */ describe( "network / reqresp / main thread", diff --git a/packages/beacon-node/test/e2e/network/reqrespEncode.test.ts b/packages/beacon-node/test/e2e/network/reqrespEncode.test.ts index 74ad3533479b..d96f405eae7c 100644 --- a/packages/beacon-node/test/e2e/network/reqrespEncode.test.ts +++ b/packages/beacon-node/test/e2e/network/reqrespEncode.test.ts @@ -22,7 +22,7 @@ import {testLogger} from "../../utils/logger.js"; import {GetReqRespHandlerFn} from "../../../src/network/reqresp/types.js"; import {LocalStatusCache} from "../../../src/network/statusCache.js"; -/* eslint-disable require-yield, @typescript-eslint/naming-convention */ +/* eslint-disable require-yield */ describe("reqresp encoder", () => { let port = 60000; diff --git a/packages/beacon-node/test/e2e/sync/finalizedSync.test.ts b/packages/beacon-node/test/e2e/sync/finalizedSync.test.ts new file mode 100644 index 000000000000..fcfe3b5156dc --- /dev/null +++ b/packages/beacon-node/test/e2e/sync/finalizedSync.test.ts @@ -0,0 +1,123 @@ +import {describe, it, afterEach} from "vitest"; +import {assert} from "chai"; +import {fromHexString} from "@chainsafe/ssz"; +import {ChainConfig} from "@lodestar/config"; +import {phase0} from "@lodestar/types"; +import {TimestampFormatCode} from "@lodestar/logger"; +import {SLOTS_PER_EPOCH} from "@lodestar/params"; +import {routes} from "@lodestar/api"; +import {EventData, EventType} from "@lodestar/api/lib/beacon/routes/events.js"; +import {getDevBeaconNode} from "../../utils/node/beacon.js"; +import {waitForEvent} from "../../utils/events/resolver.js"; +import {getAndInitDevValidators} from "../../utils/node/validator.js"; +import {ChainEvent} from "../../../src/chain/index.js"; +import {connect, onPeerConnect} from "../../utils/network.js"; +import {testLogger, LogLevel, TestLoggerOpts} from "../../utils/logger.js"; + +describe( + "sync / finalized sync", + function () { + const validatorCount = 8; + const testParams: Pick = { + // eslint-disable-next-line @typescript-eslint/naming-convention + SECONDS_PER_SLOT: 2, + }; + + const afterEachCallbacks: (() => Promise | void)[] = []; + afterEach(async () => { + while (afterEachCallbacks.length > 0) { + const callback = afterEachCallbacks.pop(); + if (callback) await callback(); + } + }); + + it("should do a finalized sync from another BN", async function () { + // single node at beginning, use main thread to verify bls + const genesisSlotsDelay = 4; + const genesisTime = Math.floor(Date.now() / 1000) + genesisSlotsDelay * testParams.SECONDS_PER_SLOT; + + const testLoggerOpts: TestLoggerOpts = { + level: LogLevel.info, + timestampFormat: { + format: TimestampFormatCode.EpochSlot, + genesisTime, + slotsPerEpoch: SLOTS_PER_EPOCH, + secondsPerSlot: testParams.SECONDS_PER_SLOT, + }, + }; + + const loggerNodeA = testLogger("FinalizedSync-Node-A", testLoggerOpts); + const loggerNodeB = testLogger("FinalizedSync-Node-B", testLoggerOpts); + + const bn = await getDevBeaconNode({ + params: testParams, + options: { + sync: {isSingleNode: true}, + network: {allowPublishToZeroPeers: true, useWorker: false}, + chain: {blsVerifyAllMainThread: true}, + }, + validatorCount, + genesisTime, + logger: loggerNodeA, + }); + + afterEachCallbacks.push(() => bn.close()); + + const {validators} = await getAndInitDevValidators({ + node: bn, + logPrefix: "FinalizedSyncVc", + validatorsPerClient: validatorCount, + validatorClientCount: 1, + startIndex: 0, + useRestApi: false, + testLoggerOpts, + }); + + afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.close()))); + + // stop beacon node after validators + afterEachCallbacks.push(() => bn.close()); + + await waitForEvent(bn.chain.emitter, ChainEvent.forkChoiceFinalized, 240000); + loggerNodeA.info("Node A emitted finalized checkpoint event"); + + const bn2 = await getDevBeaconNode({ + params: testParams, + options: { + api: {rest: {enabled: false}}, + network: {useWorker: false}, + chain: {blsVerifyAllMainThread: true}, + }, + validatorCount, + genesisTime, + logger: loggerNodeB, + }); + loggerNodeA.info("Node B created"); + + afterEachCallbacks.push(() => bn2.close()); + afterEachCallbacks.push(() => bn2.close()); + + const headSummary = bn.chain.forkChoice.getHead(); + const head = await bn.db.block.get(fromHexString(headSummary.blockRoot)); + if (!head) throw Error("First beacon node has no head block"); + const waitForSynced = waitForEvent( + bn2.chain.emitter, + routes.events.EventType.head, + 100000, + ({block}) => block === headSummary.blockRoot + ); + + await Promise.all([connect(bn2.network, bn.network), onPeerConnect(bn2.network), onPeerConnect(bn.network)]); + loggerNodeA.info("Node A connected to Node B"); + + try { + await waitForSynced; + loggerNodeB.info("Node B synced to Node A, received head block", {slot: head.message.slot}); + } catch (e) { + assert.fail("Failed to sync to other node in time"); + } + }); + }, + // chain is finalized at slot 32, plus 4 slots for genesis delay => ~72s it should sync pretty fast + {timeout: 90000} +); diff --git a/packages/beacon-node/test/e2e/sync/unknownBlockSync.test.ts b/packages/beacon-node/test/e2e/sync/unknownBlockSync.test.ts index a51beaf7b961..e64adfc94888 100644 --- a/packages/beacon-node/test/e2e/sync/unknownBlockSync.test.ts +++ b/packages/beacon-node/test/e2e/sync/unknownBlockSync.test.ts @@ -12,7 +12,7 @@ import {waitForEvent} from "../../utils/events/resolver.js"; import {getAndInitDevValidators} from "../../utils/node/validator.js"; import {ChainEvent} from "../../../src/chain/index.js"; import {NetworkEvent} from "../../../src/network/index.js"; -import {connect} from "../../utils/network.js"; +import {connect, onPeerConnect} from "../../utils/network.js"; import {testLogger, LogLevel, TestLoggerOpts} from "../../utils/logger.js"; import {BlockError, BlockErrorCode} from "../../../src/chain/errors/index.js"; import {BlockSource, getBlockInput} from "../../../src/chain/blocks/types.js"; @@ -48,7 +48,7 @@ describe("sync / unknown block sync", function () { for (const {id, event} of testCases) { it(id, async function () { // the node needs time to transpile/initialize bls worker threads - const genesisSlotsDelay = 7; + const genesisSlotsDelay = 4; const genesisTime = Math.floor(Date.now() / 1000) + genesisSlotsDelay * testParams.SECONDS_PER_SLOT; const testLoggerOpts: TestLoggerOpts = { level: LogLevel.info, @@ -71,6 +71,7 @@ describe("sync / unknown block sync", function () { chain: {blsVerifyAllMainThread: true}, }, validatorCount, + genesisTime, logger: loggerNodeA, }); @@ -100,7 +101,7 @@ describe("sync / unknown block sync", function () { chain: {blsVerifyAllMainThread: true}, }, validatorCount, - genesisTime: bn.chain.getHeadState().genesisTime, + genesisTime, logger: loggerNodeB, }); @@ -116,7 +117,11 @@ describe("sync / unknown block sync", function () { ({block}) => block === headSummary.blockRoot ); + const connected = Promise.all([onPeerConnect(bn2.network), onPeerConnect(bn.network)]); await connect(bn2.network, bn.network); + await connected; + loggerNodeA.info("Node A connected to Node B"); + const headInput = getBlockInput.preDeneb(config, head, BlockSource.gossip, null); switch (event) { @@ -147,4 +152,4 @@ describe("sync / unknown block sync", function () { await waitForSynced; }); } -}, {timeout: 30_000}); +}, {timeout: 40_000}); diff --git a/packages/beacon-node/test/mocks/mockedBeaconDb.ts b/packages/beacon-node/test/mocks/mockedBeaconDb.ts index 4f5f494a0f40..8b99c85d2345 100644 --- a/packages/beacon-node/test/mocks/mockedBeaconDb.ts +++ b/packages/beacon-node/test/mocks/mockedBeaconDb.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import {vi, Mocked} from "vitest"; import {config as minimalConfig} from "@lodestar/config/default"; import { diff --git a/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts b/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts index 5dd73bafa3f0..4ed8215ac85b 100644 --- a/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts +++ b/packages/beacon-node/test/perf/chain/opPools/aggregatedAttestationPool.test.ts @@ -1,13 +1,13 @@ import {itBench} from "@dapplion/benchmark"; -import {expect} from "chai"; import {BitArray, toHexString} from "@chainsafe/ssz"; import { CachedBeaconStateAltair, computeEpochAtSlot, computeStartSlotAtEpoch, getBlockRootAtSlot, + newFilledArray, } from "@lodestar/state-transition"; -import {HISTORICAL_ROOTS_LIMIT, SLOTS_PER_EPOCH, TIMELY_SOURCE_FLAG_INDEX} from "@lodestar/params"; +import {HISTORICAL_ROOTS_LIMIT, SLOTS_PER_EPOCH} from "@lodestar/params"; import {ExecutionStatus, ForkChoice, IForkChoiceStore, ProtoArray} from "@lodestar/fork-choice"; import {ssz} from "@lodestar/types"; // eslint-disable-next-line import/no-relative-packages @@ -15,33 +15,24 @@ import {generatePerfTestCachedStateAltair} from "../../../../../state-transition import {AggregatedAttestationPool} from "../../../../src/chain/opPools/aggregatedAttestationPool.js"; import {computeAnchorCheckpoint} from "../../../../src/chain/initState.js"; -/** Same to https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.5/specs/altair/beacon-chain.md#has_flag */ -const TIMELY_SOURCE = 1 << TIMELY_SOURCE_FLAG_INDEX; -function flagIsTimelySource(flag: number): boolean { - return (flag & TIMELY_SOURCE) === TIMELY_SOURCE; -} +const vc = 1_500_000; -// Aug 11 2021 -// getAttestationsForBlock -// ✓ getAttestationsForBlock 4.410948 ops/s 226.7086 ms/op - 64 runs 51.8 s -describe("getAttestationsForBlock", () => { +/** + * Jan 2024 + * getAttestationsForBlock vc=1500000 + * ✔ notSeenSlots=1 numMissedVotes=1 numBadVotes=10 10.48105 ops/s 95.41024 ms/op - 12 runs 18.2 s + * ✔ notSeenSlots=1 numMissedVotes=0 numBadVotes=4 11.44517 ops/s 87.37307 ms/op - 13 runs 14.5 s + * ✔ notSeenSlots=2 numMissedVotes=1 numBadVotes=10 23.86144 ops/s 41.90862 ms/op - 18 runs 34.1 s + */ +describe(`getAttestationsForBlock vc=${vc}`, () => { let originalState: CachedBeaconStateAltair; let protoArray: ProtoArray; let forkchoice: ForkChoice; before(function () { - this.timeout(2 * 60 * 1000); // Generating the states for the first time is very slow - - originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true}); - - const previousEpochParticipationArr = originalState.previousEpochParticipation.getAll(); - const currentEpochParticipationArr = originalState.currentEpochParticipation.getAll(); - - const numPreviousEpochParticipation = previousEpochParticipationArr.filter(flagIsTimelySource).length; - const numCurrentEpochParticipation = currentEpochParticipationArr.filter(flagIsTimelySource).length; + this.timeout(5 * 60 * 1000); // Generating the states for the first time is very slow - expect(numPreviousEpochParticipation).to.equal(250000, "Wrong numPreviousEpochParticipation"); - expect(numCurrentEpochParticipation).to.equal(250000, "Wrong numCurrentEpochParticipation"); + originalState = generatePerfTestCachedStateAltair({goBackOneSlot: true, vc}); const {blockHeader, checkpoint} = computeAnchorCheckpoint(originalState.config, originalState); // TODO figure out why getBlockRootAtSlot(originalState, justifiedSlot) is not the same to justifiedCheckpoint.root @@ -125,17 +116,115 @@ describe("getAttestationsForBlock", () => { forkchoice = new ForkChoice(originalState.config, fcStore, protoArray); }); + // notSeenSlots should be >=1 + for (const [notSeenSlots, numMissedVotes, numBadVotes] of [ + [1, 1, 10], + [1, 0, 4], + // notSeenSlots=2 means the previous block slot is missed + [2, 1, 10], + ]) { + itBench({ + id: `notSeenSlots=${notSeenSlots} numMissedVotes=${numMissedVotes} numBadVotes=${numBadVotes}`, + before: () => { + const state = originalState.clone(); + // by default make all validators have full participation + const previousParticipation = newFilledArray(vc, 0b111); + // origState is at slot 0 of epoch so there is no currentParticipation + const currentParticipation = newFilledArray(vc, 0); + const currentEpoch = computeEpochAtSlot(state.slot); + + for (let epochSlot = 0; epochSlot < SLOTS_PER_EPOCH; epochSlot++) { + const slot = state.slot - 1 - epochSlot; + const slotEpoch = computeEpochAtSlot(slot); + const committeeCount = state.epochCtx.getCommitteeCountPerSlot(slotEpoch); + for (let committeeIndex = 0; committeeIndex < committeeCount; committeeIndex++) { + const duties = state.epochCtx.getBeaconCommittee(slot, committeeIndex); + const participationArr = slotEpoch === currentEpoch ? currentParticipation : previousParticipation; + for (const [i, validatorIndex] of duties.entries()) { + // no attestation in previous slot is included yet as that's the spec + // for slot < previous slot, there is missed votes at every committee so the code need to keep looking for attestations because votes are not seen + if (slot >= state.slot - notSeenSlots || i < numMissedVotes) { + participationArr[validatorIndex] = 0; + } + } + } + } + state.previousEpochParticipation = ssz.altair.EpochParticipation.toViewDU(previousParticipation); + state.currentEpochParticipation = ssz.altair.EpochParticipation.toViewDU(currentParticipation); + state.commit(); + return state; + }, + beforeEach: (state) => { + const pool = getAggregatedAttestationPool(state, numMissedVotes, numBadVotes); + return {state, pool}; + }, + fn: ({state, pool}) => { + pool.getAttestationsForBlock(forkchoice, state); + }, + }); + } +}); + +/** + * Fir dev purpose to find the best way to get not seen validators. + */ +describe.skip("getAttestationsForBlock aggregationBits intersectValues vs get", () => { + const runsFactor = 1000; + // As of Jan 2004 + const committeeLen = 450; + const aggregationBits = BitArray.fromBoolArray(Array.from({length: committeeLen}, () => true)); + const notSeenValidatorIndices = Array.from({length: committeeLen}, (_, i) => i); + itBench({ - id: "getAttestationsForBlock", - beforeEach: () => getAggregatedAttestationPool(originalState), - fn: (pool) => { - // logger.info("Number of attestations in pool", pool.getAll().length); - pool.getAttestationsForBlock(forkchoice, originalState); + id: "aggregationBits.intersectValues()", + fn: () => { + for (let i = 0; i < runsFactor; i++) { + aggregationBits.intersectValues(notSeenValidatorIndices); + } }, + runsFactor, + }); + + itBench({ + id: "aggregationBits.get()", + fn: () => { + for (let i = 0; i < runsFactor; i++) { + for (let j = 0; j < committeeLen; j++) { + aggregationBits.get(j); + } + } + }, + runsFactor, + }); + + itBench({ + id: "aggregationBits.get() with push()", + fn: () => { + for (let i = 0; i < runsFactor; i++) { + const arr: number[] = []; + for (let j = 0; j < committeeLen; j++) { + if (aggregationBits.get(j)) { + arr.push(j); + } + } + } + }, + runsFactor, }); }); -function getAggregatedAttestationPool(state: CachedBeaconStateAltair): AggregatedAttestationPool { +/** + * Create the pool with the following properties: + * - state: at slot n + * - all attestations at slot n - 1 are included in block but they are not enough + * - numMissedVotes: number of missed attestations/votes at every committee + * - numBadVotes: number of bad attestations/votes at every committee, they are not included in block because they are seen in the state + */ +function getAggregatedAttestationPool( + state: CachedBeaconStateAltair, + numMissedVotes: number, + numBadVotes: number +): AggregatedAttestationPool { const pool = new AggregatedAttestationPool(); for (let epochSlot = 0; epochSlot < SLOTS_PER_EPOCH; epochSlot++) { const slot = state.slot - 1 - epochSlot; @@ -145,31 +234,82 @@ function getAggregatedAttestationPool(state: CachedBeaconStateAltair): Aggregate epoch: state.currentJustifiedCheckpoint.epoch, root: state.currentJustifiedCheckpoint.root, }; + for (let committeeIndex = 0; committeeIndex < committeeCount; committeeIndex++) { - const attestation = { - aggregationBits: BitArray.fromBitLen(64), - data: { - slot: slot, - index: committeeIndex, - beaconBlockRoot: getBlockRootAtSlot(state, slot), - source: sourceCheckpoint, - target: { - epoch, - root: getBlockRootAtSlot(state, computeStartSlotAtEpoch(epoch)), - }, + const goodAttData = { + slot: slot, + index: committeeIndex, + beaconBlockRoot: getBlockRootAtSlot(state, slot), + source: sourceCheckpoint, + target: { + epoch, + root: getBlockRootAtSlot(state, computeStartSlotAtEpoch(epoch)), }, - signature: Buffer.alloc(96), }; + // for each good att data group, there are 4 versions of aggregation bits const committee = state.epochCtx.getBeaconCommittee(slot, committeeIndex); - // all attestation has full participation so getAttestationsForBlock() has to do a lot of filter - // aggregate_and_proof messages - pool.add( - attestation, - toHexString(ssz.phase0.AttestationData.hashTreeRoot(attestation.data)), - committee.length, - committee - ); + const committeeLen = committee.length; + const goodVoteBits = BitArray.fromBoolArray(Array.from({length: committeeLen}, () => true)); + // n first validators are totally missed + for (let i = 0; i < numMissedVotes; i++) { + goodVoteBits.set(i, false); + } + // n next validators vote for different att data + for (let i = 0; i < numBadVotes; i++) { + goodVoteBits.set(i + numMissedVotes, false); + } + + // there are 4 different versions of the good vote + for (const endingBits of [0b1000, 0b0100, 0b0010, 0b0001]) { + const aggregationBits = goodVoteBits.clone(); + aggregationBits.set(committeeLen - 1, Boolean(endingBits & 0b0001)); + aggregationBits.set(committeeLen - 2, Boolean(endingBits & 0b0010)); + aggregationBits.set(committeeLen - 3, Boolean(endingBits & 0b0100)); + aggregationBits.set(committeeLen - 4, Boolean(endingBits & 0b1000)); + + const attestation = { + aggregationBits, + data: goodAttData, + signature: Buffer.alloc(96), + }; + // all attestation has full participation so getAttestationsForBlock() has to do a lot of filter + // aggregate_and_proof messages + pool.add( + attestation, + toHexString(ssz.phase0.AttestationData.hashTreeRoot(attestation.data)), + committee.length, + committee + ); + } + + if (epochSlot === 0) { + // epochSlot === 0: attestations will be included in block but it's not enough for block + // epochSlot >= 1: no attestation will be included in block but the code still need to scan through them + continue; + } + + const zeroAggregationBits = BitArray.fromBoolArray(Array.from({length: committeeLen}, () => false)); + + // n first validator votes for n different bad votes, that makes n different att data in the same slot/index + // these votes/attestations will NOT be included in block as they are seen in the state + for (let i = 0; i < numBadVotes; i++) { + const attData = ssz.phase0.AttestationData.clone(goodAttData); + attData.beaconBlockRoot = getBlockRootAtSlot(state, slot - i - 1); + const aggregationBits = zeroAggregationBits.clone(); + aggregationBits.set(i + numMissedVotes, true); + const attestation = { + aggregationBits, + data: attData, + signature: Buffer.alloc(96), + }; + pool.add( + attestation, + toHexString(ssz.phase0.AttestationData.hashTreeRoot(attestation.data)), + committee.length, + committee + ); + } } } return pool; 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 dbe86c2c5868..96dda3acaece 100644 --- a/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts +++ b/packages/beacon-node/test/perf/chain/produceBlock/produceBlockBody.test.ts @@ -41,7 +41,6 @@ describe("produceBlockBody", () => { config: state.config, db, logger, - // eslint-disable-next-line @typescript-eslint/no-empty-function processShutdownCallback: () => {}, metrics: null, anchorState: state, diff --git a/packages/beacon-node/test/perf/chain/validation/attestation.test.ts b/packages/beacon-node/test/perf/chain/validation/attestation.test.ts index ed5c17ed890b..5fce9a342509 100644 --- a/packages/beacon-node/test/perf/chain/validation/attestation.test.ts +++ b/packages/beacon-node/test/perf/chain/validation/attestation.test.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line import/no-relative-packages import {itBench, setBenchOpts} from "@dapplion/benchmark"; import {expect} from "chai"; import {ssz} from "@lodestar/types"; diff --git a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts index 923436d6f96d..41d8aa76865b 100644 --- a/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts +++ b/packages/beacon-node/test/perf/chain/verifyImportBlocks.test.ts @@ -97,7 +97,6 @@ describe.skip("verify+import blocks - range sync perf test", () => { config: state.config, db, logger, - // eslint-disable-next-line @typescript-eslint/no-empty-function processShutdownCallback: () => {}, metrics: null, anchorState: state, diff --git a/packages/beacon-node/test/sim/4844-interop.test.ts b/packages/beacon-node/test/sim/4844-interop.test.ts index d6de49e32eff..23a01bf1bb33 100644 --- a/packages/beacon-node/test/sim/4844-interop.test.ts +++ b/packages/beacon-node/test/sim/4844-interop.test.ts @@ -65,7 +65,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { } }); - // eslint-disable-next-line vitest/expect-expect it("Post-merge, run for a few blocks", async function () { console.log("\n\nPost-merge, run for a few blocks\n\n"); const {elClient, tearDownCallBack} = await runEL( diff --git a/packages/beacon-node/test/sim/merge-interop.test.ts b/packages/beacon-node/test/sim/merge-interop.test.ts index 9467c53831c8..59bb28879b12 100644 --- a/packages/beacon-node/test/sim/merge-interop.test.ts +++ b/packages/beacon-node/test/sim/merge-interop.test.ts @@ -86,7 +86,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { } }); - // eslint-disable-next-line vitest/expect-expect it("Send stub payloads to EL", async () => { const {elClient, tearDownCallBack} = await runEL( {...elSetupConfig, mode: ELStartMode.PostMerge}, @@ -203,7 +202,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { */ }); - // eslint-disable-next-line vitest/expect-expect it("Post-merge, run for a few blocks", async function () { console.log("\n\nPost-merge, run for a few blocks\n\n"); const {elClient, tearDownCallBack} = await runEL( @@ -220,7 +218,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { }); }); - // eslint-disable-next-line vitest/expect-expect it("Pre-merge, run for a few blocks", async function () { console.log("\n\nPre-merge, run for a few blocks\n\n"); const {elClient, tearDownCallBack} = await runEL( diff --git a/packages/beacon-node/test/sim/mergemock.test.ts b/packages/beacon-node/test/sim/mergemock.test.ts index 97a9d9f2aaf7..4cce95967c55 100644 --- a/packages/beacon-node/test/sim/mergemock.test.ts +++ b/packages/beacon-node/test/sim/mergemock.test.ts @@ -64,7 +64,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { }); for (const useProduceBlockV3 of [false, true]) { - // eslint-disable-next-line vitest/expect-expect it(`Test builder with useProduceBlockV3=${useProduceBlockV3}`, async function () { console.log("\n\nPost-merge, run for a few blocks\n\n"); const {elClient, tearDownCallBack} = await runEL( diff --git a/packages/beacon-node/test/sim/withdrawal-interop.test.ts b/packages/beacon-node/test/sim/withdrawal-interop.test.ts index 6fdd4fd5a0ce..4f8efa71dce3 100644 --- a/packages/beacon-node/test/sim/withdrawal-interop.test.ts +++ b/packages/beacon-node/test/sim/withdrawal-interop.test.ts @@ -70,7 +70,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { } }); - // eslint-disable-next-line vitest/expect-expect it("Send stub payloads to EL", async () => { const {elClient, tearDownCallBack} = await runEL( {...elSetupConfig, mode: ELStartMode.PostMerge, genesisTemplate: "genesisPostWithdraw.tmpl"}, @@ -185,7 +184,6 @@ describe("executionEngine / ExecutionEngineHttp", function () { ); }); - // eslint-disable-next-line vitest/expect-expect it("Post-merge, run for a few blocks", async function () { console.log("\n\nPost-merge, run for a few blocks\n\n"); const {elClient, tearDownCallBack} = await runEL( 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 b0ad63a68205..49d78cc42f6a 100644 --- a/packages/beacon-node/test/spec/presets/fork_choice.test.ts +++ b/packages/beacon-node/test/spec/presets/fork_choice.test.ts @@ -101,7 +101,6 @@ const forkChoiceTest = config: createBeaconConfig(config, state.genesisValidatorsRoot), db: getMockedBeaconDb(), logger, - // eslint-disable-next-line @typescript-eslint/no-empty-function processShutdownCallback: () => {}, clock, metrics: null, @@ -367,7 +366,6 @@ const forkChoiceTest = }; }, timeout: 10000, - // eslint-disable-next-line @typescript-eslint/no-empty-function expectFunc: () => {}, // Do not manually skip tests here, do it in packages/beacon-node/test/spec/presets/index.test.ts // EXCEPTION : this test skipped here because prefix match can't be don't for this particular test diff --git a/packages/beacon-node/test/spec/presets/light_client/sync.ts b/packages/beacon-node/test/spec/presets/light_client/sync.ts index e48bb0f361a3..84ab2c28b467 100644 --- a/packages/beacon-node/test/spec/presets/light_client/sync.ts +++ b/packages/beacon-node/test/spec/presets/light_client/sync.ts @@ -191,7 +191,6 @@ export const sync: TestRunnerFn = (fork) => { } as SyncTestCase; }, timeout: 10000, - // eslint-disable-next-line @typescript-eslint/no-empty-function expectFunc: () => {}, // Do not manually skip tests here, do it in packages/beacon-node/test/spec/presets/index.test.ts }, diff --git a/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts b/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts index c4b5b7623d85..9a38ddf36c8c 100644 --- a/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts +++ b/packages/beacon-node/test/spec/presets/light_client/update_ranking.ts @@ -57,7 +57,6 @@ newUpdate = ${renderUpdate(newUpdate)} ? ssz.allForksLightClient[fork].LightClientUpdate : ssz.altair.LightClientUpdate, }, - // eslint-disable-next-line @typescript-eslint/no-empty-function expectFunc: () => {}, // Do not manually skip tests here, do it in packages/beacon-node/test/spec/presets/index.test.ts }, diff --git a/packages/beacon-node/test/spec/presets/ssz_static.test.ts b/packages/beacon-node/test/spec/presets/ssz_static.test.ts index b721fced69a9..bcab25acde4c 100644 --- a/packages/beacon-node/test/spec/presets/ssz_static.test.ts +++ b/packages/beacon-node/test/spec/presets/ssz_static.test.ts @@ -60,7 +60,6 @@ const sszStatic = for (const testCase of fs.readdirSync(testSuiteDirpath)) { // Do not manually skip tests here, do it in packages/beacon-node/test/spec/presets/index.test.ts - // eslint-disable-next-line vitest/consistent-test-it it(testCase, function () { // Mainnet must deal with big full states and hash each one multiple times if (ACTIVE_PRESET === "mainnet") { diff --git a/packages/beacon-node/test/unit/api/impl/beacon/beacon.test.ts b/packages/beacon-node/test/unit/api/impl/beacon/beacon.test.ts index 28c8fe652e8a..28df366a0cde 100644 --- a/packages/beacon-node/test/unit/api/impl/beacon/beacon.test.ts +++ b/packages/beacon-node/test/unit/api/impl/beacon/beacon.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import {describe, it, expect, beforeAll} from "vitest"; import {ApiTestModules, getApiTestModules} from "../../../../utils/api.js"; import {getBeaconApi} from "../../../../../src/api/impl/beacon/index.js"; diff --git a/packages/beacon-node/test/unit/api/impl/validator/produceBlockV3.test.ts b/packages/beacon-node/test/unit/api/impl/validator/produceBlockV3.test.ts index 4484bcf03563..ba0267fc5810 100644 --- a/packages/beacon-node/test/unit/api/impl/validator/produceBlockV3.test.ts +++ b/packages/beacon-node/test/unit/api/impl/validator/produceBlockV3.test.ts @@ -49,6 +49,12 @@ describe("api/validator - produceBlockV3", function () { [routes.validator.BuilderSelection.BuilderAlways, 1, 1, 1, true, "engine"], [routes.validator.BuilderSelection.BuilderAlways, 1, null, 1, true, "builder"], + [routes.validator.BuilderSelection.ExecutionAlways, 2, 1, 0, false, "engine"], + [routes.validator.BuilderSelection.ExecutionAlways, 0, 1, 1, false, "engine"], + [routes.validator.BuilderSelection.ExecutionAlways, 0, null, 0, false, "builder"], + [routes.validator.BuilderSelection.ExecutionAlways, null, 0, 1, false, "engine"], + [routes.validator.BuilderSelection.ExecutionAlways, 1, 1, 1, true, "engine"], + [routes.validator.BuilderSelection.BuilderOnly, 0, 2, 0, false, "builder"], [routes.validator.BuilderSelection.ExecutionOnly, 2, 0, 1, false, "engine"], [routes.validator.BuilderSelection.BuilderOnly, 1, 1, 0, true, "builder"], diff --git a/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts b/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts index 48abfbc35675..00aa8a40168b 100644 --- a/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts +++ b/packages/beacon-node/test/unit/chain/opPools/aggregatedAttestationPool.test.ts @@ -2,14 +2,15 @@ import type {SecretKey} from "@chainsafe/bls/types"; import bls from "@chainsafe/bls"; import {BitArray, fromHexString, toHexString} from "@chainsafe/ssz"; import {describe, it, expect, beforeEach, beforeAll, afterEach, vi} from "vitest"; -import {CachedBeaconStateAllForks} from "@lodestar/state-transition"; -import {SLOTS_PER_EPOCH} from "@lodestar/params"; +import {CachedBeaconStateAllForks, newFilledArray} from "@lodestar/state-transition"; +import {FAR_FUTURE_EPOCH, MAX_EFFECTIVE_BALANCE, SLOTS_PER_EPOCH} from "@lodestar/params"; import {ssz, phase0} from "@lodestar/types"; +import {CachedBeaconStateAltair} from "@lodestar/state-transition/src/types.js"; import {MockedForkChoice, getMockedForkChoice} from "../../../mocks/mockedBeaconChain.js"; import { AggregatedAttestationPool, aggregateInto, - getParticipationFn, + getNotSeenValidatorsFn, MatchingDataAttestationGroup, } from "../../../../src/chain/opPools/aggregatedAttestationPool.js"; import {InsertOutcome} from "../../../../src/chain/opPools/types.js"; @@ -18,6 +19,7 @@ import {generateCachedAltairState} from "../../../utils/state.js"; import {renderBitArray} from "../../../utils/render.js"; import {ZERO_HASH_HEX} from "../../../../src/constants/constants.js"; import {generateProtoBlock} from "../../../utils/typeGenerator.js"; +import {generateValidators} from "../../../utils/validator.js"; /** Valid signature of random data to prevent BLS errors */ const validSignature = fromHexString( @@ -29,15 +31,43 @@ describe("AggregatedAttestationPool", function () { const altairForkEpoch = 2020; const currentEpoch = altairForkEpoch + 10; const currentSlot = SLOTS_PER_EPOCH * currentEpoch; - const originalState = generateCachedAltairState({slot: currentSlot + 1}, altairForkEpoch); - let altairState: CachedBeaconStateAllForks; + const committeeIndex = 0; const attestation = ssz.phase0.Attestation.defaultValue(); attestation.data.slot = currentSlot; + attestation.data.index = committeeIndex; attestation.data.target.epoch = currentEpoch; const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(attestation.data)); - const committee = [0, 1, 2, 3]; + const validatorOpts = { + activationEpoch: 0, + effectiveBalance: MAX_EFFECTIVE_BALANCE, + withdrawableEpoch: FAR_FUTURE_EPOCH, + exitEpoch: FAR_FUTURE_EPOCH, + }; + // this makes a committee length of 4 + const vc = 64; + const committeeLength = 4; + const validators = generateValidators(vc, validatorOpts); + const originalState = generateCachedAltairState({slot: currentSlot + 1, validators}, altairForkEpoch); + const committee = originalState.epochCtx.getBeaconCommittee(currentSlot, committeeIndex); + expect(committee.length).toEqual(committeeLength); + // 0 and 1 in committee are fully participated + const epochParticipation = newFilledArray(vc, 0b111); + for (let i = 0; i < committeeLength; i++) { + if (i === 0 || i === 1) { + epochParticipation[committee[i]] = 0b111; + } else { + epochParticipation[committee[i]] = 0b000; + } + } + (originalState as CachedBeaconStateAltair).previousEpochParticipation = + ssz.altair.EpochParticipation.toViewDU(epochParticipation); + (originalState as CachedBeaconStateAltair).currentEpochParticipation = + ssz.altair.EpochParticipation.toViewDU(epochParticipation); + originalState.commit(); + let altairState: CachedBeaconStateAllForks; + let forkchoiceStub: MockedForkChoice; beforeEach(() => { @@ -53,9 +83,16 @@ describe("AggregatedAttestationPool", function () { it("getParticipationFn", () => { // previousEpochParticipation and currentEpochParticipation is created inside generateCachedState // 0 and 1 are fully participated - const participationFn = getParticipationFn(altairState); - const participation = participationFn(currentEpoch, committee); - expect(participation).toEqual(new Set([0, 1])); + const notSeenValidatorFn = getNotSeenValidatorsFn(altairState); + const participation = notSeenValidatorFn(currentEpoch, committee); + // seen attesting indices are 0, 1 => not seen are 2, 3 + expect(participation).toEqual( + // { + // validatorIndices: [null, null, committee[2], committee[3]], + // attestingIndices: new Set([2, 3]), + // } + new Set([2, 3]) + ); }); // previousEpochParticipation and currentEpochParticipation is created inside generateCachedState @@ -68,7 +105,7 @@ describe("AggregatedAttestationPool", function () { for (const {name, attestingBits, isReturned} of testCases) { it(name, function () { - const aggregationBits = new BitArray(new Uint8Array(attestingBits), 8); + const aggregationBits = new BitArray(new Uint8Array(attestingBits), committeeLength); pool.add( {...attestation, aggregationBits}, attDataRootHex, @@ -180,13 +217,14 @@ describe("MatchingDataAttestationGroup.add()", () => { describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => { const testCases: { id: string; - seenAttestingBits: number[]; + notSeenAttestingBits: number[]; attestationsToAdd: {bits: number[]; notSeenAttesterCount: number}[]; }[] = [ // Note: attestationsToAdd MUST intersect in order to not be aggregated and distort the results { id: "All have attested", - seenAttestingBits: [0b11111111], + // same to seenAttestingBits: [0b11111111], + notSeenAttestingBits: [0b00000000], attestationsToAdd: [ {bits: [0b11111110], notSeenAttesterCount: 0}, {bits: [0b00000011], notSeenAttesterCount: 0}, @@ -194,7 +232,8 @@ describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => { }, { id: "Some have attested", - seenAttestingBits: [0b11110001], // equals to indexes [ 0, 4, 5, 6, 7 ] + // same to seenAttestingBits: [0b11110001] + notSeenAttestingBits: [0b00001110], attestationsToAdd: [ {bits: [0b11111110], notSeenAttesterCount: 3}, {bits: [0b00000011], notSeenAttesterCount: 1}, @@ -202,7 +241,8 @@ describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => { }, { id: "Non have attested", - seenAttestingBits: [0b00000000], + // same to seenAttestingBits: [0b00000000], + notSeenAttestingBits: [0b11111111], attestationsToAdd: [ {bits: [0b11111110], notSeenAttesterCount: 7}, {bits: [0b00000011], notSeenAttesterCount: 2}, @@ -213,7 +253,7 @@ describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => { const attestationData = ssz.phase0.AttestationData.defaultValue(); const committee = linspace(0, 7); - for (const {id, seenAttestingBits, attestationsToAdd} of testCases) { + for (const {id, notSeenAttestingBits, attestationsToAdd} of testCases) { it(id, () => { const attestationGroup = new MatchingDataAttestationGroup(committee, attestationData); @@ -229,8 +269,19 @@ describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => { attestationGroup.add({attestation, trueBitsCount: attestation.aggregationBits.getTrueBitIndexes().length}); } - const indices = new BitArray(new Uint8Array(seenAttestingBits), 8).intersectValues(committee); - const attestationsForBlock = attestationGroup.getAttestationsForBlock(new Set(indices)); + const notSeenAggBits = new BitArray(new Uint8Array(notSeenAttestingBits), 8); + // const notSeenValidatorIndices: (ValidatorIndex | null)[] = []; + const notSeenAttestingIndices = new Set(); + for (let i = 0; i < committee.length; i++) { + // notSeenValidatorIndices.push(notSeenAggBits.get(i) ? committee[i] : null); + if (notSeenAggBits.get(i)) { + notSeenAttestingIndices.add(i); + } + } + const attestationsForBlock = attestationGroup.getAttestationsForBlock( + // notSeenValidatorIndices, + notSeenAttestingIndices + ); for (const [i, {notSeenAttesterCount}] of attestationsToAdd.entries()) { const attestation = attestationsForBlock.find((a) => a.attestation === attestations[i]); diff --git a/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts b/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts index 29b9ba3e1912..cc6dc26f1a36 100644 --- a/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/attestation/validateGossipAttestationsSameAttData.test.ts @@ -4,7 +4,6 @@ import {afterEach, beforeEach, describe, expect, it, vi} from "vitest"; import {ForkName} from "@lodestar/params"; import {SignatureSetType} from "@lodestar/state-transition"; import {ssz} from "@lodestar/types"; -// eslint-disable-next-line import/no-relative-packages import {BlsSingleThreadVerifier} from "../../../../../src/chain/bls/singleThread.js"; import {AttestationError, AttestationErrorCode, GossipAction} from "../../../../../src/chain/errors/index.js"; import {IBeaconChain} from "../../../../../src/chain/index.js"; diff --git a/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts b/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts index 304db1365fb1..386a08641273 100644 --- a/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts +++ b/packages/beacon-node/test/unit/chain/validation/lightClientOptimisticUpdate.test.ts @@ -9,7 +9,6 @@ import {getMockedBeaconChain} from "../../../mocks/mockedBeaconChain.js"; describe("Light Client Optimistic Update validation", function () { const afterEachCallbacks: (() => Promise | void)[] = []; - // eslint-disable-next-line @typescript-eslint/naming-convention const config = createChainForkConfig({ ...defaultChainConfig, /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/packages/beacon-node/test/unit/monitoring/remoteService.ts b/packages/beacon-node/test/unit/monitoring/remoteService.ts index 20afd99396b7..c5ce0e9ef0b3 100644 --- a/packages/beacon-node/test/unit/monitoring/remoteService.ts +++ b/packages/beacon-node/test/unit/monitoring/remoteService.ts @@ -1,4 +1,4 @@ -import fastify from "fastify"; +import {fastify} from "fastify"; import {afterAll, expect} from "vitest"; import {RemoteServiceError} from "../../../src/monitoring/service.js"; import {ProcessType} from "../../../src/monitoring/types.js"; diff --git a/packages/beacon-node/test/unit/network/reqresp/utils.ts b/packages/beacon-node/test/unit/network/reqresp/utils.ts index a1a1a6c69bd2..231bd0cc42fb 100644 --- a/packages/beacon-node/test/unit/network/reqresp/utils.ts +++ b/packages/beacon-node/test/unit/network/reqresp/utils.ts @@ -56,11 +56,8 @@ export class MockLibP2pStream implements Stream { this.resultChunks.push(chunk.subarray()); } }; - // eslint-disable-next-line @typescript-eslint/no-empty-function close: Stream["close"] = async () => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function closeRead = async (): Promise => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function closeWrite = async (): Promise => {}; abort: Stream["abort"] = () => this.close(); } diff --git a/packages/beacon-node/test/unit/sync/range/chain.test.ts b/packages/beacon-node/test/unit/sync/range/chain.test.ts index a4cc54b21d11..5af96f0f1729 100644 --- a/packages/beacon-node/test/unit/sync/range/chain.test.ts +++ b/packages/beacon-node/test/unit/sync/range/chain.test.ts @@ -57,7 +57,6 @@ describe("sync / range / chain", () => { const zeroBlockBody = ssz.phase0.BeaconBlockBody.defaultValue(); const interval: NodeJS.Timeout | null = null; - // eslint-disable-next-line @typescript-eslint/no-empty-function const reportPeer: SyncChainFns["reportPeer"] = () => {}; afterEach(() => { @@ -124,7 +123,6 @@ describe("sync / range / chain", () => { const targetEpoch = 16; const peers = [peer]; - // eslint-disable-next-line @typescript-eslint/no-empty-function const processChainSegment: SyncChainFns["processChainSegment"] = async () => {}; const downloadBeaconBlocksByRange: SyncChainFns["downloadBeaconBlocksByRange"] = async (peer, request) => { const blocks: BlockInput[] = []; diff --git a/packages/beacon-node/test/utils/api.ts b/packages/beacon-node/test/utils/api.ts index 41face1ae737..7429ac79189e 100644 --- a/packages/beacon-node/test/utils/api.ts +++ b/packages/beacon-node/test/utils/api.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {Mocked} from "vitest"; import {config} from "@lodestar/config/default"; import {ForkChoice} from "@lodestar/fork-choice"; diff --git a/packages/beacon-node/test/utils/db.ts b/packages/beacon-node/test/utils/db.ts index afb276b88c60..c44aa7f878dc 100644 --- a/packages/beacon-node/test/utils/db.ts +++ b/packages/beacon-node/test/utils/db.ts @@ -1,4 +1,4 @@ -import child_process from "node:child_process"; +import childProcess from "node:child_process"; import {FilterOptions, LevelDbController} from "@lodestar/db"; import {ChainForkConfig} from "@lodestar/config"; import {BeaconDb} from "../../src/index.js"; @@ -8,7 +8,7 @@ export const TEMP_DB_LOCATION = ".tmpdb"; export async function startTmpBeaconDb(config: ChainForkConfig): Promise { // Clean-up db first - child_process.execSync(`rm -rf ${TEMP_DB_LOCATION}`); + childProcess.execSync(`rm -rf ${TEMP_DB_LOCATION}`); return new BeaconDb(config, await LevelDbController.create({name: TEMP_DB_LOCATION}, {logger: testLogger()})); } diff --git a/packages/beacon-node/test/utils/networkWithMockDb.ts b/packages/beacon-node/test/utils/networkWithMockDb.ts index 3eea47892a0f..f8a4a6181d2f 100644 --- a/packages/beacon-node/test/utils/networkWithMockDb.ts +++ b/packages/beacon-node/test/utils/networkWithMockDb.ts @@ -59,7 +59,6 @@ export async function getNetworkForTest( config: beaconConfig, db, logger, - // eslint-disable-next-line @typescript-eslint/no-empty-function processShutdownCallback: () => {}, // set genesis time so that we are at ALTAIR_FORK_EPOCH // mock timer does not work on worker thread diff --git a/packages/beacon-node/test/utils/node/beacon.ts b/packages/beacon-node/test/utils/node/beacon.ts index 8c1ae9503b7c..0163fa148102 100644 --- a/packages/beacon-node/test/utils/node/beacon.ts +++ b/packages/beacon-node/test/utils/node/beacon.ts @@ -92,7 +92,6 @@ export async function getDevBeaconNode( config: beaconConfig, db, logger, - // eslint-disable-next-line @typescript-eslint/no-empty-function processShutdownCallback: () => {}, peerId, peerStoreDir, diff --git a/packages/beacon-node/test/utils/node/validator.ts b/packages/beacon-node/test/utils/node/validator.ts index dfa627383306..ce81fc6d18ca 100644 --- a/packages/beacon-node/test/utils/node/validator.ts +++ b/packages/beacon-node/test/utils/node/validator.ts @@ -70,7 +70,6 @@ export async function getAndInitDevValidators({ api: useRestApi ? getNodeApiUrl(node) : getApiFromServerHandlers(node.api), slashingProtection, logger, - // eslint-disable-next-line @typescript-eslint/no-empty-function processShutdownCallback: () => {}, abortController, signers, diff --git a/packages/beacon-node/vitest.config.e2e.ts b/packages/beacon-node/vitest.e2e.config.ts similarity index 100% rename from packages/beacon-node/vitest.config.e2e.ts rename to packages/beacon-node/vitest.e2e.config.ts diff --git a/packages/beacon-node/vitest.config.spec.ts b/packages/beacon-node/vitest.spec.config.ts similarity index 100% rename from packages/beacon-node/vitest.config.spec.ts rename to packages/beacon-node/vitest.spec.config.ts diff --git a/packages/cli/README.md b/packages/cli/README.md index ae379a9b5d8d..177cb18bc401 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -12,7 +12,7 @@ Command line tool for Lodestar ## Getting started - Follow the [installation guide](https://chainsafe.github.io/lodestar/) to install Lodestar. -- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/usage). +- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/advanced-topics/setting-up-a-testnet/). ### Lodestar diff --git a/packages/cli/docsgen/markdown.ts b/packages/cli/docsgen/markdown.ts index c05c7ad8c90f..e7fbcab7ad4b 100644 --- a/packages/cli/docsgen/markdown.ts +++ b/packages/cli/docsgen/markdown.ts @@ -6,7 +6,7 @@ const LINE_BREAK = "\n\n
"; function renderExampleBody(example: CliExample, lodestarCommand?: string): string { const cliExample = [ - `\`\`\` + `\`\`\`sh ${lodestarCommand ? `${lodestarCommand} ` : ""}${example.command} \`\`\``, ]; @@ -113,7 +113,9 @@ function renderOption(optionName: string, option: CliOptionDefinition): string | if (!defaultValue.includes(`"`)) { defaultValue = `"${defaultValue}"`; } - defaultValue = `[ ${defaultValue} ]`; + if (!defaultValue.startsWith("[")) { + defaultValue = `[ ${defaultValue} ]`; + } } commandOption.push(`default: \`${defaultValue}\``); } diff --git a/packages/cli/package.json b/packages/cli/package.json index 839d42c7b3d6..0906047fd232 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@chainsafe/lodestar", - "version": "1.15.1", + "version": "1.16.0", "description": "Command line interface for lodestar", "author": "ChainSafe Systems", "license": "LGPL-3.0", @@ -30,16 +30,14 @@ "docs:build": "node --loader ts-node/esm ./docsgen/index.ts", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "test:unit": "vitest --run --dir test/unit/", - "test:e2e": "vitest --run --config vitest.config.e2e.ts --dir test/e2e/", - "test:sim:multifork": "LODESTAR_PRESET=minimal node --loader ts-node/esm test/sim/multi_fork.test.ts", - "test:sim:mixedclient": "LODESTAR_PRESET=minimal node --loader ts-node/esm test/sim/mixed_client.test.ts", - "test:sim:endpoints": "LODESTAR_PRESET=minimal node --loader ts-node/esm test/sim/endpoints.test.ts", - "test:sim:deneb": "LODESTAR_PRESET=minimal node --loader ts-node/esm test/sim/deneb.test.ts", - "test:sim:backup_eth_provider": "LODESTAR_PRESET=minimal node --loader ts-node/esm test/sim/backup_eth_provider.test.ts", + "test:e2e": "vitest --run --config vitest.e2e.config.ts --dir test/e2e/", + "test:sim:multifork": "LODESTAR_PRESET=minimal DOTENV_CONFIG_PATH=../../.env.test node -r dotenv/config --loader ts-node/esm test/sim/multi_fork.test.ts", + "test:sim:mixedclient": "LODESTAR_PRESET=minimal DOTENV_CONFIG_PATH=../../.env.test node -r dotenv/config --loader ts-node/esm test/sim/mixed_client.test.ts", + "test:sim:endpoints": "LODESTAR_PRESET=minimal DOTENV_CONFIG_PATH=../../.env.test node -r dotenv/config --loader ts-node/esm test/sim/endpoints.test.ts", + "test:sim:deneb": "LODESTAR_PRESET=minimal DOTENV_CONFIG_PATH=../../.env.test node -r dotenv/config --loader ts-node/esm test/sim/deneb.test.ts", + "test:sim:backup_eth_provider": "LODESTAR_PRESET=minimal DOTENV_CONFIG_PATH=../../.env.test node -r dotenv/config --loader ts-node/esm test/sim/backup_eth_provider.test.ts", "test": "yarn test:unit && yarn test:e2e", - "coverage": "codecov -F lodestar", "check-readme": "typescript-docs-verifier" }, "repository": { @@ -53,30 +51,30 @@ "blockchain" ], "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/bls": "7.1.1", - "@chainsafe/bls-keygen": "^0.3.0", - "@chainsafe/bls-keystore": "^3.0.0", + "@chainsafe/as-sha256": "^0.4.1", + "@chainsafe/bls": "7.1.3", + "@chainsafe/bls-keygen": "^0.4.0", + "@chainsafe/bls-keystore": "^3.0.1", "@chainsafe/blst": "^0.2.9", - "@chainsafe/discv5": "^7.1.0", - "@chainsafe/enr": "^2.0.2", + "@chainsafe/discv5": "^9.0.0", + "@chainsafe/enr": "^3.0.0", "@chainsafe/persistent-merkle-tree": "^0.6.1", "@chainsafe/ssz": "^0.14.0", "@chainsafe/threads": "^1.11.1", "@libp2p/crypto": "^3.0.4", "@libp2p/peer-id": "^4.0.4", "@libp2p/peer-id-factory": "^4.0.3", - "@lodestar/api": "^1.15.1", - "@lodestar/beacon-node": "^1.15.1", - "@lodestar/config": "^1.15.1", - "@lodestar/db": "^1.15.1", - "@lodestar/light-client": "^1.15.1", - "@lodestar/logger": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/state-transition": "^1.15.1", - "@lodestar/types": "^1.15.1", - "@lodestar/utils": "^1.15.1", - "@lodestar/validator": "^1.15.1", + "@lodestar/api": "^1.16.0", + "@lodestar/beacon-node": "^1.16.0", + "@lodestar/config": "^1.16.0", + "@lodestar/db": "^1.16.0", + "@lodestar/light-client": "^1.16.0", + "@lodestar/logger": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/state-transition": "^1.16.0", + "@lodestar/types": "^1.16.0", + "@lodestar/utils": "^1.16.0", + "@lodestar/validator": "^1.16.0", "@multiformats/multiaddr": "^12.1.3", "bip39": "^3.1.0", "deepmerge": "^4.3.1", @@ -91,12 +89,12 @@ "proper-lockfile": "^4.1.2", "rimraf": "^4.4.1", "source-map-support": "^0.5.21", - "uint8arrays": "^4.0.9", + "uint8arrays": "^5.0.1", "uuidv4": "^6.2.13", "yargs": "^17.7.1" }, "devDependencies": { - "@lodestar/test-utils": "^1.15.1", + "@lodestar/test-utils": "^1.16.0", "@types/debug": "^4.1.7", "@types/expand-tilde": "^2.0.0", "@types/got": "^9.6.12", diff --git a/packages/cli/src/applyPreset.ts b/packages/cli/src/applyPreset.ts index 760c18dbbcd7..25f78b7d32ac 100644 --- a/packages/cli/src/applyPreset.ts +++ b/packages/cli/src/applyPreset.ts @@ -1,8 +1,8 @@ // MUST import this file first before anything and not import any Lodestar code. -// eslint-disable-next-line no-restricted-imports, import/no-extraneous-dependencies +// eslint-disable-next-line no-restricted-imports import {hasher} from "@chainsafe/persistent-merkle-tree/lib/hasher/as-sha256.js"; -// eslint-disable-next-line no-restricted-imports, import/no-extraneous-dependencies +// eslint-disable-next-line no-restricted-imports import {setHasher} from "@chainsafe/persistent-merkle-tree/lib/hasher/index.js"; // without setting this first, persistent-merkle-tree will use noble instead diff --git a/packages/cli/src/cmds/beacon/initBeaconState.ts b/packages/cli/src/cmds/beacon/initBeaconState.ts index 0185406db443..c8c444778991 100644 --- a/packages/cli/src/cmds/beacon/initBeaconState.ts +++ b/packages/cli/src/cmds/beacon/initBeaconState.ts @@ -15,7 +15,7 @@ import { } from "@lodestar/beacon-node"; import {Checkpoint} from "@lodestar/types/phase0"; -import {downloadOrLoadFile} from "../../util/index.js"; +import {downloadOrLoadFile, wrapFnError} from "../../util/index.js"; import {defaultNetwork, GlobalArgs} from "../../options/globalOptions.js"; import { fetchWeakSubjectivityState, @@ -31,7 +31,8 @@ async function initAndVerifyWeakSubjectivityState( logger: Logger, store: BeaconStateAllForks, wsState: BeaconStateAllForks, - wsCheckpoint: Checkpoint + wsCheckpoint: Checkpoint, + opts: {ignoreWeakSubjectivityCheck?: boolean} = {} ): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint: Checkpoint}> { // Check if the store's state and wsState are compatible if ( @@ -56,12 +57,16 @@ async function initAndVerifyWeakSubjectivityState( ); } - // Instead of warning user of wss check failure, we throw because user explicity wants to use - // the checkpoint sync - ensureWithinWeakSubjectivityPeriod(config, anchorState, anchorCheckpoint); + // Throw error unless user explicitly asked not to, in testnets can happen that wss period is too small + // that even some epochs of non finalization can cause finalized checkpoint to be out of valid range + const wssCheck = wrapFnError(() => ensureWithinWeakSubjectivityPeriod(config, anchorState, anchorCheckpoint)); + const isWithinWeakSubjectivityPeriod = wssCheck.err === null; + if (!isWithinWeakSubjectivityPeriod && !opts.ignoreWeakSubjectivityCheck) { + throw wssCheck.err; + } anchorState = await initStateFromAnchorState(config, db, logger, anchorState, { - isWithinWeakSubjectivityPeriod: true, + isWithinWeakSubjectivityPeriod, isCheckpointState, }); @@ -123,7 +128,11 @@ export async function initBeaconState( if (args.checkpointState) { return readWSState( lastDbState, - {checkpointState: args.checkpointState, wssCheckpoint: args.wssCheckpoint}, + { + checkpointState: args.checkpointState, + wssCheckpoint: args.wssCheckpoint, + ignoreWeakSubjectivityCheck: args.ignoreWeakSubjectivityCheck, + }, chainForkConfig, db, logger @@ -131,7 +140,11 @@ export async function initBeaconState( } else if (args.checkpointSyncUrl) { return fetchWSStateFromBeaconApi( lastDbState, - {checkpointSyncUrl: args.checkpointSyncUrl, wssCheckpoint: args.wssCheckpoint}, + { + checkpointSyncUrl: args.checkpointSyncUrl, + wssCheckpoint: args.wssCheckpoint, + ignoreWeakSubjectivityCheck: args.ignoreWeakSubjectivityCheck, + }, chainForkConfig, db, logger @@ -158,7 +171,7 @@ export async function initBeaconState( async function readWSState( lastDbState: BeaconStateAllForks | null, - wssOpts: {checkpointState: string; wssCheckpoint?: string}, + wssOpts: {checkpointState: string; wssCheckpoint?: string; ignoreWeakSubjectivityCheck?: boolean}, chainForkConfig: ChainForkConfig, db: IBeaconDb, logger: Logger @@ -166,19 +179,21 @@ async function readWSState( // weak subjectivity sync from a provided state file: // if a weak subjectivity checkpoint has been provided, it is used for additional verification // otherwise, the state itself is used for verification (not bad, because the trusted state has been explicitly provided) - const {checkpointState, wssCheckpoint} = wssOpts; + const {checkpointState, wssCheckpoint, ignoreWeakSubjectivityCheck} = wssOpts; const stateBytes = await downloadOrLoadFile(checkpointState); const wsState = getStateTypeFromBytes(chainForkConfig, stateBytes).deserializeToViewDU(stateBytes); const config = createBeaconConfig(chainForkConfig, wsState.genesisValidatorsRoot); const store = lastDbState ?? wsState; const checkpoint = wssCheckpoint ? getCheckpointFromArg(wssCheckpoint) : getCheckpointFromState(wsState); - return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsState, checkpoint); + return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsState, checkpoint, { + ignoreWeakSubjectivityCheck, + }); } async function fetchWSStateFromBeaconApi( lastDbState: BeaconStateAllForks | null, - wssOpts: {checkpointSyncUrl: string; wssCheckpoint?: string}, + wssOpts: {checkpointSyncUrl: string; wssCheckpoint?: string; ignoreWeakSubjectivityCheck?: boolean}, chainForkConfig: ChainForkConfig, db: IBeaconDb, logger: Logger @@ -201,5 +216,7 @@ async function fetchWSStateFromBeaconApi( const {wsState, wsCheckpoint} = await fetchWeakSubjectivityState(chainForkConfig, logger, wssOpts); const config = createBeaconConfig(chainForkConfig, wsState.genesisValidatorsRoot); const store = lastDbState ?? wsState; - return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsState, wsCheckpoint); + return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsState, wsCheckpoint, { + ignoreWeakSubjectivityCheck: wssOpts.ignoreWeakSubjectivityCheck, + }); } diff --git a/packages/cli/src/cmds/beacon/options.ts b/packages/cli/src/cmds/beacon/options.ts index c9918b5d2e41..a1d5b35fe5de 100644 --- a/packages/cli/src/cmds/beacon/options.ts +++ b/packages/cli/src/cmds/beacon/options.ts @@ -12,6 +12,7 @@ type BeaconExtraArgs = { checkpointState?: string; wssCheckpoint?: string; forceCheckpointSync?: boolean; + ignoreWeakSubjectivityCheck?: boolean; beaconDir?: string; dbDir?: string; persistInvalidSszObjectsDir?: string; @@ -75,6 +76,14 @@ export const beaconExtraOptions: CliCommandOptions = { group: "weak subjectivity", }, + ignoreWeakSubjectivityCheck: { + description: + "Ignore the checkpoint sync state failing the weak subjectivity check. This is relevant in testnets where the weak subjectivity period is too small for even few epochs of non finalization causing last finalized to be out of range. This flag is not recommended for mainnet use.", + hidden: true, + type: "boolean", + group: "weak subjectivity", + }, + beaconDir: { description: "Beacon root directory", defaultDescription: defaultBeaconPaths.beaconDir, @@ -143,7 +152,7 @@ type ENRArgs = { nat?: boolean; }; -const enrOptions: Record = { +const enrOptions: CliCommandOptions = { "enr.ip": { description: "Override ENR IP entry", type: "string", diff --git a/packages/cli/src/cmds/validator/handler.ts b/packages/cli/src/cmds/validator/handler.ts index 91789c99ee3b..a98eb0755ad0 100644 --- a/packages/cli/src/cmds/validator/handler.ts +++ b/packages/cli/src/cmds/validator/handler.ts @@ -22,6 +22,7 @@ import {GlobalArgs} from "../../options/index.js"; import {YargsError, cleanOldLogFiles, getDefaultGraffiti, mkdir, parseLoggerArgs} from "../../util/index.js"; import {onGracefulShutdown, parseFeeRecipient, parseProposerConfig} from "../../util/index.js"; import {getVersionData} from "../../util/version.js"; +import {parseBuilderSelection, parseBuilderBoostFactor} from "../../util/proposerConfig.js"; import {getAccountPaths, getValidatorPaths} from "./paths.js"; import {IValidatorCliArgs, validatorMetricsDefaultOptions, validatorMonitoringDefaultOptions} from "./options.js"; import {getSignersFromArgs} from "./signers/index.js"; @@ -254,24 +255,6 @@ function getProposerConfigFromArgs( return valProposerConfig; } -function parseBuilderSelection(builderSelection?: string): routes.validator.BuilderSelection | undefined { - if (builderSelection) { - switch (builderSelection) { - case "maxprofit": - break; - case "builderalways": - break; - case "builderonly": - break; - case "executiononly": - break; - default: - throw new YargsError("Invalid input for builder selection, check help"); - } - } - return builderSelection as routes.validator.BuilderSelection; -} - function parseBroadcastValidation(broadcastValidation?: string): routes.beacon.BroadcastValidation | undefined { if (broadcastValidation) { switch (broadcastValidation) { @@ -286,13 +269,3 @@ function parseBroadcastValidation(broadcastValidation?: string): routes.beacon.B return broadcastValidation as routes.beacon.BroadcastValidation; } - -function parseBuilderBoostFactor(boostFactor?: string): bigint | undefined { - if (boostFactor === undefined) return; - - if (!/^\d+$/.test(boostFactor)) { - throw new YargsError("Invalid input for builder boost factor, must be a valid number without decimals"); - } - - return BigInt(boostFactor); -} diff --git a/packages/cli/src/cmds/validator/options.ts b/packages/cli/src/cmds/validator/options.ts index 6d8d40f2dfdb..a5b4044f6867 100644 --- a/packages/cli/src/cmds/validator/options.ts +++ b/packages/cli/src/cmds/validator/options.ts @@ -242,7 +242,8 @@ export const validatorOptions: CliCommandOptions = { "builder.selection": { type: "string", - description: "Builder block selection strategy `maxprofit`, `builderalways`, `builderonly` or `executiononly`", + description: + "Builder block selection strategy `maxprofit`, `builderalways`, `builderonly`, `executionalways`, or `executiononly`", defaultDescription: `${defaultOptions.builderSelection}`, group: "builder", }, diff --git a/packages/cli/src/networks/holesky.ts b/packages/cli/src/networks/holesky.ts index 5c0f99ee53de..0f89cadaed33 100644 --- a/packages/cli/src/networks/holesky.ts +++ b/packages/cli/src/networks/holesky.ts @@ -13,4 +13,5 @@ export const bootEnrs = [ "enr:-Ly4QGbOw4xNel5EhmDsJJ-QhC9XycWtsetnWoZ0uRy381GHdHsNHJiCwDTOkb3S1Ade0SFQkWJX_pgb3g8Jfh93rvMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQOxKv9sv3zKF8GDewgFGGHKP5HCZZpPpTrwl9eXKAWGxIhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA", "enr:-LS4QG0uV4qvcpJ-HFDJRGBmnlD3TJo7yc4jwK8iP7iKaTlfQ5kZvIDspLMJhk7j9KapuL9yyHaZmwTEZqr10k9XumyCEcmHYXR0bmV0c4gAAAAABgAAAIRldGgykGm32XQEAXAAAAEAAAAAAACCaWSCdjSCaXCErK4j-YlzZWNwMjU2azGhAgfWRBEJlb7gAhXIB5ePmjj2b8io0UpEenq1Kl9cxStJg3RjcIIjKIN1ZHCCIyg", "enr:-Le4QLoE1wFHSlGcm48a9ZESb_MRLqPPu6G0vHqu4MaUcQNDHS69tsy-zkN0K6pglyzX8m24mkb-LtBcbjAYdP1uxm4BhGV0aDKQabfZdAQBcAAAAQAAAAAAAIJpZIJ2NIJpcIQ5gR6Wg2lwNpAgAUHQBwEQAAAAAAAAADR-iXNlY3AyNTZrMaEDPMSNdcL92uNIyCsS177Z6KTXlbZakQqxv3aQcWawNXeDdWRwgiMohHVkcDaCI4I", + "enr:-KG4QC9Wm32mtzB5Fbj2ri2TEKglHmIWgvwTQCvNHBopuwpNAi1X6qOsBg_Z1-Bee-kfSrhzUQZSgDUyfH5outUprtoBgmlkgnY0gmlwhHEel3eDaXA2kP6AAAAAAAAAAlBW__4Srr-Jc2VjcDI1NmsxoQO7KE63Z4eSI55S1Yn7q9_xFkJ1Wt-a3LgiXuKGs19s0YN1ZHCCIyiEdWRwNoIjKA", ]; diff --git a/packages/cli/src/options/beaconNodeOptions/chain.ts b/packages/cli/src/options/beaconNodeOptions/chain.ts index 0c9280f5330c..a324e3060e1f 100644 --- a/packages/cli/src/options/beaconNodeOptions/chain.ts +++ b/packages/cli/src/options/beaconNodeOptions/chain.ts @@ -26,6 +26,7 @@ export type ChainArgs = { broadcastValidationStrictness?: string; "chain.minSameMessageSignatureSetsToBatch"?: number; "chain.maxShufflingCacheEpochs"?: number; + "chain.archiveBlobEpochs"?: number; }; export function parseArgs(args: ChainArgs): IBeaconNodeOptions["chain"] { @@ -53,6 +54,7 @@ export function parseArgs(args: ChainArgs): IBeaconNodeOptions["chain"] { minSameMessageSignatureSetsToBatch: args["chain.minSameMessageSignatureSetsToBatch"] ?? defaultOptions.chain.minSameMessageSignatureSetsToBatch, maxShufflingCacheEpochs: args["chain.maxShufflingCacheEpochs"] ?? defaultOptions.chain.maxShufflingCacheEpochs, + archiveBlobEpochs: args["chain.archiveBlobEpochs"], }; } @@ -212,4 +214,10 @@ Will double processing times. Use only for debugging purposes.", default: defaultOptions.chain.maxShufflingCacheEpochs, group: "chain", }, + + "chain.archiveBlobEpochs": { + description: "Number of epochs to retain finalized blobs (minimum of MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS)", + type: "number", + group: "chain", + }, }; diff --git a/packages/cli/src/util/command.ts b/packages/cli/src/util/command.ts index 0dd2fd82bc9f..ccc8f47e71a9 100644 --- a/packages/cli/src/util/command.ts +++ b/packages/cli/src/util/command.ts @@ -6,15 +6,26 @@ export interface CliExample { description?: string; } -export interface CliOptionDefinition extends Options { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface CliOptionDefinition extends Options { example?: Omit; + // Ensure `type` property matches type of `T` + type: T extends string + ? "string" + : T extends number + ? "number" + : T extends boolean + ? "boolean" + : T extends Array + ? "array" + : never; } export type CliCommandOptions = Required<{ [K in keyof OwnArgs]: undefined extends OwnArgs[K] - ? CliOptionDefinition + ? CliOptionDefinition : // If arg cannot be undefined it must specify a default value - CliOptionDefinition & Required>; + CliOptionDefinition & Required>; }>; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -57,7 +68,6 @@ export function registerCommandToYargs(yargs: Argv, cliCommand: CliCommand(promise: Promise): Promise> { return {err: err as Error}; } } +export function wrapFnError(fn: () => T): Result { + try { + return {err: null, result: fn()}; + } catch (err) { + return {err: err as Error}; + } +} diff --git a/packages/cli/src/util/lockfile.ts b/packages/cli/src/util/lockfile.ts index f7a6ddf4d57d..d28504507c7c 100644 --- a/packages/cli/src/util/lockfile.ts +++ b/packages/cli/src/util/lockfile.ts @@ -11,8 +11,8 @@ export function lockFilepath(filepath: string): void { realpath: false, }); } catch (e) { - if (isLockfileError(e) && e.code === "ELOCKED") { - e.message = `${filepath} is already in use by another process`; + if (isLockfileError(e) && (e.code === "ELOCKED" || e.code === "ENOTDIR")) { + e.message = `'${filepath}' already in use by another process`; } throw e; } @@ -38,7 +38,7 @@ export function unlockFilepath(filepath: string): void { } // https://github.com/moxystudio/node-proper-lockfile/blob/9f8c303c91998e8404a911dc11c54029812bca69/lib/lockfile.js#L53 -export type LockfileError = Error & {code: "ELOCKED" | "ENOTACQUIRED"}; +export type LockfileError = Error & {code: "ELOCKED" | "ENOTACQUIRED" | "ENOTDIR"}; function isLockfileError(e: unknown): e is LockfileError { return e instanceof Error && (e as LockfileError).code !== undefined; diff --git a/packages/cli/src/util/proposerConfig.ts b/packages/cli/src/util/proposerConfig.ts index 2f3c71236255..ae56cc45513a 100644 --- a/packages/cli/src/util/proposerConfig.ts +++ b/packages/cli/src/util/proposerConfig.ts @@ -20,6 +20,7 @@ type ProposerConfigFileSection = { // for js-yaml gas_limit?: number; selection?: routes.validator.BuilderSelection; + boost_factor?: bigint; }; }; @@ -57,7 +58,7 @@ function parseProposerConfigSection( overrideConfig?: ProposerConfig ): ProposerConfig { const {graffiti, strict_fee_recipient_check, fee_recipient, builder} = proposerFileSection; - const {gas_limit, selection: builderSelection} = builder || {}; + const {gas_limit, selection: builderSelection, boost_factor} = builder || {}; if (graffiti !== undefined && typeof graffiti !== "string") { throw Error("graffiti is not 'string"); @@ -79,6 +80,9 @@ function parseProposerConfigSection( throw Error("(Number.isNaN(Number(gas_limit)) 2"); } } + if (boost_factor !== undefined && typeof boost_factor !== "string") { + throw Error("boost_factor is not 'string"); + } return { graffiti: overrideConfig?.graffiti ?? graffiti, @@ -88,7 +92,8 @@ function parseProposerConfigSection( feeRecipient: overrideConfig?.feeRecipient ?? (fee_recipient ? parseFeeRecipient(fee_recipient) : undefined), builder: { gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined), - selection: overrideConfig?.builder?.selection ?? builderSelection, + selection: overrideConfig?.builder?.selection ?? parseBuilderSelection(builderSelection), + boostFactor: overrideConfig?.builder?.boostFactor ?? parseBuilderBoostFactor(boost_factor), }, }; } @@ -98,3 +103,33 @@ export function readProposerConfigDir(filepath: string, filename: string): Propo const proposerConfigJSON = JSON.parse(proposerConfigStr) as ProposerConfigFileSection; return proposerConfigJSON; } + +export function parseBuilderSelection(builderSelection?: string): routes.validator.BuilderSelection | undefined { + if (builderSelection) { + switch (builderSelection) { + case "maxprofit": + break; + case "builderalways": + break; + case "builderonly": + break; + case "executionalways": + break; + case "executiononly": + break; + default: + throw Error("Invalid input for builder selection, check help"); + } + } + return builderSelection as routes.validator.BuilderSelection; +} + +export function parseBuilderBoostFactor(boostFactor?: string): bigint | undefined { + if (boostFactor === undefined) return; + + if (!/^\d+$/.test(boostFactor)) { + throw Error("Invalid input for builder boost factor, must be a valid number without decimals"); + } + + return BigInt(boostFactor); +} diff --git a/packages/cli/test/e2e/importKeystoresFromApi.test.ts b/packages/cli/test/e2e/importKeystoresFromApi.test.ts index 1cf5f107e226..8733d6cd5c48 100644 --- a/packages/cli/test/e2e/importKeystoresFromApi.test.ts +++ b/packages/cli/test/e2e/importKeystoresFromApi.test.ts @@ -95,10 +95,10 @@ describe("import keystores from api", function () { validator.on("exit", (code) => { if (code !== null && code > 0) { // process should exit with code > 0, and an error related to locks. Sample error: - // vc 351591: ✖ Error: /tmp/tmp-5080-lwNxdM5Ok9ya/import-keystores-test/keystores/0x8be678633e927aa0435addad5dcd5283fef6110d91362519cd6d43e61f6c017d724fa579cc4b2972134e050b6ba120c0/voting-keystore.json is already in use by another process + // vc 351591: ✖ Error: '/tmp/tmp-5080-lwNxdM5Ok9ya/import-keystores-test/keystores/0x8be678633e927aa0435addad5dcd5283fef6110d91362519cd6d43e61f6c017d724fa579cc4b2972134e050b6ba120c0/voting-keystore.json' already in use by another process // at /home/runner/actions-runner/_work/lodestar/lodestar/node_modules/proper-lockfile/lib/lockfile.js:68:47 // ... more stack trace - if (/Error.*voting-keystore\.json is already in use by another process/.test(vcProc2Stderr.read())) { + if (/Error.*voting-keystore\.json' already in use by another process/.test(vcProc2Stderr.read())) { resolve(); } else { reject(Error(`Second validator proc exited with unknown error. stderr:\n${vcProc2Stderr.read()}`)); diff --git a/packages/cli/test/scripts/e2e_test_env.ts b/packages/cli/test/scripts/e2e_test_env.ts index b35b3524d8cf..b5fa48260194 100644 --- a/packages/cli/test/scripts/e2e_test_env.ts +++ b/packages/cli/test/scripts/e2e_test_env.ts @@ -14,6 +14,7 @@ const {forkConfig} = defineSimTestConfig({ BELLATRIX_FORK_EPOCH: bellatrixForkEpoch, CAPELLA_FORK_EPOCH: capellaForkEpoch, runTillEpoch: Infinity, + initialNodes: 2, }); const env = await SimulationEnvironment.initWithDefaults( diff --git a/packages/cli/test/sim/backup_eth_provider.test.ts b/packages/cli/test/sim/backup_eth_provider.test.ts index 40ad7d4661de..52ad83a4f5f8 100644 --- a/packages/cli/test/sim/backup_eth_provider.test.ts +++ b/packages/cli/test/sim/backup_eth_provider.test.ts @@ -16,6 +16,7 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({ ALTAIR_FORK_EPOCH: altairForkEpoch, BELLATRIX_FORK_EPOCH: bellatrixForkEpoch, runTillEpoch: runTillEpoch + syncWaitEpoch, + initialNodes: 3, }); const env = await SimulationEnvironment.initWithDefaults( @@ -78,4 +79,7 @@ await waitForSlot(env.clock.getLastSlotOfEpoch(bellatrixForkEpoch) + activePrese env, }); +await node2.beacon.job.stop(); +await node3.beacon.job.stop(); + await env.stop(); diff --git a/packages/cli/test/sim/deneb.test.ts b/packages/cli/test/sim/deneb.test.ts index 5ed84d3d2792..b66252b06892 100644 --- a/packages/cli/test/sim/deneb.test.ts +++ b/packages/cli/test/sim/deneb.test.ts @@ -18,12 +18,13 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({ ALTAIR_FORK_EPOCH: altairForkEpoch, BELLATRIX_FORK_EPOCH: bellatrixForkEpoch, runTillEpoch: runTillEpoch + syncWaitEpoch, + initialNodes: 2, }); const env = await SimulationEnvironment.initWithDefaults( { - id: "multi-fork", - logsDir: path.join(logFilesDir, "multi-fork"), + id: "deneb", + logsDir: path.join(logFilesDir, "deneb"), forkConfig, }, [ diff --git a/packages/cli/test/sim/endpoints.test.ts b/packages/cli/test/sim/endpoints.test.ts index ea55b70e26c1..a31b561e0578 100644 --- a/packages/cli/test/sim/endpoints.test.ts +++ b/packages/cli/test/sim/endpoints.test.ts @@ -16,6 +16,7 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({ ALTAIR_FORK_EPOCH: altairForkEpoch, BELLATRIX_FORK_EPOCH: bellatrixForkEpoch, runTillEpoch: 2, + initialNodes: 1, }); const env = await SimulationEnvironment.initWithDefaults( diff --git a/packages/cli/test/sim/mixed_client.test.ts b/packages/cli/test/sim/mixed_client.test.ts index ad2095f8c378..80c20471ede5 100644 --- a/packages/cli/test/sim/mixed_client.test.ts +++ b/packages/cli/test/sim/mixed_client.test.ts @@ -17,6 +17,7 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({ BELLATRIX_FORK_EPOCH: bellatrixForkEpoch, CAPELLA_FORK_EPOCH: capellaForkEpoch, runTillEpoch: runTillEpoch + syncWaitEpoch, + initialNodes: 2, }); const env = await SimulationEnvironment.initWithDefaults( diff --git a/packages/cli/test/sim/multi_fork.test.ts b/packages/cli/test/sim/multi_fork.test.ts index 24f42498c638..1302ad98bc82 100644 --- a/packages/cli/test/sim/multi_fork.test.ts +++ b/packages/cli/test/sim/multi_fork.test.ts @@ -7,6 +7,8 @@ import {SimulationEnvironment} from "../utils/simulation/SimulationEnvironment.j import {defineSimTestConfig, logFilesDir} from "../utils/simulation/utils/index.js"; import { connectAllNodes, + connectNewCLNode, + connectNewELNode, connectNewNode, waitForHead, waitForNodeSync, @@ -27,6 +29,7 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({ BELLATRIX_FORK_EPOCH: bellatrixForkEpoch, CAPELLA_FORK_EPOCH: capellaForkEpoch, runTillEpoch: runTillEpoch + syncWaitEpoch, + initialNodes: 5, }); const env = await SimulationEnvironment.initWithDefaults( @@ -171,9 +174,27 @@ const checkpointSync = await env.createNodePair({ keysCount: 0, }); +// TODO: A workaround for this issue for sim tests only +// 1. Start the execution node and let it connect to network +// 2. Wait for few seconds +// 3. And later start the beacon node and connect to network +// 4. With this delay the execution node would be synced before the beacon node starts +// https://github.com/ChainSafe/lodestar/issues/6435 +// Revert to following code once the issue is fixed +// await rangeSync.execution.job.start(); +// await rangeSync.beacon.job.start(); +// await connectNewNode(rangeSync, env.nodes); await rangeSync.execution.job.start(); +await connectNewELNode( + rangeSync.execution, + env.nodes.map((node) => node.execution) +); +await sleep(4000); await rangeSync.beacon.job.start(); -await connectNewNode(rangeSync, env.nodes); +await connectNewCLNode( + rangeSync.beacon, + env.nodes.map((node) => node.beacon) +); await checkpointSync.execution.job.start(); await checkpointSync.beacon.job.start(); @@ -258,4 +279,7 @@ await waitForHead(env, unknownBlockSync, { slot: headForUnknownBlockSync.response.data.message.slot, }); +await unknownBlockSync.beacon.job.stop(); +await unknownBlockSync.execution.job.stop(); + await env.stop(); diff --git a/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts b/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts index ee0fedf301b6..40964a0a7b7d 100644 --- a/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts +++ b/packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import {randomBytes} from "node:crypto"; -import {describe, it, expect, beforeEach} from "vitest"; +import {describe, it, expect, beforeEach, vi} from "vitest"; import tmp from "tmp"; import {Keystore} from "@chainsafe/bls-keystore"; import bls from "@chainsafe/bls"; @@ -12,6 +12,7 @@ import {LocalKeystoreDefinition} from "../../../../../src/cmds/validator/keymana const numberOfSigners = 10; describe("keystoreCache", () => { + vi.setConfig({testTimeout: 10000, hookTimeout: 50000}); let definitions: LocalKeystoreDefinition[]; let signers: SignerLocal[]; let secretKeys: Uint8Array[]; @@ -50,7 +51,7 @@ describe("keystoreCache", () => { passwords.push(password); secretKeys.push(secretKey.toBytes()); } - }, 50000); + }); describe("writeKeystoreCache", () => { it("should write a valid keystore cache file", async () => { diff --git a/packages/cli/test/unit/options/beaconNodeOptions.test.ts b/packages/cli/test/unit/options/beaconNodeOptions.test.ts index 8a9ccff5a917..22faea094314 100644 --- a/packages/cli/test/unit/options/beaconNodeOptions.test.ts +++ b/packages/cli/test/unit/options/beaconNodeOptions.test.ts @@ -36,6 +36,7 @@ describe("options / beaconNodeOptions", () => { "chain.trustedSetup": "", "chain.minSameMessageSignatureSetsToBatch": 32, "chain.maxShufflingCacheEpochs": 100, + "chain.archiveBlobEpochs": 10000, emitPayloadAttributes: false, eth1: true, @@ -139,6 +140,7 @@ describe("options / beaconNodeOptions", () => { trustedSetup: "", minSameMessageSignatureSetsToBatch: 32, maxShufflingCacheEpochs: 100, + archiveBlobEpochs: 10000, }, eth1: { enabled: true, diff --git a/packages/cli/test/unit/validator/parseProposerConfig.test.ts b/packages/cli/test/unit/validator/parseProposerConfig.test.ts index fcb6933f035b..993d9640e47c 100644 --- a/packages/cli/test/unit/validator/parseProposerConfig.test.ts +++ b/packages/cli/test/unit/validator/parseProposerConfig.test.ts @@ -17,6 +17,7 @@ const testValue = { builder: { gasLimit: 30000000, selection: undefined, + boostFactor: undefined, }, }, "0xa4855c83d868f772a579133d9f23818008417b743e8447e235d8eb78b1d8f8a9f63f98c551beb7de254400f89592314d": { @@ -26,6 +27,7 @@ const testValue = { builder: { gasLimit: 35000000, selection: routes.validator.BuilderSelection.MaxProfit, + boostFactor: BigInt(18446744073709551616), }, }, }, @@ -36,6 +38,7 @@ const testValue = { builder: { gasLimit: 30000000, selection: routes.validator.BuilderSelection.BuilderAlways, + boostFactor: BigInt(100), }, }, }; diff --git a/packages/cli/test/unit/validator/proposerConfigs/validData.yaml b/packages/cli/test/unit/validator/proposerConfigs/validData.yaml index 6b7e7074b118..5ea7e1bebea7 100644 --- a/packages/cli/test/unit/validator/proposerConfigs/validData.yaml +++ b/packages/cli/test/unit/validator/proposerConfigs/validData.yaml @@ -10,6 +10,7 @@ proposer_config: builder: gas_limit: "35000000" selection: "maxprofit" + boost_factor: "18446744073709551616" default_config: graffiti: 'default graffiti' strict_fee_recipient_check: "true" @@ -17,3 +18,4 @@ default_config: builder: gas_limit: "30000000" selection: "builderalways" + boost_factor: "100" diff --git a/packages/cli/test/utils/simulation/ExternalSignerServer.ts b/packages/cli/test/utils/simulation/ExternalSignerServer.ts index f6d258ddfa00..1a23c259023e 100644 --- a/packages/cli/test/utils/simulation/ExternalSignerServer.ts +++ b/packages/cli/test/utils/simulation/ExternalSignerServer.ts @@ -1,4 +1,4 @@ -import fastify from "fastify"; +import {fastify} from "fastify"; import {fromHexString} from "@chainsafe/ssz"; import type {SecretKey} from "@chainsafe/bls/types"; import {EXTERNAL_SIGNER_BASE_PORT} from "./constants.js"; diff --git a/packages/cli/test/utils/simulation/SimulationEnvironment.ts b/packages/cli/test/utils/simulation/SimulationEnvironment.ts index 5baec1b79048..2801c5604385 100644 --- a/packages/cli/test/utils/simulation/SimulationEnvironment.ts +++ b/packages/cli/test/utils/simulation/SimulationEnvironment.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import {EventEmitter} from "node:events"; import fs from "node:fs"; import {mkdir, writeFile} from "node:fs/promises"; import path from "node:path"; @@ -9,6 +7,9 @@ import {nodeUtils} from "@lodestar/beacon-node"; import {ChainForkConfig} from "@lodestar/config"; import {activePreset} from "@lodestar/params"; import {BeaconStateAllForks, interopSecretKey} from "@lodestar/state-transition"; +import {prettyMsToTime} from "@lodestar/utils"; +import {LogLevel, TimestampFormatCode} from "@lodestar/logger"; +import {getNodeLogger, LoggerNode} from "@lodestar/logger/node"; import {EpochClock, MS_IN_SEC} from "./EpochClock.js"; import {ExternalSignerServer} from "./ExternalSignerServer.js"; import {SimulationTracker} from "./SimulationTracker.js"; @@ -36,15 +37,13 @@ interface StartOpts { runTimeoutMs: number; } -/* eslint-disable no-console */ - export class SimulationEnvironment { readonly nodes: NodePair[] = []; readonly clock: EpochClock; readonly tracker: SimulationTracker; - readonly emitter: EventEmitter; readonly runner: IRunner; readonly externalSigner: ExternalSignerServer; + readonly logger: LoggerNode; readonly forkConfig: ChainForkConfig; readonly options: SimulationOptions; @@ -52,11 +51,20 @@ export class SimulationEnvironment { private keysCount = 0; private nodePairCount = 0; private genesisState?: BeaconStateAllForks; + private runTimeout?: NodeJS.Timeout; private constructor(forkConfig: ChainForkConfig, options: SimulationOptions) { this.forkConfig = forkConfig; this.options = options; + this.logger = getNodeLogger({ + level: LogLevel.debug, + module: `sim-${this.options.id}`, + timestampFormat: { + format: TimestampFormatCode.DateRegular, + }, + file: {level: LogLevel.debug, filepath: path.join(options.logsDir, `simulation-${this.options.id}.log`)}, + }); this.clock = new EpochClock({ genesisTime: this.options.genesisTime + this.forkConfig.GENESIS_DELAY, secondsPerSlot: this.forkConfig.SECONDS_PER_SLOT, @@ -64,10 +72,11 @@ export class SimulationEnvironment { signal: this.options.controller.signal, }); - this.emitter = new EventEmitter(); this.externalSigner = new ExternalSignerServer([]); - this.runner = new Runner({logsDir: this.options.logsDir}); + this.runner = new Runner({logsDir: this.options.logsDir, logger: this.logger.child({module: "runner"})}); this.tracker = SimulationTracker.initWithDefaultAssertions({ + logsDir: options.logsDir, + logger: this.logger, nodes: [], config: this.forkConfig, clock: this.clock, @@ -96,14 +105,20 @@ export class SimulationEnvironment { async start(opts: StartOpts): Promise { const currentTime = Date.now(); + this.logger.info( + `Starting simulation environment "${this.options.id}". currentTime=${new Date( + currentTime + ).toISOString()} simulationTimeout=${prettyMsToTime(opts.runTimeoutMs)}` + ); + if (opts.runTimeoutMs > 0) { - setTimeout(() => { + this.runTimeout = setTimeout(() => { const slots = this.clock.getSlotFor((currentTime + opts.runTimeoutMs) / MS_IN_SEC); const epoch = this.clock.getEpochForSlot(slots); const slot = this.clock.getSlotIndexInEpoch(slots); this.stop(1, `Sim run timeout in ${opts.runTimeoutMs}ms (approx. ${epoch}/${slot}).`).catch((e) => - console.error("Error on stop", e) + this.logger.error("Error on stop", e) ); }, opts.runTimeoutMs); } @@ -116,8 +131,8 @@ export class SimulationEnvironment { this.stop( 1, - `Start sequence not completed before genesis, in ${msToGenesis}ms (approx. ${epoch}/${slot}).` - ).catch((e) => console.error("Error on stop", e)); + `Start sequence not completed before genesis, in ${prettyMsToTime(msToGenesis)} (approx. ${epoch}/${slot}).` + ).catch((e) => this.logger.error("Error on stop", e)); }, msToGenesis); try { @@ -126,21 +141,27 @@ export class SimulationEnvironment { await mkdir(this.options.rootDir); } + this.logger.info("Starting the simulation runner"); await this.runner.start(); + + this.logger.info("Starting execution nodes"); await Promise.all(this.nodes.map((node) => node.execution.job.start())); + this.logger.info("Initializing genesis state for beacon nodes"); await this.initGenesisState(); if (!this.genesisState) { throw new Error("The genesis state for CL clients is not defined."); } + this.logger.info("Starting beacon nodes"); await Promise.all(this.nodes.map((node) => node.beacon.job.start())); + + this.logger.info("Starting validators"); await Promise.all(this.nodes.map((node) => node.validator?.job.start())); if (this.nodes.some((node) => node.validator?.keys.type === "remote")) { - console.log("Starting external signer..."); + this.logger.info("Starting external signer"); await this.externalSigner.start(); - console.log("Started external signer"); for (const node of this.nodes) { if (node.validator?.keys.type === "remote") { @@ -151,11 +172,12 @@ export class SimulationEnvironment { url: this.externalSigner.url, })) ); - console.log(`Imported remote keys for node ${node.id}`); + this.logger.info(`Imported remote keys for node ${node.id}`); } } } + this.logger.info("Starting the simulation tracker"); await this.tracker.start(); await Promise.all(this.nodes.map((node) => this.tracker.track(node))); } catch (error) { @@ -170,8 +192,8 @@ export class SimulationEnvironment { process.removeAllListeners("uncaughtException"); process.removeAllListeners("SIGTERM"); process.removeAllListeners("SIGINT"); - console.log(`Simulation environment "${this.options.id}" is stopping: ${message}`); - await this.tracker.stop(); + this.logger.info(`Simulation environment "${this.options.id}" is stopping: ${message}`); + await this.tracker.stop({dumpStores: true}); await Promise.all(this.nodes.map((node) => node.validator?.job.stop())); await Promise.all(this.nodes.map((node) => node.beacon.job.stop())); await Promise.all(this.nodes.map((node) => node.execution.job.stop())); @@ -179,6 +201,10 @@ export class SimulationEnvironment { await this.runner.stop(); this.options.controller.abort(); + if (this.runTimeout) { + clearTimeout(this.runTimeout); + } + if (this.tracker.getErrorCount() > 0) { this.tracker.reporter.summary(); process.exit(this.tracker.getErrorCount() > 0 ? 1 : code); diff --git a/packages/cli/test/utils/simulation/SimulationTracker.ts b/packages/cli/test/utils/simulation/SimulationTracker.ts index aa38a5c6dcec..3c2365ef9979 100644 --- a/packages/cli/test/utils/simulation/SimulationTracker.ts +++ b/packages/cli/test/utils/simulation/SimulationTracker.ts @@ -1,8 +1,11 @@ import EventEmitter from "node:events"; -import Debug from "debug"; +import path from "node:path"; +import fs from "node:fs/promises"; +import createDebug from "debug"; import {routes} from "@lodestar/api/beacon"; import {ChainForkConfig} from "@lodestar/config"; import {Epoch, Slot} from "@lodestar/types"; +import {LoggerNode} from "@lodestar/logger/node"; import {isNullish} from "../../utils.js"; import {EpochClock} from "./EpochClock.js"; import { @@ -20,13 +23,15 @@ import {defaultAssertions} from "./assertions/defaults/index.js"; import {TableReporter} from "./TableReporter.js"; import {fetchBlock} from "./utils/network.js"; -const debug = Debug("lodestar:sim:tracker"); +const debug = createDebug("lodestar:sim:tracker"); interface SimulationTrackerInitOptions { nodes: NodePair[]; config: ChainForkConfig; clock: EpochClock; signal: AbortSignal; + logger: LoggerNode; + logsDir: string; } export enum SimulationTrackerEvent { @@ -62,10 +67,20 @@ export function getStoresForAssertions( return filterStores as StoreTypes; } -/* eslint-disable no-console */ +async function pathExists(filePath: string): Promise { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +} + export class SimulationTracker { readonly emitter = new EventEmitter(); readonly reporter: SimulationReporter>; + readonly logger: LoggerNode; + readonly logsDir: string; private lastSeenSlot: Map = new Map(); private slotCapture: Map = new Map(); @@ -73,21 +88,24 @@ export class SimulationTracker { private nodes: NodePair[]; private clock: EpochClock; private forkConfig: ChainForkConfig; - private running: boolean = false; + private running = false; private errors: SimulationAssertionError[] = []; private stores: Stores; private assertions: SimulationAssertion[]; private assertionIdsMap: Record = {}; - private constructor({signal, nodes, clock, config}: SimulationTrackerInitOptions) { + private constructor({signal, nodes, clock, config, logger, logsDir}: SimulationTrackerInitOptions) { this.signal = signal; this.nodes = nodes; this.clock = clock; this.forkConfig = config; + this.logsDir = logsDir; + this.logger = logger.child({module: "tracker"}); this.stores = {} as StoreTypes & StoreType; this.assertions = [] as SimulationAssertion[]; this.reporter = new TableReporter({ + logger: this.logger.child({module: "reporter"}), clock: this.clock, forkConfig: this.forkConfig, nodes: this.nodes, @@ -162,19 +180,42 @@ export class SimulationTracker { // Start clock loop on current slot or genesis this.clockLoop(Math.max(this.clock.currentSlot, 0)).catch((e) => { - console.error("error on clockLoop", e); + this.logger.error("error on clockLoop", e); }); } - async stop(): Promise { + async stop(opts: {dumpStores: boolean}): Promise { this.running = false; + + if (opts.dumpStores) { + if (!(await pathExists(path.join(this.logsDir, "data")))) + await fs.mkdir(path.join(this.logsDir, "data"), {recursive: true}); + + for (const assertion of this.assertions) { + if (!assertion.dump) continue; + + const data = await assertion.dump({ + fork: this.forkConfig.getForkName(this.clock.currentSlot), + forkConfig: this.forkConfig, + clock: this.clock, + epoch: this.clock.currentEpoch, + store: this.stores[assertion.id], + slot: this.clock.currentSlot, + nodes: this.nodes, + }); + + for (const filename of Object.keys(data)) { + await fs.writeFile(path.join(this.logsDir, "data", filename), data[filename]); + } + } + } } async clockLoop(slot: number): Promise { while (this.running && !this.signal.aborted) { // Wait for 2/3 of the slot to consider it missed await this.clock.waitForStartOfSlot(slot + 2 / 3, slot > 0).catch((e) => { - console.error("error on waitForStartOfSlot", e); + this.logger.error("error on waitForStartOfSlot", e); }); this.reporter.progress(slot); slot++; diff --git a/packages/cli/test/utils/simulation/TableRenderer.ts b/packages/cli/test/utils/simulation/TableRenderer.ts index 5ceab13a8918..3ebeeff485fb 100644 --- a/packages/cli/test/utils/simulation/TableRenderer.ts +++ b/packages/cli/test/utils/simulation/TableRenderer.ts @@ -1,12 +1,19 @@ +import {Logger} from "@lodestar/logger"; import {strFixedSize} from "./utils/index.js"; +const V_SEP_M = " │ "; +const V_SEP_S = "│ "; +const V_SEP_E = " │"; + export class TableRenderer { + readonly logger: Logger; private columnsSizes: Record; private columns: Columns[]; private rows: Record[]; private totalWidth: number; - constructor(columnWithSizes: Record) { + constructor(columnWithSizes: Record, {logger}: {logger: Logger}) { + this.logger = logger; this.columnsSizes = columnWithSizes; this.columns = Object.keys(columnWithSizes) as Columns[]; this.rows = []; @@ -20,54 +27,42 @@ export class TableRenderer { } addEmptyRow(text: string): void { - this.printHSeparator(true); - this.printVSeparator("start"); - process.stdout.write(strFixedSize(text, this.totalWidth - 4)); - this.printVSeparator("end"); - this.printHSeparator(true); + this.printHSeparator(); + this.logger.info(`${V_SEP_S}${strFixedSize(text, this.totalWidth - 4)}${V_SEP_E}`); + this.printHSeparator(); } printHeader(): void { - this.printHSeparator(true); - this.printVSeparator("start"); + this.printHSeparator(); + const output = [V_SEP_S]; for (const [index, column] of this.columns.entries()) { - process.stdout.write(strFixedSize(column, this.columnsSizes[column])); + output.push(strFixedSize(column, this.columnsSizes[column])); if (index === this.columns.length - 1) { - this.printVSeparator("end"); + output.push(V_SEP_E); } else { - this.printVSeparator(); + output.push(V_SEP_M); } } - this.printHSeparator(true); + this.logger.info(output.join("")); + this.printHSeparator(); } printRow(rowIndex: number): void { const row = this.rows[rowIndex]; - - this.printVSeparator("start"); + const output = [V_SEP_S]; for (const [index, column] of this.columns.entries()) { const value = String(row[column]); - process.stdout.write(value.padEnd(this.columnsSizes[column])); + output.push(value.padEnd(this.columnsSizes[column])); if (index === this.columns.length - 1) { - this.printVSeparator("end"); + output.push(V_SEP_E); } else { - this.printVSeparator(); + output.push(V_SEP_M); } } + this.logger.info(output.join("")); } - private printHSeparator(lineBreak: boolean): void { - process.stdout.write(`┼${"─".repeat(this.totalWidth - 2)}┼`); - if (lineBreak) process.stdout.write("\n"); - } - - private printVSeparator(mode?: "start" | "end"): void { - if (!mode) { - process.stdout.write(" │ "); - } else if (mode === "start") { - process.stdout.write("│ "); - } else if (mode === "end") { - process.stdout.write(" │\n"); - } + private printHSeparator(): void { + this.logger.info(`┼${"─".repeat(this.totalWidth - 2)}┼`); } } diff --git a/packages/cli/test/utils/simulation/TableReporter.ts b/packages/cli/test/utils/simulation/TableReporter.ts index 1101c926269c..4a49470cc15b 100644 --- a/packages/cli/test/utils/simulation/TableReporter.ts +++ b/packages/cli/test/utils/simulation/TableReporter.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import {Slot} from "@lodestar/types"; import {isNullish} from "../../utils.js"; import {HeadSummary} from "./assertions/defaults/headAssertion.js"; @@ -10,18 +9,21 @@ import {arrayGroupBy, avg, isSingletonArray} from "./utils/index.js"; export class TableReporter extends SimulationReporter { private lastPrintedSlot = -1; - private table = new TableRenderer({ - time: 14, - fork: 10, - eph: 5, - slot: 4, - head: 10, - finzed: 8, - peers: 8, - attCount: 8, - incDelay: 8, - errors: 10, - }); + private table = new TableRenderer( + { + time: 14, + fork: 10, + eph: 5, + slot: 4, + head: 10, + finzed: 8, + peers: 8, + attCount: 8, + incDelay: 8, + errors: 10, + }, + {logger: this.options.logger} + ); bootstrap(): void { this.table.printHeader(); @@ -132,19 +134,19 @@ export class TableReporter extends SimulationReporter summary(): void { const {errors} = this.options; - console.info(`├${"─".repeat(10)} Errors (${errors.length}) ${"─".repeat(10)}┤`); + this.options.logger.error(`├${"─".repeat(10)} Errors (${errors.length}) ${"─".repeat(10)}┤`); const groupBySlot = arrayGroupBy(errors, (e) => String(e.slot as number)); for (const [slot, slotErrors] of Object.entries(groupBySlot)) { - if (slotErrors.length > 0) console.info(`├─ Slot: ${slot}`); + if (slotErrors.length > 0) this.options.logger.info(`├─ Slot: ${slot}`); const groupByAssertion = arrayGroupBy(slotErrors, (e) => e.assertionId); for (const [assertionId, assertionErrors] of Object.entries(groupByAssertion)) { - if (assertionErrors.length > 0) console.info(`├── Assertion: ${assertionId}`); + if (assertionErrors.length > 0) this.options.logger.info(`├── Assertion: ${assertionId}`); for (const error of assertionErrors) { - console.info( + this.options.logger.error( `├──── ${error.nodeId}: ${error.message} ${Object.entries(error.data ?? {}) .map(([k, v]) => `${k}=${v as string}`) .join(" ")}` diff --git a/packages/cli/test/utils/simulation/assertions/defaults/attestationCountAssertion.ts b/packages/cli/test/utils/simulation/assertions/defaults/attestationCountAssertion.ts index ed9f9bfc64e4..9c01a6bdc3ac 100644 --- a/packages/cli/test/utils/simulation/assertions/defaults/attestationCountAssertion.ts +++ b/packages/cli/test/utils/simulation/assertions/defaults/attestationCountAssertion.ts @@ -66,4 +66,19 @@ export const attestationsCountAssertion: SimulationAssertion< return errors; }, + + async dump({store, slot, nodes}) { + /* + * | Slot | Node 1 | Node 2 | Node 3 | + * |------|--------|--------|--------| + * | 1 | 10 | 10 | 10 | + * | 2 | 10 | 10 | 10 | + * | 3 | 10 | 10 | 10 | + */ + const result = [`Slot,${nodes.map((n) => n.beacon.id).join(", ")}`]; + for (let s = 1; s <= slot; s++) { + result.push(`${s}, ${nodes.map((n) => store[n.beacon.id][s] ?? "-").join(",")}`); + } + return {"attestationsCountAssertion.csv": result.join("\n")}; + }, }; diff --git a/packages/cli/test/utils/simulation/assertions/defaults/headAssertion.ts b/packages/cli/test/utils/simulation/assertions/defaults/headAssertion.ts index 74021abfc310..8d493637d228 100644 --- a/packages/cli/test/utils/simulation/assertions/defaults/headAssertion.ts +++ b/packages/cli/test/utils/simulation/assertions/defaults/headAssertion.ts @@ -37,4 +37,19 @@ export const headAssertion: SimulationAssertion<"head", HeadSummary> = { return errors; }, + + async dump({store, slot, nodes}) { + /* + * | Slot | Node 1 | Node 2 | + * |------|--------|--------| + * | 1 | 0x49d3 | 0x49d3 | + * | 2 | 0x49d3 | 0x49d3 | + * | 3 | 0x49d3 | 0x49d3 | + */ + const result = [`Slot,${nodes.map((n) => n.beacon.id).join(", ")}`]; + for (let s = 1; s <= slot; s++) { + result.push(`${s}, ${nodes.map((n) => store[n.beacon.id][s].blockRoot ?? "-").join(",")}`); + } + return {"headAssertion.csv": result.join("\n")}; + }, }; diff --git a/packages/cli/test/utils/simulation/assertions/defaults/inclusionDelayAssertion.ts b/packages/cli/test/utils/simulation/assertions/defaults/inclusionDelayAssertion.ts index 8db86fbb15aa..554df0e31ca9 100644 --- a/packages/cli/test/utils/simulation/assertions/defaults/inclusionDelayAssertion.ts +++ b/packages/cli/test/utils/simulation/assertions/defaults/inclusionDelayAssertion.ts @@ -30,4 +30,19 @@ export const inclusionDelayAssertion: SimulationAssertion<"inclusionDelay", numb return errors; }, + + async dump({store, slot, nodes}) { + /* + * | Slot | Node 1 | Node 2 | + * |------|--------|--------| + * | 1 | 1.0 | 1.0 | + * | 2 | 0.8 | 0.8 | + * | 3 | 0.5 | 0.95 | + */ + const result = [`Slot,${nodes.map((n) => n.beacon.id).join(", ")}`]; + for (let s = 1; s <= slot; s++) { + result.push(`${s}, ${nodes.map((n) => store[n.beacon.id][s] ?? "-").join(",")}`); + } + return {"inclusionDelayAssertion.csv": result.join("\n")}; + }, }; diff --git a/packages/cli/test/utils/simulation/assertions/defaults/syncCommitteeParticipationAssertion.ts b/packages/cli/test/utils/simulation/assertions/defaults/syncCommitteeParticipationAssertion.ts index 64d4039fb4a7..5cce7554d737 100644 --- a/packages/cli/test/utils/simulation/assertions/defaults/syncCommitteeParticipationAssertion.ts +++ b/packages/cli/test/utils/simulation/assertions/defaults/syncCommitteeParticipationAssertion.ts @@ -3,13 +3,14 @@ import {altair} from "@lodestar/types"; import {AssertionMatch, AssertionResult, SimulationAssertion} from "../../interfaces.js"; import {avg} from "../../utils/index.js"; -export const expectedMinSyncParticipationRate = 0.9; +// Until we identity and fix the following issue, reducing the expected sync committee participation rate from 0.9 to 0.75 +// https://github.com/ChainSafe/lodestar/issues/6432 +export const expectedMinSyncParticipationRate = 0.75; export const syncCommitteeParticipationAssertion: SimulationAssertion<"syncCommitteeParticipation", number> = { id: "syncCommitteeParticipation", - match: ({slot, clock, epoch, forkConfig, fork}) => { + match: ({slot, clock, fork}) => { if (fork === ForkName.phase0) return AssertionMatch.None; - if (epoch < forkConfig.ALTAIR_FORK_EPOCH) return AssertionMatch.Capture; return clock.isLastSlotOfEpoch(slot) ? AssertionMatch.Capture | AssertionMatch.Assert : AssertionMatch.Capture; }, @@ -49,4 +50,19 @@ export const syncCommitteeParticipationAssertion: SimulationAssertion<"syncCommi return errors; }, + + async dump({store, slot, nodes}) { + /* + * | Slot | Node 1 | Node 2 | + * |------|--------|--------| + * | 1 | 16 | 18 | + * | 2 | 12 | 12 | + * | 3 | 14 | 14 | + */ + const result = [`Slot,${nodes.map((n) => n.beacon.id).join(", ")}`]; + for (let s = 1; s <= slot; s++) { + result.push(`${s}, ${nodes.map((n) => store[n.beacon.id][s] ?? "-").join(",")}`); + } + return {"syncCommitteeParticipationAssertion.csv": result.join("\n")}; + }, }; diff --git a/packages/cli/test/utils/simulation/beacon_clients/lighthouse.ts b/packages/cli/test/utils/simulation/beacon_clients/lighthouse.ts index 159c9fa43475..4b994c63a836 100644 --- a/packages/cli/test/utils/simulation/beacon_clients/lighthouse.ts +++ b/packages/cli/test/utils/simulation/beacon_clients/lighthouse.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {writeFile} from "node:fs/promises"; import path from "node:path"; import got, {RequestError} from "got"; diff --git a/packages/cli/test/utils/simulation/execution_clients/geth.ts b/packages/cli/test/utils/simulation/execution_clients/geth.ts index bb17d1698330..2ed9a2a91e1c 100644 --- a/packages/cli/test/utils/simulation/execution_clients/geth.ts +++ b/packages/cli/test/utils/simulation/execution_clients/geth.ts @@ -21,7 +21,7 @@ export const generateGethNode: ExecutionNodeGenerator = (o const {id, mode, ttd, address, mining, clientOptions, nodeIndex} = opts; const ports = getNodePorts(nodeIndex); - const isDocker = process.env.GETH_DOCKER_IMAGE !== undefined; + const isDocker = !!process.env.GETH_DOCKER_IMAGE; const binaryPath = isDocker ? "" : `${process.env.GETH_BINARY_DIR}/geth`; const {rootDir, rootDirMounted, genesisFilePathMounted, logFilePath, jwtsecretFilePathMounted} = getNodeMountedPaths( opts.paths, diff --git a/packages/cli/test/utils/simulation/interfaces.ts b/packages/cli/test/utils/simulation/interfaces.ts index 41339b58a1e0..639aa287e6d8 100644 --- a/packages/cli/test/utils/simulation/interfaces.ts +++ b/packages/cli/test/utils/simulation/interfaces.ts @@ -6,6 +6,7 @@ import {Api as KeyManagerApi} from "@lodestar/api/keymanager"; import {ChainForkConfig} from "@lodestar/config"; import {ForkName} from "@lodestar/params"; import {Slot, allForks, Epoch} from "@lodestar/types"; +import {Logger} from "@lodestar/logger"; import {BeaconArgs} from "../../../src/cmds/beacon/options.js"; import {IValidatorCliArgs} from "../../../src/cmds/validator/options.js"; import {GlobalArgs} from "../../../src/options/index.js"; @@ -29,19 +30,19 @@ export type SimulationOptions = { }; export enum BeaconClient { - Lodestar = "beacon_lodestar", - Lighthouse = "beacon_lighthouse", + Lodestar = "beacon-lodestar", + Lighthouse = "beacon-lighthouse", } export enum ValidatorClient { - Lodestar = "validator_lodestar", - Lighthouse = "validator_lighthouse", + Lodestar = "validator-lodestar", + Lighthouse = "validator-lighthouse", } export enum ExecutionClient { - Mock = "execution_mock", - Geth = "execution_geth", - Nethermind = "execution_nethermind", + Mock = "execution-mock", + Geth = "execution-geth", + Nethermind = "execution-nethermind", } export enum ExecutionStartMode { @@ -142,7 +143,6 @@ export interface ExecutionGeneratorOptions & { - // eslint-disable-next-line @typescript-eslint/naming-convention lighthouse: { getPeers(): Promise<{ status: number; @@ -326,6 +326,11 @@ export interface SimulationAssertionInput = dependantStores: D; } +export interface SimulationDumpInput extends Omit { + nodes: NodePair[]; + store: Record>; +} + export type SimulationMatcherInput = AssertionInput; /** @@ -371,6 +376,10 @@ export interface SimulationAssertion< input: SimulationAssertionInput & StoreType> ): Promise; dependencies?: Dependencies; + // Use to dump the data to CSV files, as each assertion implementation knows + // how to make the dump more readable, so we define it in assertion + // Return object as key-value pair for file name as dump data + dump?(input: SimulationDumpInput): Promise>; } export type AssertionResult = string | [string, Record]; @@ -401,6 +410,7 @@ export abstract class SimulationReporter { stores: StoreTypes; nodes: NodePair[]; errors: SimulationAssertionError[]; + logger: Logger; } ) {} abstract bootstrap(): void; diff --git a/packages/cli/test/utils/simulation/runner/DockerRunner.ts b/packages/cli/test/utils/simulation/runner/DockerRunner.ts index 7658e198a738..999f970c05a4 100644 --- a/packages/cli/test/utils/simulation/runner/DockerRunner.ts +++ b/packages/cli/test/utils/simulation/runner/DockerRunner.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import {ChildProcess} from "node:child_process"; import {sleep} from "@lodestar/utils"; import { diff --git a/packages/cli/test/utils/simulation/runner/index.ts b/packages/cli/test/utils/simulation/runner/index.ts index 9bdca30562b4..cedd5c34e6a5 100644 --- a/packages/cli/test/utils/simulation/runner/index.ts +++ b/packages/cli/test/utils/simulation/runner/index.ts @@ -1,15 +1,17 @@ -/* eslint-disable no-console */ import {EventEmitter} from "node:events"; import path from "node:path"; +import {Logger} from "@lodestar/logger"; import {IRunner, Job, JobOptions, RunnerEvent, RunnerType} from "../interfaces.js"; import {ChildProcessRunner} from "./ChildProcessRunner.js"; import {DockerRunner} from "./DockerRunner.js"; export class Runner implements IRunner { + readonly logger: Logger; private emitter = new EventEmitter({captureRejections: true}); private runners: {[RunnerType.ChildProcess]: ChildProcessRunner; [RunnerType.Docker]: DockerRunner}; - constructor({logsDir}: {logsDir: string}) { + constructor({logsDir, logger}: {logsDir: string; logger: Logger}) { + this.logger = logger; this.runners = { [RunnerType.ChildProcess]: new ChildProcessRunner(), [RunnerType.Docker]: new DockerRunner(path.join(logsDir, "docker_runner.log")), @@ -51,25 +53,25 @@ export class Runner implements IRunner { startSequence.push(async () => { this.emitter.emit("starting", jobOption.id); - console.log(`Starting "${jobOption.id}"...`); + this.logger.info(`Starting "${jobOption.id}"...`); if (jobOption.bootstrap) await jobOption.bootstrap(); await job.start(); this.emitter.emit("started", jobOption.id); - console.log(`Started "${jobOption.id}" logFile=${jobOption.logs.stdoutFilePath}...`); + this.logger.info(`Started "${jobOption.id}" logFile=${jobOption.logs.stdoutFilePath}...`); if (childrenJob) await childrenJob.start(); }); stopSequence.push(async () => { this.emitter.emit("stopping", jobOption.id); - console.log(`Stopping "${jobOption.id}"...`); + this.logger.info(`Stopping "${jobOption.id}"...`); if (jobOption.teardown) await jobOption.teardown(); if (childrenJob) await childrenJob.stop(); await job.stop(); this.emitter.emit("stopped", jobOption.id); - console.log(`Stopped "${jobOption.id}"...`); + this.logger.info(`Stopped "${jobOption.id}"...`); }); } diff --git a/packages/cli/test/utils/simulation/utils/index.ts b/packages/cli/test/utils/simulation/utils/index.ts index dfa66bed6580..bc0a4ee98d6f 100644 --- a/packages/cli/test/utils/simulation/utils/index.ts +++ b/packages/cli/test/utils/simulation/utils/index.ts @@ -17,18 +17,44 @@ export const avg = (arr: number[]): number => { return arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length; }; +function getGenesisDelaySlots(initialNodes?: number): number { + if (process.env.GENESIS_DELAY_SLOTS) { + const genesisDelaySlots = parseInt(process.env.GENESIS_DELAY_SLOTS); + // If custom job is invoked and want to use default genesis delay then provider -1 as value + if (genesisDelaySlots >= 0) return genesisDelaySlots; + } + + if (initialNodes == null) return 40; + // Considering each node consists of EN, BN, VC and KM + // EN - Execution Node - 15s + const execution = 15; + // BN - Beacon Node - 15s + const beacon = 15; + // VC - Validator Client - 10s + const validator = 10; + // KM - Key Manager - 3s + const keyManager = 3; + // Initial script launch time - 10s + const initialLaunchScript = 10; + + return Math.ceil( + ((execution + beacon + validator + keyManager) * initialNodes + initialLaunchScript) / SIM_TESTS_SECONDS_PER_SLOT + ); +} + export function defineSimTestConfig( opts: Partial & { cliqueSealingPeriod?: number; additionalSlotsForTTD?: number; runTillEpoch: number; + // Used to calculate genesis delay + initialNodes?: number; } ): { estimatedTimeoutMs: number; forkConfig: ChainForkConfig; } { - const genesisDelaySeconds = - (process.env.GENESIS_DELAY_SLOTS ? parseInt(process.env.GENESIS_DELAY_SLOTS) : 40) * SIM_TESTS_SECONDS_PER_SLOT; + const genesisDelaySeconds = getGenesisDelaySlots(opts.initialNodes) * SIM_TESTS_SECONDS_PER_SLOT; const estimatedTimeoutMs = getEstimatedTimeInSecForRun({ diff --git a/packages/cli/test/utils/simulation/utils/paths.ts b/packages/cli/test/utils/simulation/utils/paths.ts index 0a7ffb716dd1..9f0628e34656 100644 --- a/packages/cli/test/utils/simulation/utils/paths.ts +++ b/packages/cli/test/utils/simulation/utils/paths.ts @@ -23,7 +23,7 @@ export function getNodePaths< return { rootDir: executionRootDir, dataDir: path.join(executionRootDir, "data"), - genesisFilePath: path.join(executionRootDir, "genesis.ssz"), + genesisFilePath: path.join(executionRootDir, "genesis.json"), jwtsecretFilePath: path.join(executionRootDir, "jwtsecret.txt"), logFilePath: path.join(logsDir, `${id}-${client}.log`), } as R; diff --git a/packages/cli/test/utils/simulation/validator_clients/index.ts b/packages/cli/test/utils/simulation/validator_clients/index.ts index 1334382407e8..a75cbf6b1660 100644 --- a/packages/cli/test/utils/simulation/validator_clients/index.ts +++ b/packages/cli/test/utils/simulation/validator_clients/index.ts @@ -14,14 +14,14 @@ export async function createValidatorNode( "id" | "paths" | "forkConfig" | "nodeIndex" | "genesisTime" | "runner" | "beaconUrls" > ): Promise { - const {runner, forkConfig} = options; + const {runner} = options; const clId = `${options.id}-${client}`; const opts: ValidatorGeneratorOptions = { ...options, id: clId, keys: options.keys ?? {type: "no-keys"}, - genesisTime: options.genesisTime + forkConfig.GENESIS_DELAY, + genesisTime: options.genesisTime, clientOptions: options.clientOptions ?? {}, address: "127.0.0.1", }; diff --git a/packages/cli/test/utils/simulation/validator_clients/lighthouse.ts b/packages/cli/test/utils/simulation/validator_clients/lighthouse.ts index 27b2f485588d..8df15a3ec85b 100644 --- a/packages/cli/test/utils/simulation/validator_clients/lighthouse.ts +++ b/packages/cli/test/utils/simulation/validator_clients/lighthouse.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import path from "node:path"; import {writeFile} from "node:fs/promises"; import got, {RequestError} from "got"; @@ -39,7 +38,6 @@ export const generateLighthouseValidatorNode: ValidatorNodeGenerator; - -export class LightClientRestTransport extends (EventEmitter as {new (): RestEvents}) implements LightClientTransport { +export class LightClientRestTransport implements LightClientTransport { private controller = new AbortController(); private readonly eventEmitter: StrictEventEmitter = new EventEmitter(); private subscribedEventstream = false; - constructor(private readonly api: Api) { - super(); - } + constructor(private readonly api: Api) {} async getUpdates( startPeriod: SyncPeriod, diff --git a/packages/light-client/src/utils/logger.ts b/packages/light-client/src/utils/logger.ts index 330fb673b5c0..cb47857da165 100644 --- a/packages/light-client/src/utils/logger.ts +++ b/packages/light-client/src/utils/logger.ts @@ -18,7 +18,6 @@ export function getLcLoggerConsole(opts?: {logDebug?: boolean}): ILcLogger { error: console.error, warn: console.warn, info: console.log, - // eslint-disable-next-line @typescript-eslint/no-empty-function debug: opts?.logDebug ? console.log : () => {}, }; } diff --git a/packages/light-client/test/utils/server.ts b/packages/light-client/test/utils/server.ts index 3effdd06e658..5fe1dd623ccb 100644 --- a/packages/light-client/test/utils/server.ts +++ b/packages/light-client/test/utils/server.ts @@ -1,6 +1,6 @@ -import qs from "qs"; -import fastify, {FastifyInstance} from "fastify"; -import fastifyCors from "@fastify/cors"; +import {parse as parseQueryString} from "qs"; +import {FastifyInstance, fastify} from "fastify"; +import {fastifyCors} from "@fastify/cors"; import {Api, ServerApi} from "@lodestar/api"; import {registerRoutes} from "@lodestar/api/beacon/server"; import {ChainForkConfig} from "@lodestar/config"; @@ -18,7 +18,7 @@ export async function startServer( const server = fastify({ logger: false, ajv: {customOptions: {coerceTypes: "array"}}, - querystringParser: (str) => qs.parse(str, {comma: true, parseArrays: false}), + querystringParser: (str) => parseQueryString(str, {comma: true, parseArrays: false}), }); registerRoutes(server, config, api, ["lightclient", "proof", "events"]); diff --git a/packages/logger/package.json b/packages/logger/package.json index ea649cffb958..e0b69b0a9a7d 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -55,25 +55,25 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", + "test": "yarn test:unit && yarn test:e2e", "test:unit": "vitest --run --dir test/unit/", "test:browsers": "yarn test:browsers:chrome && yarn test:browsers:firefox && yarn test:browsers:electron", "test:browsers:chrome": "vitest --run --browser chrome --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:firefox": "vitest --run --browser firefox --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:electron": "echo 'Electron tests will be introduced back in the future as soon vitest supports electron.'", - "test:e2e": "LODESTAR_PRESET=minimal vitest --run --config vitest.config.e2e.ts --dir test/e2e", + "test:e2e": "vitest --run --config vitest.e2e.config.ts --dir test/e2e", "check-readme": "typescript-docs-verifier" }, "types": "lib/index.d.ts", "dependencies": { - "@lodestar/utils": "^1.15.1", + "@lodestar/utils": "^1.16.0", "winston": "^3.8.2", "winston-daily-rotate-file": "^4.7.1", "winston-transport": "^4.5.0" }, "devDependencies": { "@chainsafe/threads": "^1.11.1", - "@lodestar/test-utils": "^1.15.1", + "@lodestar/test-utils": "^1.16.0", "@types/triple-beam": "^1.3.2", "rimraf": "^4.4.1", "triple-beam": "^1.3.0" diff --git a/packages/logger/src/node.ts b/packages/logger/src/node.ts index ec2d57b85bbb..60ca87e0367c 100644 --- a/packages/logger/src/node.ts +++ b/packages/logger/src/node.ts @@ -1,6 +1,8 @@ import path from "node:path"; import DailyRotateFile from "winston-daily-rotate-file"; import TransportStream from "winston-transport"; +// We want to keep `winston` export as it's more readable and easier to understand +/* eslint-disable import/no-named-as-default-member */ import winston from "winston"; import type {Logger as Winston} from "winston"; import {Logger, LogLevel, TimestampFormat} from "./interface.js"; diff --git a/packages/logger/src/utils/format.ts b/packages/logger/src/utils/format.ts index 2340876a371e..651dc56ce687 100644 --- a/packages/logger/src/utils/format.ts +++ b/packages/logger/src/utils/format.ts @@ -1,11 +1,9 @@ -import winston from "winston"; +import winston, {format} from "winston"; import {LodestarError, isEmptyObject} from "@lodestar/utils"; import {LoggerOptions, TimestampFormatCode} from "../interface.js"; import {logCtxToJson, logCtxToString, LogData} from "./json.js"; import {formatEpochSlotTime} from "./timeFormat.js"; -const {format} = winston; - type Format = ReturnType; // TODO: Find a more typesafe way of enforce this properties diff --git a/packages/logger/src/winston.ts b/packages/logger/src/winston.ts index db27970489ea..4e6fbbecd6b2 100644 --- a/packages/logger/src/winston.ts +++ b/packages/logger/src/winston.ts @@ -1,3 +1,5 @@ +// We want to keep `winston` export as it's more readable and easier to understand +/* eslint-disable import/no-named-as-default-member */ import winston from "winston"; import type {Logger as Winston} from "winston"; import {Logger, LoggerOptions, LogLevel, logLevelNum} from "./interface.js"; diff --git a/packages/logger/test/e2e/logger/workerLoggerHandler.ts b/packages/logger/test/e2e/logger/workerLoggerHandler.ts index b166ef15ff00..ab2097171af0 100644 --- a/packages/logger/test/e2e/logger/workerLoggerHandler.ts +++ b/packages/logger/test/e2e/logger/workerLoggerHandler.ts @@ -1,4 +1,4 @@ -import worker_threads from "node:worker_threads"; +import workerThreads from "node:worker_threads"; import {spawn, Worker} from "@chainsafe/threads"; export type LoggerWorker = { @@ -12,9 +12,8 @@ export async function getLoggerWorker(opts: WorkerData): Promise { const workerThreadjs = new Worker("./workerLogger.js", { workerData: opts, }); - const worker = workerThreadjs as unknown as worker_threads.Worker; + const worker = workerThreadjs as unknown as workerThreads.Worker; - // eslint-disable-next-line @typescript-eslint/no-explicit-any await spawn(workerThreadjs, { // A Lodestar Node may do very expensive task at start blocking the event loop and causing // the initialization to timeout. The number below is big enough to almost disable the timeout diff --git a/packages/logger/test/unit/utils/json.test.ts b/packages/logger/test/unit/utils/json.test.ts index 02a6c95e1ed9..912f15fa958b 100644 --- a/packages/logger/test/unit/utils/json.test.ts +++ b/packages/logger/test/unit/utils/json.test.ts @@ -24,13 +24,9 @@ describe("Json helper", () => { {id: "symbol", arg: Symbol("foo"), json: "Symbol(foo)"}, // Functions - // eslint-disable-next-line @typescript-eslint/no-empty-function {id: "function", arg: function () {}, json: "function() {\n }"}, - // eslint-disable-next-line @typescript-eslint/no-empty-function {id: "arrow function", arg: () => {}, json: "() => {\n }"}, - // eslint-disable-next-line @typescript-eslint/no-empty-function {id: "async function", arg: async function () {}, json: "async function() {\n }"}, - // eslint-disable-next-line @typescript-eslint/no-empty-function {id: "async arrow function", arg: async () => {}, json: "async () => {\n }"}, // Arrays diff --git a/packages/logger/vitest.config.e2e.ts b/packages/logger/vitest.e2e.config.ts similarity index 100% rename from packages/logger/vitest.config.e2e.ts rename to packages/logger/vitest.e2e.config.ts diff --git a/packages/params/package.json b/packages/params/package.json index d94090b3f6ea..8902b2bceb8e 100644 --- a/packages/params/package.json +++ b/packages/params/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/params", - "version": "1.15.1", + "version": "1.16.0", "description": "Chain parameters required for lodestar", "author": "ChainSafe Systems", "license": "Apache-2.0", @@ -52,13 +52,13 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "test": "yarn run check-types", + "test": "yarn test:unit", "test:unit": "vitest --run --dir test/unit/", "test:browsers": "yarn test:browsers:chrome && yarn test:browsers:firefox && yarn test:browsers:electron", "test:browsers:chrome": "vitest --run --browser chrome --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:firefox": "vitest --run --browser firefox --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:electron": "echo 'Electron tests will be introduced back in the future as soon vitest supports electron.'", - "test:e2e": "LODESTAR_PRESET=minimal vitest --run --config vitest.config.e2e.ts --dir test/e2e/", + "test:e2e": "vitest --run --config vitest.e2e.config.ts --dir test/e2e/", "check-readme": "typescript-docs-verifier" }, "repository": { diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index d111d090b8c3..6a95e3ca632e 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -28,7 +28,9 @@ presetStatus.frozen = true; * The active preset can be manually overridden with `setActivePreset` */ export const ACTIVE_PRESET = - userSelectedPreset ?? PresetName[process?.env?.LODESTAR_PRESET as PresetName] ?? PresetName.mainnet; + userSelectedPreset ?? + (typeof process !== "undefined" ? PresetName[process?.env?.LODESTAR_PRESET as PresetName] : undefined) ?? + PresetName.mainnet; export const activePreset = {...presets[ACTIVE_PRESET], ...userOverrides}; // These variables must be exported individually and explicitly diff --git a/packages/params/test/e2e/overridePreset.test.ts b/packages/params/test/e2e/overridePreset.test.ts index 16f29f3c5c84..24995cbaa042 100644 --- a/packages/params/test/e2e/overridePreset.test.ts +++ b/packages/params/test/e2e/overridePreset.test.ts @@ -21,8 +21,8 @@ describe("Override preset", function () { vi.setConfig({testTimeout: 30_000}); it("Should correctly override preset", async () => { - // These commands can not run with minimal preset - if (process.env.LODESTAR_PRESET === "minimal") delete process.env.LODESTAR_PRESET; + // `LODESTAR_PRESET` must not be set to properly test preset override + if (process.env.LODESTAR_PRESET) delete process.env.LODESTAR_PRESET; await exec(`node --loader ts-node/esm ${path.join(__dirname, scriptNames.ok)}`); }); diff --git a/packages/params/test/e2e/setPreset.test.ts b/packages/params/test/e2e/setPreset.test.ts index aa1371fa2eea..844239d56bab 100644 --- a/packages/params/test/e2e/setPreset.test.ts +++ b/packages/params/test/e2e/setPreset.test.ts @@ -21,8 +21,8 @@ describe("setPreset", function () { vi.setConfig({testTimeout: 30_000}); it("Should correctly set preset", async () => { - // These commands can not run with minimal preset - if (process.env.LODESTAR_PRESET === "minimal") delete process.env.LODESTAR_PRESET; + // `LODESTAR_PRESET` must not be set to properly test setting preset + if (process.env.LODESTAR_PRESET) delete process.env.LODESTAR_PRESET; await exec(`node --loader ts-node/esm ${path.join(__dirname, scriptNames.ok)}`); }); diff --git a/packages/params/vitest.config.e2e.ts b/packages/params/vitest.e2e.config.ts similarity index 100% rename from packages/params/vitest.config.e2e.ts rename to packages/params/vitest.e2e.config.ts diff --git a/packages/prover/README.md b/packages/prover/README.md index 290766219e79..21f167fc43de 100644 --- a/packages/prover/README.md +++ b/packages/prover/README.md @@ -7,7 +7,7 @@ > This package is part of [ChainSafe's Lodestar](https://lodestar.chainsafe.io) project -Typescript REST client for the [Ethereum Consensus API spec](https://github.com/ethereum/beacon-apis) +A set of tools allowing to verify EL client JSON-RPC calls. ## Usage @@ -37,13 +37,12 @@ console.log({balance, address}); You can also invoke the package as binary. ```bash -npm -i g @lodestar/prover +npm i -g @lodestar/prover -lodestar-prover start \ +lodestar-prover proxy \ --network sepolia \ - --execution-rpc https://lodestar-sepoliarpc.chainsafe.io \ - --mode rest \ - --beacon-rpc https://lodestar-sepolia.chainsafe.io \ + --executionRpcUrl https://lodestar-sepoliarpc.chainsafe.io \ + --beaconUrls https://lodestar-sepolia.chainsafe.io \ --port 8080 ``` diff --git a/packages/prover/package.json b/packages/prover/package.json index 7ca0090b58aa..a170b39dc7e7 100644 --- a/packages/prover/package.json +++ b/packages/prover/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -48,17 +48,15 @@ "build:release": "yarn clean && yarn run build", "check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"", "check-types": "tsc", - "coverage": "codecov -F lodestar-api", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "test": "yarn test:unit && yarn test:e2e", "test:unit": "vitest --run --dir test/unit/", "test:browsers": "yarn test:browsers:chrome && yarn test:browsers:firefox && yarn test:browsers:electron", "test:browsers:chrome": "vitest --run --browser chrome --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:firefox": "vitest --run --browser firefox --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:electron": "echo 'Electron tests will be introduced back in the future as soon vitest supports electron.'", - "test:e2e": "LODESTAR_PRESET=minimal vitest --run --config vitest.config.e2e.ts --dir test/e2e", + "test:e2e": "LODESTAR_PRESET=minimal vitest --run --config vitest.e2e.config.ts --dir test/e2e", "check-readme": "typescript-docs-verifier", "generate-fixtures": "node --loader ts-node/esm scripts/generate_fixtures.ts" }, @@ -72,14 +70,14 @@ "@ethereumjs/tx": "^4.1.2", "@ethereumjs/util": "^8.0.6", "@ethereumjs/vm": "^6.4.2", - "@lodestar/api": "^1.15.1", - "@lodestar/config": "^1.15.1", - "@lodestar/light-client": "^1.15.1", - "@lodestar/logger": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/types": "^1.15.1", - "@lodestar/utils": "^1.15.1", - "ethereum-cryptography": "^1.2.0", + "@lodestar/api": "^1.16.0", + "@lodestar/config": "^1.16.0", + "@lodestar/light-client": "^1.16.0", + "@lodestar/logger": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/types": "^1.16.0", + "@lodestar/utils": "^1.16.0", + "ethereum-cryptography": "^2.0.0", "find-up": "^6.3.0", "http-proxy": "^1.18.1", "js-yaml": "^4.1.0", @@ -87,7 +85,7 @@ "yargs": "^17.7.1" }, "devDependencies": { - "@lodestar/test-utils": "^1.15.1", + "@lodestar/test-utils": "^1.16.0", "@types/http-proxy": "^1.17.10", "@types/yargs": "^17.0.24", "axios": "^1.3.4", diff --git a/packages/prover/src/cli/cli.ts b/packages/prover/src/cli/cli.ts index 8ef53e781a40..2d1475f3d39d 100644 --- a/packages/prover/src/cli/cli.ts +++ b/packages/prover/src/cli/cli.ts @@ -3,7 +3,7 @@ import yargs from "yargs"; import {hideBin} from "yargs/helpers"; import {registerCommandToYargs} from "../utils/command.js"; import {getVersionData} from "../utils/version.js"; -import {cmds} from "./cmds/index.js"; +import {cmds, proverProxyStartCommand} from "./cmds/index.js"; import {globalOptions} from "./options.js"; const {version} = getVersionData(); @@ -48,6 +48,9 @@ export function getLodestarProverCli(): yargs.Argv { registerCommandToYargs(prover, cmd); } + // Register the proxy command as the default one + registerCommandToYargs(prover, {...proverProxyStartCommand, command: "*"}); + // throw an error if we see an unrecognized cmd prover.recommendCommands().strict(); diff --git a/packages/prover/src/cli/cmds/index.ts b/packages/prover/src/cli/cmds/index.ts index 3b888ab41d83..ecd2dae1da99 100644 --- a/packages/prover/src/cli/cmds/index.ts +++ b/packages/prover/src/cli/cmds/index.ts @@ -1,5 +1,6 @@ import {CliCommand} from "../../utils/command.js"; import {GlobalArgs} from "../options.js"; import {proverProxyStartCommand} from "./start/index.js"; +export {proverProxyStartCommand} from "./start/index.js"; export const cmds: Required>>["subcommands"] = [proverProxyStartCommand]; diff --git a/packages/prover/src/cli/cmds/start/handler.ts b/packages/prover/src/cli/cmds/start/handler.ts index 5f13db0cfcb6..92922f1a45a4 100644 --- a/packages/prover/src/cli/cmds/start/handler.ts +++ b/packages/prover/src/cli/cmds/start/handler.ts @@ -1,5 +1,4 @@ import {ChainConfig, chainConfigFromJson} from "@lodestar/config"; -import {LCTransport} from "../../../interfaces.js"; import {readFile} from "../../../utils/file.js"; import {createVerifiedExecutionProxy, VerifiedProxyOptions} from "../../../web3_proxy.js"; import {GlobalArgs, parseGlobalArgs} from "../../options.js"; @@ -11,25 +10,18 @@ import {parseStartArgs, StartArgs} from "./options.js"; export async function proverProxyStartHandler(args: StartArgs & GlobalArgs): Promise { const {network, logLevel, paramsFile} = parseGlobalArgs(args); const opts = parseStartArgs(args); - const {executionRpcUrl, port, wsCheckpoint} = opts; const config: Partial = paramsFile ? chainConfigFromJson(readFile(paramsFile)) : {}; const options: VerifiedProxyOptions = { + ...opts, logLevel, - executionRpcUrl, - wsCheckpoint, - unverifiedWhitelist: opts.unverifiedWhitelist, - requestTimeout: opts.requestTimeout, ...(network ? {network} : {config}), - ...(opts.transport === LCTransport.Rest - ? {transport: LCTransport.Rest, urls: opts.urls} - : {transport: LCTransport.P2P, bootnodes: opts.bootnodes}), }; const {server, proofProvider} = createVerifiedExecutionProxy(options); - server.listen(port); + server.listen(opts.port); await proofProvider.waitToBeReady(); } diff --git a/packages/prover/src/cli/cmds/start/index.ts b/packages/prover/src/cli/cmds/start/index.ts index 76ad1bb3f260..2b49a6466a61 100644 --- a/packages/prover/src/cli/cmds/start/index.ts +++ b/packages/prover/src/cli/cmds/start/index.ts @@ -4,7 +4,7 @@ import {proverProxyStartHandler} from "./handler.js"; import {StartArgs, startOptions} from "./options.js"; export const proverProxyStartCommand: CliCommand = { - command: "start", + command: "proxy", describe: "Start proxy server", examples: [ { diff --git a/packages/prover/src/cli/cmds/start/options.ts b/packages/prover/src/cli/cmds/start/options.ts index ac51aac3f17c..53ff5957765b 100644 --- a/packages/prover/src/cli/cmds/start/options.ts +++ b/packages/prover/src/cli/cmds/start/options.ts @@ -7,7 +7,6 @@ export type StartArgs = { port: number; executionRpcUrl: string; beaconUrls?: string[]; - beaconBootnodes?: string[]; wsCheckpoint?: string; unverifiedWhitelist?: string[]; requestTimeout: number; @@ -19,7 +18,7 @@ export type StartOptions = { wsCheckpoint?: string; unverifiedWhitelist?: string[]; requestTimeout: number; -} & ({transport: LCTransport.Rest; urls: string[]} | {transport: LCTransport.P2P; bootnodes: string[]}); +} & {transport: LCTransport.Rest; urls: string[]}; export const startOptions: CliCommandOptions = { port: { @@ -53,18 +52,10 @@ export const startOptions: CliCommandOptions = { }, beaconUrls: { - description: "The beacon node PRC urls for 'rest' mode.", - type: "string", - array: true, - conflicts: ["beaconBootnodes"], - group: "beacon", - }, - - beaconBootnodes: { - description: "The beacon node PRC urls for 'p2p' mode.", + description: "Urls of the beacon nodes to connect to.", type: "string", + demandOption: true, array: true, - conflicts: ["beaconUrls"], group: "beacon", }, @@ -78,17 +69,12 @@ export const startOptions: CliCommandOptions = { }; export function parseStartArgs(args: StartArgs): StartOptions { - if (!args.beaconUrls && !args.beaconBootnodes) { - throw new Error("Either --beaconUrls or --beaconBootnodes must be provided"); - } - // Remove undefined values to allow deepmerge to inject default values downstream return { port: args.port, executionRpcUrl: args.executionRpcUrl, - transport: args.beaconUrls ? LCTransport.Rest : LCTransport.P2P, + transport: LCTransport.Rest, urls: args.beaconUrls ?? [], - bootnodes: args.beaconBootnodes ?? [], wsCheckpoint: args.wsCheckpoint, unverifiedWhitelist: args.unverifiedWhitelist, requestTimeout: args.requestTimeout ?? DEFAULT_PROXY_REQUEST_TIMEOUT, diff --git a/packages/prover/src/utils/command.ts b/packages/prover/src/utils/command.ts index 81a3993f3c43..f01d9f7ab17b 100644 --- a/packages/prover/src/utils/command.ts +++ b/packages/prover/src/utils/command.ts @@ -47,7 +47,6 @@ export function registerCommandToYargs(yargs: Argv, cliCommand: CliCommand { +describe("prover/proxy", () => { it("should show help", async () => { - const output = await runCliCommand(cli, ["start", "--help"]); + const output = await runCliCommand(cli, ["proxy", "--help"]); expect(output).toEqual(expect.stringContaining("Show help")); }); - it("should fail when --executionRpcUrl is missing", async () => { - await expect(runCliCommand(cli, ["start", "--port", "8088"])).rejects.toThrow( - "Missing required argument: executionRpcUrl" + it("should fail when --executionRpcUrl and --beaconUrls are missing", async () => { + await expect(runCliCommand(cli, ["proxy", "--port", "8088"])).rejects.toThrow( + "Missing required arguments: executionRpcUrl, beaconUrls" ); }); - it("should fail when --beaconUrls and --beaconBootnodes are provided together", async () => { - await expect( - runCliCommand(cli, [ - "start", - "--beaconUrls", - "http://localhost:4000", - "--beaconBootnodes", - "http://localhost:0000", - ]) - ).rejects.toThrow("Arguments beaconBootnodes and beaconUrls are mutually exclusive"); - }); - - it("should fail when both of --beaconUrls and --beaconBootnodes are not provided", async () => { - await expect( - runCliCommand(cli, ["start", "--port", "8088", "--executionRpcUrl", "http://localhost:3000"]) - ).rejects.toThrow("Either --beaconUrls or --beaconBootnodes must be provided"); - }); - describe("when started", () => { - let proc: childProcess.ChildProcess; + let proc: childProcess.ChildProcess | null = null; const paramsFilePath = path.join("/tmp", "e2e-test-env", "params.json"); const web3: Web3 = new Web3(proxyUrl); @@ -55,7 +37,7 @@ describe("prover/start", () => { proc = await spawnCliCommand( "packages/prover/bin/lodestar-prover.js", [ - "start", + "proxy", "--port", String(proxyPort as number), "--executionRpcUrl", @@ -74,7 +56,9 @@ describe("prover/start", () => { }, 50000); afterAll(async () => { - await stopChildProcess(proc); + if (proc) { + await stopChildProcess(proc); + } }); it("should respond to verified calls", async () => { diff --git a/packages/prover/test/e2e/web3_batch_request.test.ts b/packages/prover/test/e2e/web3_batch_request.test.ts index afb995d088a0..fc99abea4bdd 100644 --- a/packages/prover/test/e2e/web3_batch_request.test.ts +++ b/packages/prover/test/e2e/web3_batch_request.test.ts @@ -1,16 +1,17 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {describe, it, expect, beforeAll} from "vitest"; -import Web3 from "web3"; +import {Web3} from "web3"; import {LCTransport} from "../../src/interfaces.js"; import {createVerifiedExecutionProvider} from "../../src/web3_provider.js"; -import {rpcUrl, beaconUrl, config} from "../utils/e2e_env.js"; +import {rpcUrl, beaconUrl, config, waitForCapellaFork} from "../utils/e2e_env.js"; import {getVerificationFailedMessage} from "../../src/utils/json_rpc.js"; /* prettier-ignore */ describe("web3_batch_requests", function () { let web3: Web3; - beforeAll(() => { + beforeAll(async () => { + await waitForCapellaFork(); + const {provider} = createVerifiedExecutionProvider(new Web3.providers.HttpProvider(rpcUrl), { transport: LCTransport.Rest, urls: [beaconUrl], diff --git a/packages/prover/test/e2e/web3_provider.test.ts b/packages/prover/test/e2e/web3_provider.test.ts index 4de51dc94051..21ed13b8787a 100644 --- a/packages/prover/test/e2e/web3_provider.test.ts +++ b/packages/prover/test/e2e/web3_provider.test.ts @@ -1,6 +1,5 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import {describe, it, expect, beforeAll} from "vitest"; -import Web3 from "web3"; +import {Web3} from "web3"; import {ethers} from "ethers"; import {LCTransport} from "../../src/interfaces.js"; import {createVerifiedExecutionProvider} from "../../src/web3_provider.js"; diff --git a/packages/prover/test/unit/utils/assertion.test.ts b/packages/prover/test/unit/utils/assertion.test.ts index 5cf481bacdf1..3ea712bab1f1 100644 --- a/packages/prover/test/unit/utils/assertion.test.ts +++ b/packages/prover/test/unit/utils/assertion.test.ts @@ -1,6 +1,6 @@ import {describe, it, expect} from "vitest"; import {ethers} from "ethers"; -import Web3 from "web3"; +import {Web3} from "web3"; import {isSendProvider, isWeb3jsProvider, isEthersProvider} from "../../../src/utils/assertion.js"; describe("utils/assertion", () => { diff --git a/packages/prover/test/unit/verified_requests/eth_getBalance.test.ts b/packages/prover/test/unit/verified_requests/eth_getBalance.test.ts index ea0c902750c8..4032f4c25f19 100644 --- a/packages/prover/test/unit/verified_requests/eth_getBalance.test.ts +++ b/packages/prover/test/unit/verified_requests/eth_getBalance.test.ts @@ -3,12 +3,12 @@ import {createForkConfig} from "@lodestar/config"; import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; import {VERIFICATION_FAILED_RESPONSE_CODE} from "../../../src/constants.js"; import {eth_getBalance} from "../../../src/verified_requests/eth_getBalance.js"; -import eth_getBalance_eoa from "../../fixtures/sepolia/eth_getBalance_eoa.json" assert {type: "json"}; -import eth_getBalance_contract from "../../fixtures/sepolia/eth_getBalance_contract.json" assert {type: "json"}; +import ethGetBalanceEoa from "../../fixtures/sepolia/eth_getBalance_eoa.json" assert {type: "json"}; +import ethGetBalanceContract from "../../fixtures/sepolia/eth_getBalance_contract.json" assert {type: "json"}; import {cloneTestFixture, generateReqHandlerOptionsMock} from "../../mocks/request_handler.js"; import {getVerificationFailedMessage} from "../../../src/utils/json_rpc.js"; -const testCases = [eth_getBalance_eoa, eth_getBalance_contract]; +const testCases = [ethGetBalanceEoa, ethGetBalanceContract]; describe("verified_requests / eth_getBalance", () => { for (const testCase of testCases) { diff --git a/packages/prover/test/unit/verified_requests/eth_getBlockByHash.test.ts b/packages/prover/test/unit/verified_requests/eth_getBlockByHash.test.ts index 8052b290ff0d..fca484657c01 100644 --- a/packages/prover/test/unit/verified_requests/eth_getBlockByHash.test.ts +++ b/packages/prover/test/unit/verified_requests/eth_getBlockByHash.test.ts @@ -3,13 +3,13 @@ import {createForkConfig} from "@lodestar/config"; import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; import {VERIFICATION_FAILED_RESPONSE_CODE} from "../../../src/constants.js"; import {eth_getBlockByHash} from "../../../src/verified_requests/eth_getBlockByHash.js"; -import eth_getBlock_with_contractCreation from "../../fixtures/sepolia/eth_getBlock_with_contractCreation.json" assert {type: "json"}; -import eth_getBlock_with_no_accessList from "../../fixtures/sepolia/eth_getBlock_with_no_accessList.json" assert {type: "json"}; +import ethGetBlockWithContractCreation from "../../fixtures/sepolia/eth_getBlock_with_contractCreation.json" assert {type: "json"}; +import ethGetBlockWithNoAccessList from "../../fixtures/sepolia/eth_getBlock_with_no_accessList.json" assert {type: "json"}; import {TestFixture, cloneTestFixture, generateReqHandlerOptionsMock} from "../../mocks/request_handler.js"; import {ELBlock} from "../../../src/types.js"; import {getVerificationFailedMessage} from "../../../src/utils/json_rpc.js"; -const testCases = [eth_getBlock_with_no_accessList, eth_getBlock_with_contractCreation] as [ +const testCases = [ethGetBlockWithNoAccessList, ethGetBlockWithContractCreation] as [ TestFixture, TestFixture, ]; diff --git a/packages/prover/test/unit/verified_requests/eth_getBlockByNumber.test.ts b/packages/prover/test/unit/verified_requests/eth_getBlockByNumber.test.ts index fa60be225d20..81f644a46024 100644 --- a/packages/prover/test/unit/verified_requests/eth_getBlockByNumber.test.ts +++ b/packages/prover/test/unit/verified_requests/eth_getBlockByNumber.test.ts @@ -4,12 +4,12 @@ import {NetworkName, networksChainConfig} from "@lodestar/config/networks"; import {VERIFICATION_FAILED_RESPONSE_CODE} from "../../../src/constants.js"; import {ELBlock} from "../../../src/types.js"; import {eth_getBlockByNumber} from "../../../src/verified_requests/eth_getBlockByNumber.js"; -import eth_getBlock_with_contractCreation from "../../fixtures/sepolia/eth_getBlock_with_contractCreation.json" assert {type: "json"}; -import eth_getBlock_with_no_accessList from "../../fixtures/sepolia/eth_getBlock_with_no_accessList.json" assert {type: "json"}; +import ethGetBlockWithContractCreation from "../../fixtures/sepolia/eth_getBlock_with_contractCreation.json" assert {type: "json"}; +import ethGetBlockWithNoAccessList from "../../fixtures/sepolia/eth_getBlock_with_no_accessList.json" assert {type: "json"}; import {TestFixture, cloneTestFixture, generateReqHandlerOptionsMock} from "../../mocks/request_handler.js"; import {getVerificationFailedMessage} from "../../../src/utils/json_rpc.js"; -const testCases = [eth_getBlock_with_no_accessList, eth_getBlock_with_contractCreation] as [ +const testCases = [ethGetBlockWithNoAccessList, ethGetBlockWithContractCreation] as [ TestFixture, TestFixture, ]; diff --git a/packages/prover/test/unit/web3_provider.node.test.ts b/packages/prover/test/unit/web3_provider.node.test.ts index eed6c336483f..a83fbde4d858 100644 --- a/packages/prover/test/unit/web3_provider.node.test.ts +++ b/packages/prover/test/unit/web3_provider.node.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect, afterEach, vi} from "vitest"; -import Web3 from "web3"; +import {Web3} from "web3"; import {ethers} from "ethers"; import {createVerifiedExecutionProvider, ProofProvider, LCTransport} from "@lodestar/prover/browser"; import {ELRpc} from "../../src/utils/rpc.js"; diff --git a/packages/prover/vitest.config.e2e.ts b/packages/prover/vitest.e2e.config.ts similarity index 100% rename from packages/prover/vitest.config.e2e.ts rename to packages/prover/vitest.e2e.config.ts diff --git a/packages/reqresp/README.md b/packages/reqresp/README.md index b384c2c178a2..6f8aea550ca6 100644 --- a/packages/reqresp/README.md +++ b/packages/reqresp/README.md @@ -53,7 +53,7 @@ You will need to go over the [specification](https://github.com/ethereum/beacon- ## Getting started - Follow the [installation guide](https://chainsafe.github.io/lodestar/) to install Lodestar. -- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/usage/local). +- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/advanced-topics/setting-up-a-testnet/). ## Contributors diff --git a/packages/reqresp/package.json b/packages/reqresp/package.json index 66700cf317d0..20627f4de384 100644 --- a/packages/reqresp/package.json +++ b/packages/reqresp/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -45,10 +45,8 @@ "build:release": "yarn clean && yarn run build", "check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"", "check-types": "tsc", - "coverage": "codecov -F lodestar-api", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "test": "yarn test:unit", "test:unit": "vitest --run --dir test/unit/", "check-readme": "typescript-docs-verifier" @@ -56,9 +54,9 @@ "dependencies": { "@chainsafe/fast-crc32c": "^4.1.1", "@libp2p/interface": "^1.1.1", - "@lodestar/config": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/utils": "^1.15.1", + "@lodestar/config": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/utils": "^1.16.0", "it-all": "^3.0.4", "it-pipe": "^3.0.1", "snappy": "^7.2.2", @@ -67,8 +65,8 @@ "uint8arraylist": "^2.4.7" }, "devDependencies": { - "@lodestar/logger": "^1.15.1", - "@lodestar/types": "^1.15.1", + "@lodestar/logger": "^1.16.0", + "@lodestar/types": "^1.16.0", "libp2p": "1.1.1" }, "peerDependencies": { diff --git a/packages/reqresp/src/ReqResp.ts b/packages/reqresp/src/ReqResp.ts index d89acd8e2689..dc1459d87497 100644 --- a/packages/reqresp/src/ReqResp.ts +++ b/packages/reqresp/src/ReqResp.ts @@ -57,9 +57,6 @@ export class ReqResp { private readonly protocolPrefix: string; /** `${protocolPrefix}/${method}/${version}/${encoding}` */ - // Use any to avoid TS error on registering protocol - // Type 'unknown' is not assignable to type 'Resp' - // eslint-disable-next-line @typescript-eslint/no-explicit-any private readonly registeredProtocols = new Map(); private readonly dialOnlyProtocols = new Map(); @@ -80,28 +77,29 @@ export class ReqResp { * * Made it explicit method to avoid any developer mistake */ - registerDialOnlyProtocol(protocol: DialOnlyProtocol, opts?: ReqRespRegisterOpts): void { + registerDialOnlyProtocol(protocol: DialOnlyProtocol): void { const protocolID = this.formatProtocolID(protocol); - // libp2p will throw on error on duplicates, allow to overwrite behavior - if (opts?.ignoreIfDuplicate && this.registeredProtocols.has(protocolID)) { - return; - } - this.registeredProtocols.set(protocolID, protocol); this.dialOnlyProtocols.set(protocolID, true); } /** * Register protocol as supported and to libp2p. - * async because libp2p registar persists the new protocol list in the peer-store. + * async because libp2p registrar persists the new protocol list in the peer-store. * Throws if the same protocol is registered twice. * Can be called at any time, no concept of started / stopped */ async registerProtocol(protocol: Protocol, opts?: ReqRespRegisterOpts): Promise { const protocolID = this.formatProtocolID(protocol); + + // libp2p will throw if handler for protocol is already registered, allow to overwrite behavior + if (opts?.ignoreIfDuplicate && this.registeredProtocols.has(protocolID)) { + return; + } + const {handler: _handler, inboundRateLimits, ...rest} = protocol; - this.registerDialOnlyProtocol(rest, opts); + this.registerDialOnlyProtocol(rest); this.dialOnlyProtocols.set(protocolID, false); if (inboundRateLimits) { @@ -113,7 +111,7 @@ export class ReqResp { /** * Remove protocol as supported and from libp2p. - * async because libp2p registar persists the new protocol list in the peer-store. + * async because libp2p registrar persists the new protocol list in the peer-store. * Does NOT throw if the protocolID is unknown. * Can be called at any time, no concept of started / stopped */ @@ -251,7 +249,6 @@ export class ReqResp { // Override } - // eslint-disable-next-line @typescript-eslint/no-explicit-any protected onIncomingRequestError(_protocol: ProtocolDescriptor, _error: RequestError): void { // Override } diff --git a/packages/reqresp/src/request/errors.ts b/packages/reqresp/src/request/errors.ts index ccc3c12f97a1..ba3904e563ed 100644 --- a/packages/reqresp/src/request/errors.ts +++ b/packages/reqresp/src/request/errors.ts @@ -31,6 +31,8 @@ export enum RequestErrorCode { RESP_TIMEOUT = "REQUEST_ERROR_RESP_TIMEOUT", /** Request rate limited */ REQUEST_RATE_LIMITED = "REQUEST_ERROR_RATE_LIMITED", + /** For malformed SSZ (metadata) responses */ + SSZ_OVER_MAX_SIZE = "SSZ_SNAPPY_ERROR_OVER_SSZ_MAX_SIZE", } type RequestErrorType = @@ -47,7 +49,8 @@ type RequestErrorType = | {code: RequestErrorCode.EMPTY_RESPONSE} | {code: RequestErrorCode.TTFB_TIMEOUT} | {code: RequestErrorCode.RESP_TIMEOUT} - | {code: RequestErrorCode.REQUEST_RATE_LIMITED}; + | {code: RequestErrorCode.REQUEST_RATE_LIMITED} + | {code: RequestErrorCode.SSZ_OVER_MAX_SIZE}; export const REQUEST_ERROR_CLASS_NAME = "RequestError"; diff --git a/packages/reqresp/test/fixtures/messages.ts b/packages/reqresp/test/fixtures/messages.ts index 7c5eedaeb3d4..5558e7c3e13a 100644 --- a/packages/reqresp/test/fixtures/messages.ts +++ b/packages/reqresp/test/fixtures/messages.ts @@ -181,5 +181,4 @@ const ALTAIR_FORK_EPOCH = Math.floor(slotBlockAltair / SLOTS_PER_EPOCH); // eslint-disable-next-line @typescript-eslint/naming-convention export const beaconConfig = createBeaconConfig({...chainConfig, ALTAIR_FORK_EPOCH}, ZERO_HASH); -// eslint-disable-next-line @typescript-eslint/no-empty-function export const getEmptyHandler = () => async function* emptyHandler(): AsyncGenerator {}; diff --git a/packages/reqresp/test/unit/ReqResp.test.ts b/packages/reqresp/test/unit/ReqResp.test.ts index b62b1883cce1..8d78a46f292f 100644 --- a/packages/reqresp/test/unit/ReqResp.test.ts +++ b/packages/reqresp/test/unit/ReqResp.test.ts @@ -63,5 +63,13 @@ describe("ResResp", () => { expect(reqresp.getRegisteredProtocols()).toEqual(["/eth2/beacon_chain/req/number_to_string/1/ssz_snappy"]); expect(libp2p.handle).toHaveBeenCalledOnce(); }); + + it("should not register handler twice for same protocol if ignoreIfDuplicate=true", async () => { + await reqresp.registerProtocol(numberToStringProtocol, {ignoreIfDuplicate: true}); + expect(libp2p.handle).toHaveBeenCalledOnce(); + + await reqresp.registerProtocol(numberToStringProtocol, {ignoreIfDuplicate: true}); + expect(libp2p.handle).toHaveBeenCalledOnce(); + }); }); }); diff --git a/packages/reqresp/test/unit/encoders/responseDecode.test.ts b/packages/reqresp/test/unit/encoders/responseDecode.test.ts index 6e73fbbba315..8fc919c46313 100644 --- a/packages/reqresp/test/unit/encoders/responseDecode.test.ts +++ b/packages/reqresp/test/unit/encoders/responseDecode.test.ts @@ -13,7 +13,6 @@ describe("encoders / responseDecode", () => { it.each(responseEncodersTestCases)("$id", async ({protocol, responseChunks, chunks}) => { const responses = (await pipe( arrToSource(chunks), - // eslint-disable-next-line @typescript-eslint/no-empty-function responseDecode(protocol, {onFirstHeader: () => {}, onFirstResponseChunk: () => {}}), all )) as ResponseIncoming[]; @@ -32,7 +31,6 @@ describe("encoders / responseDecode", () => { await expectRejectedWithLodestarError( pipe( arrToSource(chunks as Uint8Array[]), - // eslint-disable-next-line @typescript-eslint/no-empty-function responseDecode(protocol, {onFirstHeader: () => {}, onFirstResponseChunk: () => {}}), all ), diff --git a/packages/reqresp/test/utils/index.ts b/packages/reqresp/test/utils/index.ts index c3c133352186..afddace8328d 100644 --- a/packages/reqresp/test/utils/index.ts +++ b/packages/reqresp/test/utils/index.ts @@ -73,11 +73,8 @@ export class MockLibP2pStream implements Stream { } }; - // eslint-disable-next-line @typescript-eslint/no-empty-function close: Stream["close"] = async () => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function closeRead = async (): Promise => {}; - // eslint-disable-next-line @typescript-eslint/no-empty-function closeWrite = async (): Promise => {}; abort: Stream["abort"] = () => this.close(); } diff --git a/packages/reqresp/test/utils/response.ts b/packages/reqresp/test/utils/response.ts index b231a3cfb275..b6da1b40bd0e 100644 --- a/packages/reqresp/test/utils/response.ts +++ b/packages/reqresp/test/utils/response.ts @@ -8,11 +8,7 @@ import {arrToSource} from "../utils/index.js"; export async function* responseEncode(responseChunks: ResponseChunk[], protocol: Protocol): AsyncIterable { for (const chunk of responseChunks) { if (chunk.status === RespStatus.SUCCESS) { - yield* pipe( - arrToSource([chunk.payload]), - // eslint-disable-next-line @typescript-eslint/no-empty-function - responseEncodeSuccess(protocol, {onChunk: () => {}}) - ); + yield* pipe(arrToSource([chunk.payload]), responseEncodeSuccess(protocol, {onChunk: () => {}})); } else { yield* responseEncodeError(protocol, chunk.status, chunk.errorMessage); } diff --git a/packages/spec-test-util/package.json b/packages/spec-test-util/package.json index e94fc35c7111..8b4ab28da0fd 100644 --- a/packages/spec-test-util/package.json +++ b/packages/spec-test-util/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/spec-test-util", - "version": "1.15.1", + "version": "1.16.0", "description": "Spec test suite generator from yaml test files", "author": "ChainSafe Systems", "license": "Apache-2.0", @@ -46,9 +46,9 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", + "test": "yarn test:unit && yarn test:e2e", "test:unit": "vitest --run --passWithNoTests --dir test/unit/", - "test:e2e": "vitest --run --config vitest.config.e2e.ts --dir test/e2e/", + "test:e2e": "vitest --run --config vitest.e2e.config.ts --dir test/e2e/", "check-readme": "typescript-docs-verifier" }, "repository": { @@ -62,8 +62,7 @@ "blockchain" ], "dependencies": { - "@lodestar/utils": "^1.15.1", - "async-retry": "^1.3.3", + "@lodestar/utils": "^1.16.0", "axios": "^1.3.4", "rimraf": "^4.4.1", "snappyjs": "^0.7.0", @@ -71,7 +70,6 @@ "vitest": "^1.2.1" }, "devDependencies": { - "@types/async-retry": "1.4.3", "@types/tar": "^6.1.4" }, "peerDependencies": { diff --git a/packages/spec-test-util/src/downloadTests.ts b/packages/spec-test-util/src/downloadTests.ts index d3da52c0306b..e13478934927 100644 --- a/packages/spec-test-util/src/downloadTests.ts +++ b/packages/spec-test-util/src/downloadTests.ts @@ -1,16 +1,15 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import fs from "node:fs"; import path from "node:path"; import stream from "node:stream"; import {promisify} from "node:util"; import {rimraf} from "rimraf"; import axios from "axios"; -import tar from "tar"; -import retry from "async-retry"; +import {x as extractTar} from "tar"; +import {retry} from "@lodestar/utils"; export const defaultSpecTestsRepoUrl = "https://github.com/ethereum/consensus-spec-tests"; -// eslint-disable-next-line @typescript-eslint/no-empty-function const logEmpty = (): void => {}; export type DownloadTestsOptions = { @@ -68,7 +67,6 @@ export async function downloadGenericSpecTests( const url = `${specTestsRepoUrl ?? defaultSpecTestsRepoUrl}/releases/download/${specVersion}/${test}.tar.gz`; await retry( - // async (bail) => { async () => { const {data, headers} = await axios({ method: "get", @@ -81,7 +79,7 @@ export async function downloadGenericSpecTests( log(`Downloading ${url} - ${totalSize} bytes`); // extract tar into output directory - await promisify(stream.pipeline)(data, tar.x({cwd: outputDir})); + await promisify(stream.pipeline)(data, extractTar({cwd: outputDir})); log(`Downloaded ${url}`); }, diff --git a/packages/spec-test-util/vitest.config.e2e.ts b/packages/spec-test-util/vitest.e2e.config.ts similarity index 100% rename from packages/spec-test-util/vitest.config.e2e.ts rename to packages/spec-test-util/vitest.e2e.config.ts diff --git a/packages/state-transition/package.json b/packages/state-transition/package.json index ed76e116fe82..7c3e02a878b9 100644 --- a/packages/state-transition/package.json +++ b/packages/state-transition/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -52,21 +52,22 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", + "test": "yarn test:unit", "test:unit": "vitest --run --dir test/unit/", "check-readme": "typescript-docs-verifier" }, "types": "lib/index.d.ts", "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", - "@chainsafe/bls": "7.1.1", + "@chainsafe/as-sha256": "^0.4.1", + "@chainsafe/bls": "7.1.3", "@chainsafe/blst": "^0.2.9", "@chainsafe/persistent-merkle-tree": "^0.6.1", "@chainsafe/persistent-ts": "^0.19.1", "@chainsafe/ssz": "^0.14.0", - "@lodestar/config": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/types": "^1.15.1", - "@lodestar/utils": "^1.15.1", + "@lodestar/config": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/types": "^1.16.0", + "@lodestar/utils": "^1.16.0", "bigint-buffer": "^1.1.5", "buffer-xor": "^2.0.2" }, diff --git a/packages/state-transition/src/util/sszBytes.ts b/packages/state-transition/src/util/sszBytes.ts index 25b65626a0dd..b5141e1673e5 100644 --- a/packages/state-transition/src/util/sszBytes.ts +++ b/packages/state-transition/src/util/sszBytes.ts @@ -37,14 +37,14 @@ export const VALIDATOR_BYTES_SIZE = 121; */ const SLOT_BYTES_POSITION_IN_STATE = 40; -export function getForkFromStateBytes(config: ChainForkConfig, bytes: Buffer | Uint8Array): ForkSeq { +export function getForkFromStateBytes(config: ChainForkConfig, bytes: Uint8Array): ForkSeq { const slot = bytesToInt(bytes.subarray(SLOT_BYTES_POSITION_IN_STATE, SLOT_BYTES_POSITION_IN_STATE + SLOT_BYTE_COUNT)); return config.getForkSeq(slot); } export function getStateTypeFromBytes( config: ChainForkConfig, - bytes: Buffer | Uint8Array + bytes: Uint8Array ): allForks.AllForksSSZTypes["BeaconState"] { const slot = getStateSlotFromBytes(bytes); return config.getForkTypes(slot).BeaconState; diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 1d976c5e609e..9d8e8c306e37 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@lodestar/test-utils", "private": true, - "version": "1.15.1", + "version": "1.16.0", "description": "Test utilities reused across other packages", "author": "ChainSafe Systems", "license": "Apache-2.0", @@ -44,7 +44,6 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "check-readme": "typescript-docs-verifier" }, "repository": { @@ -58,10 +57,10 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "7.1.1", - "@chainsafe/bls-keystore": "^3.0.0", - "@lodestar/params": "^1.15.1", - "@lodestar/utils": "^1.15.1", + "@chainsafe/bls": "7.1.3", + "@chainsafe/bls-keystore": "^3.0.1", + "@lodestar/params": "^1.16.0", + "@lodestar/utils": "^1.16.0", "axios": "^1.3.4", "testcontainers": "^10.2.1", "tmp": "^0.2.1", diff --git a/packages/test-utils/src/childProcess.ts b/packages/test-utils/src/childProcess.ts index 35149b1ca967..ae01d7ce0e86 100644 --- a/packages/test-utils/src/childProcess.ts +++ b/packages/test-utils/src/childProcess.ts @@ -3,7 +3,7 @@ import childProcess from "node:child_process"; import stream from "node:stream"; import fs from "node:fs"; import path from "node:path"; -import {sleep} from "@lodestar/utils"; +import {prettyMsToTime, sleep} from "@lodestar/utils"; import {TestContext} from "./interfaces.js"; /** @@ -79,6 +79,19 @@ export async function execChildProcess(cmd: string | string[], options?: ExecChi }); } +/** + * Check if process with given pid is running + */ +export function isPidRunning(pid: number): boolean { + try { + // Signal 0 is a special signal that checks if the process exists + process.kill(pid, 0); + return true; + } catch { + return false; + } +} + export const stopChildProcess = async ( childProcess: childProcess.ChildProcess, signal: NodeJS.Signals | number = "SIGTERM" @@ -87,11 +100,20 @@ export const stopChildProcess = async ( return; } - return new Promise((resolve, reject) => { + const pid = childProcess.pid; + + await new Promise((resolve, reject) => { childProcess.once("error", reject); - childProcess.once("close", resolve); + // We use `exit` instead of `close` as multiple processes can share same `stdio` + childProcess.once("exit", resolve); childProcess.kill(signal); }); + + if (pid != null && isPidRunning(pid)) { + // Wait for sometime and try to kill this time + await sleep(500); + await stopChildProcess(childProcess, "SIGKILL"); + } }; /** @@ -306,13 +328,15 @@ export async function spawnChildProcess( const timeSinceHealthCheckStart = Date.now() - startHealthCheckMs; if (timeSinceHealthCheckStart > logHealthChecksAfterMs) { console.log( - `Health check unsuccessful. logPrefix=${logPrefix} pid=${proc.pid} timeSinceHealthCheckStart=${timeSinceHealthCheckStart}` + `Health check unsuccessful. logPrefix=${logPrefix} pid=${ + proc.pid + } timeSinceHealthCheckStart=${prettyMsToTime(timeSinceHealthCheckStart)}` ); } } }) .catch((e) => { - console.error("error on health check, health functions must never throw", e); + console.error("Error on health check, health functions must never throw", e); }); }, healthCheckIntervalMs); @@ -322,7 +346,9 @@ export async function spawnChildProcess( if (intervalId !== undefined) { reject( new Error( - `Health check timeout. logPrefix=${logPrefix} pid=${proc.pid} healthTimeoutMs=${healthTimeoutMs}` + `Health check timeout. logPrefix=${logPrefix} pid=${proc.pid} healthTimeout=${prettyMsToTime( + healthTimeoutMs ?? 0 + )}` ) ); } @@ -336,9 +362,9 @@ export async function spawnChildProcess( reject( new Error( - `process exited before healthy. logPrefix=${logPrefix} pid=${ - proc.pid - } healthTimeoutMs=${healthTimeoutMs} code=${code} command="${command} ${args.join(" ")}"` + `Process exited before healthy. logPrefix=${logPrefix} pid=${proc.pid} healthTimeout=${prettyMsToTime( + healthTimeoutMs ?? 0 + )} code=${code} command="${command} ${args.join(" ")}"` ) ); }); diff --git a/packages/test-utils/src/externalSigner.ts b/packages/test-utils/src/externalSigner.ts index 20cf59cc26d0..96de139613b6 100644 --- a/packages/test-utils/src/externalSigner.ts +++ b/packages/test-utils/src/externalSigner.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import tmp from "tmp"; +import {dirSync as tmpDirSync} from "tmp"; import {GenericContainer, Wait, StartedTestContainer} from "testcontainers"; import {ForkSeq} from "@lodestar/params"; @@ -23,7 +23,7 @@ export async function startExternalSigner({ password: string; }): Promise { // path to store configuration - const tmpDir = tmp.dirSync({ + const tmpDir = tmpDirSync({ unsafeCleanup: true, // In Github runner NodeJS process probably runs as root, so web3signer doesn't have permissions to read config dir mode: 755, diff --git a/packages/types/package.json b/packages/types/package.json index 3888eeae2cc2..c492eed43014 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": { ".": { @@ -61,9 +61,10 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", + "test": "yarn test:unit", "test:constants:minimal": "LODESTAR_PRESET=minimal vitest --run --dir test/constants/", "test:constants:mainnet": "LODESTAR_PRESET=mainnet vitest --run --dir test/constants/", - "test:unit": "wrapper() { yarn test:constants:minimal $@ && yarn test:constants:mainnet $@ && vitest --run --dir test/unit/ $@; }; wrapper", + "test:unit": "wrapper() { yarn test:constants:minimal $@ && yarn test:constants:mainnet $@ && LODESTAR_PRESET=mainnet vitest --run --dir test/unit/ $@; }; wrapper", "test:browsers": "yarn test:browsers:chrome && yarn test:browsers:firefox && yarn test:browsers:electron", "test:browsers:chrome": "vitest --run --browser chrome --config ./vitest.browser.config.ts --dir test/unit", "test:browsers:firefox": "vitest --run --browser firefox --config ./vitest.browser.config.ts --dir test/unit", @@ -73,7 +74,8 @@ "types": "lib/index.d.ts", "dependencies": { "@chainsafe/ssz": "^0.14.0", - "@lodestar/params": "^1.15.1" + "@lodestar/params": "^1.16.0", + "ethereum-cryptography": "^2.0.0" }, "keywords": [ "ethereum", diff --git a/packages/types/src/bellatrix/sszTypes.ts b/packages/types/src/bellatrix/sszTypes.ts index c6919d04b631..08f0378ef92a 100644 --- a/packages/types/src/bellatrix/sszTypes.ts +++ b/packages/types/src/bellatrix/sszTypes.ts @@ -9,7 +9,7 @@ import { import {ssz as primitiveSsz} from "../primitive/index.js"; import {ssz as phase0Ssz} from "../phase0/index.js"; import {ssz as altairSsz} from "../altair/index.js"; -import {stringType} from "../utils/StringType.js"; +import {stringType} from "../utils/stringType.js"; const { Bytes32, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index d90b55909884..6931271aaa29 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -3,6 +3,6 @@ export * as ssz from "./sszTypes.js"; // Typeguards export * from "./utils/typeguards.js"; // String type -export {StringType, stringType} from "./utils/StringType.js"; +export {StringType, stringType} from "./utils/stringType.js"; // Container utils export * from "./utils/container.js"; diff --git a/packages/types/src/primitive/sszTypes.ts b/packages/types/src/primitive/sszTypes.ts index 65c81d1247b9..068a32e2cc17 100644 --- a/packages/types/src/primitive/sszTypes.ts +++ b/packages/types/src/primitive/sszTypes.ts @@ -1,4 +1,5 @@ import {ByteVectorType, UintNumberType, UintBigintType, BooleanType} from "@chainsafe/ssz"; +import {ExecutionAddressType} from "../utils/executionAddress.js"; export const Boolean = new BooleanType(); export const Byte = new UintNumberType(1); @@ -61,4 +62,4 @@ export const BLSPubkey = Bytes48; export const BLSSignature = Bytes96; export const Domain = Bytes32; export const ParticipationFlags = new UintNumberType(1, {setBitwiseOR: true}); -export const ExecutionAddress = Bytes20; +export const ExecutionAddress = new ExecutionAddressType(); diff --git a/packages/types/src/utils/executionAddress.ts b/packages/types/src/utils/executionAddress.ts new file mode 100644 index 000000000000..9d555c016f04 --- /dev/null +++ b/packages/types/src/utils/executionAddress.ts @@ -0,0 +1,48 @@ +import {keccak256} from "ethereum-cryptography/keccak.js"; +import {ByteVectorType} from "@chainsafe/ssz"; + +export type ByteVector = Uint8Array; + +export class ExecutionAddressType extends ByteVectorType { + constructor() { + super(20, {typeName: "ExecutionAddress"}); + } + toJson(value: ByteVector): unknown { + const str = super.toJson(value) as string; + return toChecksumAddress(str); + } +} + +function isAddressValid(address: string): boolean { + return /^(0x)?[0-9a-f]{40}$/i.test(address); +} + +/** + * Formats an address according to [ERC55](https://eips.ethereum.org/EIPS/eip-55) + */ +export function toChecksumAddress(address: string): string { + if (!isAddressValid(address)) { + throw Error(`Invalid address: ${address}`); + } + + const rawAddress = (address.startsWith("0x") ? address.slice(2) : address).toLowerCase(); + const chars = rawAddress.split(""); + + // Inspired by https://github.com/ethers-io/ethers.js/blob/cac1da1f912c2ae9ba20f25aa51a91766673cd76/src.ts/address/address.ts#L8 + const expanded = new Uint8Array(chars.length); + for (let i = 0; i < expanded.length; i++) { + expanded[i] = rawAddress[i].charCodeAt(0); + } + + const hashed = keccak256(expanded); + for (let i = 0; i < chars.length; i += 2) { + if (hashed[i >> 1] >> 4 >= 8) { + chars[i] = chars[i].toUpperCase(); + } + if ((hashed[i >> 1] & 0x0f) >= 8) { + chars[i + 1] = chars[i + 1].toUpperCase(); + } + } + + return "0x" + chars.join(""); +} diff --git a/packages/types/src/utils/StringType.ts b/packages/types/src/utils/stringType.ts similarity index 100% rename from packages/types/src/utils/StringType.ts rename to packages/types/src/utils/stringType.ts diff --git a/packages/types/test/unit/executionAddress.test.ts b/packages/types/test/unit/executionAddress.test.ts new file mode 100644 index 000000000000..841cd52468f5 --- /dev/null +++ b/packages/types/test/unit/executionAddress.test.ts @@ -0,0 +1,72 @@ +import {describe, it, expect} from "vitest"; +import {toChecksumAddress} from "../../src/utils/executionAddress.js"; + +describe("toChecksumAddress", () => { + it("should fail with invalid addresses", () => { + expect(() => toChecksumAddress("1234")).toThrowError("Invalid address: 1234"); + expect(() => toChecksumAddress("0x1234")).toThrowError("Invalid address: 0x1234"); + }); + + it("should format addresses as ERC55", () => { + type TestCase = { + address: string; + checksumAddress: string; + }; + + const testCases: TestCase[] = [ + // Input all caps + { + address: "0x52908400098527886E0F7030069857D2E4169EE7", + checksumAddress: "0x52908400098527886E0F7030069857D2E4169EE7", + }, + { + address: "0xDE709F2102306220921060314715629080E2FB77", + checksumAddress: "0xde709f2102306220921060314715629080e2fb77", + }, + // Without 0x prefix + { + address: "52908400098527886e0f7030069857d2e4169ee7", + checksumAddress: "0x52908400098527886E0F7030069857D2E4169EE7", + }, + // All caps + { + address: "0x52908400098527886e0f7030069857d2e4169ee7", + checksumAddress: "0x52908400098527886E0F7030069857D2E4169EE7", + }, + { + address: "0x8617e340b3d01fa5f11f306f4090fd50e238070d", + checksumAddress: "0x8617E340B3D01FA5F11F306F4090FD50E238070D", + }, + // All lower + { + address: "0xde709f2102306220921060314715629080e2fb77", + checksumAddress: "0xde709f2102306220921060314715629080e2fb77", + }, + { + address: "0x27b1fdb04752bbc536007a920d24acb045561c26", + checksumAddress: "0x27b1fdb04752bbc536007a920d24acb045561c26", + }, + // Normal + { + address: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", + checksumAddress: "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + }, + { + address: "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359", + checksumAddress: "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + }, + { + address: "0xdbf03b407c01e7cd3cbea99509d93f8dddc8c6fb", + checksumAddress: "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + }, + { + address: "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", + checksumAddress: "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", + }, + ]; + + for (const {address, checksumAddress} of testCases) { + expect(toChecksumAddress(address)).toBe(checksumAddress); + } + }); +}); diff --git a/packages/utils/package.json b/packages/utils/package.json index 1a5497c94581..06c89f6593ca 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -11,7 +11,7 @@ "bugs": { "url": "https://github.com/ChainSafe/lodestar/issues" }, - "version": "1.15.1", + "version": "1.16.0", "type": "module", "exports": "./lib/index.js", "files": [ @@ -30,7 +30,6 @@ "check-types": "tsc && vitest --run --typecheck --dir test/types/", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "test:unit": "vitest --run --dir test/unit", "test:browsers": "yarn test:browsers:chrome && yarn test:browsers:firefox && yarn test:browsers:electron", "test:browsers:chrome": "vitest --run --browser chrome --config ./vitest.browser.config.ts --dir test/unit", @@ -40,7 +39,7 @@ }, "types": "lib/index.d.ts", "dependencies": { - "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/as-sha256": "^0.4.1", "any-signal": "3.0.1", "bigint-buffer": "^1.1.5", "case": "^1.6.3", diff --git a/packages/utils/src/format.ts b/packages/utils/src/format.ts index 6a88ead41490..8bd8a40273f1 100644 --- a/packages/utils/src/format.ts +++ b/packages/utils/src/format.ts @@ -1,4 +1,5 @@ import {toHexString} from "./bytes.js"; +import {ETH_TO_WEI} from "./ethConversion.js"; /** * Format bytes as `0x1234…1234` @@ -27,3 +28,33 @@ export function truncBytes(root: Uint8Array | string): string { const str = typeof root === "string" ? root : toHexString(root); return str.slice(0, 14); } + +/** + * Format a bigint value as a decimal string + */ +export function formatBigDecimal(numerator: bigint, denominator: bigint, maxDecimalFactor: bigint): string { + const full = numerator / denominator; + const fraction = ((numerator - full * denominator) * maxDecimalFactor) / denominator; + + // zeros to be added post decimal are number of zeros in maxDecimalFactor - number of digits in fraction + const zerosPostDecimal = String(maxDecimalFactor).length - 1 - String(fraction).length; + return `${full}.${"0".repeat(zerosPostDecimal)}${fraction}`; +} + +// display upto 5 decimal places +const MAX_DECIMAL_FACTOR = BigInt("100000"); + +/** + * Format wei as ETH, with up to 5 decimals and append ' ETH' + */ +export function prettyWeiToEth(wei: bigint): string { + return `${formatBigDecimal(wei, ETH_TO_WEI, MAX_DECIMAL_FACTOR)} ETH`; +} + +/** + * Format milliseconds to time format HH:MM:SS.ms + */ +export function prettyMsToTime(timeMs: number): string { + const date = new Date(0, 0, 0, 0, 0, 0, timeMs); + return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`; +} diff --git a/packages/utils/src/metrics.ts b/packages/utils/src/metrics.ts index a25518280ee1..f8a965f2800a 100644 --- a/packages/utils/src/metrics.ts +++ b/packages/utils/src/metrics.ts @@ -1,3 +1,5 @@ +import {NonEmptyArray} from "./types.js"; + export type NoLabels = Record; export type LabelsGeneric = Record; export type LabelKeys = Extract; @@ -39,7 +41,7 @@ export interface Counter { export type GaugeConfig = { name: string; help: string; -} & (NoLabels extends Labels ? {labelNames?: never} : {labelNames: [LabelKeys, ...LabelKeys[]]}); +} & (NoLabels extends Labels ? {labelNames?: never} : {labelNames: NonEmptyArray>}); export type HistogramConfig = GaugeConfig & { buckets?: number[]; diff --git a/packages/utils/src/promise.ts b/packages/utils/src/promise.ts index 51e1cdbaf1e3..2be7467bfff4 100644 --- a/packages/utils/src/promise.ts +++ b/packages/utils/src/promise.ts @@ -1,4 +1,6 @@ +import {ErrorAborted, TimeoutError} from "./errors.js"; import {sleep} from "./sleep.js"; +import {ArrayToTuple, NonEmptyArray} from "./types.js"; /** * While promise t is not finished, call function `fn` per `interval` @@ -25,115 +27,126 @@ export async function callFnWhenAwait( return t; } -enum PromiseStatus { - resolved, - rejected, - pending, -} - -type PromiseState = - | {status: PromiseStatus.resolved; value: T} - | {status: PromiseStatus.rejected; value: Error} - | {status: PromiseStatus.pending; value: null}; - -function mapStatusesToResponses(promisesStates: PromiseState[]): (Error | T)[] { - return promisesStates.map((pmStatus) => { - switch (pmStatus.status) { - case PromiseStatus.resolved: - return pmStatus.value; - case PromiseStatus.rejected: - return pmStatus.value; - case PromiseStatus.pending: - return Error("pending"); +export type PromiseResult = { + promise: Promise; +} & ( + | { + status: "pending"; } - }); -} + | { + status: "fulfilled"; + value: T; + durationMs: number; + } + | { + status: "rejected"; + reason: Error; + durationMs: number; + } +); +export type PromiseFulfilledResult = PromiseResult & {status: "fulfilled"}; +export type PromiseRejectedResult = PromiseResult & {status: "rejected"}; -export enum RaceEvent { - /** all reject/resolve before cutoff */ - precutoff = "precutoff-return", - /** cutoff reached as some were pending till cutoff **/ - cutoff = "cutoff-reached", - /** atleast one resolved till cutoff so no race required */ - resolvedatcutoff = "resolved-at-cutoff", - /** if none reject/resolve before cutoff but one resolves or all reject before timeout */ - pretimeout = "pretimeout-return", - /** timeout reached as none resolved and some were pending till timeout*/ - timeout = "timeout-reached", +/** + * Wrap a promise to an object to track the status and value of the promise + */ +export function wrapPromise(promise: PromiseLike): PromiseResult { + const startedAt = Date.now(); + + const result = { + promise: promise.then( + (value) => { + result.status = "fulfilled"; + (result as PromiseFulfilledResult).value = value; + (result as PromiseFulfilledResult).durationMs = Date.now() - startedAt; + return value; + }, + (reason: unknown) => { + result.status = "rejected"; + (result as PromiseRejectedResult).reason = reason as Error; + (result as PromiseRejectedResult).durationMs = Date.now() - startedAt; + throw reason; + } + ), + status: "pending", + } as PromiseResult; - // events for the promises for better tracking - /** promise resolved */ - resolved = "resolved", - /** promise rejected */ - rejected = "rejected", + return result; } +type ReturnPromiseWithTuple>> = { + [Index in keyof ArrayToTuple]: PromiseResult>; +}; + /** - * Wait for promises to resolve till cutoff and then race them beyond the cutoff with an overall timeout - * @return resolved values or rejections or still pending errors corresponding to input promises + * Two phased approach for resolving promises: + * - first wait `resolveTimeoutMs` or until all promises settle + * - then wait `raceTimeoutMs - resolveTimeoutMs` or until at least a single promise resolves + * + * Returns a list of promise results, see `PromiseResult` */ -export async function racePromisesWithCutoff( - promises: Promise[], - cutoffMs: number, - timeoutMs: number, - eventCb: (event: RaceEvent, delayMs: number, index?: number) => void -): Promise<(Error | T)[]> { - // start the cutoff and timeout timers - let cutoffObserved = false; - const cutoffPromise = sleep(cutoffMs).then(() => { - cutoffObserved = true; - }); - let timeoutObserved = false; - const timeoutPromise = sleep(timeoutMs).then(() => { - timeoutObserved = true; - }); - const startTime = Date.now(); +export async function resolveOrRacePromises>>( + promises: T, + { + resolveTimeoutMs, + raceTimeoutMs, + signal, + }: { + resolveTimeoutMs: number; + raceTimeoutMs: number; + signal?: AbortSignal; + } +): Promise> | never { + if (raceTimeoutMs <= resolveTimeoutMs) { + throw new Error("Race time must be greater than resolve time"); + } + const resolveTimeoutError = new TimeoutError( + `Given promises can't be resolved within resolveTimeoutMs=${resolveTimeoutMs}` + ); + const raceTimeoutError = new TimeoutError( + `Not a any single promise be resolved in given raceTimeoutMs=${raceTimeoutMs}` + ); - // Track promises status and resolved values/rejected errors - // Even if the promises reject with the following decoration promises will not throw - const promisesStates = [] as PromiseState[]; - promises.forEach((promise, index) => { - promisesStates[index] = {status: PromiseStatus.pending, value: null}; - promise - .then((value) => { - eventCb(RaceEvent.resolved, Date.now() - startTime, index); - promisesStates[index] = {status: PromiseStatus.resolved, value}; - }) - .catch((e: Error) => { - eventCb(RaceEvent.rejected, Date.now() - startTime, index); - promisesStates[index] = {status: PromiseStatus.rejected, value: e}; - }); - }); + const promiseResults = promises.map((p) => wrapPromise(p)) as ReturnPromiseWithTuple; + // We intentionally want an array of promises here + // eslint-disable-next-line @typescript-eslint/no-floating-promises + promises = (promiseResults as PromiseResult[]).map((p) => p.promise) as unknown as T; - // Wait till cutoff time unless all original promises resolve/reject early - await Promise.allSettled(promises.map((promise) => Promise.race([promise, cutoffPromise]))); - if (cutoffObserved) { - // If any is resolved, then just simply return as we are post cutoff - const anyResolved = promisesStates.reduce( - (acc, pmState) => acc || pmState.status === PromiseStatus.resolved, - false - ); - if (anyResolved) { - eventCb(RaceEvent.resolvedatcutoff, Date.now() - startTime); - return mapStatusesToResponses(promisesStates); - } else { - eventCb(RaceEvent.cutoff, Date.now() - startTime); + try { + await Promise.race([ + Promise.allSettled(promises), + sleep(resolveTimeoutMs, signal).then(() => { + throw resolveTimeoutError; + }), + ]); + + return promiseResults; + } catch (err) { + if (err instanceof ErrorAborted) { + return promiseResults; + } + if (err !== resolveTimeoutError) { + throw err; } - } else { - eventCb(RaceEvent.precutoff, Date.now() - startTime); - return mapStatusesToResponses(promisesStates); } - // Post deadline resolve with any of the promise or all rejected before timeout - await Promise.any(promises.map((promise) => Promise.race([promise, timeoutPromise]))).catch( - // just ignore if all reject as we will returned mapped rejections - // eslint-disable-next-line @typescript-eslint/no-empty-function - (_e) => {} - ); - if (timeoutObserved) { - eventCb(RaceEvent.timeout, Date.now() - startTime); - } else { - eventCb(RaceEvent.pretimeout, Date.now() - startTime); + try { + await Promise.race([ + Promise.any(promises), + sleep(raceTimeoutMs - resolveTimeoutMs, signal).then(() => { + throw raceTimeoutError; + }), + ]); + + return promiseResults; + } catch (err) { + if (err instanceof ErrorAborted) { + return promiseResults; + } + if (err !== raceTimeoutError && !(err instanceof AggregateError)) { + throw err; + } } - return mapStatusesToResponses(promisesStates); + + return promiseResults; } diff --git a/packages/utils/src/retry.ts b/packages/utils/src/retry.ts index 19ebffc16898..21d3b9a805e4 100644 --- a/packages/utils/src/retry.ts +++ b/packages/utils/src/retry.ts @@ -6,11 +6,17 @@ export type RetryOptions = { */ retries?: number; /** - * An optional Function that is invoked after the provided callback throws - * It expects a boolean to know if it should retry or not - * Useful to make retrying conditional on the type of error thrown + * An optional Function that is invoked after the provided callback throws. + * It expects a boolean to know if it should retry or not. + * Useful to make retrying conditional on the type of error thrown. */ shouldRetry?: (lastError: Error) => boolean; + /** + * An optional Function that is invoked right before a retry is performed. + * It's passed the Error that triggered it and a number identifying the attempt. + * Useful to track number of retries and errors in logs or metrics. + */ + onRetry?: (lastError: Error, attempt: number) => unknown; /** * Milliseconds to wait before retrying again */ @@ -27,10 +33,14 @@ export type RetryOptions = { export async function retry(fn: (attempt: number) => A | Promise, opts?: RetryOptions): Promise { const maxRetries = opts?.retries ?? 5; const shouldRetry = opts?.shouldRetry; + const onRetry = opts?.onRetry; let lastError: Error = Error("RetryError"); for (let i = 1; i <= maxRetries; i++) { try { + // If not the first attempt, invoke right before retrying + if (i > 1) onRetry?.(lastError, i); + return await fn(i); } catch (e) { lastError = e as Error; diff --git a/packages/utils/src/sleep.ts b/packages/utils/src/sleep.ts index 9f46f12074af..c31a9daffd32 100644 --- a/packages/utils/src/sleep.ts +++ b/packages/utils/src/sleep.ts @@ -12,7 +12,6 @@ export async function sleep(ms: number, signal?: AbortSignal): Promise { return new Promise((resolve, reject) => { if (signal && signal.aborted) return reject(new ErrorAborted()); - // eslint-disable-next-line @typescript-eslint/no-empty-function let onDone: () => void = () => {}; const timeout = setTimeout(() => { diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index f302c9a4c80c..5b46d65053ef 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -14,3 +14,14 @@ export type RecursivePartial = { export function bnToNum(bn: bigint): number { return Number(bn); } + +export type NonEmptyArray = [T, ...T[]]; + +/** + * ArrayToTuple converts an `Array` to `[T, ...T]` + * + * eg: `[1, 2, 3]` from type `number[]` to `[number, number, number]` + */ +export type ArrayToTuple> = { + [Index in keyof Tuple]: Tuple[Index]; +}; diff --git a/packages/utils/test/unit/err.test.ts b/packages/utils/test/unit/err.test.ts index 54908c50ccd3..94cebe3ed1a1 100644 --- a/packages/utils/test/unit/err.test.ts +++ b/packages/utils/test/unit/err.test.ts @@ -1,8 +1,6 @@ import {describe, it, expect} from "vitest"; import {Err, isErr, mapOkResults, mapOkResultsAsync, Result} from "../../src/err.js"; -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ - describe("Result Err", () => { describe("isErr works with any type", () => { const values: any[] = [ @@ -15,7 +13,6 @@ describe("Result Err", () => { null, [1, 2], new Uint8Array(1), - // eslint-disable-next-line @typescript-eslint/no-empty-function function test() {}, {a: 1}, new AbortController(), diff --git a/packages/utils/test/unit/format.test.ts b/packages/utils/test/unit/format.test.ts new file mode 100644 index 000000000000..11e8b89cb7a1 --- /dev/null +++ b/packages/utils/test/unit/format.test.ts @@ -0,0 +1,23 @@ +import {describe, it, expect} from "vitest"; +import {formatBigDecimal} from "../../src/format.js"; + +describe("format", () => { + describe("formatBigDecimal", () => { + const testCases: [bigint, bigint, bigint, string][] = [ + [BigInt("103797739275696858"), BigInt("1000000000000000000"), BigInt("100000"), "0.10379"], + [BigInt("103797739275696858"), BigInt("1000000000000000000"), BigInt("1000"), "0.103"], + [BigInt("10379773927569685"), BigInt("1000000000000000000"), BigInt("1000"), "0.010"], + [BigInt("1037977392756968"), BigInt("1000000000000000000"), BigInt("1000"), "0.001"], + [BigInt("1037977392756968"), BigInt("1000000000000000000"), BigInt("100000"), "0.00103"], + [BigInt("58200000000000000"), BigInt("1000000000000000000"), BigInt("100000"), "0.05820"], + [BigInt("111103797739275696858"), BigInt("1000000000000000000"), BigInt("100000"), "111.10379"], + [BigInt("111103797739275696858"), BigInt("1000000000000000000"), BigInt("1000"), "111.103"], + [BigInt("1037977392756"), BigInt("1000000000000000000"), BigInt("100000"), "0.00000"], + ]; + for (const [numerator, denominator, decimalFactor, expectedString] of testCases) { + it(`format ${numerator} / ${denominator} correctly to ${expectedString}`, () => { + expect(formatBigDecimal(numerator, denominator, decimalFactor)).toBe(expectedString); + }); + } + }); +}); diff --git a/packages/utils/test/unit/promise.test.ts b/packages/utils/test/unit/promise.test.ts new file mode 100644 index 000000000000..c76e72f70a22 --- /dev/null +++ b/packages/utils/test/unit/promise.test.ts @@ -0,0 +1,73 @@ +/* Causing this error on usage of expect.any(Number) */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import {describe, it, expect} from "vitest"; +import {wrapPromise, PromiseRejectedResult, PromiseFulfilledResult} from "../../src/promise.js"; + +describe("promise", () => { + describe("wrapPromise", () => { + const timeoutMs = 100; + // TODO: Debug how promise is resolved quicker than the timeout + const promiseDurationMin = timeoutMs - 1; + // Add some margin for execution + const promiseDurationMax = timeoutMs + 20; + + it("should have initial status to pending", async () => { + const result = wrapPromise(Promise.resolve("my value")); + expect(result.status).toBe("pending"); + }); + + it("should resolve to value for a resolved promise", async () => { + const promise = Promise.resolve("my value"); + const result = wrapPromise(promise); + + await expect(promise).resolves.toBe("my value"); + expect(result).toEqual({ + value: "my value", + status: "fulfilled", + promise, + durationMs: expect.any(Number), + }); + }); + + it("should throw error for rejected promise", async () => { + const promise = Promise.reject("test error"); + const result = wrapPromise(promise); + + await expect(promise).rejects.toThrow("test error"); + await expect(result.promise).rejects.toThrow("test error"); + expect(result).toEqual({ + reason: "test error", + status: "rejected", + promise, + durationMs: expect.any(Number), + }); + }); + + it("should have correct durationMs attribute for promise which is resolved", async () => { + const promise = new Promise((resolve) => { + setTimeout(() => { + resolve("Resolved Value"); + }, timeoutMs); + }); + const result = wrapPromise(promise); + + await expect(promise).resolves.toBe("Resolved Value"); + expect((result as PromiseFulfilledResult).durationMs).toBeGreaterThanOrEqual(promiseDurationMin); + expect((result as PromiseFulfilledResult).durationMs).toBeLessThanOrEqual(promiseDurationMax); + }); + + it("should have correct durationMs attribute for promise which is rejected", async () => { + const promise = new Promise((_, reject) => { + setTimeout(() => { + reject("Rejected Error"); + }, timeoutMs); + }); + const result = wrapPromise(promise); + + await expect(promise).rejects.toThrow("Rejected Error"); + await expect(result.promise).rejects.toThrow("Rejected Error"); + expect((result as PromiseRejectedResult).durationMs).toBeGreaterThanOrEqual(promiseDurationMin); + expect((result as PromiseRejectedResult).durationMs).toBeLessThanOrEqual(promiseDurationMax); + }); + }); +}); diff --git a/packages/utils/test/unit/promiserace.test.ts b/packages/utils/test/unit/promiserace.test.ts index 5d0567553522..1f31a55014a4 100644 --- a/packages/utils/test/unit/promiserace.test.ts +++ b/packages/utils/test/unit/promiserace.test.ts @@ -1,7 +1,8 @@ import {describe, it, expect} from "vitest"; -import {racePromisesWithCutoff, RaceEvent} from "../../src/promise.js"; +import {resolveOrRacePromises, PromiseResult} from "../../src/promise.js"; +import {NonEmptyArray} from "../../src/types.js"; -describe("racePromisesWithCutoff", () => { +describe("resolveOrRacePromises", () => { const cutoffMs = 1000; const timeoutMs = 1500; @@ -9,96 +10,73 @@ describe("racePromisesWithCutoff", () => { new Promise((resolve) => { setTimeout(() => resolve(value), delay); }); + const rejectAfter = (value: string, delay: number): Promise => new Promise((_resolve, reject) => { setTimeout(() => reject(Error(value)), delay); }); - // For brevity in testcases - const precutoff = RaceEvent.precutoff; - const cutoff = RaceEvent.cutoff; - const resolvedatcutoff = RaceEvent.resolvedatcutoff; - const pretimeout = RaceEvent.pretimeout; - const timeout = RaceEvent.timeout; - const resolved = RaceEvent.resolved; - const rejected = RaceEvent.rejected; - - // Second item in testcase row i.e. array of numbers represent delay at which promise to be resolved - // or rejected (-ve number) - // Third item in testcase row is the expected value or error message in string - // Last item is expected events - const testcases: [string, number[], (string | Error)[], RaceEvent[]][] = [ - ["all resolve pre-cutoff", [100, 200], ["100", "200"], [resolved, resolved, precutoff]], - ["all resolve/reject pre-cutoff", [100, -200], ["100", "-200"], [resolved, rejected, precutoff]], - ["all reject pre-cutoff", [-100, -200], ["-100", "-200"], [rejected, rejected, precutoff]], - ["all reject pre-timeout", [-1100, -1200], ["-1100", "-1200"], [cutoff, rejected, rejected, pretimeout]], - ["race and resolve pre-timeout", [1100, 1200], ["1100", "pending"], [cutoff, resolved, pretimeout]], - [ - "race and resolve/reject pre-timeout", - [-1100, 1200, 1300], - ["-1100", "1200", "pending"], - [cutoff, rejected, resolved, pretimeout], - ], + const testCases: [string, number[], (string | Error)[]][] = [ + ["all resolve pre-cutoff", [100, 200], ["100", "200"]], + ["all resolve/reject pre-cutoff", [100, -200], ["100", "-200"]], + ["all reject pre-cutoff", [-100, -200], ["-100", "-200"]], + ["all reject pre-timeout", [-1100, -1200], ["-1100", "-1200"]], + ["race and resolve pre-timeout", [1100, 1200], ["1100", "pending"]], + ["race and resolve/reject pre-timeout", [-1100, 1200, 1300], ["-1100", "1200", "pending"]], [ "some resolve pre-cutoff with no race post cutoff", [100, -200, -1100, 1200], ["100", "-200", "pending", "pending"], - [resolved, rejected, resolvedatcutoff], ], [ "some reject pre-cutoff, with race resolution pre-timeout", [-100, -200, -1100, 1100, 1200], ["-100", "-200", "-1100", "1100", "pending"], - [rejected, rejected, cutoff, rejected, resolved, pretimeout], - ], - [ - "some reject pre-cutoff, rest reject pre-timeout", - [-100, -200, -1100, -1200], - ["-100", "-200", "-1100", "-1200"], - [rejected, rejected, cutoff, rejected, rejected, pretimeout], ], + ["some reject pre-cutoff, rest reject pre-timeout", [-100, -200, -1100, -1200], ["-100", "-200", "-1100", "-1200"]], [ "some resolve/reject pre-cutoff, some resolve/reject pre-timeout but no race beyond cutoff", [100, -200, -1100, 1100, 1700, -1700], ["100", "-200", "pending", "pending", "pending", "pending"], - [resolved, rejected, resolvedatcutoff], ], [ "none resolve/reject pre-cutoff with race resolution pre timeout", [-1100, 1200, 1700], ["-1100", "1200", "pending"], - [cutoff, rejected, resolved, pretimeout], - ], - [ - "none resolve pre-cutoff with race resolution pre timeout", - [1100, 1200, 1700], - ["1100", "pending", "pending"], - [cutoff, resolved, pretimeout], ], + ["none resolve pre-cutoff with race resolution pre timeout", [1100, 1200, 1700], ["1100", "pending", "pending"]], [ "some reject pre-cutoff, some reject pre-timeout, but no resolution till timeout", [-100, -1100, -1200, 1700, -1800], ["-100", "-1100", "-1200", "pending", "pending"], - [rejected, cutoff, rejected, rejected, timeout], ], - ["none resolve/reject pre timeout", [1600, -1700], ["pending", "pending"], [cutoff, timeout]], + ["none resolve/reject pre timeout", [1600, -1700], ["pending", "pending"]], ]; - for (const [name, promises, results, events] of testcases) { + for (const [name, timeouts, results] of testCases) { it(name, async () => { - const testPromises = promises.map((timeMs) => { + const testPromises = timeouts.map((timeMs) => { if (timeMs > 0) { return resolveAfter(`${timeMs}`, timeMs); } else { return rejectAfter(`${timeMs}`, -timeMs); } }); - const testEvents: RaceEvent[] = []; - const testResults = await racePromisesWithCutoff(testPromises, cutoffMs, timeoutMs, (event, _delayMs) => - testEvents.push(event) - ); - const testResultsCmp = testResults.map((res: string | Error) => (res instanceof Error ? res.message : res)); - expect({results: testResultsCmp, events: testEvents}).toEqual({results, events}); + const testResults = (await resolveOrRacePromises(testPromises as unknown as NonEmptyArray>, { + resolveTimeoutMs: cutoffMs, + raceTimeoutMs: timeoutMs, + })) as PromiseResult[]; + const testResultsCmp = testResults.map((r) => { + switch (r.status) { + case "fulfilled": + return r.value; + case "rejected": + return r.reason.message; + default: + return "pending"; + } + }); + expect(testResultsCmp).toEqual(results); }); } }); diff --git a/packages/validator/README.md b/packages/validator/README.md index 584f7ee3f559..b075c2f293f4 100644 --- a/packages/validator/README.md +++ b/packages/validator/README.md @@ -13,7 +13,7 @@ eth-consensus api compatible beacon nodes/databases/loggers. ## Getting started - Follow the [installation guide](https://chainsafe.github.io/lodestar/) to install Lodestar. -- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/usage/local). +- Quickly try out the whole stack by [starting a local testnet](https://chainsafe.github.io/lodestar/advanced-topics/setting-up-a-testnet/). ## License diff --git a/packages/validator/package.json b/packages/validator/package.json index bbc64ede4de0..c415d44c5a46 100644 --- a/packages/validator/package.json +++ b/packages/validator/package.json @@ -1,6 +1,6 @@ { "name": "@lodestar/validator", - "version": "1.15.1", + "version": "1.16.0", "description": "A Typescript implementation of the validator client", "author": "ChainSafe Systems", "license": "LGPL-3.0", @@ -27,13 +27,11 @@ "check-types": "tsc", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", - "pretest": "yarn run check-types", "test:unit": "vitest --run --dir test/unit/", - "test": "yarn test:unit", - "test:spec": "vitest --run --config vitest.config.spec.ts --dir test/spec/", - "test:e2e": "LODESTAR_PRESET=mainnet vitest --run --config vitest.config.e2e.ts --dir test/e2e", + "test": "yarn test:unit && yarn test:e2e", + "test:spec": "vitest --run --config vitest.spec.config.ts --dir test/spec/", + "test:e2e": "vitest --run --config vitest.e2e.config.ts --dir test/e2e", "download-spec-tests": "node --loader=ts-node/esm test/spec/downloadTests.ts", - "coverage": "codecov -F lodestar-validator", "check-readme": "typescript-docs-verifier" }, "repository": { @@ -47,20 +45,20 @@ "blockchain" ], "dependencies": { - "@chainsafe/bls": "7.1.1", + "@chainsafe/bls": "7.1.3", "@chainsafe/ssz": "^0.14.0", - "@lodestar/api": "^1.15.1", - "@lodestar/config": "^1.15.1", - "@lodestar/db": "^1.15.1", - "@lodestar/params": "^1.15.1", - "@lodestar/state-transition": "^1.15.1", - "@lodestar/types": "^1.15.1", - "@lodestar/utils": "^1.15.1", + "@lodestar/api": "^1.16.0", + "@lodestar/config": "^1.16.0", + "@lodestar/db": "^1.16.0", + "@lodestar/params": "^1.16.0", + "@lodestar/state-transition": "^1.16.0", + "@lodestar/types": "^1.16.0", + "@lodestar/utils": "^1.16.0", "bigint-buffer": "^1.1.5", "strict-event-emitter-types": "^2.0.0" }, "devDependencies": { - "@lodestar/test-utils": "^1.15.1", + "@lodestar/test-utils": "^1.16.0", "bigint-buffer": "^1.1.5", "rimraf": "^4.4.1" } diff --git a/packages/validator/src/metrics.ts b/packages/validator/src/metrics.ts index 4acf66955769..56d94318d12b 100644 --- a/packages/validator/src/metrics.ts +++ b/packages/validator/src/metrics.ts @@ -299,22 +299,22 @@ export function getMetrics(register: MetricsRegisterExtra, gitData: LodestarGitD buckets: [0.01, 0.1, 1, 2, 5], }), - requestErrors: register.gauge<{routeId: string}>({ + requestErrors: register.gauge<{routeId: string; baseUrl: string}>({ name: "vc_rest_api_client_request_errors_total", help: "Total count of errors on REST API client requests by routeId", - labelNames: ["routeId"], + labelNames: ["routeId", "baseUrl"], }), - requestToFallbacks: register.gauge<{routeId: string}>({ + requestToFallbacks: register.gauge<{routeId: string; baseUrl: string}>({ name: "vc_rest_api_client_request_to_fallbacks_total", help: "Total count of requests to fallback URLs on REST API by routeId", - labelNames: ["routeId"], + labelNames: ["routeId", "baseUrl"], }), - urlsScore: register.gauge<{urlIndex: number}>({ + urlsScore: register.gauge<{urlIndex: number; baseUrl: string}>({ name: "vc_rest_api_client_urls_score", help: "Current score of REST API URLs by url index", - labelNames: ["urlIndex"], + labelNames: ["urlIndex", "baseUrl"], }), }, diff --git a/packages/validator/src/services/block.ts b/packages/validator/src/services/block.ts index dd7cea13c293..faec8d9e04ee 100644 --- a/packages/validator/src/services/block.ts +++ b/packages/validator/src/services/block.ts @@ -11,18 +11,14 @@ import { } from "@lodestar/types"; import {ChainForkConfig} from "@lodestar/config"; import {ForkPreBlobs, ForkBlobs, ForkSeq, ForkExecution} from "@lodestar/params"; -import {ETH_TO_WEI, extendError, prettyBytes} from "@lodestar/utils"; +import {extendError, prettyBytes, prettyWeiToEth} from "@lodestar/utils"; import {Api, ApiError, routes} from "@lodestar/api"; import {IClock, LoggerVc} from "../util/index.js"; import {PubkeyHex} from "../types.js"; import {Metrics} from "../metrics.js"; -import {formatBigDecimal} from "../util/format.js"; import {ValidatorStore} from "./validatorStore.js"; import {BlockDutiesService, GENESIS_SLOT} from "./blockDuties.js"; -// display upto 5 decimal places -const MAX_DECIMAL_FACTOR = BigInt("100000"); - // The following combination of blocks and blobs can be produced // i) a full block pre deneb // ii) a full block and full blobs post deneb @@ -213,20 +209,15 @@ export class BlockProposingService { blindedLocal, builderBoostFactor, }); - ApiError.assert(res, "Failed to produce block: validator.produceBlockV2"); + ApiError.assert(res, "Failed to produce block: validator.produceBlockV3"); const {response} = res; const debugLogCtx = { executionPayloadSource: response.executionPayloadSource, executionPayloadBlinded: response.executionPayloadBlinded, - // winston logger doesn't like bigint - executionPayloadValue: `${formatBigDecimal(response.executionPayloadValue, ETH_TO_WEI, MAX_DECIMAL_FACTOR)} ETH`, - consensusBlockValue: `${formatBigDecimal(response.consensusBlockValue, ETH_TO_WEI, MAX_DECIMAL_FACTOR)} ETH`, - totalBlockValue: `${formatBigDecimal( - response.executionPayloadValue + response.consensusBlockValue, - ETH_TO_WEI, - MAX_DECIMAL_FACTOR - )} ETH`, + executionPayloadValue: prettyWeiToEth(response.executionPayloadValue), + consensusBlockValue: prettyWeiToEth(response.consensusBlockValue), + totalBlockValue: prettyWeiToEth(response.executionPayloadValue + response.consensusBlockValue), // TODO PR: should be used in api call instead of adding in log strictFeeRecipientCheck, builderSelection, diff --git a/packages/validator/src/services/emitter.ts b/packages/validator/src/services/emitter.ts index dddf1583eb0a..19f9ac1de54a 100644 --- a/packages/validator/src/services/emitter.ts +++ b/packages/validator/src/services/emitter.ts @@ -1,5 +1,5 @@ import {EventEmitter} from "events"; -import StrictEventEmitter from "strict-event-emitter-types"; +import {StrictEventEmitter} from "strict-event-emitter-types"; import {Slot} from "@lodestar/types"; import {HeadEventData} from "./chainHeaderTracker.js"; diff --git a/packages/validator/src/services/validatorStore.ts b/packages/validator/src/services/validatorStore.ts index 03811062c2ad..d1474e7fdd28 100644 --- a/packages/validator/src/services/validatorStore.ts +++ b/packages/validator/src/services/validatorStore.ts @@ -277,6 +277,7 @@ export class ValidatorStore { boostFactor = MAX_BUILDER_BOOST_FACTOR; break; + case routes.validator.BuilderSelection.ExecutionAlways: case routes.validator.BuilderSelection.ExecutionOnly: boostFactor = BigInt(0); } diff --git a/packages/validator/src/util/format.ts b/packages/validator/src/util/format.ts index 6f4b61cf6826..a405383232c9 100644 --- a/packages/validator/src/util/format.ts +++ b/packages/validator/src/util/format.ts @@ -1,12 +1,3 @@ export function isValidatePubkeyHex(pubkeyHex: string): boolean { return /^0x[0-9a-fA-F]{96}$/.test(pubkeyHex); } - -export function formatBigDecimal(numerator: bigint, denominator: bigint, maxDecimalFactor: bigint): string { - const full = numerator / denominator; - const fraction = ((numerator - full * denominator) * maxDecimalFactor) / denominator; - - // zeros to be added post decimal are number of zeros in maxDecimalFactor - number of digits in fraction - const zerosPostDecimal = String(maxDecimalFactor).length - 1 - String(fraction).length; - return `${full}.${"0".repeat(zerosPostDecimal)}${fraction}`; -} diff --git a/packages/validator/test/unit/utils/clock.test.ts b/packages/validator/test/unit/utils/clock.test.ts index 15f95ddbe2ee..dfa9d386e663 100644 --- a/packages/validator/test/unit/utils/clock.test.ts +++ b/packages/validator/test/unit/utils/clock.test.ts @@ -11,7 +11,7 @@ describe("util / Clock", function () { beforeEach(() => { controller = new AbortController(); - vi.useFakeTimers(); + vi.useFakeTimers({now: Date.now()}); }); afterEach(() => { diff --git a/packages/validator/test/unit/utils/format.test.ts b/packages/validator/test/unit/utils/format.test.ts deleted file mode 100644 index ab75c8fa1b72..000000000000 --- a/packages/validator/test/unit/utils/format.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {describe, it, expect} from "vitest"; -import {formatBigDecimal} from "../../../src/util/format.js"; - -describe("util / formatBigDecimal", function () { - const testCases: [bigint, bigint, bigint, string][] = [ - [BigInt("103797739275696858"), BigInt("1000000000000000000"), BigInt("100000"), "0.10379"], - [BigInt("103797739275696858"), BigInt("1000000000000000000"), BigInt("1000"), "0.103"], - [BigInt("10379773927569685"), BigInt("1000000000000000000"), BigInt("1000"), "0.010"], - [BigInt("1037977392756968"), BigInt("1000000000000000000"), BigInt("1000"), "0.001"], - [BigInt("1037977392756968"), BigInt("1000000000000000000"), BigInt("100000"), "0.00103"], - [BigInt("58200000000000000"), BigInt("1000000000000000000"), BigInt("100000"), "0.05820"], - [BigInt("111103797739275696858"), BigInt("1000000000000000000"), BigInt("100000"), "111.10379"], - [BigInt("111103797739275696858"), BigInt("1000000000000000000"), BigInt("1000"), "111.103"], - [BigInt("1037977392756"), BigInt("1000000000000000000"), BigInt("100000"), "0.00000"], - ]; - for (const [numerator, denominator, decimalFactor, expectedString] of testCases) { - it(`format ${numerator} / ${denominator} correctly to ${expectedString}`, () => { - expect(formatBigDecimal(numerator, denominator, decimalFactor)).toBe(expectedString); - }); - } -}); diff --git a/packages/validator/test/utils/clock.ts b/packages/validator/test/utils/clock.ts index b5a161e7efea..da9406ecfc35 100644 --- a/packages/validator/test/utils/clock.ts +++ b/packages/validator/test/utils/clock.ts @@ -11,7 +11,6 @@ export class ClockMock implements IClock { private readonly everySlot: RunEveryFn[] = []; private readonly everyEpoch: RunEveryFn[] = []; - // eslint-disable-next-line @typescript-eslint/no-empty-function start = (): void => {}; runEverySlot = (fn: RunEveryFn): number => this.everySlot.push(fn); runEveryEpoch = (fn: RunEveryFn): number => this.everyEpoch.push(fn); diff --git a/packages/validator/vitest.config.e2e.ts b/packages/validator/vitest.e2e.config.ts similarity index 100% rename from packages/validator/vitest.config.e2e.ts rename to packages/validator/vitest.e2e.config.ts diff --git a/packages/validator/vitest.config.spec.ts b/packages/validator/vitest.spec.config.ts similarity index 100% rename from packages/validator/vitest.config.spec.ts rename to packages/validator/vitest.spec.config.ts diff --git a/scripts/run_e2e_env.sh b/scripts/run_e2e_env.sh index 08f1680b30c6..e81eb501f407 100755 --- a/scripts/run_e2e_env.sh +++ b/scripts/run_e2e_env.sh @@ -1,18 +1,19 @@ #!/bin/bash +DIR="$(CDPATH= cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + function start_app() { mkdir -p test-logs/e2e-test-env export LODESTAR_PRESET=minimal - nohup node --loader ts-node/esm packages/cli/test/scripts/e2e_test_env.ts > test-logs/e2e-test-env/simulation.out 2>&1 & + export DOTENV_CONFIG_PATH="$DIR/../.env.test" + nohup node -r dotenv/config --loader ts-node/esm packages/cli/test/scripts/e2e_test_env.ts > test-logs/e2e-test-env/simulation.out 2>&1 & echo $! > test-logs/e2e-test-env/simulation.pid echo "Wait for the node to be ready" - npx wait-port -t 60000 0.0.0.0:5001 + npx wait-port -t 120000 0.0.0.0:5001 } function stop_app() { - kill -9 $(cat test-logs/e2e-test-env/simulation.pid) - # Incase the process pid file is not present - kill -9 $(lsof -t -i:5001) + kill -s TERM $(cat test-logs/e2e-test-env/simulation.pid) } diff --git a/scripts/vitest/customMatchers.ts b/scripts/vitest/setupFiles/customMatchers.ts similarity index 100% rename from scripts/vitest/customMatchers.ts rename to scripts/vitest/setupFiles/customMatchers.ts diff --git a/scripts/vitest/setupFiles/dotenv.ts b/scripts/vitest/setupFiles/dotenv.ts new file mode 100644 index 000000000000..71364c7426bc --- /dev/null +++ b/scripts/vitest/setupFiles/dotenv.ts @@ -0,0 +1,8 @@ +import path from "node:path"; +// It's a dev dependency +// eslint-disable-next-line import/no-extraneous-dependencies +import {config} from "dotenv"; +// eslint-disable-next-line @typescript-eslint/naming-convention +const __dirname = new URL(".", import.meta.url).pathname; + +config({path: path.join(__dirname, "../../../.env.test")}); diff --git a/vitest.base.browser.config.ts b/vitest.base.browser.config.ts index 5559e0f77b9c..a07f4d842b06 100644 --- a/vitest.base.browser.config.ts +++ b/vitest.base.browser.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ "**/.{idea,git,cache,output,temp}/**", "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*", ], - setupFiles: [path.join(__dirname, "./scripts/vitest/customMatchers.ts")], + setupFiles: [path.join(__dirname, "./scripts/vitest/setupFiles/customMatchers.ts")], reporters: ["default", "hanging-process"], coverage: { enabled: false, diff --git a/vitest.base.unit.config.ts b/vitest.base.unit.config.ts index b087466fec12..293b29214239 100644 --- a/vitest.base.unit.config.ts +++ b/vitest.base.unit.config.ts @@ -14,7 +14,10 @@ export default defineConfig({ "**/.{idea,git,cache,output,temp}/**", "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*", ], - setupFiles: [path.join(__dirname, "./scripts/vitest/customMatchers.ts")], + setupFiles: [ + path.join(__dirname, "./scripts/vitest/setupFiles/customMatchers.ts"), + path.join(__dirname, "./scripts/vitest/setupFiles/dotenv.ts"), + ], reporters: ["default", "hanging-process"], coverage: { enabled: process.env.CI === "true", diff --git a/yarn.lock b/yarn.lock index ce67d0db390d..669492e94737 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,6 +22,14 @@ semver "^6.1.0" uuid "^3.3.3" +"@actions/core@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a" + integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g== + dependencies: + "@actions/http-client" "^2.0.1" + uuid "^8.3.2" + "@actions/core@^1.2.6": version "1.10.0" resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.0.tgz#44551c3c71163949a2f06e94d9ca2157a0cfac4f" @@ -256,26 +264,11 @@ resolved "https://registry.yarnpkg.com/@chainsafe/as-chacha20poly1305/-/as-chacha20poly1305-0.1.0.tgz#7da6f8796f9b42dac6e830a086d964f1f9189e09" integrity sha512-BpNcL8/lji/GM3+vZ/bgRWqJ1q5kwvTFmGPk7pxm/QQZDbaMI98waOHjEymTjq2JmdD/INdNBFOVSyJofXg7ew== -"@chainsafe/as-sha256@^0.3.1": - version "0.3.1" - resolved "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz" - integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg== - "@chainsafe/as-sha256@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.4.1.tgz#cfc0737e25f8c206767bdb6703e7943e5d44513e" integrity sha512-IqeeGwQihK6Y2EYLFofqs2eY2ep1I2MvQXHzOAI+5iQN51OZlUkrLgyAugu2x86xZewDk5xas7lNczkzFzF62w== -"@chainsafe/bls-hd-key@^0.2.0": - version "0.2.1" - resolved "https://registry.npmjs.org/@chainsafe/bls-hd-key/-/bls-hd-key-0.2.1.tgz" - integrity sha512-FGmRLcOd9KxfH9q7x+FT20lJy9ooQ/Xd5fFLFGpPaf9GW4AnE0oGPGakPuV5//g8db7OzZ3ZfVMKtB4M7qq/wA== - dependencies: - assert "^2.0.0" - bcrypto "^5.4.0" - bn.js "^5.1.1" - buffer "^5.4.3" - "@chainsafe/bls-hd-key@^0.3.0": version "0.3.0" resolved "https://registry.npmjs.org/@chainsafe/bls-hd-key/-/bls-hd-key-0.3.0.tgz" @@ -283,16 +276,6 @@ dependencies: "@noble/hashes" "^1.0.0" -"@chainsafe/bls-keygen@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@chainsafe/bls-keygen/-/bls-keygen-0.3.0.tgz#d7472a945f6f49b5cb357241bfba2f5c12a635c5" - integrity sha512-5Iq6E5E987hyio74G1fXPYI3t9iVeHxRX1tDMpnCV9T82rPz061yFsMz3W3aXE26+k6+fcz0bsYX3ijOizkx+A== - dependencies: - "@chainsafe/bls-hd-key" "^0.2.0" - bip39 "^3.0.2" - buffer "^5.4.3" - randombytes "^2.1.0" - "@chainsafe/bls-keygen@^0.4.0": version "0.4.0" resolved "https://registry.npmjs.org/@chainsafe/bls-keygen/-/bls-keygen-0.4.0.tgz" @@ -302,22 +285,21 @@ "@noble/hashes" "^1.0.0" "@scure/bip39" "^1.0.0" -"@chainsafe/bls-keystore@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@chainsafe/bls-keystore/-/bls-keystore-3.0.0.tgz#e28c979f7664417e4917fa0d4d32fa2b9416e9c6" - integrity sha512-vlRIIXnn555wq2emhqnSR7btno17M0sCcfdQ+Dhgr7IH6n0CMoTGw9qcrpnNYwM+9OPm3matSYeZc9mNlXf7fQ== +"@chainsafe/bls-keystore@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@chainsafe/bls-keystore/-/bls-keystore-3.0.1.tgz#1eaf1d89d3628b5fbab62fcd10f48703f2c428cc" + integrity sha512-U6m/tMgdEUq60RhcIj7xUD5n6zuavSbThR+szuAglmUa9gY8QfInxXpGAT4hyc2QjbLy9115vJVyhyu7TDl+DQ== dependencies: - ethereum-cryptography "^1.0.0" - uuid "8.3.2" + ethereum-cryptography "^2.0.0" + uuid "^9.0.0" -"@chainsafe/bls@7.1.1": - version "7.1.1" - resolved "https://registry.npmjs.org/@chainsafe/bls/-/bls-7.1.1.tgz" - integrity sha512-56hjFJujW0Z0Ntkr8y22xIAfT4SChYWQtCGM8dfUMkWWX8xLOXkhBFFSNTvPXA5cBlsFlo102CtBo+6A60II2w== +"@chainsafe/bls@7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@chainsafe/bls/-/bls-7.1.3.tgz#8d488357b187a511cfb94c96eddc7aa9f62644a9" + integrity sha512-d21eYdWxDSb63n7nB+viD+3U4yJW8huiKRibJyh8X7btPLoXkvtmDf7geYyHVbKfLDgbuHkc+b48pfPQkUTLxA== dependencies: "@chainsafe/bls-keygen" "^0.4.0" bls-eth-wasm "^0.4.8" - randombytes "^2.1.0" "@chainsafe/blst@^0.2.9": version "0.2.9" @@ -328,37 +310,36 @@ node-fetch "^2.6.1" node-gyp "^8.4.0" -"@chainsafe/discv5@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@chainsafe/discv5/-/discv5-7.1.0.tgz#c892075c84bdc75428774d4993e7346205ff8724" - integrity sha512-spO801KWe0C9XrXkVt4E1paXP1ZtfsgxOf+FSW6yCDQTWFVGRfcl/ww4B7JtdJOnZauqaHqXxAuNBpSafJoalw== +"@chainsafe/discv5@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/discv5/-/discv5-9.0.0.tgz#05d4d9d671894b41f0fafa8f32c48ae3ed761bd1" + integrity sha512-7s23ziqsHG/KRgkX79qB/w8kuqPrY8aJaF2aRDy9cScJocJ6ZaOnXhEc8Ku1AcSyrvfGp+tY8R4rDABcxRY+Wg== dependencies: - "@chainsafe/enr" "^2.0.2" - "@libp2p/crypto" "^3.0.4" + "@chainsafe/enr" "^3.0.0" + "@libp2p/crypto" "^4.0.1" "@libp2p/interface" "^1.1.1" "@multiformats/multiaddr" "^12.1.10" bcrypto "^5.4.0" bigint-buffer "^1.1.5" debug "^4.3.1" - err-code "^3.0.1" lru-cache "^10.1.0" rlp "^2.2.6" strict-event-emitter-types "^2.0.0" -"@chainsafe/enr@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@chainsafe/enr/-/enr-2.0.2.tgz#29814ae506a87d466640cddbac49369029334f09" - integrity sha512-90IEkHHb5ZHk2BuyX5QkLomaxH+HXF41wFOzDC2Hpla6c3ersxAJJms4kJSot7j20Uzfka4Xjxvkwjj8WfL77g== +"@chainsafe/enr@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/enr/-/enr-3.0.0.tgz#71c83d4381d703bbcd19245ce733eb7c779a30ed" + integrity sha512-D8M8sqnvOim0jWlTdr2IhLyVe0GSUgpk+QO6UaLY4pQVdW1myJP8REp7xdbv1193ULVEkJQFTJAZexTOtmu3jw== dependencies: - "@libp2p/crypto" "^3.0.4" + "@libp2p/crypto" "^4.0.1" "@libp2p/interface" "^1.1.1" "@libp2p/peer-id" "^4.0.4" "@multiformats/multiaddr" "^12.1.10" - base64url "^3.0.1" - bcrypto "^5.4.0" bigint-buffer "^1.1.5" + ethereum-cryptography "^2.1.3" rlp "^2.2.6" uint8-varint "^2.0.2" + uint8arrays "^5.0.1" "@chainsafe/eslint-plugin-node@^11.2.3": version "11.2.3" @@ -390,26 +371,42 @@ resolved "https://registry.yarnpkg.com/@chainsafe/is-ip/-/is-ip-2.0.2.tgz#7311e7403f11d8c5cfa48111f56fcecaac37c9f6" integrity sha512-ndGqEMG1W5WkGagaqOZHpPU172AGdxr+LD15sv3WIUvT5oCFUrG1Y0CW/v2Egwj4JXEvSibaIIIqImsm98y1nA== -"@chainsafe/libp2p-gossipsub@^11.1.0": - version "11.1.0" - resolved "https://registry.yarnpkg.com/@chainsafe/libp2p-gossipsub/-/libp2p-gossipsub-11.1.0.tgz#e5ebd8dd08601845073f3b8c9601e047d7bdc279" - integrity sha512-6baEtpC9gu5D9bXv86zIdNIOekKgQPV3KRWvnsldtsKpiWtUv6K2mLsl3wsDi0nW/c3zxTAneaJMTLQoSXysJQ== - dependencies: - "@libp2p/crypto" "^3.0.1" - "@libp2p/interface" "^1.0.1" - "@libp2p/interface-internal" "^1.0.1" - "@libp2p/peer-id" "^4.0.1" - "@libp2p/pubsub" "^9.0.0" - "@multiformats/multiaddr" "^12.1.3" - abortable-iterator "^5.0.1" +"@chainsafe/libp2p-gossipsub@^11.2.1": + version "11.2.1" + resolved "https://registry.yarnpkg.com/@chainsafe/libp2p-gossipsub/-/libp2p-gossipsub-11.2.1.tgz#80a993cca657084c861b78513ee0ff516bfb96f9" + integrity sha512-2NvlOY4Jfwn7U/sKF0kILl3+luHxq9hhEiBqZRqLTIV8LYmMQl9VpTMgMvRwKzgn/NDeZzsPb8olk2o00tkmZw== + dependencies: + "@libp2p/crypto" "^4.0.1" + "@libp2p/interface" "^1.1.2" + "@libp2p/interface-internal" "^1.0.7" + "@libp2p/peer-id" "^4.0.5" + "@libp2p/pubsub" "^9.0.8" + "@multiformats/multiaddr" "^12.1.14" denque "^2.1.0" - it-length-prefixed "^9.0.1" + it-length-prefixed "^9.0.4" it-pipe "^3.0.1" - it-pushable "^3.2.0" - multiformats "^12.0.1" - protobufjs "^7.2.4" - uint8arraylist "^2.4.3" - uint8arrays "^4.0.4" + it-pushable "^3.2.3" + multiformats "^13.0.1" + protobufjs "^7.2.6" + uint8arraylist "^2.4.8" + uint8arrays "^5.0.1" + +"@chainsafe/libp2p-identify@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@chainsafe/libp2p-identify/-/libp2p-identify-1.0.0.tgz#28191e619715a87c140d8b516ee85cb7d39e41e0" + integrity sha512-X+VWUC0xeCFIulE4BU5M8FmTxZ/OKzku+9/1UaX2EG1LcqQkCDrPi6CCODbE0SraqImG4aVHRbiCFWxKEfE8wQ== + dependencies: + "@libp2p/interface" "^1.1.2" + "@libp2p/interface-internal" "^1.0.7" + "@libp2p/peer-id" "^4.0.5" + "@libp2p/peer-record" "^7.0.7" + "@multiformats/multiaddr" "^12.1.10" + "@multiformats/multiaddr-matcher" "^1.1.0" + it-protobuf-stream "^1.1.1" + protons-runtime "^5.0.0" + uint8arraylist "^2.4.7" + uint8arrays "^5.0.0" + wherearewe "^2.0.1" "@chainsafe/libp2p-noise@^14.1.0": version "14.1.0" @@ -659,10 +656,10 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== -"@eslint/eslintrc@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" - integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -674,10 +671,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.50.0": - version "8.50.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.50.0.tgz#9e93b850f0f3fa35f5fa59adfd03adae8488e484" - integrity sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ== +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== "@ethereumjs/block@^4.2.2": version "4.2.2" @@ -1255,13 +1252,13 @@ resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@humanwhocodes/config-array@^0.11.11": - version "0.11.11" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" - integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -1269,10 +1266,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" @@ -1456,7 +1453,7 @@ "@multiformats/mafmt" "^12.1.6" "@multiformats/multiaddr" "^12.1.10" -"@libp2p/crypto@^3.0.0", "@libp2p/crypto@^3.0.1", "@libp2p/crypto@^3.0.4": +"@libp2p/crypto@^3.0.0", "@libp2p/crypto@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@libp2p/crypto/-/crypto-3.0.4.tgz#8768b262c24a036774c6c5e290a1f0d76535a7d3" integrity sha512-FzSwBo+RJOUzdzEwug5ZL4dAGKwEBWTLzj+EmUTHHY6c87+oLh571DQk/w0oYObSD9hYbcKePgSBaZeBx0JaZg== @@ -1470,6 +1467,20 @@ uint8arraylist "^2.4.3" uint8arrays "^5.0.0" +"@libp2p/crypto@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@libp2p/crypto/-/crypto-4.0.1.tgz#350f3397c8fba18d30a74173c3791fe199821062" + integrity sha512-lKGbX8TvQt4JbqlttdexEz2VtYJnTwY31kVBDQviwt0pMF+6Uy2hzNnEQ1FHZBwnow8BIlyb6UevHfgyOFlnkw== + dependencies: + "@libp2p/interface" "^1.1.2" + "@noble/curves" "^1.1.0" + "@noble/hashes" "^1.3.3" + asn1js "^3.0.5" + multiformats "^13.0.0" + protons-runtime "^5.0.0" + uint8arraylist "^2.4.7" + uint8arrays "^5.0.0" + "@libp2p/identify@^1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@libp2p/identify/-/identify-1.0.9.tgz#6756d74919b7a171c7cdcdce45669b9f633fbb0f" @@ -1487,7 +1498,7 @@ uint8arrays "^5.0.0" wherearewe "^2.0.1" -"@libp2p/interface-internal@^1.0.1", "@libp2p/interface-internal@^1.0.5": +"@libp2p/interface-internal@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@libp2p/interface-internal/-/interface-internal-1.0.5.tgz#b7687e4c5cb765fd686fcd442d2cc4c49e8304d7" integrity sha512-qT4APD2nZKEGnkn4LfM2mzNbYv9bx/2FyvYaJ4exjzIIBPiRmjrek7hfWErKkazCDwO51+WuZ/DERdd32O9Fxg== @@ -1497,7 +1508,17 @@ "@multiformats/multiaddr" "^12.1.10" uint8arraylist "^2.4.3" -"@libp2p/interface@^1.0.0", "@libp2p/interface@^1.0.1", "@libp2p/interface@^1.1.1": +"@libp2p/interface-internal@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@libp2p/interface-internal/-/interface-internal-1.0.7.tgz#e15ad52e148e31558972dd2f4800ad8be61ced88" + integrity sha512-r1nGpnGdkq0U7ow5i093OPWPBJXQP3BGwijino8cCZokYwF2P/CU+yeYvL8ncL8fPYLKuuUjLNGO4Z8Th5sqSQ== + dependencies: + "@libp2p/interface" "^1.1.2" + "@libp2p/peer-collections" "^5.1.5" + "@multiformats/multiaddr" "^12.1.10" + uint8arraylist "^2.4.7" + +"@libp2p/interface@^1.0.0", "@libp2p/interface@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@libp2p/interface/-/interface-1.1.1.tgz#f37ea4930bd74e1299fbcafa49fdab39a28abba9" integrity sha512-g6xgF+q38ZDTRkjuJfuOByS4N0zGld+VPRiWPXYX8wA/9vS6lqJwKUoC6V33KUhP/zXHCkJaSD6z94fUbNM8vw== @@ -1509,6 +1530,18 @@ progress-events "^1.0.0" uint8arraylist "^2.4.3" +"@libp2p/interface@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@libp2p/interface/-/interface-1.1.2.tgz#debfd9d1bd4b81929c9e30eb35c2801ca246ce2b" + integrity sha512-uC4hxtEJuWiDiZfokkSNEEbCzdyZrqb5kp67Wc5PjZsySZ2IoImdIfie003yQXlB1xBp/XUJzdC6kVu4M7LUmg== + dependencies: + "@multiformats/multiaddr" "^12.1.10" + it-pushable "^3.2.3" + it-stream-types "^2.0.1" + multiformats "^13.0.0" + progress-events "^1.0.0" + uint8arraylist "^2.4.7" + "@libp2p/logger@^4.0.1", "@libp2p/logger@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@libp2p/logger/-/logger-4.0.4.tgz#98c5357e8b857d93a506f6818db6abe734d342ee" @@ -1520,6 +1553,17 @@ interface-datastore "^8.2.0" multiformats "^13.0.0" +"@libp2p/logger@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@libp2p/logger/-/logger-4.0.5.tgz#6790776b4b2d587b75ccbdf85885c5d11533d19f" + integrity sha512-cXETMNZINnxeQBlfQ2S4di92FDDU89R7RHagrpebGrM7oLl5nf/Mw6myc23kGaM3/2YG3ko2rl9sYjemu0azTA== + dependencies: + "@libp2p/interface" "^1.1.2" + "@multiformats/multiaddr" "^12.1.10" + debug "^4.3.4" + interface-datastore "^8.2.0" + multiformats "^13.0.0" + "@libp2p/mdns@^10.0.10": version "10.0.10" resolved "https://registry.yarnpkg.com/@libp2p/mdns/-/mdns-10.0.10.tgz#02f08f1a485e3640ce575460af63a0b8c4171b64" @@ -1564,13 +1608,13 @@ uint8arraylist "^2.4.3" uint8arrays "^5.0.0" -"@libp2p/peer-collections@^5.0.0", "@libp2p/peer-collections@^5.1.3": - version "5.1.3" - resolved "https://registry.yarnpkg.com/@libp2p/peer-collections/-/peer-collections-5.1.3.tgz#52734a31fe52f01f2ff67132ed0eae28b3229ae4" - integrity sha512-qiQHO8s4neLaAZmjHHWvPc091Lp9nOEokjnTmrE2/YBNjKoiA1aPCf7gM/KasynuquFceTQBDVd/Y79Mfqxw4w== +"@libp2p/peer-collections@^5.1.3", "@libp2p/peer-collections@^5.1.5": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@libp2p/peer-collections/-/peer-collections-5.1.5.tgz#8b241f129dd94f5b56a17fc0614b8e9b80687b44" + integrity sha512-/9VisdPC7+15n/0XntjGCzJ2Ky/zZnqdnuLNEwdu2LuTCbWTaqItG36ecgcVdO9L/V4mELwgY5XCjZKBDrYgjA== dependencies: - "@libp2p/interface" "^1.1.1" - "@libp2p/peer-id" "^4.0.4" + "@libp2p/interface" "^1.1.2" + "@libp2p/peer-id" "^4.0.5" "@libp2p/peer-id-factory@^4.0.3": version "4.0.3" @@ -1584,12 +1628,12 @@ uint8arraylist "^2.4.3" uint8arrays "^5.0.0" -"@libp2p/peer-id@^4.0.0", "@libp2p/peer-id@^4.0.1", "@libp2p/peer-id@^4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@libp2p/peer-id/-/peer-id-4.0.4.tgz#3de8f012f2abdc1ab287ad652d24de587e653ca3" - integrity sha512-UHWpo0f34IOaAhlvMNtCMAFVVhv29Dy3IqNvfugFNwzv5p+Jo6TfPGd78H7RX2WIzyVzIgBYxVxmIIHHcqZQ5Q== +"@libp2p/peer-id@^4.0.0", "@libp2p/peer-id@^4.0.4", "@libp2p/peer-id@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@libp2p/peer-id/-/peer-id-4.0.5.tgz#ed8be246b4d7ba2b7806968b4bfa8d59b82a4b2a" + integrity sha512-/J9U6I/CWSOsYrTpFZpRQrhOhi+bp9WFp7+9Gc7kVt/oevIYTapUEjpxevjViem9ddR5RbdYeCj4ZLHA04QOoQ== dependencies: - "@libp2p/interface" "^1.1.1" + "@libp2p/interface" "^1.1.2" multiformats "^13.0.0" uint8arrays "^5.0.0" @@ -1608,6 +1652,21 @@ uint8arraylist "^2.4.3" uint8arrays "^5.0.0" +"@libp2p/peer-record@^7.0.7": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@libp2p/peer-record/-/peer-record-7.0.7.tgz#e55145b2509592696f42ff73c38f813efbbbc688" + integrity sha512-RsggFJVAWQBA2z+ZJsK5nKHDKLmSd89IhFiE5GyImedQFiMkJz/gDFROzfNF2NdOyEBNdRy5SmC9scNFRQQD9A== + dependencies: + "@libp2p/crypto" "^4.0.1" + "@libp2p/interface" "^1.1.2" + "@libp2p/peer-id" "^4.0.5" + "@libp2p/utils" "^5.2.3" + "@multiformats/multiaddr" "^12.1.10" + protons-runtime "^5.0.0" + uint8-varint "^2.0.2" + uint8arraylist "^2.4.7" + uint8arrays "^5.0.0" + "@libp2p/peer-store@^10.0.5": version "10.0.5" resolved "https://registry.yarnpkg.com/@libp2p/peer-store/-/peer-store-10.0.5.tgz#b969d1707f5dcbf4d110e099270de285b075aa02" @@ -1637,24 +1696,24 @@ prom-client "^15.0.0" uint8arraylist "^2.4.3" -"@libp2p/pubsub@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@libp2p/pubsub/-/pubsub-9.0.0.tgz#e9d3869addd653868f87797849dbebfd23cd3a38" - integrity sha512-yvgKBNKtF09x4ahbxJrxj/OBTNvOoJibR28YaTlrlCIDU78wMMhIx89Ma14g2FAN1OsxagifyAgq188vGrsGfA== - dependencies: - "@libp2p/crypto" "^3.0.1" - "@libp2p/interface" "^1.0.1" - "@libp2p/interface-internal" "^1.0.1" - "@libp2p/peer-collections" "^5.0.0" - "@libp2p/peer-id" "^4.0.1" - "@libp2p/utils" "^5.0.2" +"@libp2p/pubsub@^9.0.8": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@libp2p/pubsub/-/pubsub-9.0.8.tgz#4c222c94edb5a5b623ed10fa7555bb70a19ddc0d" + integrity sha512-p2UEfjQPMQgEJTXPdinWCMA6A1sLR7Hvfu8mtoOS1azgtTtqmMCNPtx+3acnNSnWItQFswl9w2HWjspfUcCF1w== + dependencies: + "@libp2p/crypto" "^4.0.1" + "@libp2p/interface" "^1.1.2" + "@libp2p/interface-internal" "^1.0.7" + "@libp2p/peer-collections" "^5.1.5" + "@libp2p/peer-id" "^4.0.5" + "@libp2p/utils" "^5.2.3" it-length-prefixed "^9.0.3" it-pipe "^3.0.1" - it-pushable "^3.2.1" - multiformats "^12.1.3" - p-queue "^7.4.1" - uint8arraylist "^2.4.3" - uint8arrays "^4.0.6" + it-pushable "^3.2.3" + multiformats "^13.0.0" + p-queue "^8.0.0" + uint8arraylist "^2.4.7" + uint8arrays "^5.0.0" "@libp2p/tcp@9.0.10": version "9.0.10" @@ -1668,25 +1727,26 @@ "@types/sinon" "^17.0.0" stream-to-it "^0.2.2" -"@libp2p/utils@^5.0.2", "@libp2p/utils@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@libp2p/utils/-/utils-5.2.0.tgz#896bcb9ceb5b90caea0b2a8a5bb353dc2116cc13" - integrity sha512-zAremC/0u7mhS32TS++WBlsjwmKKKonEB7dZMfTtKH0QfghII8vcBOgBCTEqhtm0AqwsT6vIlm6wAg9bbZKbQA== +"@libp2p/utils@^5.2.0", "@libp2p/utils@^5.2.3": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@libp2p/utils/-/utils-5.2.3.tgz#32c1dd68d661d7d93ed3428c7817da2e4b85817f" + integrity sha512-N+9pQHQ/XrxXP/RCiWUSUXnkFCWcyzMxlGXY+aQUfcfLi5M2eFtPSz2Tc5dWmYGVJeI9CNafok+72YUsPZHfOQ== dependencies: "@chainsafe/is-ip" "^2.0.2" - "@libp2p/interface" "^1.1.1" - "@libp2p/logger" "^4.0.4" + "@libp2p/interface" "^1.1.2" + "@libp2p/logger" "^4.0.5" "@multiformats/multiaddr" "^12.1.10" "@multiformats/multiaddr-matcher" "^1.1.0" + delay "^6.0.0" get-iterator "^2.0.1" is-loopback-addr "^2.0.1" - it-pushable "^3.2.2" + it-pushable "^3.2.3" it-stream-types "^2.0.1" + netmask "^2.0.2" p-defer "^4.0.0" - private-ip "^3.0.1" race-event "^1.1.0" - race-signal "^1.0.1" - uint8arraylist "^2.4.3" + race-signal "^1.0.2" + uint8arraylist "^2.4.7" "@lukeed/ms@^2.0.1": version "2.0.1" @@ -1709,15 +1769,15 @@ "@multiformats/multiaddr" "^12.0.0" multiformats "^12.0.1" -"@multiformats/multiaddr@^12.0.0", "@multiformats/multiaddr@^12.1.10", "@multiformats/multiaddr@^12.1.3": - version "12.1.12" - resolved "https://registry.yarnpkg.com/@multiformats/multiaddr/-/multiaddr-12.1.12.tgz#d1609933dc5589d53f6b77fb88fe5e5ea787deae" - integrity sha512-hrY4uN/oeYhn410jBSpVXn37eenn4djKOj6Dh20Yh4xzGgqmS6u+/X08zQfHgWNjk7NJejPUcRfHEfs8e/MOcw== +"@multiformats/multiaddr@^12.0.0", "@multiformats/multiaddr@^12.1.10", "@multiformats/multiaddr@^12.1.14", "@multiformats/multiaddr@^12.1.3": + version "12.1.14" + resolved "https://registry.yarnpkg.com/@multiformats/multiaddr/-/multiaddr-12.1.14.tgz#d021072667f4dfc566cdddcb45feee60fecc8cfd" + integrity sha512-1C0Mo73chzu7pTzTquuKs5vUtw70jhqg1i6pUNznGb0WV6RFa6vyB+D697Os5+cLx+DiItrAY6VzMtlGQsMzYg== dependencies: "@chainsafe/is-ip" "^2.0.1" "@chainsafe/netmask" "^2.0.0" "@libp2p/interface" "^1.0.0" - dns-over-http-resolver "3.0.0" + dns-over-http-resolver "^3.0.2" multiformats "^13.0.0" uint8-varint "^2.0.1" uint8arrays "^5.0.0" @@ -1792,46 +1852,29 @@ resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.4.0.tgz#e3f69e3ce935683dd8dadb636652a5cb5cd5958c" integrity sha512-xaUaUUDWbHIFSxaQ/pIe+33VG2mfJp6N/KxKLmZr5biWdNznCAmfu24QRhX10BbVAuqOahAoyp0S4M9md6GPDw== -"@noble/curves@1.0.0", "@noble/curves@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" - integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== - dependencies: - "@noble/hashes" "1.3.0" - -"@noble/curves@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" - integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== +"@noble/curves@1.3.0", "@noble/curves@^1.1.0", "@noble/curves@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" + integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== dependencies: - "@noble/hashes" "1.3.1" + "@noble/hashes" "1.3.3" "@noble/hashes@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== -"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" - integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== - -"@noble/hashes@1.3.0", "@noble/hashes@^1.2.0", "@noble/hashes@^1.3.0", "@noble/hashes@~1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== - -"@noble/hashes@1.3.1", "@noble/hashes@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" - integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== +"@noble/hashes@1.3.3", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0", "@noble/hashes@^1.3.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.3.3", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== -"@noble/hashes@^1.0.0", "@noble/hashes@~1.0.0": +"@noble/hashes@~1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== -"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": +"@noble/secp256k1@1.7.1": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== @@ -2370,17 +2413,10 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@pkgr/utils@^2.3.1": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" - integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== - dependencies: - cross-spawn "^7.0.3" - fast-glob "^3.3.0" - is-glob "^4.0.3" - open "^9.1.0" - picocolors "^1.0.0" - tslib "^2.6.0" +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== "@polka/url@^1.0.0-next.24": version "1.0.0-next.24" @@ -2551,47 +2587,30 @@ "@scure/base@~1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@scure/base/-/base-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.0.0.tgz#109fb595021de285f05a7db6806f2f48296fcee7" integrity sha512-gIVaYhUsy+9s58m/ETjSJVKHhKTBMmcRb9cEV5/5dwvfDlfORjKrFsDeDHWRrm6RjcPvCLZFwGJjAjLj1gg4HA== -"@scure/base@~1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== - -"@scure/bip32@1.1.5": +"@scure/base@~1.1.4": version "1.1.5" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" - integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== - dependencies: - "@noble/hashes" "~1.2.0" - "@noble/secp256k1" "~1.7.0" - "@scure/base" "~1.1.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157" + integrity sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ== -"@scure/bip32@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" - integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== - dependencies: - "@noble/curves" "~1.0.0" - "@noble/hashes" "~1.3.0" - "@scure/base" "~1.1.0" - -"@scure/bip39@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" - integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== +"@scure/bip32@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" + integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== dependencies: - "@noble/hashes" "~1.2.0" - "@scure/base" "~1.1.0" + "@noble/curves" "~1.3.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" -"@scure/bip39@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" - integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== +"@scure/bip39@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" + integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== dependencies: - "@noble/hashes" "~1.3.0" - "@scure/base" "~1.1.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" "@scure/bip39@^1.0.0": version "1.0.0" @@ -2791,13 +2810,6 @@ resolved "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-5.0.1.tgz" integrity sha512-wYxU3kp5zItbxKmeRYCEplS2MW7DzyBnxPGj+GJVHZEUZiK/nn5Ei1sUFgURDh+X051+zsGe28iud3oHjrYWQQ== -"@types/async-retry@1.4.3": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@types/async-retry/-/async-retry-1.4.3.tgz#8b78f6ce88d97e568961732cdd9e5325cdc8c246" - integrity sha512-B3C9QmmNULVPL2uSJQ088eGWTNPIeUk35hca6CV8rRDJ8GXuQJP5CCVWA1ZUCrb9xYP7Js/RkLqnNNwKhe+Zsw== - dependencies: - "@types/retry" "*" - "@types/buffer-xor@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@types/buffer-xor/-/buffer-xor-2.0.0.tgz" @@ -2960,11 +2972,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.192.tgz#5790406361a2852d332d41635d927f1600811285" integrity sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A== -"@types/long@^4.0.1": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== - "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" @@ -3011,11 +3018,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.5.tgz#4c6a79adf59a8e8193ac87a0e522605b16587258" integrity sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w== -"@types/node@11.11.6": - version "11.11.6" - resolved "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz" - integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== - "@types/node@18.15.13": version "18.15.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" @@ -3207,16 +3209,16 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz#f18cc75c9cceac8080a9dc2e7d166008c5207b9f" - integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q== +"@typescript-eslint/eslint-plugin@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" + integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.7.2" - "@typescript-eslint/type-utils" "6.7.2" - "@typescript-eslint/utils" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/type-utils" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -3224,60 +3226,47 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.2.tgz#e0ae93771441b9518e67d0660c79e3a105497af4" - integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw== +"@typescript-eslint/parser@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== dependencies: - "@typescript-eslint/scope-manager" "6.7.2" - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/typescript-estree" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz#b6d2abb825b29ab70cb542d220e40c61c1678116" - integrity sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ== - dependencies: - "@typescript-eslint/types" "6.19.0" - "@typescript-eslint/visitor-keys" "6.19.0" - -"@typescript-eslint/scope-manager@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz#cf59a2095d2f894770c94be489648ad1c78dc689" - integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw== +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== dependencies: - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/type-utils@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz#ed921c9db87d72fa2939fee242d700561454f367" - integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ== +"@typescript-eslint/type-utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" + integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== dependencies: - "@typescript-eslint/typescript-estree" "6.7.2" - "@typescript-eslint/utils" "6.7.2" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/utils" "6.21.0" debug "^4.3.4" ts-api-utils "^1.0.1" -"@typescript-eslint/types@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.19.0.tgz#689b0498c436272a6a2059b09f44bcbd90de294a" - integrity sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A== +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/types@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066" - integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg== - -"@typescript-eslint/typescript-estree@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz#0813ba364a409afb4d62348aec0202600cb468fa" - integrity sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ== +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== dependencies: - "@typescript-eslint/types" "6.19.0" - "@typescript-eslint/visitor-keys" "6.19.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -3285,60 +3274,31 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/typescript-estree@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz#ce5883c23b581a5caf878af641e49dd0349238c7" - integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ== - dependencies: - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/visitor-keys" "6.7.2" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" - -"@typescript-eslint/utils@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf" - integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ== +"@typescript-eslint/utils@6.21.0", "@typescript-eslint/utils@^6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" + integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.7.2" - "@typescript-eslint/types" "6.7.2" - "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" semver "^7.5.4" -"@typescript-eslint/utils@^6.15.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.19.0.tgz#557b72c3eeb4f73bef8037c85dae57b21beb1a4b" - integrity sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw== +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.19.0" - "@typescript-eslint/types" "6.19.0" - "@typescript-eslint/typescript-estree" "6.19.0" - semver "^7.5.4" - -"@typescript-eslint/visitor-keys@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz#4565e0ecd63ca1f81b96f1dd76e49f746c6b2b49" - integrity sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ== - dependencies: - "@typescript-eslint/types" "6.19.0" + "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@typescript-eslint/visitor-keys@6.7.2": - version "6.7.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c" - integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ== - dependencies: - "@typescript-eslint/types" "6.7.2" - eslint-visitor-keys "^3.4.1" +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== "@vitest/browser@^1.2.1": version "1.2.1" @@ -3523,14 +3483,6 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -abortable-iterator@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/abortable-iterator/-/abortable-iterator-5.0.1.tgz#5d93eba6fa8287a973a9ea090c64ca08b3777780" - integrity sha512-hlZ5Z8UwqrKsJcelVPEqDduZowJPBQJ9ZhBC2FXpja3lXy8X6MoI5uMzIgmrA8+3jcVnp8TF/tx+IBBqYJNUrg== - dependencies: - get-iterator "^2.0.0" - it-stream-types "^2.0.1" - abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" @@ -3834,11 +3786,6 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -argv@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz" - integrity sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas= - aria-query@^5.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" @@ -3859,25 +3806,20 @@ array-differ@^3.0.0: resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" array-union@^2.1.0: @@ -3885,35 +3827,46 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.findlastindex@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" - integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== +array.prototype.filter@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz#423771edeb417ff5914111fff4277ea0624c0d0e" + integrity sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== +array.prototype.findlastindex@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz#d1c50f0b3a9da191981ff8942a0aedd82794404f" + integrity sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" arraybuffer.prototype.slice@^1.0.2: @@ -3961,6 +3914,15 @@ asn1@^0.2.4: dependencies: safer-buffer "~2.1.0" +asn1js@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== + dependencies: + pvtsutils "^1.3.2" + pvutils "^1.1.3" + tslib "^2.4.0" + assert@^1.1.1: version "1.5.0" resolved "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz" @@ -3996,13 +3958,6 @@ async-lock@^1.4.0: resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.0.tgz#c8b6630eff68fbbdd8a5b6eb763dac3bfbb8bf02" integrity sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ== -async-retry@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz" - integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== - dependencies: - retry "0.13.1" - async@^3.2.3: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" @@ -4023,13 +3978,6 @@ atomic-sleep@^1.0.0: resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -available-typed-arrays@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz" - integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== - dependencies: - array-filter "^1.0.0" - available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" @@ -4091,11 +4039,6 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base64url@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz" - integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - basic-ftp@^5.0.2: version "5.0.3" resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.3.tgz#b14c0fe8111ce001ec913686434fe0c2fb461228" @@ -4131,11 +4074,6 @@ big-integer@^1.6.17: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== -big-integer@^1.6.44: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - bigint-buffer@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz" @@ -4173,16 +4111,6 @@ bintrees@1.0.1: resolved "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ= -bip39@^3.0.2: - version "3.0.4" - resolved "https://registry.npmjs.org/bip39/-/bip39-3.0.4.tgz" - integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw== - dependencies: - "@types/node" "11.11.6" - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - bip39@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" @@ -4223,7 +4151,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.1, bn.js@^4.11.9: resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1: +bn.js@^5.0.0: version "5.2.0" resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== @@ -4238,13 +4166,6 @@ boolean@^3.0.1: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== -bplist-parser@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" - integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== - dependencies: - big-integer "^1.6.44" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -4325,7 +4246,7 @@ browserify-des@^1.0.0: inherits "^2.0.1" safe-buffer "^5.1.2" -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: +browserify-rsa@^4.0.0, browserify-rsa@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz" integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== @@ -4334,19 +4255,19 @@ browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: randombytes "^2.0.1" browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + version "4.2.2" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.2.tgz#e78d4b69816d6e3dd1c747e64e9947f9ad79bc7e" + integrity sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg== dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" + bn.js "^5.2.1" + browserify-rsa "^4.1.0" create-hash "^1.2.0" create-hmac "^1.1.7" - elliptic "^6.5.3" + elliptic "^6.5.4" inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" + parse-asn1 "^5.1.6" + readable-stream "^3.6.2" + safe-buffer "^5.2.1" browserify-zlib@^0.2.0: version "0.2.0" @@ -4391,7 +4312,7 @@ buffer@4.9.2, buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.2.1, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.7.1: +buffer@^5.2.1, buffer@^5.5.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -4440,13 +4361,6 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" -bundle-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" - integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== - dependencies: - run-applescript "^5.0.0" - byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -4573,13 +4487,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" @@ -4834,17 +4749,6 @@ cmd-shim@6.0.1: resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== -codecov@^3.8.3: - version "3.8.3" - resolved "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz" - integrity sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA== - dependencies: - argv "0.0.2" - ignore-walk "3.0.4" - js-yaml "3.14.1" - teeny-request "7.1.1" - urlgrey "1.0.0" - color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -5304,14 +5208,7 @@ dateformat@^3.0.3: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: - version "4.3.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@4.3.4, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -5387,24 +5284,6 @@ deepmerge@^4.3.1: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-browser-id@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" - integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== - dependencies: - bplist-parser "^0.2.0" - untildify "^4.0.0" - -default-browser@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" - integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== - dependencies: - bundle-name "^3.0.0" - default-browser-id "^3.0.0" - execa "^7.1.1" - titleize "^3.0.0" - defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -5417,10 +5296,10 @@ defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-data-property@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.0.tgz#0db13540704e1d8d479a0656cf781267531b9451" - integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== dependencies: get-intrinsic "^1.2.1" gopd "^1.0.1" @@ -5431,20 +5310,7 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-lazy-prop@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" - integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== - -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -define-properties@^1.2.0: +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -5462,6 +5328,11 @@ degenerator@^5.0.0: escodegen "^2.1.0" esprima "^4.0.1" +delay@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-6.0.0.tgz#43749aefdf6cabd9e17b0d00bd3904525137e607" + integrity sha512-2NJozoOHQ4NuZuVIr5CWd0iiLVIRSDepakaovIN+9eIDHEhdCAEvSy2cuf1DCrPPQLvHmbqTHODlhHg8UCy4zw== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -5564,7 +5435,7 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -dns-over-http-resolver@3.0.0, dns-over-http-resolver@^2.1.1: +dns-over-http-resolver@^2.1.1, dns-over-http-resolver@^3.0.2: version "2.1.3" resolved "https://registry.yarnpkg.com/dns-over-http-resolver/-/dns-over-http-resolver-2.1.3.tgz#bb7f2e10cc18d960339a6e30e21b8c1d99be7b38" integrity sha512-zjRYFhq+CsxPAouQWzOsxNMvEN+SHisjzhX8EMxd2Y0EG3thvn6wXQgMJLnTDImkhe4jhLbOQpXtL10nALBOSA== @@ -5643,6 +5514,11 @@ dotenv-expand@~10.0.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== +dotenv@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.1.tgz#1d9931f1d3e5d2959350d1250efab299561f7f11" + integrity sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ== + dotenv@~16.3.1: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" @@ -5701,7 +5577,7 @@ electron@^26.2.2: "@types/node" "^18.11.18" extract-zip "^2.0.1" -elliptic@6.5.4, elliptic@^6.5.3: +elliptic@6.5.4, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -5790,147 +5666,26 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.1: - version "1.18.0" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz" - integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== +es-abstract@^1.18.0-next.2, es-abstract@^1.22.1, es-abstract@^1.22.3: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.2" - is-callable "^1.2.3" - is-negative-zero "^2.0.1" - is-regex "^1.1.2" - is-string "^1.0.5" - object-inspect "^1.9.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.0" - -es-abstract@^1.18.0-next.2: - version "1.20.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.3.tgz#90b143ff7aedc8b3d189bcfac7f1e3e3f81e9da1" - integrity sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.6" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.2" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-abstract@^1.19.0, es-abstract@^1.19.5: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.4.3" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== - dependencies: - array-buffer-byte-length "^1.0.0" - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.3" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - -es-abstract@^1.22.1: - version "1.22.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" - integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.2" - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function.prototype.name "^1.1.6" - get-intrinsic "^1.2.1" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" internal-slot "^1.0.5" is-array-buffer "^3.0.2" is-callable "^1.2.7" @@ -5940,7 +5695,7 @@ es-abstract@^1.22.1: is-string "^1.0.7" is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.3" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.5.1" @@ -5954,7 +5709,17 @@ es-abstract@^1.22.1: typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.11" + which-typed-array "^1.1.13" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-errors@^1.0.0, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-set-tostringtag@^2.0.1: version "2.0.1" @@ -5972,6 +5737,13 @@ es-shim-unscopables@^1.0.0: dependencies: has "^1.0.3" +es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -6055,14 +5827,14 @@ escodegen@^2.1.0: optionalDependencies: source-map "~0.6.1" -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" eslint-import-resolver-typescript@^3.6.1: version "3.6.1" @@ -6099,43 +5871,43 @@ eslint-plugin-es@^4.1.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@^2.28.1: - version "2.28.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz#63b8b5b3c409bfc75ebaf8fb206b07ab435482c4" - integrity sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A== +eslint-plugin-import@^2.29.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== dependencies: - array-includes "^3.1.6" - array.prototype.findlastindex "^1.2.2" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" + eslint-import-resolver-node "^0.3.9" eslint-module-utils "^2.8.0" - has "^1.0.3" - is-core-module "^2.13.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.fromentries "^2.0.6" - object.groupby "^1.0.0" - object.values "^1.1.6" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" semver "^6.3.1" - tsconfig-paths "^3.14.2" + tsconfig-paths "^3.15.0" -eslint-plugin-prettier@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz#6887780ed95f7708340ec79acfdf60c35b9be57a" - integrity sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w== +eslint-plugin-prettier@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz#17cfade9e732cef32b5f5be53bd4e07afd8e67e1" + integrity sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw== dependencies: prettier-linter-helpers "^1.0.0" - synckit "^0.8.5" + synckit "^0.8.6" -eslint-plugin-vitest@^0.3.20: - version "0.3.20" - resolved "https://registry.yarnpkg.com/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.20.tgz#9c4e02dd0252ad359b1e02dbcfbe4cf7ad8d091a" - integrity sha512-O05k4j9TGMOkkghj9dRgpeLDyOSiVIxQWgNDPfhYPm5ioJsehcYV/zkRLekQs+c8+RBCVXucSED3fYOyy2EoWA== +eslint-plugin-vitest@^0.3.22: + version "0.3.22" + resolved "https://registry.yarnpkg.com/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.22.tgz#207eb4630768f6400cd91a6df23688e8a1fd0898" + integrity sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw== dependencies: - "@typescript-eslint/utils" "^6.15.0" + "@typescript-eslint/utils" "^6.21.0" eslint-scope@^7.2.2: version "7.2.2" @@ -6157,33 +5929,24 @@ eslint-visitor-keys@^1.1.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== - -eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.50.0: - version "8.50.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.50.0.tgz#2ae6015fee0240fcd3f83e1e25df0287f487d6b2" - integrity sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg== +eslint@^8.56.0: + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "8.50.0" - "@humanwhocodes/config-array" "^0.11.11" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.56.0" + "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -6220,16 +5983,7 @@ esm@^3.2.25: resolved "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" - integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -espree@^9.6.1: +espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -6279,25 +6033,15 @@ esutils@^2.0.2: resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -ethereum-cryptography@^1.0.0, ethereum-cryptography@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" - integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== - dependencies: - "@noble/hashes" "1.2.0" - "@noble/secp256k1" "1.7.1" - "@scure/bip32" "1.1.5" - "@scure/bip39" "1.1.1" - -ethereum-cryptography@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.0.0.tgz#e052b49fa81affae29402e977b8d3a31f88612b6" - integrity sha512-g25m4EtfQGjstWgVE1aIz7XYYjf3kH5kG17ULWVB5dH6uLahsoltOhACzSxyDV+fhn4gbR4xRrOXGe6r2uh4Bg== +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" + integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== dependencies: - "@noble/curves" "1.0.0" - "@noble/hashes" "1.3.0" - "@scure/bip32" "1.3.0" - "@scure/bip39" "1.2.0" + "@noble/curves" "1.3.0" + "@noble/hashes" "1.3.3" + "@scure/bip32" "1.3.3" + "@scure/bip39" "1.2.2" ethers@^5.7.1: version "5.7.2" @@ -6416,21 +6160,6 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" - integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.1" - human-signals "^4.3.0" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^3.0.7" - strip-final-newline "^3.0.0" - execa@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" @@ -6508,32 +6237,10 @@ fast-fifo@^1.1.0, fast-fifo@^1.2.0: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" - integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" - integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== +fast-glob@^3.2.9, fast-glob@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -6585,13 +6292,6 @@ fast-uri@^2.0.0, fast-uri@^2.1.0: resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-2.2.0.tgz#519a0f849bef714aad10e9753d69d8f758f7445a" integrity sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg== -fast-url-parser@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz" - integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= - dependencies: - punycode "^1.3.2" - fastify-plugin@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-4.5.0.tgz#8b853923a0bba6ab6921bb8f35b81224e6988d91" @@ -6619,17 +6319,10 @@ fastify@^4.19.0: semver "^7.5.0" tiny-lru "^11.0.1" -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - -fastq@^1.6.1: - version "1.11.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz" - integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== +fastq@^1.6.0, fastq@^1.6.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.0.tgz#ca5e1a90b5e68f97fc8b61330d5819b82f5fab03" + integrity sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w== dependencies: reusify "^1.0.4" @@ -6776,11 +6469,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -6914,26 +6602,11 @@ fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function-bind@^1.1.2: +function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" @@ -6949,26 +6622,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^4.0.0: - version "4.0.2" - resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.2.tgz" - integrity sha512-aSPRm2CvA9R8QyU5eXMFPd+cYkyxLsXHd2l5/FOH2V/eml//M04G6KZOmTap07O1PvEwNcl2NndyLfK8g3QrKA== - dependencies: - ansi-regex "^5.0.1" - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - gauge@^4.0.3: version "4.0.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" @@ -7021,35 +6679,7 @@ get-func-name@^2.0.0, get-func-name@^2.0.2: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== @@ -7064,11 +6694,6 @@ get-iterator@^1.0.2: resolved "https://registry.npmjs.org/get-iterator/-/get-iterator-1.0.2.tgz" integrity sha512-v+dm9bNVfOYsY1OrhaCrmyOcYoSeVvbt+hHZ0Au+T+p1y+0Uyj9aMaGIeUTT6xdpRbWzDeYKvfOslPhggQMcsg== -get-iterator@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-iterator/-/get-iterator-2.0.0.tgz#c9ac9f8002e5d8d6b4dc9dae07c30945022a58c1" - integrity sha512-BDJawD5PU2gZv6Vlp8O28H4GnZcsr3h9gZUvnAP5xXP3WOy/QAoOsyMepSkw21jur+4t5Vppde72ChjhTIzxzg== - get-iterator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/get-iterator/-/get-iterator-2.0.1.tgz#a904829f61bace789e0d64bd1a504c511a015c3f" @@ -7249,7 +6874,7 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.0: +glob@^8.0.0, glob@^8.0.1: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -7260,28 +6885,7 @@ glob@^8.0.0: minimatch "^5.0.1" once "^1.3.0" -glob@^8.0.1: - version "8.0.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" - integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -glob@^9.2.0: - version "9.3.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.4.tgz#e75dee24891a80c25cc7ee1dd327e126b98679af" - integrity sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA== - dependencies: - fs.realpath "^1.0.0" - minimatch "^8.0.2" - minipass "^4.2.4" - path-scurry "^1.6.1" - -glob@^9.3.0, glob@^9.3.1: +glob@^9.2.0, glob@^9.3.1: version "9.3.5" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q== @@ -7370,21 +6974,11 @@ got@^12.6.1: p-cancelable "^3.0.0" responselike "^3.0.0" -graceful-fs@4.2.11, graceful-fs@^4.2.2: +graceful-fs@4.2.11, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -graceful-fs@^4.2.6: - version "4.2.9" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== - grapheme-splitter@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" @@ -7427,19 +7021,19 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -7567,7 +7161,7 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -7644,11 +7238,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -human-signals@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" - integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== - human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -7685,13 +7274,6 @@ ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore-walk@3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz" - integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== - dependencies: - minimatch "^3.0.4" - ignore-walk@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" @@ -7706,20 +7288,10 @@ ignore-walk@^6.0.0: dependencies: minimatch "^7.4.2" -ignore@^5.0.4, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -ignore@^5.1.1: - version "5.1.8" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -ignore@^5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== +ignore@^5.0.4, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" @@ -7841,28 +7413,18 @@ inquirer@^9.1.5: wrap-ansi "^8.1.0" interface-datastore@^8.0.0, interface-datastore@^8.2.0, interface-datastore@^8.2.7: - version "8.2.7" - resolved "https://registry.yarnpkg.com/interface-datastore/-/interface-datastore-8.2.7.tgz#6a1e168fd98a271a92b85223bae58fa30031fb9c" - integrity sha512-ot5B5+VogufRfjhedAXZHm5NuEKyYZkDyVpTjBYIrxYUpS5GIfF2soE/dsd/FiBVqubcxa4IEToMXL5ruMwhjw== + version "8.2.10" + resolved "https://registry.yarnpkg.com/interface-datastore/-/interface-datastore-8.2.10.tgz#2d7fc026c8185378c4d3433fe942d9d6838f95cb" + integrity sha512-D8RuxMdjOPB+j6WMDJ+I2aXTDzUT6DIVjgzo1E+ODL7w8WrSFl9FXD2SYmgj6vVzdb7Kb5qmAI9pEnDZJz7ifg== dependencies: interface-store "^5.0.0" - nanoid "^5.0.3" - uint8arrays "^4.0.2" + uint8arrays "^5.0.0" interface-store@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/interface-store/-/interface-store-5.1.0.tgz#1735cead844fe452d62c307fafbaaa1d261e6ff3" integrity sha512-mjUwX3XSoreoxCS3sXS3pSRsGnUjl9T06KBqt/T7AgE9Sgp4diH64ZyURJKnj2T5WmCvTbC0Dm+mwQV5hfLSBQ== -internal-slot@^1.0.3: - version "1.0.6" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" - integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== - dependencies: - get-intrinsic "^1.2.2" - hasown "^2.0.0" - side-channel "^1.0.4" - internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -7882,7 +7444,7 @@ ip-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-5.0.0.tgz#cd313b2ae9c80c07bd3851e12bf4fa4dc5480632" integrity sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw== -ip@^1.1.5, ip@^1.1.8: +ip@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== @@ -7955,16 +7517,11 @@ is-buffer@^2.0.5: resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.3, is-callable@^1.2.6, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - is-ci@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" @@ -7972,40 +7529,12 @@ is-ci@3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== - dependencies: - has "^1.0.3" - -is-core-module@^2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== - dependencies: - has "^1.0.3" - -is-core-module@^2.3.0: - version "2.5.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz" - integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg== - dependencies: - has "^1.0.3" - -is-core-module@^2.5.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== - dependencies: - has "^1.0.3" - -is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.3.0, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" is-date-object@^1.0.1: version "1.0.5" @@ -8019,11 +7548,6 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-docker@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" - integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== - is-electron@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/is-electron/-/is-electron-2.2.0.tgz" @@ -8044,27 +7568,13 @@ is-generator-function@^1.0.7: resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz" integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== -is-glob@^4.0.0, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-inside-container@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" - integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== - dependencies: - is-docker "^3.0.0" - is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" @@ -8109,7 +7619,7 @@ is-nan@^1.2.1: call-bind "^1.0.0" define-properties "^1.1.3" -is-negative-zero@^2.0.1, is-negative-zero@^2.0.2: +is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== @@ -8178,7 +7688,7 @@ is-property@^1.0.0, is-property@^1.0.2: resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== -is-regex@^1.1.2, is-regex@^1.1.4: +is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -8236,35 +7746,13 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -is-typed-array@^1.1.12: +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.3, is-typed-array@^1.1.9: version "1.1.12" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: which-typed-array "^1.1.11" -is-typed-array@^1.1.3: - version "1.1.5" - resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz" - integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.2" - es-abstract "^1.18.0-next.2" - foreach "^2.0.5" - has-symbols "^1.0.1" - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" @@ -8324,26 +7812,12 @@ isomorphic-ws@^5.0.0: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== -istanbul-lib-coverage@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== - -istanbul-lib-coverage@^3.2.2: +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-report@^3.0.1: +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== @@ -8369,12 +7843,7 @@ istanbul-reports@^3.1.6: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -it-all@^3.0.0, it-all@^3.0.1, it-all@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/it-all/-/it-all-3.0.2.tgz#620b82c702c9c6d1c4caddb6407dba4a4baa970b" - integrity sha512-ujqWETXhsDbF6C+6X6fvRw5ohlowRoy/o/h9BC8D+R3JQ13oLQ153w9gSWkWupOY7omZFQbJiAL1aJo5Gwe2yw== - -it-all@^3.0.4: +it-all@^3.0.0, it-all@^3.0.1, it-all@^3.0.2, it-all@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/it-all/-/it-all-3.0.4.tgz#08f2e3eb3df04fa4525a343dcacfbdf91ffee162" integrity sha512-UMiy0i9DqCHBdWvMbzdYvVGa5/w4t1cc4nchpbnjdLhklglv8mQeEYnii0gvKESJuL1zV32Cqdb33R6/GPfxpQ== @@ -8388,15 +7857,10 @@ it-byte-stream@^1.0.0: it-stream-types "^2.0.1" uint8arraylist "^2.4.1" -it-drain@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/it-drain/-/it-drain-3.0.2.tgz#4fb2ab30119072268c68a895fa5b9f2037942c44" - integrity sha512-0hJvS/4Ktt9wT/bktmovjjMAY8r6FCsXqpL3zjqBBNwoL21VgQfguEnwbLSGuCip9Zq1vfU43cbHkmaRZdBfOg== - -it-drain@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/it-drain/-/it-drain-3.0.3.tgz#f80719d3d0d7e7d02dc298d86ca9d0e7f7bd666b" - integrity sha512-l4s+izxUpFAR2axprpFiCaq0EtxK1QMd0LWbEtau5b+OegiZ5xdRtz35iJyh6KZY9QtuwEiQxydiOfYJc7stoA== +it-drain@^3.0.1, it-drain@^3.0.3: + version "3.0.5" + resolved "https://registry.yarnpkg.com/it-drain/-/it-drain-3.0.5.tgz#d7aed18a16a12c157fa477653fb42c1b4f08491c" + integrity sha512-qYFe4SWdvs9oJGUY5bSjvmiLUMLzFEODNOQUdYdCIkuIgQF+AUB2INhM4yQ09buJ2rhHKDFxvTD/+yUq6qg0XA== it-filter@^3.0.0: version "3.0.2" @@ -8412,39 +7876,27 @@ it-foreach@^2.0.3: dependencies: it-peekable "^3.0.0" -it-length-prefixed-stream@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/it-length-prefixed-stream/-/it-length-prefixed-stream-1.0.2.tgz#dbcb291118fc9cf031c89ab4be3f99cdb452f548" - integrity sha512-gWevodoctgwWUaRJN9t+xEs1H1GQNYAjLCR7FO50fon9Ph4OJGgrxPKTc26QXKrC/cIQZLkHYClphUw0wl1k2A== - dependencies: - it-byte-stream "^1.0.0" - it-length-prefixed "^9.0.1" - it-stream-types "^2.0.1" - uint8-varint "^2.0.1" - uint8arraylist "^2.4.1" - -it-length-prefixed-stream@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/it-length-prefixed-stream/-/it-length-prefixed-stream-1.1.4.tgz#5e5a73685e820366d9a9dd944decef55b46f09ef" - integrity sha512-6YcQ5jsaYnuXBqF+oSGjSdSY9jF7HWl7yh+dxYytXxbE2GcdiOpn6pLM7m6AlIID9MCzQqMY5nOzaiatQ8A3/A== +it-length-prefixed-stream@^1.0.0, it-length-prefixed-stream@^1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/it-length-prefixed-stream/-/it-length-prefixed-stream-1.1.6.tgz#b757b96d352da6e97b66002dd2ead32893ba2337" + integrity sha512-MEby4r8n3XIYXjaWT3DweCuhBPQmFVT8RdI1BNjYQ5gelbFD3NLdjYpTI3TVmSEs/aJfgpfVFZzy6iP7OCxIgw== dependencies: it-byte-stream "^1.0.0" - it-length-prefixed "^9.0.1" it-stream-types "^2.0.1" uint8-varint "^2.0.1" uint8arraylist "^2.4.1" -it-length-prefixed@^9.0.1, it-length-prefixed@^9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/it-length-prefixed/-/it-length-prefixed-9.0.3.tgz#73af16f786cab60a0a9bfc2997e88eb26d3a72ca" - integrity sha512-YAu424ceYpXctxtjcLOqn7vJq082CaoP8J646ZusYISfQc3bpzQErgTUqMFj81V262KG2W9/YMBHsy6A/4yvmg== +it-length-prefixed@^9.0.1, it-length-prefixed@^9.0.3, it-length-prefixed@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/it-length-prefixed/-/it-length-prefixed-9.0.4.tgz#8096c3270420fe8abaa920c7b4d5e5895144008e" + integrity sha512-lz28fykbG0jq7s5XtvlzGxO5BeSOw6ikymkRllxjL21V5VKLcvB4pHr9wPvEnsAJ2et1xpOk3BRTMq9XrhgKsg== dependencies: err-code "^3.0.1" it-reader "^6.0.1" it-stream-types "^2.0.1" uint8-varint "^2.0.1" uint8arraylist "^2.0.0" - uint8arrays "^4.0.2" + uint8arrays "^5.0.1" it-map@^3.0.1: version "3.0.3" @@ -8453,14 +7905,7 @@ it-map@^3.0.1: dependencies: it-peekable "^3.0.0" -it-merge@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/it-merge/-/it-merge-3.0.1.tgz#20cc293593586e5afcbfed8ba88a94def5ccfcfa" - integrity sha512-I6hjU1ABO+k3xY1H6JtCSDXvUME88pxIXSgKeT4WI5rPYbQzpr98ldacVuG95WbjaJxKl6Qot6lUdxduLBQPHA== - dependencies: - it-pushable "^3.1.0" - -it-merge@^3.0.1: +it-merge@^3.0.0, it-merge@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/it-merge/-/it-merge-3.0.3.tgz#c7d407c8e0473accf7f9958ce2e0f60276002e84" integrity sha512-FYVU15KC5pb/GQX1Ims+lee8d4pdqGVCpWr0lkNj8o4xuNo7jY71k6GuEiWdP+T7W1bJqewSxX5yoTy5yZpRVA== @@ -8506,19 +7951,17 @@ it-protobuf-stream@^1.0.2: protons-runtime "^5.0.0" uint8arraylist "^2.4.1" -it-pushable@^3.0.0, it-pushable@^3.1.0, it-pushable@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/it-pushable/-/it-pushable-3.1.3.tgz#b6f4a1e0236502f12b5661b40468b629799baf0e" - integrity sha512-f50iQ85HISS6DaWCyrqf9QJ6G/kQtKIMf9xZkgZgyOvxEQDfn8OfYcLXXquCqgoLboxQtAW1ZFZyFIAsLHDtJw== - -it-pushable@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/it-pushable/-/it-pushable-3.2.1.tgz#72d7ccf7e7c0ccecf8cbaf74064f7be56e775c59" - integrity sha512-sLFz2Q0oyDCJpTciZog7ipP4vSftfPy3e6JnH6YyztRa1XqkpGQaafK3Jw/JlfEBtCXfnX9uVfcpu3xpSAqCVQ== +it-protobuf-stream@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/it-protobuf-stream/-/it-protobuf-stream-1.1.2.tgz#4444d78fcae0fce949b4cbea622bf1d92667e64f" + integrity sha512-epZBuG+7cPaTxCR/Lf3ApshBdA9qfflGPQLfLLrp9VQ0w67Z2xo4H+SLLetav57/29oPtAXwVaoyemg99JOWzA== dependencies: - p-defer "^4.0.0" + it-length-prefixed-stream "^1.0.0" + it-stream-types "^2.0.1" + protons-runtime "^5.0.0" + uint8arraylist "^2.4.1" -it-pushable@^3.2.1, it-pushable@^3.2.2: +it-pushable@^3.0.0, it-pushable@^3.1.2, it-pushable@^3.2.0, it-pushable@^3.2.1, it-pushable@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/it-pushable/-/it-pushable-3.2.3.tgz#e2b80aed90cfbcd54b620c0a0785e546d4e5f334" integrity sha512-gzYnXYK8Y5t5b/BnJUr7glfQLO4U5vyb05gPx/TyTw+4Bv1zM9gFk4YsOrnulWefMewlphCjKkakFvj1y99Tcg== @@ -8604,14 +8047,6 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@^3.10.0: - version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" @@ -8619,6 +8054,14 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +js-yaml@^3.10.0: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsdom@^23.0.1: version "23.0.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-23.0.1.tgz#ede7ff76e89ca035b11178d200710d8982ebfee0" @@ -9182,10 +8625,10 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== -lru-cache@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" - integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== +lru-cache@^10.1.0, "lru-cache@^9.1.1 || ^10.0.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== lru-cache@^5.1.1: version "5.1.1" @@ -9201,26 +8644,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.14.1: +lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -lru-cache@^7.4.4, lru-cache@^7.5.1: - version "7.14.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.0.tgz#21be64954a4680e303a09e9468f880b98a0b3c7f" - integrity sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ== - -lru-cache@^7.7.1: - version "7.10.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.10.1.tgz#db577f42a94c168f676b638d15da8fb073448cab" - integrity sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A== - -"lru-cache@^9.1.1 || ^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" - integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== - magic-string@^0.30.3, magic-string@^0.30.5: version "0.30.5" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" @@ -9252,13 +8680,6 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - make-error@^1.1.1: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" @@ -9286,28 +8707,7 @@ make-fetch-happen@^10.0.3: socks-proxy-agent "^7.0.0" ssri "^9.0.0" -make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1: - version "11.1.0" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.0.tgz#f26b05e89317e960b75fd5e080e40d40f8d7b2a5" - integrity sha512-7ChuOzCb1LzdQZrTy0ky6RsCoMYeM+Fh4cY0+4zsJVhNcH5Q3OJojLY1mGkD0xAhWB29lskECVb6ZopofwjldA== - dependencies: - agentkeepalive "^4.2.1" - cacache "^17.0.0" - http-cache-semantics "^4.1.1" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^4.0.0" - minipass-fetch "^3.0.0" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.3" - promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" - ssri "^10.0.0" - -make-fetch-happen@^11.0.3, make-fetch-happen@^11.1.1: +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== @@ -9808,12 +9208,7 @@ multiformats@^12.0.1: resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.0.1.tgz#dd3e19dd44114c2672e4795a36888d263be30131" integrity sha512-s01wijBJoDUqESWSzePY0lvTw7J3PVO9x2Cc6ASI5AMZM2Gnhh7BC17+nlFhHKU7dDzaCaRfb+NiqNzOsgPUoQ== -multiformats@^12.1.3: - version "12.1.3" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-12.1.3.tgz#cbf7a9861e11e74f8228b21376088cb43ba8754e" - integrity sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw== - -multiformats@^13.0.0: +multiformats@^13.0.0, multiformats@^13.0.1: version "13.0.1" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-13.0.1.tgz#c0622affa5171189eacd57c06f977195ca7acb08" integrity sha512-bt3R5iXe2O8xpp3wkmQhC73b/lC4S2ihU8Dndwcsysqbydqb8N+bpP116qMcClZ17g58iSIwtXUTcg2zT4sniA== @@ -9864,11 +9259,6 @@ nanoid@^4.0.0: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e" integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== -nanoid@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.3.tgz#6c97f53d793a7a1de6a38ebb46f50f95bf9793c7" - integrity sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA== - napi-macros@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz" @@ -9919,15 +9309,17 @@ node-domexception@^1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7: version "2.6.7" - resolved "https://registry.npmjs.org/@achingbrain/node-fetch/-/node-fetch-2.6.7.tgz" - integrity sha512-iTASGs+HTFK5E4ZqcMsHmeJ4zodyq8L38lZV33jwqcBJYoUt3HjN4+ot+O9/0b+ke8ddE7UgOtVuZN/OkV19/g== + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" -node-fetch@^2.6.12: - version "2.6.12" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" - integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== +node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" @@ -9966,32 +9358,16 @@ node-gyp@^8.4.0: tar "^6.1.2" which "^2.0.2" -node-gyp@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.0.0.tgz#e1da2067427f3eb5bb56820cb62bc6b1e4bd2089" - integrity sha512-Ma6p4s+XCTPxCuAMrOA/IJRmVy16R8Sdhtwl4PrCr7IBlj4cPawF0vg/l7nOT1jPbuNS7lIRJpBSvVsXwEZuzw== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" - nopt "^5.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - -node-gyp@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" - integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== +node-gyp@^9.0.0, node-gyp@^9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" + integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== dependencies: env-paths "^2.2.0" exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^11.0.3" + make-fetch-happen "^10.0.3" nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" @@ -10202,20 +9578,7 @@ npm-pick-manifest@^8.0.0: npm-package-arg "^10.0.0" semver "^7.3.5" -npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3: - version "14.0.4" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.4.tgz#43dfa55ce7c0d0c545d625c7a916bab5b95f7038" - integrity sha512-pMS2DRkwg+M44ct65zrN/Cr9IHK1+n6weuefAo6Er4lc+/8YBCU0Czq04H3ZiSigluh7pb2rMM5JpgcytctB+Q== - dependencies: - make-fetch-happen "^11.0.0" - minipass "^4.0.0" - minipass-fetch "^3.0.0" - minipass-json-stream "^1.0.1" - minizlib "^2.1.2" - npm-package-arg "^10.0.0" - proc-log "^3.0.0" - -npm-registry-fetch@^14.0.5: +npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3, npm-registry-fetch@^14.0.5: version "14.0.5" resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz#fe7169957ba4986a4853a650278ee02e568d115d" integrity sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA== @@ -10230,7 +9593,7 @@ npm-registry-fetch@^14.0.5: npm-run-all@^4.1.5: version "4.1.5" - resolved "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== dependencies: ansi-styles "^3.2.1" @@ -10257,17 +9620,7 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -npmlog@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz" - integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg== - dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.0" - set-blocking "^2.0.0" - -npmlog@^6.0.2: +npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== @@ -10345,20 +9698,10 @@ object-hash@^2.0.1: resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== -object-inspect@^1.12.0: - version "1.12.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.1.tgz#28a661153bad7e470e4b01479ef1cb91ce511191" - integrity sha512-Y/jF6vnvEtOPGiKD1+q+X0CiUYRQtEHp89MLLUJ7TUivtH8Ugn2+3A7Rynqk7BRsAoqeOQWnFnjpDrKSxDgIGA== - -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -object-inspect@^1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== object-is@^1.0.1: version "1.1.5" @@ -10373,7 +9716,7 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.2, object.assign@^4.1.4: +object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -10383,7 +9726,7 @@ object.assign@^4.1.2, object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.fromentries@^2.0.6: +object.fromentries@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== @@ -10392,24 +9735,25 @@ object.fromentries@^2.0.6: define-properties "^1.2.0" es-abstract "^1.22.1" -object.groupby@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" - integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== +object.groupby@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.2.tgz#494800ff5bab78fd0eff2835ec859066e00192ec" + integrity sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + array.prototype.filter "^1.0.3" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.0.0" -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== +object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" obliterator@^2.0.1: version "2.0.4" @@ -10468,16 +9812,6 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -open@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" - integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== - dependencies: - default-browser "^4.0.0" - define-lazy-prop "^3.0.0" - is-inside-container "^1.0.0" - is-wsl "^2.2.0" - openapi-types@^12.0.0, openapi-types@^12.0.2: version "12.1.3" resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" @@ -10651,13 +9985,13 @@ p-queue@^7.2.0: eventemitter3 "^4.0.7" p-timeout "^5.0.2" -p-queue@^7.4.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-7.4.1.tgz#7f86f853048beca8272abdbb7cec1ed2afc0f265" - integrity sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA== +p-queue@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-8.0.1.tgz#718b7f83836922ef213ddec263ff4223ce70bef8" + integrity sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA== dependencies: eventemitter3 "^5.0.1" - p-timeout "^5.0.2" + p-timeout "^6.1.2" p-reduce@2.1.0, p-reduce@^2.0.0, p-reduce@^2.1.0: version "2.1.0" @@ -10676,10 +10010,10 @@ p-timeout@^5.0.2: resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-5.1.0.tgz#b3c691cf4415138ce2d9cfe071dba11f0fee085b" integrity sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== -p-timeout@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.1.tgz#bcee5e37d730f5474d973b6ff226751a1a5e6ff1" - integrity sha512-yqz2Wi4fiFRpMmK0L2pGAU49naSUaP23fFIQL2Y6YT+qDGPoFwpvgQM/wzc6F8JoenUkIlAFa4Ql7NguXBxI7w== +p-timeout@^6.0.0, p-timeout@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-6.1.2.tgz#22b8d8a78abf5e103030211c5fc6dee1166a6aa5" + integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ== p-try@^1.0.0: version "1.0.0" @@ -10757,7 +10091,7 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0, parse-asn1@^5.1.5: +parse-asn1@^5.0.0, parse-asn1@^5.1.6: version "5.1.6" resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz" integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== @@ -10857,7 +10191,7 @@ path-key@^4.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== -path-parse@^1.0.5, path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.5, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -10892,7 +10226,7 @@ pathval@^1.1.1: resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== -pbkdf2@^3.0.3, pbkdf2@^3.0.9: +pbkdf2@^3.0.3: version "3.1.2" resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== @@ -10913,12 +10247,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4: - version "2.2.3" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz" - integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== - -picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -11076,15 +10405,7 @@ progress@2.0.3, progress@^2.0.3: resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -prom-client@^15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-15.0.0.tgz#067da874a2aa5d2e21bd5cdba9f24a8178bdab6a" - integrity sha512-UocpgIrKyA2TKLVZDSfm8rGkL13C19YrQBAiG3xo3aDFWcHedxRxI3z+cIcucoxpSO0h5lff5iv/SXoxyeopeA== - dependencies: - "@opentelemetry/api" "^1.4.0" - tdigest "^0.1.1" - -prom-client@^15.1.0: +prom-client@^15.0.0, prom-client@^15.1.0: version "15.1.0" resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-15.1.0.tgz#816a4a2128da169d0471093baeccc6d2f17a4613" integrity sha512-cCD7jLTqyPdjEPBo/Xk4Iu8jxjuZgZJ3e/oET3L+ZwOuap/7Cw3dH/TJSsZKs1TQLZ2IHpIlRAKw82ef06kmMw== @@ -11128,29 +10449,10 @@ properties-reader@^2.2.0: dependencies: mkdirp "^1.0.4" -protobufjs@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.0.0.tgz#8c678e1351fd926178fce5a4213913e8d990974f" - integrity sha512-ffNIEm+quOcYtQvHdW406v1NQmZSuqVklxsXk076BtuFnlYZfigLU+JOMrTD8TUOyqHYbRI/fSVNvgd25YeN3w== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^5.0.0" - -protobufjs@^7.2.4: - version "7.2.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.4.tgz#3fc1ec0cdc89dd91aef9ba6037ba07408485c3ae" - integrity sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ== +protobufjs@^7.0.0, protobufjs@^7.2.6: + version "7.2.6" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" + integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -11219,16 +10521,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== -psl@^1.1.33: - version "1.8.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" @@ -11251,20 +10548,15 @@ pump@^3.0.0: punycode@1.3.2: version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: +punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== -punycode@^2.3.1: +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -11281,6 +10573,18 @@ puppeteer-core@^20.9.0: devtools-protocol "0.0.1147663" ws "8.13.0" +pvtsutils@^1.3.2: + version "1.3.5" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" + integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== + dependencies: + tslib "^2.6.1" + +pvutils@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== + qs@^6.11.0, qs@^6.11.1: version "6.11.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" @@ -11338,7 +10642,7 @@ race-event@^1.1.0: resolved "https://registry.yarnpkg.com/race-event/-/race-event-1.1.0.tgz#69c2d855653acf11d8b23ea8f6fa50e1180a088b" integrity sha512-8BTiN6IAbov8mqkVEc3LiYbtUzanLfzFhwPF7kZV74ztYeQXdFPIgMCd/sy8xie6ZMtf2JPeMBedx78/RRNO3g== -race-signal@^1.0.1, race-signal@^1.0.2: +race-signal@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/race-signal/-/race-signal-1.0.2.tgz#e42379fba0cec4ee8dab7c9bbbd4aa6e0d14c25f" integrity sha512-o3xNv0iTcIDQCXFlF6fPAMEBRjFxssgGoRqLbg06m+AdzEXXLUmoNOoUHTVz2NoBI8hHwKFKoC6IqyNtWr2bww== @@ -11381,7 +10685,7 @@ read-package-json-fast@^3.0.0: json-parse-even-better-errors "^3.0.0" npm-normalize-package-bin "^3.0.0" -read-package-json@6.0.4: +read-package-json@6.0.4, read-package-json@^6.0.0: version "6.0.4" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.4.tgz#90318824ec456c287437ea79595f4c2854708836" integrity sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw== @@ -11391,16 +10695,6 @@ read-package-json@6.0.4: normalize-package-data "^5.0.0" npm-normalize-package-bin "^3.0.0" -read-package-json@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-6.0.1.tgz#566cb06bc05dbddefba4607e9096d5a9efbcd836" - integrity sha512-AaHqXxfAVa+fNL07x8iAghfKOds/XXsu7zoouIVsbm7PEbQ3nMWXlvjcbrNLjElnUHWQtAo4QEa0RXuvD4XlpA== - dependencies: - glob "^9.3.0" - json-parse-even-better-errors "^3.0.0" - normalize-package-data "^5.0.0" - npm-normalize-package-bin "^3.0.0" - read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -11444,7 +10738,7 @@ read@^2.0.0: dependencies: mute-stream "~1.0.0" -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -11466,6 +10760,15 @@ readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.3.0.tgz#0914d0c72db03b316c9733bb3461d64a3cc50cba" @@ -11476,14 +10779,7 @@ readable-stream@^4.0.0: events "^3.3.0" process "^0.11.10" -readdir-glob@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.2.tgz#b185789b8e6a43491635b6953295c5c5e3fd224c" - integrity sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA== - dependencies: - minimatch "^5.1.0" - -readdir-glob@^1.1.2: +readdir-glob@^1.0.0, readdir-glob@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== @@ -11522,15 +10818,6 @@ regenerator-runtime@^0.11.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" @@ -11587,23 +10874,7 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.10.0, resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.10.1: - version "1.16.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz" - integrity sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig== - dependencies: - path-parse "^1.0.6" - -resolve@^1.17.0: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.17.0, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -11654,11 +10925,6 @@ ret@~0.2.0: resolved "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz" integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== -retry@0.13.1: - version "0.13.1" - resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -11765,13 +11031,6 @@ rrweb-cssom@^0.6.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== -run-applescript@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" - integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== - dependencies: - execa "^5.0.0" - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -11796,24 +11055,10 @@ rustbn.js@~0.2.0: resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== -rxjs@^7.2.0: - version "7.3.0" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz" - integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw== - dependencies: - tslib "~2.1.0" - -rxjs@^7.5.5: - version "7.5.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" - integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== - dependencies: - tslib "^2.1.0" - -rxjs@^7.8.0: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== +rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.0: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -11832,7 +11077,7 @@ safe-array-concat@^1.0.1: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -11912,7 +11157,7 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.1: +semver@^6.1.0, semver@^6.2.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -11955,6 +11200,17 @@ set-cookie-parser@^2.4.1: resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz" integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg== +set-function-length@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + dependencies: + define-data-property "^1.1.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + set-function-name@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -12130,15 +11386,7 @@ socks-proxy-agent@^8.0.1, socks-proxy-agent@^8.0.2: debug "^4.3.4" socks "^2.7.1" -socks@^2.6.1, socks@^2.6.2: - version "2.6.2" - resolved "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz" - integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== - dependencies: - ip "^1.1.5" - smart-buffer "^4.2.0" - -socks@^2.7.1: +socks@^2.6.1, socks@^2.6.2, socks@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== @@ -12321,13 +11569,6 @@ stream-browserify@^3.0.0: inherits "~2.0.4" readable-stream "^3.5.0" -stream-events@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz" - integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== - dependencies: - stubs "^3.0.0" - stream-http@^2.7.2: version "2.8.3" resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz" @@ -12396,15 +11637,6 @@ string.prototype.padend@^3.0.0: define-properties "^1.1.3" es-abstract "^1.18.0-next.2" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" @@ -12414,24 +11646,6 @@ string.prototype.trim@^1.2.8: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimend@^1.0.4, string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trimend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" @@ -12441,24 +11655,6 @@ string.prototype.trimend@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.4, string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trimstart@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" @@ -12496,14 +11692,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== - dependencies: - ansi-regex "^6.0.1" - -strip-ansi@^7.1.0: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== @@ -12563,11 +11752,6 @@ strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -stubs@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz" - integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= - sumchecker@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" @@ -12630,13 +11814,13 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -synckit@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" - integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== +synckit@^0.8.6: + version "0.8.8" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" + integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== dependencies: - "@pkgr/utils" "^2.3.1" - tslib "^2.5.0" + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" systeminformation@^5.17.12: version "5.21.7" @@ -12718,17 +11902,6 @@ tdigest@^0.1.1: dependencies: bintrees "1.0.1" -teeny-request@7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.1.tgz" - integrity sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg== - dependencies: - http-proxy-agent "^4.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" - stream-events "^1.0.5" - uuid "^8.0.0" - temp-dir@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" @@ -12837,11 +12010,6 @@ tinyspy@^2.2.0: resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce" integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg== -titleize@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" - integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -12892,16 +12060,7 @@ tough-cookie@^3.0.1: psl "^1.1.28" punycode "^2.1.1" -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tough-cookie@^4.1.3: +tough-cookie@^4.0.0, tough-cookie@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -12933,7 +12092,7 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -triple-beam@^1.2.0, triple-beam@^1.3.0: +triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== @@ -12962,10 +12121,10 @@ ts-node@^10.8.1, ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.14.2: - version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" - integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.2" @@ -12991,7 +12150,7 @@ tsconfig@^7.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@2.4.0, tslib@^2.3.0, tslib@^2.4.0: +tslib@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== @@ -13001,31 +12160,11 @@ tslib@^1.10.0: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.2.0: - version "2.3.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== - -tslib@^2.0.1: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz" - integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== - -tslib@^2.5.0, tslib@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" - integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== - -tslib@~2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz" @@ -13197,20 +12336,13 @@ uint8-varint@^2.0.0, uint8-varint@^2.0.1, uint8-varint@^2.0.2: uint8arraylist "^2.0.0" uint8arrays "^5.0.0" -uint8arraylist@^2.0.0, uint8arraylist@^2.4.1, uint8arraylist@^2.4.3, uint8arraylist@^2.4.7: +uint8arraylist@^2.0.0, uint8arraylist@^2.4.1, uint8arraylist@^2.4.3, uint8arraylist@^2.4.7, uint8arraylist@^2.4.8: version "2.4.8" resolved "https://registry.yarnpkg.com/uint8arraylist/-/uint8arraylist-2.4.8.tgz#5a4d17f4defd77799cb38e93fd5db0f0dceddc12" integrity sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ== dependencies: uint8arrays "^5.0.1" -uint8arrays@^4.0.2, uint8arrays@^4.0.4, uint8arrays@^4.0.6, uint8arrays@^4.0.9: - version "4.0.10" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-4.0.10.tgz#3ec5cde3348903c140e87532fc53f46b8f2e921f" - integrity sha512-AnJNUGGDJAgFw/eWu/Xb9zrVKEGlwJJCaeInlf3BkecE/zcTobk5YXYIPNQJO1q5Hh1QZrQQHf0JvcHqz2hqoA== - dependencies: - multiformats "^12.0.1" - uint8arrays@^5.0.0, uint8arrays@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-5.0.1.tgz#6016ef944379eabb6de605934ead4d7a698c9f07" @@ -13218,7 +12350,7 @@ uint8arrays@^5.0.0, uint8arrays@^5.0.1: dependencies: multiformats "^13.0.0" -unbox-primitive@^1.0.0, unbox-primitive@^1.0.2: +unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== @@ -13281,7 +12413,7 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== @@ -13296,11 +12428,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - unzipper@^0.10.14: version "0.10.14" resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1" @@ -13353,13 +12480,6 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" -urlgrey@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/urlgrey/-/urlgrey-1.0.0.tgz" - integrity sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w== - dependencies: - fast-url-parser "^1.1.3" - userhome@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/userhome/-/userhome-1.0.0.tgz#b6491ff12d21a5e72671df9ccc8717e1c6688c0b" @@ -13384,19 +12504,7 @@ util@^0.11.0: dependencies: inherits "2.0.3" -util@^0.12.0: - version "0.12.3" - resolved "https://registry.npmjs.org/util/-/util-0.12.3.tgz" - integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - safe-buffer "^5.1.2" - which-typed-array "^1.1.2" - -util@^0.12.4, util@^0.12.5: +util@^0.12.0, util@^0.12.4, util@^0.12.5: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== @@ -13412,7 +12520,7 @@ uuid@3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2: +uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -13894,41 +13002,16 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.11: - version "1.1.11" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" - integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -which-typed-array@^1.1.2: - version "1.1.4" - resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz" - integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== - dependencies: - available-typed-arrays "^1.0.2" - call-bind "^1.0.0" - es-abstract "^1.18.0-next.1" - foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" - -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.2: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.4" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^1.2.9: version "1.3.1" @@ -13983,18 +13066,10 @@ winston-daily-rotate-file@^4.7.1: triple-beam "^1.3.0" winston-transport "^4.4.0" -winston-transport@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz" - integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== - dependencies: - readable-stream "^2.3.7" - triple-beam "^1.2.0" - -winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== +winston-transport@^4.4.0, winston-transport@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.6.0.tgz#f1c1a665ad1b366df72199e27892721832a19e1b" + integrity sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg== dependencies: logform "^2.3.2" readable-stream "^3.6.0" @@ -14105,7 +13180,7 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@8.13.0, ws@^8.8.1: +ws@8.13.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== @@ -14115,10 +13190,10 @@ ws@8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== -ws@^8.14.2, ws@^8.8.0: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== +ws@^8.14.2, ws@^8.8.0, ws@^8.8.1: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== xml-name-validator@^5.0.0: version "5.0.0" @@ -14127,7 +13202,7 @@ xml-name-validator@^5.0.0: xml2js@0.4.19: version "0.4.19" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== dependencies: sax ">=0.6.0" @@ -14135,7 +13210,7 @@ xml2js@0.4.19: xml2js@^0.4.19: version "0.4.23" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== dependencies: sax ">=0.6.0" @@ -14224,7 +13299,7 @@ yargs@16.2.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@17.7.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.1: +yargs@17.7.1: version "17.7.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== @@ -14237,7 +13312,7 @@ yargs@17.7.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@17.7.2: +yargs@17.7.2, yargs@^17.1.1, yargs@^17.5.1, yargs@^17.6.2, yargs@^17.7.1: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -14250,19 +13325,6 @@ yargs@17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^17.1.1: - version "17.6.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" - integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"