diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..4b842733 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,38 @@ +#! Main CI Specification for DataHaven Repository + +name: CI + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + branches: [main] + +concurrency: + group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + # First Tier + build-operator: + uses: ./.github/workflows/task-build-operator.yml + secrets: inherit # For demonstrative purposes, we don't use any secrets yet. + ts-build: + uses: ./.github/workflows/task-ts-build.yml + ts-lint: + uses: ./.github/workflows/task-ts-lint.yml + unit-tests: + uses: ./.github/workflows/task-rust-tests.yml + contract-tests: + uses: ./.github/workflows/task-foundry-tests.yml + rust-lint: + uses: ./.github/workflows/task-rust-lint.yml + + # Second Tier + e2e-tests: + needs: [build-operator] + uses: ./.github/workflows/task-e2e.yml + with: + binary-hash: ${{ needs.build-operator.outputs.binary-hash }} diff --git a/.github/workflows/actions/setup-env/action.yml b/.github/workflows/actions/setup-env/action.yml new file mode 100644 index 00000000..7e3bad1e --- /dev/null +++ b/.github/workflows/actions/setup-env/action.yml @@ -0,0 +1,59 @@ +name: "Setup Rust Environment" +description: "Creates a Rust environment with the specified toolchain, cache, and dependencies" + +inputs: + cache-key: + description: "Cache key used to retrieve built data. Usually matches the profile of the build" + required: false + default: "cache" + + cache-targets: + description: "Whether to cache targets" + required: false + default: "true" + + + +runs: + using: "composite" + steps: + - name: Set Rust version + shell: bash + run: | + echo "BUILD_RUST_VERSION=$(rustc --version)" >> $GITHUB_ENV + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.9 + + - name: Set Rust caching env vars + if: github.event_name != 'release' && github.event_name != 'workflow_dispatch' + shell: bash + run: | + echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV + sccache --show-stats + + - name: Cache cargo + target + sccache2 + uses: Swatinem/rust-cache@v2 + with: + cache-targets: true + cache-all-crates: true + shared-key: ${{ runner.os }}-${{inputs.cache-key}}-${{ hashFiles('operator/rust-toolchain.toml') }}-${{ hashFiles('operator/Cargo.lock') }} + workspaces: | + operator -> target + cache-on-failure: true + cache-bin: true + cache-directories: | + ~/.cache/sccache + ~/.cargo/registry + ~/.cargo/git + + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - uses: rui314/setup-mold@v1 + - name: Install libpq-dev + shell: bash + run: sudo apt-get update && sudo apt-get install -y libpq-dev libclang-dev + - name: Install Protoc + uses: arduino/setup-protoc@v3 diff --git a/.github/workflows/foundry-tests.yml b/.github/workflows/foundry-tests.yml deleted file mode 100644 index 6d5b9f4c..00000000 --- a/.github/workflows/foundry-tests.yml +++ /dev/null @@ -1,55 +0,0 @@ -# Foundry Tests: CI for Foundry components (smart contracts for EigenLayer and Snowbridge interaction) -# -# Overview: -# 1. All Foundry Tests: Executes the full suite of Foundry tests found within the `./contracts` directory - -name: Foundry AVS Smart Contract Tests - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - -env: - FOUNDRY_PROFILE: ci - -jobs: - test: - strategy: - fail-fast: false - matrix: - partition: [1] - - name: Foundry Tests - runs-on: ubuntu-latest - defaults: - run: - working-directory: contracts - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - - name: Show Forge version - run: | - forge --version - - - name: Run Forge fmt - run: | - forge fmt --check - id: fmt - - - name: Run Forge build - run: | - forge build --sizes - id: build - - - name: Run Forge tests - run: | - forge test -vvv - id: test diff --git a/.github/workflows/rust-lint.yml b/.github/workflows/rust-lint.yml deleted file mode 100644 index 171296cd..00000000 --- a/.github/workflows/rust-lint.yml +++ /dev/null @@ -1,129 +0,0 @@ -# Lint and Format: CI for Rust components (DataHaven runtime and node Rust tests) -# -# Overview: -# 1. Check Rust Format: Check that the Rust code is formatted correctly -# 2. Check Rust Lint: Check that the Rust code is linted correctly - -name: Lint and Format - -on: - pull_request: - push: - branches: - - main - - perm-* - workflow_dispatch: - inputs: - pull_request: - description: set to pull_request number to execute on external pr - required: false - -env: - CARGO_TERM_COLOR: always - WORKING_DIR: operator - -jobs: - setup: - runs-on: ubuntu-latest - outputs: - node_changed: ${{ steps.node_check.outputs.changed }} - env: - SKIP_BUILD_LABEL_PRESENT: ${{ contains(github.event.pull_request.labels.*.name, 'skip-node-build') }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Check if Substrate Node needs rebuild - id: node_check - run: | - BASE_SHA="${{ github.event.pull_request.base.sha || github.event.before }}" - HEAD_SHA="${{ github.sha }}" - - if [[ "${{ env.SKIP_BUILD_LABEL_PRESENT }}" != "true" ]] && git diff --name-only $BASE_SHA $HEAD_SHA | grep -E '^operator/(client|node|pallets|runtime)/|^operator/Cargo\.toml$'; then - echo "changed=true" >> $GITHUB_OUTPUT - else - echo "Comparing changes from $BASE_SHA to $HEAD_SHA" - echo "changed=false" >> $GITHUB_OUTPUT - fi - - cargo-fmt: - needs: [ setup ] - if: needs.setup.outputs.node_changed == 'true' - name: "Check format with rustfmt" - runs-on: ubuntu-latest - defaults: - run: - working-directory: ${{ env.WORKING_DIR }} - steps: - - uses: actions/checkout@v4 - - - name: Cache dependencies - uses: Swatinem/rust-cache@v2 - - # ! If this action starts failing, it may be because of this, - # ! For now this is not needed, and makes the workflow slower - # - name: Install protoc - # run: | - # sudo apt-get update - # sudo apt-get install -y protobuf-compiler - # protoc --version - - - name: Run cargo fmt - run: cargo fmt --all -- --check - - check-rust-lint: - needs: [ setup ] - if: needs.setup.outputs.node_changed == 'true' - name: "Check lint with clippy" - runs-on: ubuntu-latest - defaults: - run: - working-directory: ${{ env.WORKING_DIR }} - steps: - - uses: actions/checkout@v4 - - - name: Install protoc - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler - protoc --version - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - - name: Cache dependencies - uses: Swatinem/rust-cache@v2 - - - name: Install libpq-dev - run: sudo apt-get update && sudo apt-get install -y libpq-dev - - - name: Install protoc - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler - protoc --version - - - name: Run cargo clippy - run: SKIP_WASM_BUILD=1 env -u RUSTFLAGS cargo clippy --features try-runtime,runtime-benchmarks --locked - env: - RUSTFLAGS: -D warnings - - check-cargo-sort: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ${{ env.WORKING_DIR }} - - steps: - - name: Check out the repository to the runner - uses: actions/checkout@v4 - - - name: Make script executable - run: chmod +x scripts/sort-cargo-deps.sh - - - name: Run the script on all Cargo.toml files - run: find . -name "Cargo.toml" -print0 | xargs -0 -n1 -I{} bash -c 'scripts/sort-cargo-deps.sh "{}" check' || exit 1 \ No newline at end of file diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml deleted file mode 100644 index aad17f18..00000000 --- a/.github/workflows/rust-tests.yml +++ /dev/null @@ -1,128 +0,0 @@ -# Rust Tests: CI for Rust components (DataHaven runtime and node Rust tests) -# -# Overview: -# 1. Prepare: This job handles the setup phase where the cargo nextest archive is created -# and uploaded to the workflow for use in the subsequent jobs -# 2. All Rust Tests: Executes the full suite of Rust tests across two partitions to -# to reduce total execution time. - -name: DataHave Operator Rust Tests - -on: - pull_request: - push: - branches: - - main - workflow_dispatch: - -jobs: - setup: - runs-on: ubuntu-latest - outputs: - node_changed: ${{ steps.node_check.outputs.changed }} - env: - SKIP_BUILD_LABEL_PRESENT: ${{ contains(github.event.pull_request.labels.*.name, 'skip-node-build') }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Check if Substrate Node needs rebuild - id: node_check - run: | - BASE_SHA="${{ github.event.pull_request.base.sha || github.event.before }}" - HEAD_SHA="${{ github.sha }}" - - if [[ "${{ env.SKIP_BUILD_LABEL_PRESENT }}" != "true" ]] && git diff --name-only $BASE_SHA $HEAD_SHA | grep -E '^operator/(client|node|pallets|runtime)/|^operator/Cargo\.toml$'; then - echo "changed=true" >> $GITHUB_OUTPUT - else - echo "Comparing changes from $BASE_SHA to $HEAD_SHA" - echo "changed=false" >> $GITHUB_OUTPUT - fi - - prepare: - needs: [setup] - if: needs.setup.outputs.node_changed == 'true' - name: Prepare artifacts for Rust tests - runs-on: ubuntu-latest - env: - SCCACHE_GHA_ENABLED: "true" - RUSTC_WRAPPER: "sccache" - CARGO_INCREMENTAL: "0" - CARGO_TERM_COLOR: always - defaults: - run: - working-directory: ./operator - steps: - - uses: actions/checkout@v4 - with: - # By default actions/checkout checks out a merge commit. Check out the PR head instead. - # https://github.com/actions/checkout#checkout-pull-request-head-commit-instead-of-merge-commit - ref: ${{ github.event.pull_request.head.sha }} - - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.9 - - uses: rui314/setup-mold@v1 - - name: Install nextest - uses: taiki-e/install-action@nextest - # Install libpq-dev - - name: Install libpq-dev - run: sudo apt-get update && sudo apt-get install -y libpq-dev - - name: Install Protoc - uses: arduino/setup-protoc@v3 - - name: Build and archive tests - run: cargo nextest archive --archive-file nextest-archive.tar.zst - - name: Upload archive to workflow - uses: actions/upload-artifact@v4 - with: - name: nextest-archive - path: ./operator/nextest-archive.tar.zst - - all-rust-tests: - needs: [setup, prepare] - if: needs.setup.outputs.node_changed == 'true' - name: Run all Operator Rust tests (/w partitioning) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - partition: [1, 2] - defaults: - run: - working-directory: ./operator - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Install nextest - uses: taiki-e/install-action@nextest - - name: Download archive - uses: actions/download-artifact@v4 - with: - name: nextest-archive - - name: Run Tests for All Projects! - run: | - ~/.cargo/bin/cargo-nextest nextest run \ - --archive-file ../nextest-archive.tar.zst \ - --partition count:${{ matrix.partition }}/2 - - tests-result-checker: - name: Check tests were successful - needs: [setup, all-rust-tests] - if: always() - runs-on: ubuntu-latest - steps: - - name: Validate test results - run: | - echo "node_changed: ${{ needs.setup.outputs.node_changed }}" - echo "matrix result: ${{ needs.all-rust-tests.result }}" - - if [ "${{ needs.setup.outputs.node_changed }}" == "true" ]; then - if [ "${{ needs.all-rust-tests.result }}" != "success" ]; then - echo "Rust tests failed or were cancelled" - exit 1 - else - echo "Rust tests passed" - fi - else - echo "No relevant changes — skipping rust tests" - fi diff --git a/.github/workflows/task-build-operator.yml b/.github/workflows/task-build-operator.yml new file mode 100644 index 00000000..abf20fd6 --- /dev/null +++ b/.github/workflows/task-build-operator.yml @@ -0,0 +1,57 @@ +# Build Operator: CI for building the operator binary + +name: DataHave Operator Rust Tests + +on: + workflow_dispatch: + workflow_call: + outputs: + binary-hash: + description: "The hash of the operator binary" + value: ${{ jobs.build-node.outputs.binary-hash }} + +jobs: + build-node: + outputs: + binary-hash: ${{ steps.hash-binary.outputs.datahaven-node-hash }} + name: Build operator binary + runs-on: ubuntu-latest + env: + RUSTC_WRAPPER: "sccache" + CARGO_INCREMENTAL: "0" + CARGO_TERM_COLOR: always + RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=mold" + defaults: + run: + working-directory: ./operator + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 1 + - uses: ./.github/workflows/actions/setup-env + with: + cache-key: CI + - name: Build node binary + run: cargo build --profile ci --locked + - name: Hash binary + id: hash-binary + run: | + TIMESTAMP=$(date +%s) + + BINARY_PATH=./target/ci/datahaven-node + HASH=$(echo "$TIMESTAMP" | cat - $BINARY_PATH | sha256sum | awk '{ print $1 }') + echo "datahaven-node-hash=$HASH" >> $GITHUB_OUTPUT + echo "Hash of the datahaven-node is: $HASH (with timestamp: $TIMESTAMP)" + + - name: Upload binary to workflow + uses: actions/upload-artifact@v4 + with: + name: datahaven-node-${{ steps.hash-binary.outputs.datahaven-node-hash }} + path: operator/target/ci/datahaven-node + retention-days: 1 + + - name: Build Stats + run: | + sccache --show-stats + diff --git a/.github/workflows/e2e.yml b/.github/workflows/task-e2e.yml similarity index 54% rename from .github/workflows/e2e.yml rename to .github/workflows/task-e2e.yml index 05c90d9b..a6a50279 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/task-e2e.yml @@ -8,15 +8,13 @@ name: E2E - Kurtosis Deploy and Verify on: - push: - branches: - - main - pull_request: workflow_dispatch: - -concurrency: - group: "tests-${{ github.head_ref }}" - cancel-in-progress: true + workflow_call: + inputs: + binary-hash: + description: "The hash of the operator binary" + required: true + type: string env: FOUNDRY_PROFILE: ci @@ -50,12 +48,31 @@ jobs: restore-keys: | ${{ runner.os }}-bun- - - uses: actions/cache@v4 + - name: Cache Foundry libraries + uses: actions/cache/restore@v4 with: - path: ~/.foundry/cache - key: ${{ runner.os }}-foundry-${{ hashFiles('**/foundry.toml') }} + path: ../contracts/lib + key: ${{ runner.os }}-foundry-libs-${{ hashFiles('.gitmodules') }} restore-keys: | - ${{ runner.os }}-foundry- + ${{ runner.os }}-foundry-libs- + + - name: Cache Foundry build artifacts + uses: actions/cache/restore@v4 + with: + path: | + ../contracts/out + ../contracts/cache + key: ${{ runner.os }}-foundry-build-${{ hashFiles('contracts/foundry.toml', 'contracts/**/*.sol') }} + restore-keys: | + ${{ runner.os }}-foundry-build- + + - name: Download operator binary + uses: actions/download-artifact@v4 + with: + name: datahaven-node-${{ inputs.binary-hash }} + path: operator/target/release/ + - run: chmod +x ../operator/target/release/datahaven-node + - run: ../operator/target/release/datahaven-node --help - run: bun install - run: bun start:e2e:ci diff --git a/.github/workflows/task-foundry-tests.yml b/.github/workflows/task-foundry-tests.yml new file mode 100644 index 00000000..41fffc94 --- /dev/null +++ b/.github/workflows/task-foundry-tests.yml @@ -0,0 +1,56 @@ +# Foundry Tests: CI for Foundry components (smart contracts for EigenLayer and Snowbridge interaction) +# +# Overview: +# 1. All Foundry Tests: Executes the full suite of Foundry tests found within the `./contracts` directory + +name: Foundry AVS Smart Contract Tests + +on: + workflow_dispatch: + workflow_call: + +env: + FOUNDRY_PROFILE: ci + +jobs: + test: + strategy: + fail-fast: false + matrix: + partition: [1] + + name: Foundry Tests + runs-on: ubuntu-latest + defaults: + run: + working-directory: contracts + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Cache Foundry libraries + uses: actions/cache@v4 + with: + path: contracts/lib + key: ${{ runner.os }}-foundry-libs-${{ hashFiles('contracts/.gitmodules') }} + restore-keys: | + ${{ runner.os }}-foundry-libs- + + - name: Cache Foundry build artifacts + uses: actions/cache@v4 + with: + path: | + contracts/out + contracts/cache + key: ${{ runner.os }}-foundry-build-${{ hashFiles('contracts/foundry.toml', 'contracts/**/*.sol') }} + restore-keys: | + ${{ runner.os }}-foundry-build- + + - run: forge --version + - run: forge fmt --check + - run: forge build --sizes + - run: forge test -vvv diff --git a/.github/workflows/task-rust-lint.yml b/.github/workflows/task-rust-lint.yml new file mode 100644 index 00000000..26779a4d --- /dev/null +++ b/.github/workflows/task-rust-lint.yml @@ -0,0 +1,72 @@ +# Lint and Format: CI for Rust components (DataHaven runtime and node Rust tests) +# +# Overview: +# 1. Check Rust Format: Check that the Rust code is formatted correctly +# 2. Check Rust Lint: Check that the Rust code is linted correctly + +name: Lint and Format + +on: + workflow_call: + workflow_dispatch: + inputs: + pull_request: + description: set to pull_request number to execute on external pr + required: false + +env: + CARGO_TERM_COLOR: always + WORKING_DIR: operator + RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=mold" + +jobs: + cargo-fmt: + name: "Check format with rustfmt" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ${{ env.WORKING_DIR }} + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/workflows/actions/setup-env + with: + cache-key: FMT + cache-targets: false + + - name: Run cargo fmt + run: cargo fmt --all -- --check + + check-rust-lint: + name: "Check lint with clippy" + runs-on: ubuntu-latest + defaults: + run: + working-directory: ${{ env.WORKING_DIR }} + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/workflows/actions/setup-env + with: + cache-key: LINT + + - name: Run cargo clippy + run: SKIP_WASM_BUILD=1 cargo clippy --features try-runtime,runtime-benchmarks --locked --release + + check-cargo-sort: + name: "Check Cargo sort" + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ${{ env.WORKING_DIR }} + + steps: + - name: Check out the repository to the runner + uses: actions/checkout@v4 + + - name: Make script executable + run: chmod +x scripts/sort-cargo-deps.sh + + - name: Run the script on all Cargo.toml files + run: find . -name "Cargo.toml" -print0 | xargs -0 -n1 -I{} bash -c 'scripts/sort-cargo-deps.sh "{}" check' || exit 1 diff --git a/.github/workflows/task-rust-tests.yml b/.github/workflows/task-rust-tests.yml new file mode 100644 index 00000000..3fd4e57c --- /dev/null +++ b/.github/workflows/task-rust-tests.yml @@ -0,0 +1,88 @@ +# Rust Tests: CI for Rust components (DataHaven runtime and node Rust tests) +# +# Overview: +# 1. Prepare: This job handles the setup phase where the cargo nextest archive is created +# and uploaded to the workflow for use in the subsequent jobs +# 2. All Rust Tests: Executes the full suite of Rust tests across two partitions to +# to reduce total execution time. + +name: DataHave Operator Rust Tests + +on: + workflow_dispatch: + workflow_call: + +jobs: + + prepare: + name: Prepare artifacts for Rust tests + runs-on: ubuntu-latest + env: + RUSTC_WRAPPER: "sccache" + CARGO_INCREMENTAL: "0" + CARGO_TERM_COLOR: always + RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=mold" + defaults: + run: + working-directory: ./operator + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + - uses: ./.github/workflows/actions/setup-env + with: + cache-key: "TEST" + - name: Install nextest + uses: taiki-e/install-action@nextest + - name: Build and archive tests + run: cargo nextest archive --archive-file nextest-archive.tar.zst + - name: Upload archive to workflow + uses: actions/upload-artifact@v4 + with: + name: nextest-archive + path: ./operator/nextest-archive.tar.zst + + all-rust-tests: + name: Run all Operator Rust tests (/w partitioning) + needs: [prepare] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + partition: [1, 2] + defaults: + run: + working-directory: ./operator + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Install nextest + uses: taiki-e/install-action@nextest + - name: Download archive + uses: actions/download-artifact@v4 + with: + name: nextest-archive + - name: Run Tests for All Projects! + run: | + ~/.cargo/bin/cargo-nextest nextest run \ + --archive-file ../nextest-archive.tar.zst \ + --partition count:${{ matrix.partition }}/2 + + tests-result-checker: + name: Check tests were successful + needs: [all-rust-tests] + runs-on: ubuntu-latest + steps: + - name: Validate test results + run: | + echo "matrix result: ${{ needs.all-rust-tests.result }}" + + if [ "${{ needs.all-rust-tests.result }}" != "success" ]; then + echo "Rust tests failed or were cancelled" + exit 1 + else + echo "Rust tests passed" + fi + diff --git a/.github/workflows/ts-build.yml b/.github/workflows/task-ts-build.yml similarity index 96% rename from .github/workflows/ts-build.yml rename to .github/workflows/task-ts-build.yml index 46bb0b73..6be6683c 100644 --- a/.github/workflows/ts-build.yml +++ b/.github/workflows/task-ts-build.yml @@ -1,11 +1,8 @@ name: TS Build on: - push: - branches: - - main - pull_request: workflow_dispatch: + workflow_call: jobs: generate-wagmi: diff --git a/.github/workflows/ts-lint.yml b/.github/workflows/task-ts-lint.yml similarity index 95% rename from .github/workflows/ts-lint.yml rename to .github/workflows/task-ts-lint.yml index a0a40b45..9420b225 100644 --- a/.github/workflows/ts-lint.yml +++ b/.github/workflows/task-ts-lint.yml @@ -1,11 +1,8 @@ name: TS Lint & Format on: - push: - branches: - - main - pull_request: workflow_dispatch: + workflow_call: jobs: typecheck: diff --git a/.github/workflows/rust-audit.yml b/.github/workflows/weekly-audit.yml similarity index 96% rename from .github/workflows/rust-audit.yml rename to .github/workflows/weekly-audit.yml index 87e8b89a..302ffaed 100644 --- a/.github/workflows/rust-audit.yml +++ b/.github/workflows/weekly-audit.yml @@ -6,6 +6,7 @@ name: Audit Rust dependencies on: + workflow_dispatch: schedule: - cron: '0 0 * * 0' push: diff --git a/contracts/src/DataHavenTest.sol b/contracts/src/DataHavenTest.sol new file mode 100644 index 00000000..701bbeed --- /dev/null +++ b/contracts/src/DataHavenTest.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0 +// This is a test contract for the DataHaven project +// It is deployed as pure bytecode in kurtosis +pragma solidity >=0.8.2 <0.9.0; + +contract DataHavenTest { + uint256 public number; + address public owner; + + constructor() { + number = 10; + owner = msg.sender; + } + + function decrement() external { + require(number > 0, "Number should be greater than 0"); + number = number - 1; + } + + function reset() external { + require(msg.sender == owner, "Only callable by owner!"); + number = 10; + } +} diff --git a/operator/Cargo.toml b/operator/Cargo.toml index b21337de..cd915465 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -206,3 +206,9 @@ fc-mapping-sync = { git = "https://github.com/polkadot-evm/frontier", branch = " fc-rpc = { git = "https://github.com/polkadot-evm/frontier", branch = "stable2412", default-features = false } fc-rpc-core = { git = "https://github.com/polkadot-evm/frontier", branch = "stable2412", default-features = false } fc-storage = { git = "https://github.com/polkadot-evm/frontier", branch = "stable2412", default-features = false } + +[profile.ci] +inherits = "release" +incremental = false +codegen-units = 16 +lto = false diff --git a/test/bun.lock b/test/bun.lock index d31bdce5..2cf75af1 100644 --- a/test/bun.lock +++ b/test/bun.lock @@ -23,6 +23,7 @@ "tiny-invariant": "^1.3.3", "viem": "^2.28.0", "wagmi": "^2.15.0", + "yaml": "^2.7.1", "zod": "^3.24.3", }, "devDependencies": { @@ -194,7 +195,7 @@ "@metamask/utils": ["@metamask/utils@8.5.0", "", { "dependencies": { "@ethereumjs/tx": "^4.2.0", "@metamask/superstruct": "^3.0.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.3", "@types/debug": "^4.1.7", "debug": "^4.3.4", "pony-cause": "^2.1.10", "semver": "^7.5.4", "uuid": "^9.0.1" } }, "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ=="], - "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], + "@noble/ciphers": ["@noble/ciphers@1.2.1", "", {}, "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA=="], "@noble/curves": ["@noble/curves@1.8.2", "", { "dependencies": { "@noble/hashes": "1.7.2" } }, "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g=="], @@ -932,6 +933,8 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], + "yargs": ["yargs@17.7.2", "", { "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" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], @@ -942,6 +945,8 @@ "zustand": ["zustand@5.0.0", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ=="], + "@coinbase/wallet-sdk/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@dotenvx/dotenvx/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -956,6 +961,8 @@ "@metamask/sdk-communication-layer/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "@metamask/utils/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@metamask/utils/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@reown/appkit/@walletconnect/types": ["@walletconnect/types@2.19.2", "", { "dependencies": { "@walletconnect/events": "1.0.1", "@walletconnect/heartbeat": "1.2.2", "@walletconnect/jsonrpc-types": "1.0.4", "@walletconnect/keyvaluestorage": "1.1.1", "@walletconnect/logger": "2.1.2", "events": "3.3.0" } }, "sha512-/LZWhkVCUN+fcTgQUxArxhn2R8DF+LSd/6Wh9FnpjeK/Sdupx1EPS8okWG6WPAqq2f404PRoNAfQytQ82Xdl3g=="], @@ -992,8 +999,6 @@ "@walletconnect/time/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], - "@walletconnect/utils/@noble/ciphers": ["@noble/ciphers@1.2.1", "", {}, "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA=="], - "@walletconnect/utils/@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], "@walletconnect/utils/@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], @@ -1016,6 +1021,8 @@ "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "eciesjs/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "engine.io-client/ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], @@ -1038,6 +1045,8 @@ "obj-multiplex/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], @@ -1060,6 +1069,8 @@ "@metamask/eth-json-rpc-provider/@metamask/json-rpc-engine/@metamask/utils": ["@metamask/utils@8.5.0", "", { "dependencies": { "@ethereumjs/tx": "^4.2.0", "@metamask/superstruct": "^3.0.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.3", "@types/debug": "^4.1.7", "debug": "^4.3.4", "pony-cause": "^2.1.10", "semver": "^7.5.4", "uuid": "^9.0.1" } }, "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ=="], + "@metamask/rpc-errors/@metamask/utils/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@metamask/rpc-errors/@metamask/utils/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/sign-client": ["@walletconnect/sign-client@2.19.2", "", { "dependencies": { "@walletconnect/core": "2.19.2", "@walletconnect/events": "1.0.1", "@walletconnect/heartbeat": "1.2.2", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/logger": "2.1.2", "@walletconnect/time": "1.0.2", "@walletconnect/types": "2.19.2", "@walletconnect/utils": "2.19.2", "events": "3.3.0" } }, "sha512-a/K5PRIFPCjfHq5xx3WYKHAAF8Ft2I1LtxloyibqiQOoUtNLfKgFB1r8sdMvXM7/PADNPe4iAw4uSE6PrARrfg=="], @@ -1128,12 +1139,12 @@ "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@metamask/eth-json-rpc-provider/@metamask/json-rpc-engine/@metamask/utils/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + "@metamask/eth-json-rpc-provider/@metamask/json-rpc-engine/@metamask/utils/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/sign-client/@walletconnect/core": ["@walletconnect/core@2.19.2", "", { "dependencies": { "@walletconnect/heartbeat": "1.2.2", "@walletconnect/jsonrpc-provider": "1.0.14", "@walletconnect/jsonrpc-types": "1.0.4", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/jsonrpc-ws-connection": "1.0.16", "@walletconnect/keyvaluestorage": "1.1.1", "@walletconnect/logger": "2.1.2", "@walletconnect/relay-api": "1.0.11", "@walletconnect/relay-auth": "1.1.0", "@walletconnect/safe-json": "1.0.2", "@walletconnect/time": "1.0.2", "@walletconnect/types": "2.19.2", "@walletconnect/utils": "2.19.2", "@walletconnect/window-getters": "1.0.1", "es-toolkit": "1.33.0", "events": "3.3.0", "uint8arrays": "3.1.0" } }, "sha512-iu0mgLj51AXcKpdNj8+4EdNNBd/mkNjLEhZn6UMc/r7BM9WbmpPMEydA39WeRLbdLO4kbpmq4wTbiskI1rg+HA=="], - "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/@noble/ciphers": ["@noble/ciphers@1.2.1", "", {}, "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA=="], - "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], @@ -1142,8 +1153,6 @@ "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/sign-client/@walletconnect/core": ["@walletconnect/core@2.19.2", "", { "dependencies": { "@walletconnect/heartbeat": "1.2.2", "@walletconnect/jsonrpc-provider": "1.0.14", "@walletconnect/jsonrpc-types": "1.0.4", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/jsonrpc-ws-connection": "1.0.16", "@walletconnect/keyvaluestorage": "1.1.1", "@walletconnect/logger": "2.1.2", "@walletconnect/relay-api": "1.0.11", "@walletconnect/relay-auth": "1.1.0", "@walletconnect/safe-json": "1.0.2", "@walletconnect/time": "1.0.2", "@walletconnect/types": "2.19.2", "@walletconnect/utils": "2.19.2", "@walletconnect/window-getters": "1.0.1", "es-toolkit": "1.33.0", "events": "3.3.0", "uint8arrays": "3.1.0" } }, "sha512-iu0mgLj51AXcKpdNj8+4EdNNBd/mkNjLEhZn6UMc/r7BM9WbmpPMEydA39WeRLbdLO4kbpmq4wTbiskI1rg+HA=="], - "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/@noble/ciphers": ["@noble/ciphers@1.2.1", "", {}, "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA=="], - "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], @@ -1152,8 +1161,6 @@ "@reown/appkit/@walletconnect/universal-provider/@walletconnect/sign-client/@walletconnect/core": ["@walletconnect/core@2.19.2", "", { "dependencies": { "@walletconnect/heartbeat": "1.2.2", "@walletconnect/jsonrpc-provider": "1.0.14", "@walletconnect/jsonrpc-types": "1.0.4", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/jsonrpc-ws-connection": "1.0.16", "@walletconnect/keyvaluestorage": "1.1.1", "@walletconnect/logger": "2.1.2", "@walletconnect/relay-api": "1.0.11", "@walletconnect/relay-auth": "1.1.0", "@walletconnect/safe-json": "1.0.2", "@walletconnect/time": "1.0.2", "@walletconnect/types": "2.19.2", "@walletconnect/utils": "2.19.2", "@walletconnect/window-getters": "1.0.1", "es-toolkit": "1.33.0", "events": "3.3.0", "uint8arrays": "3.1.0" } }, "sha512-iu0mgLj51AXcKpdNj8+4EdNNBd/mkNjLEhZn6UMc/r7BM9WbmpPMEydA39WeRLbdLO4kbpmq4wTbiskI1rg+HA=="], - "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/@noble/ciphers": ["@noble/ciphers@1.2.1", "", {}, "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA=="], - "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], @@ -1162,7 +1169,7 @@ "@walletconnect/utils/viem/ox/@noble/curves": ["@noble/curves@1.8.2", "", { "dependencies": { "@noble/hashes": "1.7.2" } }, "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g=="], - "@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + "@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], "qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -1184,20 +1191,28 @@ "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + "@walletconnect/utils/viem/ox/@noble/curves/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + "qrcode/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "qrcode/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/curves": ["@noble/curves@1.8.2", "", { "dependencies": { "@noble/hashes": "1.7.2" } }, "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g=="], - "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/curves": ["@noble/curves@1.8.2", "", { "dependencies": { "@noble/hashes": "1.7.2" } }, "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g=="], - "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/curves": ["@noble/curves@1.8.2", "", { "dependencies": { "@noble/hashes": "1.7.2" } }, "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g=="], - "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/curves/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + + "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/curves/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], + + "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/viem/ox/@noble/curves/@noble/hashes": ["@noble/hashes@1.7.2", "", {}, "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ=="], } } diff --git a/test/cli/handlers/launch/index.ts b/test/cli/handlers/launch/index.ts index a70a173d..f7e1267c 100644 --- a/test/cli/handlers/launch/index.ts +++ b/test/cli/handlers/launch/index.ts @@ -1,6 +1,5 @@ import type { Command } from "@commander-js/extra-typings"; import { deployContracts } from "scripts/deploy-contracts"; -import { launchKurtosis } from "scripts/launch-kurtosis"; import sendTxn from "scripts/send-txn"; import invariant from "tiny-invariant"; import { @@ -12,6 +11,7 @@ import { } from "utils"; import { checkDependencies } from "./checks"; import { performDatahavenOperations } from "./datahaven"; +import { launchKurtosis } from "./kurtosis"; import { LaunchedNetwork } from "./launchedNetwork"; import { performRelayerOperations } from "./relayer"; import { performSummaryOperations } from "./summary"; @@ -28,13 +28,16 @@ export interface LaunchOptions { relayer?: boolean; relayerBinPath?: string; skipCleaning?: boolean; + alwaysClean?: boolean; datahavenBinPath?: string; datahaven?: boolean; + kurtosisNetworkArgs?: string; + slotTime?: number; } export const BASE_SERVICES = [ "cl-1-lighthouse-reth", - "cl-1-lighthouse-reth", + "cl-2-lighthouse-reth", "el-1-reth-lighthouse", "el-2-reth-lighthouse", "dora" @@ -53,11 +56,7 @@ const launchFunction = async (options: LaunchOptions, launchedNetwork: LaunchedN await checkDependencies(); logger.trace("Launching Kurtosis enclave"); - await launchKurtosis({ - launchKurtosis: options.launchKurtosis, - blockscout: options.blockscout, - skipCleaning: options.skipCleaning - }); + await launchKurtosis(options); logger.trace("Kurtosis enclave launched"); logger.trace("Send test transaction"); @@ -116,7 +115,9 @@ const launchFunction = async (options: LaunchOptions, launchedNetwork: LaunchedN printDivider(); performSummaryOperations(options, launchedNetwork); - logger.debug("Launch function completed successfully"); + const fullEnd = performance.now(); + const fullMinutes = ((fullEnd - timeStart) / (1000 * 60)).toFixed(1); + logger.info(`Launch function completed successfully in ${fullMinutes} minutes`); }; export const launch = async (options: LaunchOptions) => { @@ -132,14 +133,7 @@ export const launch = async (options: LaunchOptions) => { export const launchPreActionHook = ( thisCmd: Command<[], LaunchOptions & { [key: string]: any }> ) => { - const { - blockscout, - verified, - fundValidators, - setupValidators, - updateValidatorSet, - deployContracts - } = thisCmd.opts(); + const { blockscout, verified, fundValidators, setupValidators, deployContracts } = thisCmd.opts(); if (verified && !blockscout) { thisCmd.error("--verified requires --blockscout to be set"); } diff --git a/test/scripts/launch-kurtosis.ts b/test/cli/handlers/launch/kurtosis.ts similarity index 66% rename from test/scripts/launch-kurtosis.ts rename to test/cli/handlers/launch/kurtosis.ts index 6948842c..6eff88f7 100644 --- a/test/scripts/launch-kurtosis.ts +++ b/test/cli/handlers/launch/kurtosis.ts @@ -1,4 +1,6 @@ import { $ } from "bun"; +import type { LaunchOptions } from "cli/handlers"; +import invariant from "tiny-invariant"; import { type KurtosisService, confirmWithTimeout, @@ -7,27 +9,18 @@ import { printDivider, printHeader } from "utils"; +import { parse, stringify } from "yaml"; /** * Launches a Kurtosis Ethereum network enclave for testing. * - * This function checks if a Kurtosis network is already running. If it is: - * - With `launchKurtosis: false` - keeps the existing enclave - * - With `launchKurtosis: true` - cleans and relaunches the enclave - * - With `launchKurtosis: undefined` - prompts the user to decide whether to relaunch - * - * If no network is running, it launches a new one. - * * @param options - Configuration options - * @param options.launchKurtosis - Whether to forcibly launch Kurtosis (true), keep existing (false), or prompt user (undefined) - * @param options.blockscout - Whether to add Blockscout service (true/undefined) or not (false) - * @param options.skipCleaning - Whether to skip cleaning Kurtosis (true) or not (false) * @returns Object containing success status and Docker services information */ export const launchKurtosis = async ( - options: { launchKurtosis?: boolean; blockscout?: boolean; skipCleaning?: boolean } = {} + options: LaunchOptions = {} ): Promise> => { - if (await checkKurtosisRunning()) { + if ((await checkKurtosisRunning()) && !options.alwaysClean) { logger.info("â„šī¸ Kurtosis network is already running."); logger.trace("Checking if launchKurtosis option was set via flags"); @@ -73,10 +66,9 @@ export const launchKurtosis = async ( } logger.info("🚀 Starting Kurtosis enclave..."); - const configFile = - options.blockscout === true - ? "configs/kurtosis/minimal-with-bs.yaml" - : "configs/kurtosis/minimal.yaml"; + + const configFile = await modifyConfig(options, "configs/kurtosis/minimal.yaml"); + logger.info(`Using Kurtosis config file: ${configFile}`); const { stderr, stdout, exitCode } = @@ -107,3 +99,42 @@ const checkKurtosisRunning = async (): Promise => { const text = await $`kurtosis enclave ls | grep "datahaven-ethereum" | grep RUNNING`.text(); return text.length > 0; }; + +const modifyConfig = async (options: LaunchOptions, configFile: string) => { + const outputDir = "tmp/configs"; + logger.debug(`Ensuring output directory exists: ${outputDir}`); + await $`mkdir -p ${outputDir}`.quiet(); + + const file = Bun.file(configFile); + invariant(file, `❌ Config file ${configFile} not found`); + + const config = await file.text(); + logger.debug(`Parsing config at ${configFile}`); + logger.trace(config); + + const parsedConfig = parse(config); + + if (options.blockscout) { + parsedConfig.additional_services.push("blockscout"); + } + + if (options.slotTime) { + parsedConfig.network_params.seconds_per_slot = options.slotTime; + } + + if (options.kurtosisNetworkArgs) { + logger.debug(`Using custom Kurtosis network args: ${options.kurtosisNetworkArgs}`); + const args = options.kurtosisNetworkArgs.split(" "); + for (const arg of args) { + const [key, value] = arg.split("="); + parsedConfig.network_params[key] = value; + } + } + + logger.trace(parsedConfig); + const outputFile = `${outputDir}/modified-config.yaml`; + logger.debug(`Modified config saving to ${outputFile}`); + + await Bun.write(outputFile, stringify(parsedConfig)); + return outputFile; +}; diff --git a/test/cli/index.ts b/test/cli/index.ts index d3288e6a..0b9ae985 100644 --- a/test/cli/index.ts +++ b/test/cli/index.ts @@ -1,7 +1,16 @@ #!/usr/bin/env bun -import { Command } from "@commander-js/extra-typings"; +import { Command, InvalidArgumentError } from "@commander-js/extra-typings"; import { launch, launchPreActionHook } from "./handlers"; +// Function to parse integer +function parseIntValue(value: string): number { + const parsedValue = Number.parseInt(value, 10); + if (Number.isNaN(parsedValue)) { + throw new InvalidArgumentError("Not a number."); + } + return parsedValue; +} + // So far we only have the launch command // we can expand this to more commands in the future const program = new Command() @@ -14,13 +23,16 @@ const program = new Command() .option("-u, --update-validator-set", "Update validator set") .option("--no-update-validator-set", "Skip update validator set") .option("-b, --blockscout", "Enable Blockscout") + .option("--slot-time ", "Set slot time in seconds", parseIntValue) .option("--datahaven", "Enable Datahaven network to be launched") + .option("--kurtosis-network-args ", "CustomKurtosis network args") .option( "--datahaven-bin-path ", "Path to the datahaven binary", "../operator/target/release/datahaven-node" ) .option("-v, --verified", "Verify smart contracts with Blockscout") + .option("--always-clean", "Always clean Kurtosis", false) .option("-q, --skip-cleaning", "Skip cleaning Kurtosis") .option("-r, --relayer", "Enable Relayer") .option( diff --git a/test/configs/kurtosis/minimal-with-bs.yaml b/test/configs/kurtosis/minimal-with-bs.yaml deleted file mode 100644 index 81474637..00000000 --- a/test/configs/kurtosis/minimal-with-bs.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Ethereum Private Testnet Configuration -# This configuration file is used with the ethPandaOps Ethereum Package for Kurtosis -# (https://github.com/ethpandaops/ethereum-package) -# -# Purpose: Sets up a minimal Ethereum testnet with multiple execution and consensus clients -# Usage: kurtosis run github.com/ethpandaops/ethereum-package --args-file configs/kurtosis/minimal-with-bs.yaml - -participants: - - el_type: reth - cl_type: lighthouse - count: 2 - -additional_services: - - dora - - blockscout - -network_params: - preset: minimal - num_validator_keys_per_node: 128 - prefunded_accounts: '{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": {"balance": "10ETH"}, "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc": {"balance": "10ETH"}, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8": {"balance": "10ETH"},"0x976ea74026e726554db657fa54763abd0c3a0aa9": {"balance": "10ETH"}}' \ No newline at end of file diff --git a/test/configs/kurtosis/minimal.yaml b/test/configs/kurtosis/minimal.yaml index b435ebe2..4e15b696 100644 --- a/test/configs/kurtosis/minimal.yaml +++ b/test/configs/kurtosis/minimal.yaml @@ -1,10 +1,3 @@ -# Ethereum Private Testnet Configuration -# This configuration file is used with the ethPandaOps Ethereum Package for Kurtosis -# (https://github.com/ethpandaops/ethereum-package) -# -# Purpose: Sets up a minimal Ethereum testnet with multiple execution and consensus clients -# Usage: kurtosis run github.com/ethpandaops/ethereum-package --args-file configs/kurtosis/minimal.yaml - participants: - el_type: reth cl_type: lighthouse @@ -14,6 +7,25 @@ additional_services: - dora network_params: - preset: minimal + preset: mainnet + seconds_per_slot: 2 num_validator_keys_per_node: 128 - prefunded_accounts: '{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": {"balance": "10ETH"}, "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc": {"balance": "10ETH"}, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8": {"balance": "10ETH"},"0x976ea74026e726554db657fa54763abd0c3a0aa9": {"balance": "10ETH"}}' \ No newline at end of file + prefunded_accounts: '{ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": {"balance": "10ETH"}, + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc": {"balance": "10ETH"}, + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8": {"balance": "10ETH"}, + "0x976ea74026e726554db657fa54763abd0c3a0aa9": {"balance": "10ETH"} + }' + # Preloaded with DataHavenTest contract + additional_preloaded_contracts: | + { + "0x1111111111111111111111111111111111111111": { + "balance": "0ETH", + "code": "0x608060405234801561000f575f5ffd5b506004361061004a575f3560e01c80632baeceb71461004e5780638381f58a146100585780638da5cb5b14610073578063d826f88f1461009e575b5f5ffd5b6100566100a6565b005b6100605f5481565b6040519081526020015b60405180910390f35b600154610086906001600160a01b031681565b6040516001600160a01b03909116815260200161006a565b61005661010d565b5f5f54116100fb5760405162461bcd60e51b815260206004820152601f60248201527f4e756d6265722073686f756c642062652067726561746572207468616e20300060448201526064015b60405180910390fd5b60015f54610109919061016d565b5f55565b6001546001600160a01b031633146101675760405162461bcd60e51b815260206004820152601760248201527f4f6e6c792063616c6c61626c65206279206f776e65722100000000000000000060448201526064016100f2565b600a5f55565b8181038181111561018c57634e487b7160e01b5f52601160045260245ffd5b9291505056fea2646970667358221220ac5899491afd834afd223fd632497d1c0c7593961eda22f04c58db4b504999cf64736f6c634300081c0033", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000a", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266" + }, + "nonce": "1" + } + } diff --git a/test/package.json b/test/package.json index 57d1b8d2..33c4a7de 100644 --- a/test/package.json +++ b/test/package.json @@ -10,9 +10,8 @@ "build:docker:relayer": "bun -e \"import build from './scripts/snowbridge-relayer.ts'; build()\"", "generate:wagmi": "wagmi generate", "generate:snowbridge-cfgs": "bun -e \"import {generateSnowbridgeConfigs} from './scripts/gen-snowbridge-cfgs.ts'; await generateSnowbridgeConfigs()\"", - "start:e2e:verified": "bun cli --verified --blockscout --deploy-contracts", - "start:e2e:minimal": "bun cli", - "start:e2e:ci": "bun cli -d --setup-validators --update-validator-set --fund-validators", + "start:e2e:verified": "bun cli --verified --blockscout --deploy-contracts --setup-validators --update-validator-set --fund-validators --slot-time 1", + "start:e2e:ci": "bun cli -d --setup-validators --update-validator-set --fund-validators --always-clean --slot-time 2", "start:e2e:minrelayer": "bun cli --relayer -d --no-setup-validators --no-update-validator-set --no-fund-validators --datahaven", "stop:e2e": "pkill datahaven ; kurtosis enclave stop datahaven-ethereum && kurtosis clean && kurtosis engine stop && docker container prune -f", "stop:e2e:verified": "bun stop:e2e", @@ -48,6 +47,7 @@ "tiny-invariant": "^1.3.3", "viem": "^2.28.0", "wagmi": "^2.15.0", + "yaml": "^2.7.1", "zod": "^3.24.3" }, "trustedDependencies": [ diff --git a/test/resources/datahaven-integration-test-flow.md b/test/resources/datahaven-integration-test-flow.md index c9baa9b2..8f1ecdf4 100644 --- a/test/resources/datahaven-integration-test-flow.md +++ b/test/resources/datahaven-integration-test-flow.md @@ -34,15 +34,9 @@ The first step involves setting up the testing infrastructure using Kurtosis, a ```bash # Start the E2E CLI environment with the minimal configuration bun cli -# Alternative -bun start:e2e:minimal # Start the E2E CLI environment with Blockscout and verified contracts bun start:e2e:verified - -# Behind the scenes both commands run: -bun run scripts/launch-kurtosis.ts -# And then continue setting up the environment with the next steps. ``` ## 2. Ethereum-side Contract Deployment @@ -85,12 +79,14 @@ In this phase, we register validators as operators in EigenLayer and sync the va ### Steps 1. **Fund Validators with Tokens** + - Use `fund-validators.ts` script to fund validators with necessary tokens - Transfers 5% of creator's tokens to each validator - Transfers 1% of creator's ETH to validators with zero balance - Ensures validators have sufficient funds for operations 2. **Register Operators in EigenLayer** + - Use `setup-validators.ts` script to register validators - Deposits stake and registers for operator sets - Sets up the validator set in the Ethereum side diff --git a/test/scripts/deploy-contracts.ts b/test/scripts/deploy-contracts.ts index 09d2192f..39dddc9d 100644 --- a/test/scripts/deploy-contracts.ts +++ b/test/scripts/deploy-contracts.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; import invariant from "tiny-invariant"; -import { confirmWithTimeout, logger, printHeader } from "utils"; +import { confirmWithTimeout, logger, printHeader, runShellCommandWithLogger } from "utils"; interface DeployContractsOptions { rpcUrl: string; @@ -63,12 +63,7 @@ export const deployContracts = async (options: DeployContractsOptions): Promise< } logger.debug(buildStdout.toString()); - // Get forge path - const { stdout: forgePath } = await $`which forge`.quiet(); - const forgeExecutable = forgePath.toString().trim(); - - // Prepare deployment command - let deployCommand = `${forgeExecutable} script script/deploy/DeployLocal.s.sol --rpc-url ${rpcUrl} --color never -vv --no-rpc-rate-limit --non-interactive --broadcast`; + let deployCommand = `forge script script/deploy/DeployLocal.s.sol --rpc-url ${rpcUrl} --color never -vv --no-rpc-rate-limit --non-interactive --broadcast`; if (verified && blockscoutBackendUrl) { deployCommand += ` --verify --verifier blockscout --verifier-url ${blockscoutBackendUrl}/api/ --delay 0`; @@ -77,14 +72,8 @@ export const deployContracts = async (options: DeployContractsOptions): Promise< logger.info("âŗ Deploying contracts (this might take a few minutes)..."); - const { exitCode: deployExitCode, stderr: deployStderr } = await $`sh -c ${deployCommand}` - .cwd("../contracts") - .nothrow(); - - if (deployExitCode !== 0) { - logger.error(deployStderr.toString()); - throw Error("❌ Contracts have failed to deploy properly."); - } + // Using custom shell command to improve logging with forge's stdoutput + await runShellCommandWithLogger(deployCommand, { cwd: "../contracts" }); logger.success("Contracts deployed successfully"); return true; @@ -121,7 +110,6 @@ if (import.meta.main) { } } - // Check required parameters if (!options.rpcUrl) { console.error("Error: --rpc-url parameter is required"); process.exit(1); @@ -132,7 +120,6 @@ if (import.meta.main) { process.exit(1); } - // Run deployment deployContracts({ rpcUrl: options.rpcUrl, verified: options.verified, diff --git a/test/scripts/setup-validators.ts b/test/scripts/setup-validators.ts index b834e0b0..cfb1c5d2 100644 --- a/test/scripts/setup-validators.ts +++ b/test/scripts/setup-validators.ts @@ -1,9 +1,8 @@ import fs from "node:fs"; import path from "node:path"; -// Setup of validators for DataHaven import { $ } from "bun"; import invariant from "tiny-invariant"; -import { confirmWithTimeout, logger, printHeader } from "../utils/index"; +import { confirmWithTimeout, logger, printHeader, runShellCommandWithLogger } from "../utils/index"; interface SetupValidatorsOptions { rpcUrl: string; @@ -113,16 +112,11 @@ export const setupValidators = async (options: SetupValidatorsOptions): Promise< const validators = config.validators; logger.info(`Found ${validators.length} validators to register`); - // Get forge path - const { stdout: forgePath } = await $`which forge`.quiet(); - const forgeExecutable = forgePath.toString().trim(); - // Iterate through all validators to register them for (let i = 0; i < validators.length; i++) { const validator = validators[i]; logger.info(`Setting up validator ${i} (${validator.publicKey})`); - // Setting up the environment variables directly const env = { ...process.env, NETWORK: networkName, @@ -133,20 +127,10 @@ export const setupValidators = async (options: SetupValidatorsOptions): Promise< }; // Prepare command to register validator - const signupCommand = `${forgeExecutable} script script/transact/SignUpValidator.s.sol --rpc-url ${rpcUrl} --broadcast --no-rpc-rate-limit --non-interactive`; - + const signupCommand = `forge script script/transact/SignUpValidator.s.sol --rpc-url ${rpcUrl} --broadcast --no-rpc-rate-limit --non-interactive`; logger.debug(`Running command: ${signupCommand}`); - // Run with environment variables directly passed to the environment - const { exitCode, stderr } = await $`sh -c ${signupCommand}` - .cwd("../contracts") - .env(env) - .nothrow(); - - if (exitCode !== 0) { - logger.error(`Failed to register validator ${validator.publicKey}: ${stderr.toString()}`); - continue; - } + await runShellCommandWithLogger(signupCommand, { env, cwd: "../contracts" }); logger.success(`Successfully registered validator ${validator.publicKey}`); } diff --git a/test/utils/index.ts b/test/utils/index.ts index 7a91c9d7..a803da0f 100644 --- a/test/utils/index.ts +++ b/test/utils/index.ts @@ -1,10 +1,11 @@ export * from "./blockscout"; export * from "./constants"; +export * from "./contracts"; export * from "./docker"; export * from "./input"; -export * from "./logger"; -export * from "./rpc"; -export * from "./viem"; export * from "./kurtosis"; +export * from "./logger"; export * from "./parser"; -export * from "./contracts"; +export * from "./rpc"; +export * from "./shell"; +export * from "./viem"; diff --git a/test/utils/input.ts b/test/utils/input.ts index 2f26a222..66fa68d0 100644 --- a/test/utils/input.ts +++ b/test/utils/input.ts @@ -40,7 +40,7 @@ export const timeoutConfirm = createPrompt((cfg, clearInterval(id); done(cfg.default ?? true); } - }, 200); + }, 500); return () => clearInterval(id); }, []); @@ -64,7 +64,7 @@ export const timeoutConfirm = createPrompt((cfg, const main = `${prefix} ${theme.style.message(cfg.message, status)} \ ${defaultBadge} ${input}`; - const border = chalk.yellow("=".repeat(cfg.message.length + 40)); + const border = chalk.yellow("=".repeat(80)); const hint = theme.style.help( chalk.magenta( `⏱ Will default to ${chalk.bold(cfg.default ? "YES" : "NO")} in ${chalk.bold((left / 1000).toFixed(0))}s` @@ -77,12 +77,14 @@ ${main} ${border}`; }); -export const confirmWithTimeout = ( +export const confirmWithTimeout = async ( question: string, defaultValue: boolean, timeoutSeconds: number -) => - timeoutConfirm({ +) => { + await Bun.sleep(50); //debounce + + return timeoutConfirm({ message: question, default: defaultValue, timeoutMs: timeoutSeconds * 1000, @@ -93,3 +95,4 @@ export const confirmWithTimeout = ( } } }); +}; diff --git a/test/utils/shell.ts b/test/utils/shell.ts new file mode 100644 index 00000000..646634ec --- /dev/null +++ b/test/utils/shell.ts @@ -0,0 +1,57 @@ +import { existsSync } from "node:fs"; +import { spawn } from "bun"; +import { logger } from "./logger"; + +export const runShellCommandWithLogger = async ( + command: string, + options?: { cwd?: string; env?: object } +) => { + const { cwd = ".", env = {} } = options || {}; + + try { + if (!existsSync(cwd)) { + logger.error("❌ CWD does not exist:", cwd); + throw new Error("❌ CWD does not exist"); + } + + const proc = spawn(["sh", "-c", command], { + cwd, + stdout: "pipe", + stderr: "pipe", + env: { + ...process.env, + ...env + } + }); + + const stdoutReader = proc.stdout.getReader(); + const stderrReader = proc.stderr.getReader(); + + const readStream = async ( + reader: typeof stdoutReader | typeof stderrReader, + streamName: string + ) => { + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + const text = new TextDecoder().decode(value); + const trimmedText = text.trim(); + logger.info(trimmedText.includes("\n") ? `\n${trimmedText}` : trimmedText); + } + } catch (err) { + logger.error(`Error reading from ${streamName} stream:`, err); + } finally { + reader.releaseLock(); + } + }; + + Promise.all([readStream(stdoutReader, "stdout"), readStream(stderrReader, "stderr")]); + + await proc.exited; + } catch (err) { + logger.error("❌ Error running shell command:", command, "in", cwd); + logger.error(err); + throw err; + } +};