forked from ethereum/execution-specs
-
Notifications
You must be signed in to change notification settings - Fork 1
feat(consume): Consume tests with runloop monad #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pdobacz
wants to merge
21
commits into
forks/monad_nine
Choose a base branch
from
execute-with-eestnet
base: forks/monad_nine
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
5e477c5
Add MonadFixtureConsumer for consuming fixtures on the monad runloop
pdobacz 9b73972
docs: use uv for monad runloop testing setup
pdobacz b9faa85
perf: shrink triedb chunk sizing for monad fixture consumer
pdobacz 6d969a3
fix: tear down eest-runner container when pytest exits
pdobacz db3a35f
docs: note runloop parallelism is CPU-bound
pdobacz cf47e1f
feat: --monad-runloop fill flag for runloop-conformant block hashes
pdobacz d156e0f
Minor fixes to MONAD_RUNLOOP_TESTING.md
pdobacz 4816466
consumer: support MONAD_NEXT (rev 10) + page-encoded triedb
pdobacz 1f56129
consumer: schedule-aware triedb encoding for MIP-8 transition
pdobacz e01e1ef
docs: MIP-8 dual-db fork-transition flow in MONAD_RUNLOOP_TESTING
pdobacz 476c732
fill: make --monad-runloop transition fills pass
pdobacz e9f7baa
ci: add monad_runloop fixture release feature
pdobacz de3afcb
forks: drop unused PragueToMONAD_EIGHT transition fork
pdobacz 67a8275
ci: add consume_release workflow
pdobacz 8c1a23f
consume: resolve releases from monad-developers/execution-specs
pdobacz 01e2ca5
ci(consume): paste full consume.log in summary, fix fields, drop html
pdobacz d415ab4
ci(consume): run consume direct with -v
pdobacz 386f946
docs: condense MONAD_RUNLOOP_TESTING to running + overview
pdobacz c1e541d
ci(consume): route workflow inputs through env vars
pdobacz bca4994
docs: use ssh clone url for the private harness repo
pdobacz 797da40
fix(consume): resolve release asset for tests- prefixed tags
pdobacz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| # Manually run a filled fixtures release against a pinned monad stack via | ||
| # `consume direct` + the eest-runner runloop client. | ||
|
|
||
| name: Consume Release (monad runloop) | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| release: | ||
| description: "Fixtures source: release spec (e.g. monad_runloop@v1.1.0-rc1), a fixtures.tar.gz URL, or latest" | ||
| required: true | ||
| default: "monad_runloop@latest" | ||
| harness_ref: | ||
| description: "monad-eest-rust-harness ref (branch/tag/SHA)" | ||
| required: false | ||
| default: "execute-with-eestnet" | ||
| monad_bft_repo: | ||
| description: "Override monad-bft from this owner/repo (e.g. category-labs/monad-bft); empty = keep the harness's pinned commit" | ||
| required: false | ||
| default: "" | ||
| monad_bft_ref: | ||
| description: "Ref/SHA in monad_bft_repo (default branch if empty); applies only when monad_bft_repo is set" | ||
| required: false | ||
| default: "" | ||
| monad_execution_repo: | ||
| description: "Override monad-execution from this owner/repo; empty = keep the harness's pinned commit" | ||
| required: false | ||
| default: "" | ||
| monad_execution_ref: | ||
| description: "Ref/SHA in monad_execution_repo (default branch if empty); applies only when monad_execution_repo is set" | ||
| required: false | ||
| default: "" | ||
| filter: | ||
| description: "Optional -k expression to consume a subset" | ||
| required: false | ||
| default: "" | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| consume: | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 1440 # hosted runners still cap at 6h; raise the runner for a full release | ||
| steps: | ||
| - name: Validate inputs | ||
| shell: bash | ||
| env: | ||
| BFT_REPO: ${{ inputs.monad_bft_repo }} | ||
| BFT_REF: ${{ inputs.monad_bft_ref }} | ||
| EXEC_REPO: ${{ inputs.monad_execution_repo }} | ||
| EXEC_REF: ${{ inputs.monad_execution_ref }} | ||
| run: | | ||
| set -euo pipefail | ||
| fail=0 | ||
| if [ -n "$BFT_REF" ] && [ -z "$BFT_REPO" ]; then | ||
| echo "::error::monad_bft_ref is set but monad_bft_repo is empty; set monad_bft_repo to override monad-bft." | ||
| fail=1 | ||
| fi | ||
| if [ -n "$EXEC_REF" ] && [ -z "$EXEC_REPO" ]; then | ||
| echo "::error::monad_execution_ref is set but monad_execution_repo is empty; set monad_execution_repo to override monad-execution." | ||
| fail=1 | ||
| fi | ||
| [ "$fail" -eq 0 ] | ||
|
|
||
| - name: Checkout consumer (execution-specs) | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
|
|
||
| - name: Checkout monad-eest-rust-harness (build input) | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| repository: monad-exp/monad-eest-rust-harness | ||
| ref: ${{ inputs.harness_ref }} | ||
| path: monad-eest-rust-harness | ||
| submodules: recursive | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.MONAD_STACK_TOKEN }} | ||
|
|
||
| # Optional submodule overrides. Set a *_repo to pull that submodule from | ||
| # a different fork at *_ref (default branch if ref is empty); leave it | ||
| # empty to keep the harness's pinned commit. actions/checkout handles | ||
| # auth, so no manual credential setup is needed. The monad-bft override | ||
| # recurses (so monad-execution lands at the new bft's pin); the | ||
| # monad-execution override runs after, so it is not reset. | ||
| - name: Override monad-bft | ||
| if: ${{ inputs.monad_bft_repo != '' }} | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| repository: ${{ inputs.monad_bft_repo }} | ||
| ref: ${{ inputs.monad_bft_ref }} | ||
| path: monad-eest-rust-harness/monad-bft | ||
| submodules: recursive | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.MONAD_STACK_TOKEN }} | ||
|
|
||
| - name: Override monad-execution | ||
| if: ${{ inputs.monad_execution_repo != '' }} | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| repository: ${{ inputs.monad_execution_repo }} | ||
| ref: ${{ inputs.monad_execution_ref }} | ||
| path: monad-eest-rust-harness/monad-bft/monad-execution | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.MONAD_STACK_TOKEN }} | ||
|
|
||
| - name: Log pinned stack | ||
| id: stack | ||
| working-directory: monad-eest-rust-harness | ||
| shell: bash | ||
| run: | | ||
| bft=$(git -C monad-bft rev-parse HEAD) | ||
| exec=$(git -C monad-bft/monad-execution rev-parse HEAD) | ||
| echo "monad-bft: $bft" | ||
| echo "monad-execution: $exec" | ||
| echo "bft=$bft" >> "$GITHUB_OUTPUT" | ||
| echo "exec=$exec" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Build the monad-builder image | ||
| working-directory: monad-eest-rust-harness | ||
| shell: bash | ||
| run: docker build -t monad-builder:latest - < monad-bft/docker/builder/Dockerfile | ||
|
|
||
| - name: Build the eest-runner stack (build.sh) | ||
| working-directory: monad-eest-rust-harness | ||
| shell: bash | ||
| run: ./build.sh | ||
|
|
||
| - name: Setup uv | ||
| uses: ./.github/actions/setup-uv | ||
|
|
||
| - name: Install EEST | ||
| shell: bash | ||
| run: uv sync --no-progress | ||
|
|
||
| - name: Provision hugepages | ||
| shell: bash | ||
| run: sudo sysctl -w vm.nr_hugepages=3072 | ||
|
|
||
| - name: Consume release | ||
| shell: bash | ||
| env: | ||
| RELEASE: ${{ inputs.release }} | ||
| FILTER: ${{ inputs.filter }} | ||
| run: | | ||
| set -o pipefail | ||
| FILTER_ARG=() | ||
| if [ -n "$FILTER" ]; then | ||
| FILTER_ARG=(-k "$FILTER") | ||
| fi | ||
| uv run consume direct \ | ||
| --input "$RELEASE" \ | ||
| --bin "$GITHUB_WORKSPACE/monad-eest-rust-harness/bin/eest-runner" \ | ||
| "${FILTER_ARG[@]}" -v | tee consume.log | ||
|
pdobacz marked this conversation as resolved.
|
||
|
|
||
| - name: Summary | ||
| if: always() | ||
| shell: bash | ||
| env: | ||
| RELEASE: ${{ inputs.release }} | ||
| FILTER: ${{ inputs.filter || '(none)' }} | ||
| HARNESS_REF: ${{ inputs.harness_ref }} | ||
| BFT_SHA: ${{ steps.stack.outputs.bft }} | ||
| EXEC_SHA: ${{ steps.stack.outputs.exec }} | ||
| run: | | ||
| { | ||
| echo "## consume \`${RELEASE:-(unknown)}\`" | ||
| echo "" | ||
| echo "| input | value |" | ||
| echo "|---|---|" | ||
| echo "| harness_ref | \`${HARNESS_REF}\` |" | ||
| echo "| monad-bft | \`${BFT_SHA:-(unknown)}\` |" | ||
| echo "| monad-execution | \`${EXEC_SHA:-(unknown)}\` |" | ||
| echo "| filter | \`${FILTER:-(none)}\` |" | ||
| echo "" | ||
| echo '```' | ||
| cat consume.log 2>/dev/null || echo "(no consume.log produced)" | ||
| echo '```' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| - name: Upload log | ||
| if: always() | ||
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | ||
| with: | ||
| name: consume-report | ||
| path: consume.log | ||
| if-no-files-found: warn | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| # Running EEST fixtures on the monad runloop | ||
|
|
||
| Executes blockchain fixtures (including those generated from state | ||
| tests) against the monad execution client's `runloop`, using the | ||
| consensus ledger directory as the block source. Each `postState` | ||
| account is compared (balance/nonce/code/storage) against the | ||
| executed result. | ||
|
|
||
| ## Repos | ||
|
|
||
| | Repo / branch | Role | | ||
| |---|---| | ||
| | `monad-exp/monad-eest-rust-harness` @ `execute-with-eestnet` | `eest-runner` harness: builds consensus blocks from a fixture and runs them on the runloop | | ||
| | `monad-bft` @ `execute-with-eestnet` (submodule of the above) | consensus block types + ledger writer; pins monad-execution below | | ||
| | `monad` @ `execute-with-eestnet` (submodule of monad-bft) | execution client with the `EestNet` chain (id 30143, per-fixture revision schedule, runtime genesis) and the extended `monad_runloop_*` FFI | | ||
| | this repo @ `execute-with-eestnet` | `MonadFixtureConsumer` (`packages/testing/.../client_clis/clis/monad.py`) wired into `consume direct` | | ||
|
|
||
| ## One-time setup | ||
|
|
||
| Requirements: docker, ~10 GB disk for the builder image and build | ||
| artifacts, ~6 GB RAM for hugepages. | ||
|
|
||
| ```sh | ||
| git clone --branch execute-with-eestnet \ | ||
| git@github.com:monad-exp/monad-eest-rust-harness.git | ||
| cd monad-eest-rust-harness | ||
| git submodule update --init --recursive | ||
|
|
||
| # Toolchain image (gcc-15 + rust), from monad-bft's Dockerfile: | ||
| curl -fsSL https://raw.githubusercontent.com/category-labs/monad-bft/master/docker/builder/Dockerfile \ | ||
| | docker build -t monad-builder:latest - | ||
|
|
||
| ./build.sh # builds + syncs binaries/libs into install/ | ||
| bin/eest-runner --version | ||
| ``` | ||
|
|
||
| In this repo: | ||
|
|
||
| ```sh | ||
| uv sync | ||
| ``` | ||
|
|
||
| Always rebuild with `./build.sh`; it syncs `libmonad_execution.so` | ||
| alongside the binary (copying only the binary leaves a stale library | ||
| that fails silently). | ||
|
|
||
| ## Fill + consume | ||
|
|
||
| ```sh | ||
| uv run fill --clean -m blockchain_test <test paths...> \ | ||
| --fork MONAD_NINE --chain-id 30143 --monad-runloop \ | ||
| --output ../fixtures_eestnet | ||
|
|
||
| uv run consume direct --input ../fixtures_eestnet \ | ||
| --bin ../monad-eest-rust-harness/bin/eest-runner | ||
| ``` | ||
|
|
||
| - `--monad-runloop` stamps monad blocks with the consensus-derived | ||
| header fields the runloop produces (prev_randao from the round-0 BLS | ||
| signature, 32-byte extra_data, the proposal gas limit, zero | ||
| requests_hash). | ||
| - `--chain-id 30143` is EestNet's chain id. | ||
| - Block timestamps need no special handling: the consumer derives the | ||
| monad revision schedule from the fixture's `network` | ||
| (`FORK_REVISION_SCHEDULES` in `clis/monad.py`) and the harness | ||
| injects it into the chain. | ||
| - `consume` parallelism is CPU-bound: one runloop peaks near 4 cores (~386% CPU | ||
| across ~14 threads), so budget ~5 vCPUs per worker (`-n N` needs | ||
| roughly `5 * N` cores). | ||
|
|
||
| ## Behavior and known limits | ||
|
|
||
| - Fixtures containing expected-invalid blocks are skipped: the ledger | ||
| machine validates blocks at proposal time and cannot write invalid | ||
| ones. | ||
| - The runloop runs in a privileged container (io_uring) and needs | ||
| `vm.nr_hugepages >= 2048` on the host; the `bin/` wrappers | ||
| re-provision this automatically (it resets on host reboot). | ||
|
|
||
| ## Fork-transition flow (MIP-8 dual-db) | ||
|
|
||
| MONAD_NINE storage is slot-encoded; MONAD_NEXT (MIP-8) is | ||
| page-encoded. A transition fixture (e.g. | ||
| `MONAD_NINEToMONAD_NEXTAtTime15k`) crosses that boundary mid-run, so | ||
| the run keeps both encodings live in one triedb: a slot-encoded | ||
| primary timeline and a page-encoded secondary timeline. | ||
|
|
||
| - **Consumer** (`clis/monad.py`): loads the fixture, maps its | ||
| `network` to a `(revision, from_timestamp)` schedule, skips | ||
| `expectException` blocks, and provisions the db. Revisions spanning | ||
| the fork activate the secondary timeline via `monad-mpt | ||
| --activate-secondary`. | ||
| - **Harness** (`eest-runner`): fakes consensus with a Ledger | ||
| simulator (`propose` then `finalize` per block) that writes BFT | ||
| headers/bodies to `--ledger-dir` — the same artifacts monad-bft | ||
| persists on a real network — then drives the runloop over the FFI | ||
| and writes `output.json`. | ||
| - **Execution** (`runloop_monad`): reads each block from the ledger | ||
| dir and dispatches on `get_monad_revision(timestamp)` via | ||
| `SWITCH_MONAD_TRAITS` — block 1 runs MONAD_NINE, block 2 MONAD_NEXT. | ||
| - **Dual-write** (`commit_block`): both timelines get the same | ||
| `StateDeltas` every block. Slot is canonical before the fork, page | ||
| after; the canonical root flips to page at the first MONAD_NEXT | ||
| block, matching the fixture's final `blockHeader.stateRoot`. | ||
| - **Assertions**: `state_root` and every `postState` account | ||
| (balance, nonce, code, per-slot storage) match `output`, with no | ||
| unexpected non-zero slots. | ||
|
|
||
| ### Correspondence to production | ||
|
|
||
| eest-runner runs the same execution core as a production node, with | ||
| two differences: the block source and the revision schedule. | ||
|
|
||
| | Concern | eest-runner | Production node | | ||
| |---|---|---| | ||
| | Block source | Ledger sim writes headers/bodies to `--ledger-dir` | `monad-bft` `MonadBlockFileLedger` persists consensus-committed blocks to the ledger dir | | ||
| | Revision schedule | runtime, from fixture (`EestNet::get_monad_revision`) | compiled-in timestamps (`MonadMainnet::get_monad_revision`) | | ||
| | Execution loop | `runloop_monad` | the same `runloop_monad` | | ||
| | Per-block dispatch | `SWITCH_MONAD_TRAITS` on `get_monad_revision(timestamp)` | the same | | ||
| | Block execution | `execute_block<traits>` | the same | | ||
| | Commit | `commit_block<traits>` with dual-write | the same function; dual-write engages whenever a secondary timeline is active | | ||
| | State root / db | `mpt::Db` + `TrieDb`, optional secondary | the same | | ||
|
|
||
| eest-only shims: the C ABI in `runloop_interface_monad.cpp`, the `EestNet` | ||
| chain (runtime schedule), `MonadRunloopDbCache` / `set_balance` (balance | ||
| overrides, unused here), genesis loading from the fixture, and the Rust | ||
| Ledger simulator that fabricates the BFT blocks consensus would deliver. | ||
| The production fork migration uses the same dual-db mechanism: an operator | ||
| runs `monad-mpt --activate-secondary --state-machine monad` on the live | ||
| slot-encoded db before the fork, the node opens the secondary timeline and | ||
| dual-writes both encodings across the boundary, and the authoritative root | ||
| flips from slot to page at the first MONAD_NEXT block. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.