diff --git a/.github/configs/feature.yaml b/.github/configs/feature.yaml index e966f7f6b7..9f8cbfa260 100644 --- a/.github/configs/feature.yaml +++ b/.github/configs/feature.yaml @@ -9,3 +9,8 @@ monad: # (triggered by tarball output) fails to proceed on no tests processed # in 1st phase (exit code 5) fill-params: --suppress-no-test-exit-code -m blockchain_test --from=MONAD_EIGHT --until=MONAD_NEXT --chain-id=143 -k "not invalid_header" + +monad_runloop: + evm-type: eels + # Like `monad`, but `--monad-runloop` and eestnet chain id `30143` + fill-params: --suppress-no-test-exit-code -m blockchain_test --from=MONAD_EIGHT --until=MONAD_NEXT --chain-id=30143 --monad-runloop -k "not invalid_header" diff --git a/.github/workflows/consume_release.yaml b/.github/workflows/consume_release.yaml new file mode 100644 index 0000000000..0bfb477c57 --- /dev/null +++ b/.github/workflows/consume_release.yaml @@ -0,0 +1,207 @@ +# 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: "" + # TEMP: run on push to this branch to exercise the workflow before + # workflow_dispatch is available on the default branch. Revert this commit. + push: + branches: [execute-with-eestnet] + +permissions: + contents: write # TEMP: write is needed to see/download the draft release in this repo + +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 || 'execute-with-eestnet' }} # TEMP fallback for push + 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 + + # TEMP: the release fetcher can't see drafts (unauthenticated), so pull + # the draft asset here with the repo's own token, then consume locally. + - name: Download draft fixtures (TEMP) + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + gh release download "tests-monad_runloop@v1.1.0-rc2" \ + --repo "${{ github.repository }}" \ + --pattern 'fixtures_monad_runloop.tar.gz' --clobber + mkdir -p draft_fixtures + tar xzf fixtures_monad_runloop.tar.gz -C draft_fixtures + + - name: Consume release + id: consume + shell: bash + env: + # TEMP (push): consume the downloaded draft fixtures, mip8 fork-transition only. + RELEASE: draft_fixtures/fixtures + FILTER: ${{ inputs.filter || 'mip8_pageified_storage and test_fork_transition' }} + run: | + set -o pipefail + echo "release=$RELEASE" >> "$GITHUB_OUTPUT" + echo "filter=${FILTER:-(none)}" >> "$GITHUB_OUTPUT" + 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 + + - name: Summary + if: always() + shell: bash + env: + RELEASE: ${{ steps.consume.outputs.release }} + FILTER: ${{ steps.consume.outputs.filter }} + HARNESS_REF: ${{ inputs.harness_ref || 'execute-with-eestnet' }} + 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 diff --git a/MONAD_RUNLOOP_TESTING.md b/MONAD_RUNLOOP_TESTING.md new file mode 100644 index 0000000000..7b2e9c50a8 --- /dev/null +++ b/MONAD_RUNLOOP_TESTING.md @@ -0,0 +1,278 @@ +# Running EEST fixtures on the monad runloop + +Executes blockchain fixtures (including those generated from state +tests) against the production monad execution client, via its +`runloop` and the consensus ledger directory. The oracle is the +fixture's `postState`, compared account by account +(balance/nonce/code/storage). + +## Repos + +| Repo / branch | Role | +|---|---| +| `monad-exp/monad-eest-rust-harness` @ `execute-with-eestnet` | `eest-runner` harness: builds consensus blocks from a digested 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 \ + https://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 \ + --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) so filled block hashes and EIP-2935 history storage + match the runloop; without it those slots diverge and fail the + post-state compare. +- `--chain-id 30143` is EestNet's chain id; transactions must be + signed for it, so it is required at fill time. +- 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). On a small host (e.g. 4 vCPUs) run + sequentially — `-n` above 1 oversubscribes and runs slower. + +## 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) + +End-to-end trace of one `MONAD_NINEToMONAD_NEXTAtTime15k` blockchain-test +fixture, from JSON input through the consumer, the `eest-runner` harness, +the C++ FFI, the dual-db, and back to the consumer's assertions. The +execution core (everything in `monad-bft/monad-execution`) is the same +code a production node runs when consuming consensus blocks; only the +block source and the revision schedule differ. MONAD_NINE storage is +slot-encoded; MONAD_NEXT (MIP-8) is page-encoded, so a transition fixture +crosses an encoding boundary mid-run. + +### The fixture + +Declared in `tests/monad_ten/mip8_pageified_storage/test_fork_transition.py` +with `@pytest.mark.valid_at_transition_to("MONAD_NEXT")`; the transition +fork is `@transition_fork(to_fork=MONAD_NEXT, from_fork=MONAD_NINE, +at_timestamp=15_000)` (`forks/forks/transition.py`). The filler emits two +blocks straddling the boundary: block 1 at `timestamp=14_999` (executes +under MONAD_NINE, slot-encoded) and block 2 at `timestamp=15_000` +(MONAD_NEXT, page-encoded). The fixture carries +`"network": "MONAD_NINEToMONAD_NEXTAtTime15k"`, `genesisRLP`, `pre`, +`blocks[]`, and `postState`. The final block's `blockHeader.stateRoot` is +the page-encoded root. + +### Consumer: fixture to harness input + +`MonadFixtureConsumer.consume_fixture()` in `clis/monad.py` loads the +fixture, asserts the network is in `FORK_REVISION_SCHEDULES`, and skips +`expectException` blocks (the ledger cannot propose invalid blocks). The +schedule maps EEST fork names to `(revision, from_timestamp)` tuples: + +```python +"MONAD_NINE": [(9, 0)], +"MONAD_NEXT": [(10, 0)], +"MONAD_NINEToMONAD_NEXTAtTime15k": [(9, 0), (10, 15_000)], +``` + +It builds the harness input doc (`genesis_alloc`, `genesis_rlp`, +`revision_schedule`, digested `blocks`). + +### Dual-db provisioning (`_init_triedb`) + +The slot/page decision is made before the harness runs: + +```python +uses_page = any(rev >= 10 for rev in revisions) # MONAD_NEXT+ +uses_slot = any(rev < 10 for rev in revisions) # pre-MIP-8 +primary = "monad" if uses_page and not uses_slot else "ethereum" +``` + +- Pure MONAD_NINE -> `ethereum` (slot) primary, single timeline. +- Pure MONAD_NEXT -> `monad` (page) primary, single timeline. +- Transition -> `ethereum` primary **plus** a second invocation: + +```bash +monad-mpt --storage --create --chunk-capacity 26 \ + --root-offsets-chunk-count 2 --state-machine ethereum +monad-mpt --storage --activate-secondary --state-machine monad +``` + +`--activate-secondary` stamps the secondary timeline's +`state_machine_kind = monad`, shrinks the primary chunk ring, and hands +chunks to the secondary. Result on disk: one triedb file with a +slot-encoded **primary** timeline and an empty page-encoded **secondary** +timeline (`timeline_id` is `{primary=0, secondary=1}`). + +### Harness: simulate consensus, then execute + +`_run_harness` launches `eest-runner` (`--input/--output/--ledger-dir/ +--db`) with `PR_SET_PDEATHSIG` and a named container for cleanup. The +Rust harness (`rust-harness/eest-runner/src/main.rs`): + +1. Parses the input JSON. +2. Mimics consensus with a Ledger simulator (`propose` then `finalize` + per block), writing BFT headers/bodies into `--ledger-dir` — the same + artifacts monad-bft persists on a real network. +3. Builds the runloop over the FFI (`monad_runloop_new_eest` parses the + schedule into an `EestNet` chain). +4. Calls `runloop.run(n_blocks)`, then reads the root and post-state + back, writing `output.json`. + +The `monad-cxx` crate links dynamically against `libmonad_execution.so`. + +### FFI: opening the dual-db + +`MonadRunloopImpl` ctor in +`category/execution/runloop/runloop_interface_monad.cpp` registers the +state machines (`register_ethereum_state_machines` -> `OnDiskMachine` +slot; `register_monad_state_machines` -> `MonadOnDiskMachine` page) before +constructing `mpt::Db`, since the ctor rebuilds each timeline's machine +from its persisted kind. Then: + +```cpp +if (raw_db.timeline_active(mpt::timeline_id::secondary)) { + secondary_raw_db = raw_db.open_secondary_timeline(); + secondary_triedb.emplace(*secondary_raw_db); + MONAD_ASSERT(secondary_triedb->is_page_encoded(), ...); +} +``` + +Genesis is loaded into both timelines. For pure fixtures the secondary +stays empty and the paths below collapse to a single timeline. + +### Execution loop (`runloop_monad`) + +`monad_runloop_run` calls `runloop_monad(... secondary_db, is_first_run)`, +passing the secondary as a raw pointer. `runloop_monad` reads each block +from the ledger directory (the same files the harness, or production +consensus, wrote) and dispatches per block: + +```cpp +auto const rev = chain.get_monad_revision(header.timestamp); // 9 or 10 +SWITCH_MONAD_TRAITS(propose_block, ..., db, ..., secondary_db); +``` + +`SWITCH_MONAD_TRAITS` turns the runtime revision into a compile-time +`MonadTraits<...>`. Block 1 dispatches to MONAD_NINE, block 2 to +MONAD_NEXT — the fork boundary is purely `get_monad_revision(timestamp)`. +The secondary db is threaded through every state-mutating call +(`set_block_and_prefix`, `update_voted_metadata`, `finalize`, +`BlockState(db, vm, secondary_db)`). + +### Per-block dual-write (`commit_block`) + +`category/execution/monad/db/commit_block_migration.cpp`. Single timeline +(`secondary_db == nullptr`): `PageCommitBuilder` at MONAD_NEXT+, else +`CommitBuilder`. Dual timeline (the transition path): both timelines get +the same `StateDeltas` every block, only the encoding differs, and the +canonical db commits first so its live roots feed the other's +`populate_header`: + +```cpp +CommitBuilder builder (header.number); // slot -> primary +PageCommitBuilder builder2(header.number, *secondary_db); // page -> secondary +if constexpr (traits::monad_rev() >= MONAD_NEXT) { // post-fork: page canonical + correct_db = secondary_db; + secondary_db->commit(...); primary_db.commit(...); +} else { // pre-fork: slot canonical + correct_db = &primary_db; + primary_db.commit(...); secondary_db->commit(...); +} +``` + +The flip at `>= MONAD_NEXT` is the entire transition: pre-fork the slot +timeline is authoritative, post-fork the page timeline is. Each +`Db::commit` is two-stage (upsert deltas, populate header roots from +stage-1 state, upsert header + write root). + +Encoding difference: slot stores trie path `keccak(addr)|keccak(slot)` +-> raw 32-byte value. Page groups slots by `page_key = slot >> 7` (128 +slots/page), reads the whole page on first touch, merges deltas, and +stores one entry per page keyed `keccak(addr)|keccak(page_key)`; the page +hash is MIP-8's Induced-Subtree Merkle Commitment (BLAKE3 over a sparse +128-slot bitmap). A single static-encoding db cannot span the transition +because block 1 must emit slot entries and block 2 page entries against a +consistent account trie; the dual-db runs both encodings in lockstep so +each side stays internally consistent across the boundary. + +### Final root and assertions + +`monad_runloop_get_state_root` returns the secondary (page) root in +dual-db mode, else the single timeline's root. A transition fixture ends +in block 2 (MONAD_NEXT), so the canonical root is the page timeline's — +matching the fixture's final `blockHeader.stateRoot`. The consumer then +asserts (`clis/monad.py`): + +- `output["state_root"]` == `fixture["blocks"][-1]["blockHeader"]["stateRoot"]`. +- Every `postState` account's balance, nonce, code, and per-slot storage + matches `output["post_state"]`, with no unexpected non-zero slots. + +### Correspondence to production + +The execution core is identical; only the block source and schedule +differ. + +| 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` | the same | +| Commit | `commit_block` 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 — exactly what these +transition fixtures verify. diff --git a/packages/testing/pyproject.toml b/packages/testing/pyproject.toml index 6aacab447a..0dcf463a4e 100644 --- a/packages/testing/pyproject.toml +++ b/packages/testing/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "pytest-html>=4.1.0,<5", "pytest-metadata>=3,<4", "pytest-xdist>=3.3.1,<4", - "coincurve>=20.0.0,<21", + "coincurve>=20.0.0,<22", "trie>=3.1.0,<4", "semver>=3.0.1,<4", "pydantic>=2.12.3,<3", diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/consume/releases.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/consume/releases.py index 556fa7fe2c..0743c23a5b 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/consume/releases.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/consume/releases.py @@ -19,6 +19,7 @@ ) SUPPORTED_REPOS = [ + "monad-developers/execution-spec-tests", "ethereum/execution-spec-tests", "ethereum/execution-specs", "ethereum/tests", diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/filler.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/filler.py index fb7fac5f23..d446fbef52 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/filler.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/filler/filler.py @@ -70,7 +70,10 @@ ) from execution_testing.specs import BaseTest from execution_testing.specs.base import FillResult, OpMode -from execution_testing.test_types import EnvironmentDefaults +from execution_testing.test_types import ( + EnvironmentDefaults, + MonadRunloopDefaults, +) from execution_testing.test_types.chain_config_types import ( DEFAULT_CHAIN_ID, ChainConfigDefaults, @@ -573,6 +576,18 @@ def pytest_addoption(parser: pytest.Parser) -> None: f"(Default: {EnvironmentDefaults.gas_limit})" ), ) + test_group.addoption( + "--monad-runloop", + action="store_true", + dest="monad_runloop", + default=False, + help=( + "Stamp monad blocks with the consensus-derived header fields " + "the production runloop produces (prev_randao, extra_data, " + "gas_limit, requests_hash), so filled block hashes and EIP-2935 " + "history storage match the monad `eest-runner` consumer." + ), + ) test_group.addoption( "--generate-pre-alloc-groups", action="store_true", @@ -694,6 +709,17 @@ def pytest_configure(config: pytest.Config) -> None: if option_was_explicitly_set(config, "--block-gas-limit"): EnvironmentDefaults.gas_limit = config.getoption("block_gas_limit") + # Stamp monad blocks with runloop-conformant header fields if requested. + if config.getoption("monad_runloop"): + MonadRunloopDefaults.enabled = True + # The runloop builds every block at the monad proposal gas limit, so + # make it the default block gas limit too: tests that read + # env.gas_limit (the GASLIMIT opcode, tx-gas-vs-block-limit checks) + # then build expectations that match execution. An explicit + # --block-gas-limit still wins. + if not option_was_explicitly_set(config, "--block-gas-limit"): + EnvironmentDefaults.gas_limit = MonadRunloopDefaults.gas_limit + # Initialize fixture output configuration config.fixture_output = FixtureOutput.from_config( # type: ignore[attr-defined] config @@ -1827,6 +1853,11 @@ def pytest_collection_modifyitems( if marker.name == "fill": for mark in marker.args: item.add_marker(mark) + if marker.name == "monad_runloop" and config.getoption( + "monad_runloop", False + ): + for mark in marker.args: + item.add_marker(mark) if "yul" in item.fixturenames: # type: ignore item.add_marker(pytest.mark.yul_test) diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/shared/execute_fill.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/shared/execute_fill.py index d827d96927..2651faaac6 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/shared/execute_fill.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/shared/execute_fill.py @@ -188,6 +188,12 @@ def pytest_configure(config: pytest.Config) -> None: "markers", "execute: Markers to be added in execute mode only.", ) + config.addinivalue_line( + "markers", + "monad_runloop: Markers to be added only when filling with " + "--monad-runloop (e.g. skips for tests that need a test-controlled " + "block gas limit the runloop overrides).", + ) config.addinivalue_line( "markers", "exception_test: Negative tests that include an invalid block or " diff --git a/packages/testing/src/execution_testing/client_clis/__init__.py b/packages/testing/src/execution_testing/client_clis/__init__.py index 2a9e603a11..85ee33f52e 100644 --- a/packages/testing/src/execution_testing/client_clis/__init__.py +++ b/packages/testing/src/execution_testing/client_clis/__init__.py @@ -32,6 +32,7 @@ ) from .clis.execution_specs import ExecutionSpecsTransitionTool from .clis.geth import GethFixtureConsumer, GethTransitionTool +from .clis.monad import MonadFixtureConsumer from .clis.nethermind import Nethtest, NethtestFixtureConsumer from .clis.nimbus import NimbusTransitionTool from .ethereum_cli import CLINotFoundInPathError, UnknownCLIError @@ -74,6 +75,7 @@ "GethFixtureConsumer", "GethTransitionTool", "LazyAlloc", + "MonadFixtureConsumer", "Nethtest", "NethtestFixtureConsumer", "NimbusTransitionTool", diff --git a/packages/testing/src/execution_testing/client_clis/clis/monad.py b/packages/testing/src/execution_testing/client_clis/clis/monad.py new file mode 100644 index 0000000000..7fa29612b2 --- /dev/null +++ b/packages/testing/src/execution_testing/client_clis/clis/monad.py @@ -0,0 +1,394 @@ +""" +Monad `eest-runner` fixture consumer. + +Executes blockchain fixtures against the production monad runloop via +the `eest-runner` harness binary (built from monad-bft + monad +execution, EestNet chain). The fixture is digested into a simple input +document (genesis allocation + per-block timestamp/base fee/beneficiary +and raw signed transactions); the harness drives the consensus ledger +and the runloop, then emits the resulting post-state, which is compared +against the fixture's `postState`. +""" + +import ctypes +import json +import os +import re +import signal +import subprocess +import tempfile +import uuid +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple + +import pytest + +from execution_testing.fixtures import BlockchainFixture, FixtureFormat +from execution_testing.test_types import Transaction + +from ..fixture_consumer_tool import FixtureConsumerTool + +# Monad revision schedule per fixture `network`, as +# (monad_revision, activation timestamp) pairs. Non-transition +# fixtures run a single revision from genesis; transition fixtures +# activate at the EEST transition fork's boundary. +FORK_REVISION_SCHEDULES = { + "MONAD_EIGHT": [(8, 0)], + "MONAD_NINE": [(9, 0)], + "MONAD_NEXT": [(10, 0)], + "MONAD_EIGHTToMONAD_NINEAtTime15k": [(8, 0), (9, 15_000)], + "MONAD_NINEToMONAD_NEXTAtTime15k": [(9, 0), (10, 15_000)], +} + + +_LIBC: Optional[ctypes.CDLL] +try: + _LIBC = ctypes.CDLL("libc.so.6", use_errno=True) +except OSError: + _LIBC = None + + +def _set_pdeathsig() -> None: + """ + Have the kernel SIGTERM this child when its parent dies. + + Runs in the child between fork and exec. `PR_SET_PDEATHSIG` (1) + fires even if pytest is SIGKILLed, so the docker client receives a + signal it can forward to stop the container instead of leaving the + runloop orphaned. + """ + if _LIBC is not None: + _LIBC.prctl(1, signal.SIGTERM) + + +def _hex32(value: str) -> str: + """Normalize a hex quantity to a 0x-prefixed 64-nibble word.""" + return f"0x{int(value, 16):064x}" + + +def _digest_genesis_alloc(pre: Dict[str, Any]) -> Dict[str, Any]: + """Convert a fixture `pre` alloc into the harness genesis alloc.""" + alloc: Dict[str, Any] = {} + for address, account in pre.items(): + entry: Dict[str, Any] = { + "wei_balance": account.get("balance", "0x0"), + "nonce": account.get("nonce", "0x0"), + } + code = account.get("code", "0x") + if code and code != "0x": + entry["code"] = code + storage = { + _hex32(slot): _hex32(value) + for slot, value in account.get("storage", {}).items() + if int(value, 16) != 0 + } + if storage: + entry["storage"] = storage + alloc[address] = entry + return alloc + + +def _digest_blocks(fixture: Dict[str, Any]) -> list: + """Convert fixture blocks into the harness block list.""" + blocks = [] + for block in fixture["blocks"]: + header = block["blockHeader"] + txs = [] + for tx_json in block.get("transactions", []): + for auth in tx_json.get("authorizationList", []): + # Fixtures serialize both `v` and `yParity`; the + # Transaction model accepts only one of them. + if "v" in auth: + auth.pop("yParity", None) + tx = Transaction.model_validate(tx_json) + txs.append(tx.rlp().hex()) + blocks.append( + { + "timestamp": int(header["timestamp"], 16), + "base_fee": int(header["baseFeePerGas"], 16), + "beneficiary": header["coinbase"], + "txs": txs, + } + ) + return blocks + + +def _compare_account( + address: str, expected: Dict[str, Any], actual: Optional[Dict[str, Any]] +) -> list: + """Compare one expected post-state account; return mismatch strings.""" + mismatches = [] + if actual is None: + actual = {} + for field in ("balance", "nonce"): + expected_value = int(expected.get(field, "0x0"), 16) + actual_value = int(actual.get(field, "0x0"), 16) + if expected_value != actual_value: + mismatches.append( + f"{address}: {field} expected {hex(expected_value)}, " + f"got {hex(actual_value)}" + ) + expected_code = expected.get("code", "0x") or "0x" + actual_code = actual.get("code", "0x") or "0x" + if expected_code.lower() != actual_code.lower(): + mismatches.append( + f"{address}: code expected {expected_code}, got {actual_code}" + ) + actual_storage = { + int(slot, 16): int(value, 16) + for slot, value in actual.get("storage", {}).items() + } + expected_storage = { + int(slot, 16): int(value, 16) + for slot, value in expected.get("storage", {}).items() + } + for slot, expected_value in expected_storage.items(): + actual_value = actual_storage.get(slot, 0) + if expected_value != actual_value: + mismatches.append( + f"{address}: storage[{hex(slot)}] expected " + f"{hex(expected_value)}, got {hex(actual_value)}" + ) + for slot, actual_value in actual_storage.items(): + if slot not in expected_storage and actual_value != 0: + mismatches.append( + f"{address}: unexpected storage[{hex(slot)}] = " + f"{hex(actual_value)}" + ) + return mismatches + + +class MonadFixtureConsumer( + FixtureConsumerTool, + fixture_formats=[BlockchainFixture], +): + """Monad's `eest-runner` fixture consumer for blockchain tests.""" + + default_binary = Path("eest-runner") + detect_binary_pattern = re.compile(r"^eest-runner\b") + version_flag: str = "--version" + + def __init__( + self, + binary: Optional[Path] = None, + trace: bool = False, + ): + """Initialize the MonadFixtureConsumer.""" + super().__init__(binary=binary) + self.trace = trace + + def _init_triedb( + self, db_path: Path, schedule: List[Tuple[int, int]] + ) -> None: + """ + Create and format a fresh triedb file via `monad-mpt`. + + The storage encoding depends on the fixture's revision schedule. + MIP-8 (MONAD_NEXT, revision >= 10) uses a page-encoded triedb; older + revisions use slot encoding. A fixture that crosses the boundary + (e.g. a MONAD_NINE->MONAD_NEXT transition) needs both: a slot-encoded + primary plus an activated page-encoded secondary timeline, so the + runloop can dual-write across the fork. + """ + monad_mpt = self.binary.parent / "monad-mpt" + revisions = [revision for revision, _ in schedule] + uses_page = any(revision >= 10 for revision in revisions) + uses_slot = any(revision < 10 for revision in revisions) + + with open(db_path, "wb") as f: + f.truncate(2 * 1024**3) + + # Shrunk chunk capacity / history ring keep per-test time at ~2s + # (the production defaults dominate runtime). `monad` is the + # page-encoded state machine, `ethereum` the slot-encoded one. + primary = "monad" if uses_page and not uses_slot else "ethereum" + subprocess.run( + [ + str(monad_mpt), + "--storage", + str(db_path), + "--create", + "--chunk-capacity", + "26", + "--root-offsets-chunk-count", + "2", + "--state-machine", + primary, + ], + capture_output=True, + text=True, + check=True, + ) + + if uses_page and uses_slot: + # Transition fixture: add a page-encoded secondary timeline. + subprocess.run( + [ + str(monad_mpt), + "--storage", + str(db_path), + "--activate-secondary", + "--state-machine", + "monad", + ], + capture_output=True, + text=True, + check=True, + ) + + def _run_harness( + self, + input_path: Path, + output_path: Path, + ledger_dir: Path, + db_path: Path, + ) -> Tuple[str, str, int]: + """ + Run `eest-runner`, tearing down its container when we exit. + + A killed docker client does not stop its container, so a + dockerized runloop would otherwise keep spinning past pytest. + Two teardown paths cover this: an explicit `docker kill` on the + named container when this call is interrupted, and a parent-death + signal so the client is also stopped if pytest dies without + unwinding (SIGTERM/SIGHUP, or SIGKILL). + """ + container_name = f"eest-runner-{uuid.uuid4().hex}" + process = subprocess.Popen( + [ + str(self.binary), + "--input", + str(input_path), + "--output", + str(output_path), + "--ledger-dir", + str(ledger_dir), + "--db", + str(db_path), + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + start_new_session=True, + preexec_fn=_set_pdeathsig, + env={ + **os.environ, + "EEST_RUNNER_CONTAINER_NAME": container_name, + }, + ) + try: + stdout, stderr = process.communicate() + except BaseException: + try: + subprocess.run( + ["docker", "kill", container_name], + capture_output=True, + ) + except Exception: + pass + process.kill() + process.wait() + raise + return stdout, stderr, process.returncode + + def consume_fixture( + self, + fixture_format: FixtureFormat, + fixture_path: Path, + fixture_name: Optional[str] = None, + debug_output_path: Optional[Path] = None, + ) -> None: + """Execute a blockchain fixture on the monad runloop and verify.""" + assert fixture_format == BlockchainFixture + + with open(fixture_path) as f: + fixtures = json.load(f) + if fixture_name is None: + assert len(fixtures) == 1, "fixture_name required" + fixture_name = next(iter(fixtures)) + fixture = fixtures[fixture_name] + + network = fixture["network"] + assert network in FORK_REVISION_SCHEDULES, ( + f"no monad revision schedule for network {network}" + ) + if any("expectException" in block for block in fixture["blocks"]): + pytest.skip("invalid blocks cannot be proposed through the ledger") + input_doc = { + "genesis_alloc": _digest_genesis_alloc(fixture["pre"]), + "genesis_rlp": fixture["genesisRLP"], + "revision_schedule": [ + {"revision": revision, "from_timestamp": from_timestamp} + for revision, from_timestamp in FORK_REVISION_SCHEDULES[ + network + ] + ], + "blocks": _digest_blocks(fixture), + } + + with tempfile.TemporaryDirectory(prefix="eest-runner-") as tmp: + tmp_path = Path(tmp) + input_path = tmp_path / "input.json" + output_path = tmp_path / "output.json" + ledger_dir = tmp_path / "ledger" + ledger_dir.mkdir() + db_path = tmp_path / "triedb" + self._init_triedb(db_path, FORK_REVISION_SCHEDULES[network]) + + input_path.write_text(json.dumps(input_doc, indent=2)) + + stdout, stderr, returncode = self._run_harness( + input_path, output_path, ledger_dir, db_path + ) + + if debug_output_path is not None: + debug_output_path.mkdir(parents=True, exist_ok=True) + (debug_output_path / "input.json").write_text( + input_path.read_text() + ) + (debug_output_path / "stdout.txt").write_text(stdout) + (debug_output_path / "stderr.txt").write_text(stderr) + if output_path.exists(): + (debug_output_path / "output.json").write_text( + output_path.read_text() + ) + + if returncode != 0: + raise Exception( + f"eest-runner failed (exit {returncode}):\n" + f"stdout:\n{stdout}\nstderr:\n{stderr}" + ) + + output = json.loads(output_path.read_text()) + + actual_post = { + address.lower(): account + for address, account in output["post_state"].items() + } + post_state = fixture.get("postState") + assert post_state is not None, ( + "fixture has no postState (hash-only fixtures not supported)" + ) + + mismatches = [] + for address, expected in post_state.items(): + actual = actual_post.get(address.lower()) + mismatches.extend(_compare_account(address, expected, actual)) + + # Assert the final state root, the last executed block's root. Under + # monad's synchronous execution it equals the fixture's last block + # `stateRoot`; invalid-block fixtures are skipped above, so every + # block carries a header. + expected_state_root = fixture["blocks"][-1]["blockHeader"]["stateRoot"] + actual_state_root = output["state_root"] + if int(expected_state_root, 16) != int(actual_state_root, 16): + mismatches.append( + f"state_root expected {expected_state_root}, " + f"got {actual_state_root}" + ) + + if mismatches: + raise Exception( + "post-state mismatch on the monad runloop:\n" + + "\n".join(mismatches) + ) diff --git a/packages/testing/src/execution_testing/forks/__init__.py b/packages/testing/src/execution_testing/forks/__init__.py index 1041e1157a..a35793a41c 100644 --- a/packages/testing/src/execution_testing/forks/__init__.py +++ b/packages/testing/src/execution_testing/forks/__init__.py @@ -41,7 +41,6 @@ MONAD_NINEToMONAD_NEXTAtTime15k, OsakaToBPO1AtTime15k, ParisToShanghaiAtTime15k, - PragueToMONAD_EIGHTAtTime15k, PragueToOsakaAtTime15k, ShanghaiToCancunAtTime15k, ) @@ -122,7 +121,6 @@ "PragueToOsakaAtTime15k", "Osaka", "OsakaToBPO1AtTime15k", - "PragueToMONAD_EIGHTAtTime15k", "MONAD_EIGHT", "MONAD_EIGHTToMONAD_NINEAtTime15k", "MONAD_NINE", diff --git a/packages/testing/src/execution_testing/forks/forks/transition.py b/packages/testing/src/execution_testing/forks/forks/transition.py index 6500b48b78..e63edafc18 100644 --- a/packages/testing/src/execution_testing/forks/forks/transition.py +++ b/packages/testing/src/execution_testing/forks/forks/transition.py @@ -56,13 +56,6 @@ class PragueToOsakaAtTime15k(TransitionBaseClass): pass -@transition_fork(to_fork=MONAD_EIGHT, from_fork=Prague, at_timestamp=15_000) -class PragueToMONAD_EIGHTAtTime15k(TransitionBaseClass): # noqa: N801 - """Prague to MONAD_EIGHT transition at Timestamp 15k.""" - - pass - - @transition_fork( to_fork=MONAD_NINE, from_fork=MONAD_EIGHT, at_timestamp=15_000 ) diff --git a/packages/testing/src/execution_testing/specs/blockchain.py b/packages/testing/src/execution_testing/specs/blockchain.py index 853d10bb1a..d3c86e001e 100644 --- a/packages/testing/src/execution_testing/specs/blockchain.py +++ b/packages/testing/src/execution_testing/specs/blockchain.py @@ -83,10 +83,11 @@ FixtureTransactionReceipt, ) from execution_testing.fixtures.post_verifications import PostVerifications -from execution_testing.forks import Fork, TransitionFork +from execution_testing.forks import MONAD_EIGHT, Fork, TransitionFork from execution_testing.test_types import ( Alloc, Environment, + MonadRunloopDefaults, Removable, Requests, TestPhase, @@ -773,6 +774,22 @@ def make_genesis( ) -> Tuple[Alloc, FixtureBlock]: """Create a genesis block from the blockchain test definition.""" env = self.get_genesis_environment() + + # Keep the genesis gas limit at monad's proposal_gas_limit so the + # whole chain has a constant gas limit; otherwise the jump from the + # EEST default to block 1's stamped limit fails the EIP-1559 gas + # limit bound check during fill. Resolve the fork at genesis so + # transition forks are covered too. + genesis_fork = self.fork.fork_at( + block_number=env.number, timestamp=env.timestamp + ) + if ( + MonadRunloopDefaults.enabled + and isinstance(genesis_fork, type) + and issubclass(genesis_fork, MONAD_EIGHT) + ): + env = env.copy(gas_limit=MonadRunloopDefaults.gas_limit) + assert env.withdrawals is None or len(env.withdrawals) == 0, ( "withdrawals must be empty at genesis" ) @@ -825,6 +842,25 @@ def generate_block_data( block_number=env.number, timestamp=env.timestamp ) env = env.set_fork_requirements(fork) + + # When filling with --monad-runloop, monad blocks must carry the + # consensus-derived header fields the production runloop produces. + # gas_limit and prev_randao feed execution (gas, PREVRANDAO) and the + # EIP-2935 block-hash chain, so they are set on the env before t8n; + # extra_data and requests_hash are stamped onto the header below. + # Gate on the per-block resolved fork so transition forks (whose + # `self.fork` is the transition class) are covered too. + monad_runloop = ( + MonadRunloopDefaults.enabled + and isinstance(fork, type) + and issubclass(fork, MONAD_EIGHT) + ) + if monad_runloop: + env = env.copy( + gas_limit=MonadRunloopDefaults.gas_limit, + prev_randao=MonadRunloopDefaults.prev_randao, + ) + txs = [tx.with_signature_and_sender() for tx in block.txs] if failing_tx_count := len([tx for tx in txs if tx.error]) > 0: @@ -935,6 +971,12 @@ def generate_block_data( ) requests_list = block.requests + if monad_runloop: + # monad stamps a 32-zero-byte extra_data and requests_hash, + # rather than empty extra_data and the EIP-7685 requests root. + header.extra_data = Bytes(b"\x00" * 32) + header.requests_hash = Hash(0) + # Decode BAL from RLP bytes provided by the transition tool. t8n_bal_rlp = transition_tool_output.result.block_access_list t8n_bal: BlockAccessList | None = None diff --git a/packages/testing/src/execution_testing/test_types/__init__.py b/packages/testing/src/execution_testing/test_types/__init__.py index b9029ceb3a..976edd477e 100644 --- a/packages/testing/src/execution_testing/test_types/__init__.py +++ b/packages/testing/src/execution_testing/test_types/__init__.py @@ -17,6 +17,7 @@ from .block_types import ( Environment, EnvironmentDefaults, + MonadRunloopDefaults, Withdrawal, ) from .chain_config_types import ChainConfig, ChainConfigDefaults @@ -72,6 +73,7 @@ "DepositRequest", "Environment", "EnvironmentDefaults", + "MonadRunloopDefaults", "EOA", "NetworkWrappedTransaction", "Removable", diff --git a/packages/testing/src/execution_testing/test_types/block_types.py b/packages/testing/src/execution_testing/test_types/block_types.py index 56d367e2e6..b9c8a93a79 100644 --- a/packages/testing/src/execution_testing/test_types/block_types.py +++ b/packages/testing/src/execution_testing/test_types/block_types.py @@ -37,6 +37,29 @@ class EnvironmentDefaults: gas_limit: int = DEFAULT_BLOCK_GAS_LIMIT +@dataclass +class MonadRunloopDefaults: + """ + Consensus-derived header values the monad runloop produces. + + When `fill` runs with `--monad-runloop`, monad blocks are stamped + with these so filled block hashes and EIP-2935 history storage match + the production runloop (which the `eest-runner` consumer executes). + They are the only header fields that differ from the synchronous EEST + defaults; the execution-result roots already agree. + """ + + enabled: bool = False + # prev_randao = blake3(rlp(BLS round-signature over Round(0))) with the + # harness's all-zero proposer key. Constant because the harness never + # advances the round (Round(0) for every block). + prev_randao: int = ( + 0x15B2551B9CE2307DCFF661B7DEE9CC4D09F304F03E7887B0E948A1C29F0E5826 + ) + # proposal_gas_limit of the active monad chain revision (V_0_11_0). + gas_limit: int = 200_000_000 + + class WithdrawalGeneric(CamelModel, Generic[NumberBoundTypeVar]): """ Withdrawal generic type, used as a parent class for `Withdrawal` and diff --git a/pyproject.toml b/pyproject.toml index 13acf161f5..a1a8835aaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ ] dependencies = [ "pycryptodome>=3.22,<4", - "coincurve>=20,<21", + "coincurve>=20,<22", "typing_extensions>=4.4", "py-ecc>=8.0.0b2,<9", "ethereum-types>=0.4.1,<0.5", diff --git a/src/ethereum/crypto/elliptic_curve.py b/src/ethereum/crypto/elliptic_curve.py index 03a4f95aa1..b68414411a 100644 --- a/src/ethereum/crypto/elliptic_curve.py +++ b/src/ethereum/crypto/elliptic_curve.py @@ -73,8 +73,7 @@ def secp256k1_recover(r: U256, s: U256, v: U256, msg_hash: Hash32) -> Bytes: except ValueError as e: raise InvalidSignatureError from e - public_key = public_key.format(compressed=False)[1:] - return public_key + return public_key.format(compressed=False)[1:] SECP256R1N = U256( diff --git a/tests/frontier/validation/test_transaction.py b/tests/frontier/validation/test_transaction.py index 1b665a4e07..237b4186ce 100644 --- a/tests/frontier/validation/test_transaction.py +++ b/tests/frontier/validation/test_transaction.py @@ -21,6 +21,13 @@ from execution_testing.test_types.transaction_types import TransactionDefaults +@pytest.mark.monad_runloop( + pytest.mark.skip( + reason="Requires a test-controlled block gas limit; the monad " + "runloop fixes every block to the proposal gas limit, so the " + "over-limit tx no longer exceeds it." + ) +) @pytest.mark.exception_test @pytest.mark.eels_base_coverage def test_tx_gas_limit( diff --git a/tests/monad_nine/mip4_checkreservebalance/test_fork_transition.py b/tests/monad_nine/mip4_checkreservebalance/test_fork_transition.py index 3ed92bd2ac..450e8f62f3 100644 --- a/tests/monad_nine/mip4_checkreservebalance/test_fork_transition.py +++ b/tests/monad_nine/mip4_checkreservebalance/test_fork_transition.py @@ -23,7 +23,7 @@ REFERENCE_SPEC_VERSION = ref_spec_mip4.version -@pytest.mark.valid_at_transition_to("MONAD_NINE", subsequent_forks=True) +@pytest.mark.valid_at_transition_to("MONAD_NINE") def test_fork_transition( blockchain_test: BlockchainTestFiller, pre: Alloc, diff --git a/tests/osaka/eip7951_p256verify_precompiles/test_p256verify_before_fork.py b/tests/osaka/eip7951_p256verify_precompiles/test_p256verify_before_fork.py index 6e1e1f0831..7bc643d49c 100644 --- a/tests/osaka/eip7951_p256verify_precompiles/test_p256verify_before_fork.py +++ b/tests/osaka/eip7951_p256verify_precompiles/test_p256verify_before_fork.py @@ -22,6 +22,10 @@ REFERENCE_SPEC_GIT_PATH = ref_spec_7951.git_path REFERENCE_SPEC_VERSION = ref_spec_7951.version +# NOTE: the PragueToMONAD_EIGHT transition fork this targets was dropped as +# unused, so this marker currently resolves to no forks and the test +# generates nothing. P256VERIFY at MONAD_EIGHT+ is covered by the +# non-transition p256verify tests. pytestmark = pytest.mark.valid_at_transition_to("MONAD_EIGHT") diff --git a/uv.lock b/uv.lock index 0a19f45b03..4a0070ff0f 100644 --- a/uv.lock +++ b/uv.lock @@ -27,15 +27,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] -[[package]] -name = "asn1crypto" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" }, -] - [[package]] name = "attrs" version = "25.4.0" @@ -374,34 +365,40 @@ wheels = [ [[package]] name = "coincurve" -version = "20.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asn1crypto" }, - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/4c/9e5e51e6c12cec6444c86697992f9c6ccffa19f84d042ff939c8b89206ff/coincurve-20.0.0.tar.gz", hash = "sha256:872419e404300302e938849b6b92a196fabdad651060b559dc310e52f8392829", size = 122865, upload-time = "2024-06-02T18:15:50.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/a7/d60a41b3f0a546854c9b7ca65ab99a5fdf1c9e158ae264a580de8f23fd1c/coincurve-20.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:44087d1126d43925bf9a2391ce5601bf30ce0dba4466c239172dc43226696018", size = 1255635, upload-time = "2024-06-02T18:14:42.483Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4a/727fab66c0fbecfd7beeb38467910bd3652a77df649565e30160a9d2bae2/coincurve-20.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccf0ba38b0f307a9b3ce28933f6c71dc12ef3a0985712ca09f48591afd597c8", size = 1255536, upload-time = "2024-06-02T18:14:44.077Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8b/25d4ae5bb60665023e6d71681fada88ee95b5010dae6fc0b44d8b23b8df1/coincurve-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566bc5986debdf8572b6be824fd4de03d533c49f3de778e29f69017ae3fe82d8", size = 1191928, upload-time = "2024-06-02T18:14:45.739Z" }, - { url = "https://files.pythonhosted.org/packages/0d/86/8c32c512fa27bfe7cfe70329fd43ebac23c0c8cec202cf6e4f52854e7ce3/coincurve-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4d70283168e146f025005c15406086513d5d35e89a60cf4326025930d45013a", size = 1194365, upload-time = "2024-06-02T18:14:47.008Z" }, - { url = "https://files.pythonhosted.org/packages/fe/74/fefbe512f54df7d02a7ea4821b87cf199a91b3565cdf0c94448b3f6b1af1/coincurve-20.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:763c6122dd7d5e7a81c86414ce360dbe9a2d4afa1ca6c853ee03d63820b3d0c5", size = 1204658, upload-time = "2024-06-02T18:14:48.348Z" }, - { url = "https://files.pythonhosted.org/packages/09/68/05b29f881f628ce8e8468f5f7420f6c4d7c129f43964e81d15bf388ae67a/coincurve-20.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f00c361c356bcea386d47a191bb8ac60429f4b51c188966a201bfecaf306ff7f", size = 1215301, upload-time = "2024-06-02T18:14:49.84Z" }, - { url = "https://files.pythonhosted.org/packages/ee/5d/d91549cf5a163797b0724dc2dcd551b908b6beddb6598b37743df7f6f3ec/coincurve-20.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4af57bdadd2e64d117dd0b33cfefe76e90c7a6c496a7b034fc65fd01ec249b15", size = 1204505, upload-time = "2024-06-02T18:14:51.816Z" }, - { url = "https://files.pythonhosted.org/packages/37/0f/898022e08760fb57d281f3695576e859b0f8a8ac629670223d9066c3f60d/coincurve-20.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a26437b7cbde13fb6e09261610b788ca2a0ca2195c62030afd1e1e0d1a62e035", size = 1209305, upload-time = "2024-06-02T18:14:53.39Z" }, - { url = "https://files.pythonhosted.org/packages/57/b9/643567d3f680ddf8d1bf10a56112ae7755296500d8eaaef498be637a8533/coincurve-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed51f8bba35e6c7676ad65539c3dbc35acf014fc402101fa24f6b0a15a74ab9e", size = 1198932, upload-time = "2024-06-02T18:14:54.751Z" }, - { url = "https://files.pythonhosted.org/packages/b3/3a/898f5c12469b292042608dd0702bcb0420ec32bac6b1ca2a0dd790f922bd/coincurve-20.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:594b840fc25d74118407edbbbc754b815f1bba9759dbf4f67f1c2b78396df2d3", size = 1193318, upload-time = "2024-06-02T18:14:55.981Z" }, - { url = "https://files.pythonhosted.org/packages/8f/24/e1bf259dd57186fbdc7cec51909db320884162cfad5ec72cbaa63573ff9d/coincurve-20.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4df4416a6c0370d777aa725a25b14b04e45aa228da1251c258ff91444643f688", size = 1255671, upload-time = "2024-06-02T18:14:57.863Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c5/1817f87d1cd5ff50d8537fe60fb96f66b76dd02da885d970952e6189a801/coincurve-20.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1ccc3e4db55abf3fc0e604a187fdb05f0702bc5952e503d9a75f4ae6eeb4cb3a", size = 1255565, upload-time = "2024-06-02T18:14:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/90/9f/35e15f993717ed1dcc4c26d9771f073a1054af26808a0f421783bb4cd7e0/coincurve-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8335b1658a2ef5b3eb66d52647742fe8c6f413ad5b9d5310d7ea6d8060d40f", size = 1191953, upload-time = "2024-06-02T18:15:01.047Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3d/6a9bc32e69b738b5e05f5027bace1da6722352a4a447e495d3c03a601d99/coincurve-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ac025e485a0229fd5394e0bf6b4a75f8a4f6cee0dcf6f0b01a2ef05c5210ff", size = 1194425, upload-time = "2024-06-02T18:15:02.919Z" }, - { url = "https://files.pythonhosted.org/packages/1a/a6/15424973dc47fc7c87e3c0f8859f6f1b1032582ee9f1b85fdd5d1e33d630/coincurve-20.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e46e3f1c21b3330857bcb1a3a5b942f645c8bce912a8a2b252216f34acfe4195", size = 1204678, upload-time = "2024-06-02T18:15:04.308Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e7/71ddb4d66c11c4ad13e729362f8852e048ae452eba3dfcf57751842bb292/coincurve-20.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:df9ff9b17a1d27271bf476cf3fa92df4c151663b11a55d8cea838b8f88d83624", size = 1215395, upload-time = "2024-06-02T18:15:05.701Z" }, - { url = "https://files.pythonhosted.org/packages/b9/7d/03e0a19cfff1d86f5d019afc69cfbff02caada701ed5a4a50abc63d4261c/coincurve-20.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4155759f071375699282e03b3d95fb473ee05c022641c077533e0d906311e57a", size = 1204552, upload-time = "2024-06-02T18:15:07.107Z" }, - { url = "https://files.pythonhosted.org/packages/07/cd/e9bd4ca7d931653a35c74194da04191a9aecc54b8f48a554cd538dc810e4/coincurve-20.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0530b9dd02fc6f6c2916716974b79bdab874227f560c422801ade290e3fc5013", size = 1209392, upload-time = "2024-06-02T18:15:08.663Z" }, - { url = "https://files.pythonhosted.org/packages/99/54/260053f14f74b99b645084231e1c76994134ded49407a3bba23a8ffc0ff6/coincurve-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:eacf9c0ce8739c84549a89c083b1f3526c8780b84517ee75d6b43d276e55f8a0", size = 1198932, upload-time = "2024-06-02T18:15:10.786Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b5/c465e09345dd38b9415f5d47ae7683b3f461db02fcc03e699b6b5687ab2b/coincurve-20.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:52a67bfddbd6224dfa42085c88ad176559801b57d6a8bd30d92ee040de88b7b3", size = 1193324, upload-time = "2024-06-02T18:15:12.511Z" }, +version = "21.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/a2/f2a38eb05b747ed3e54e1be33be339d4a14c1f5cc6a6e2b342b5e8160d51/coincurve-21.0.0.tar.gz", hash = "sha256:8b37ce4265a82bebf0e796e21a769e56fdbf8420411ccbe3fafee4ed75b6a6e5", size = 128986, upload-time = "2025-03-08T15:31:24.266Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/5a/9aaa096d830b5d1386335759e73038a5352f8cd670efed55d242f92d0bce/coincurve-21.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:65ec42cab9c60d587fb6275c71f0ebc580625c377a894c4818fb2a2b583a184b", size = 1390936, upload-time = "2025-03-08T15:30:14.716Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e4/37dd30ed171432e32c075a03237915c0e69a5a524a807f380d910b276a2a/coincurve-21.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5828cd08eab928db899238874d1aab12fa1236f30fe095a3b7e26a5fc81df0a3", size = 1384762, upload-time = "2025-03-08T15:30:16.475Z" }, + { url = "https://files.pythonhosted.org/packages/09/fd/78870f4babed4981feb9b97b3189aec0f01a1a24be8a1ac04807dc68aa0d/coincurve-21.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de1cac75182de9f71ce41415faafcaf788303e21cbd0188064e268d61625e5", size = 1597025, upload-time = "2025-03-08T15:30:18.566Z" }, + { url = "https://files.pythonhosted.org/packages/9d/fb/b4850f8afc941655ef4c1204b50f9e21f841c6a64aa83a559277ca305cbd/coincurve-21.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07cda058d9394bea30d57a92fdc18ee3ca6b5bc8ef776a479a2ffec917105836", size = 1603987, upload-time = "2025-03-08T15:30:20.65Z" }, + { url = "https://files.pythonhosted.org/packages/9d/b7/df41dbcec3f70e383fa024949ce8956ff3b2a1b9eac330fba18c2115eece/coincurve-21.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9070804d7c71badfe4f0bf19b728cfe7c70c12e733938ead6b1db37920b745c0", size = 1604762, upload-time = "2025-03-08T15:30:22.271Z" }, + { url = "https://files.pythonhosted.org/packages/70/84/1b2437fc22590073eefb3da0418648b2d5b768951ef851822be8c164b998/coincurve-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:669ab5db393637824b226de058bb7ea0cb9a0236e1842d7b22f74d4a8a1f1ff1", size = 1637469, upload-time = "2025-03-08T15:30:24.305Z" }, + { url = "https://files.pythonhosted.org/packages/9c/4b/893763b3964b3044071a450fdada4c5024dc16f7644258a7bd06cf41e2ba/coincurve-21.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3bcd538af097b3914ec3cb654262e72e224f95f2e9c1eb7fbd75d843ae4e528e", size = 1601177, upload-time = "2025-03-08T15:30:25.805Z" }, + { url = "https://files.pythonhosted.org/packages/77/45/d2f42159cb461f5b070ff848244f1b83f3ea9ec3a3435368f9be33e4e276/coincurve-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45b6a5e6b5536e1f46f729829d99ce1f8f847308d339e8880fe7fa1646935c10", size = 1635597, upload-time = "2025-03-08T15:30:28.113Z" }, + { url = "https://files.pythonhosted.org/packages/9a/7c/528cff0aa17acd6c64b10c4bd8bb0adb6c96420be4e170916150537f36f6/coincurve-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:87597cf30dfc05fa74218810776efacf8816813ab9fa6ea1490f94e9f8b15e77", size = 1328626, upload-time = "2025-03-08T15:30:29.757Z" }, + { url = "https://files.pythonhosted.org/packages/cb/91/845b00da05b132e7bb3f3d1c4c301c195b39a9dc8f9962295ff340a27f18/coincurve-21.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:b992d1b1dac85d7f542d9acbcf245667438839484d7f2b032fd032256bcd778e", size = 1325365, upload-time = "2025-03-08T15:30:31.405Z" }, + { url = "https://files.pythonhosted.org/packages/f3/61/a2d9e109f99b6f5e65e653ac998b0944c5b82c568ac142fcbb381a4803be/coincurve-21.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f60ad56113f08e8c540bb89f4f35f44d434311433195ffff22893ccfa335070c", size = 1391948, upload-time = "2025-03-08T15:30:32.899Z" }, + { url = "https://files.pythonhosted.org/packages/24/5a/2da75ee00a722ef1fa068ada3bc34c564595ead86fef573434e2f0cb0a5c/coincurve-21.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1cb1cd19fb0be22e68ecb60ad950b41f18b9b02eebeffaac9391dc31f74f08f2", size = 1384958, upload-time = "2025-03-08T15:30:34.705Z" }, + { url = "https://files.pythonhosted.org/packages/dc/50/6bf0bf7e8a9a9dd419ecc1e479dcb9fbfe657029276ad703806a25a2bef2/coincurve-21.0.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05d7e255a697b3475d7ae7640d3bdef3d5bc98ce9ce08dd387f780696606c33b", size = 1606576, upload-time = "2025-03-08T15:30:36.796Z" }, + { url = "https://files.pythonhosted.org/packages/bd/ab/9e89908fdd09ad522938085587aaa821b022f4def16c286c5580cfc85811/coincurve-21.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a366c314df7217e3357bb8c7d2cda540b0bce180705f7a0ce2d1d9e28f62ad4", size = 1613642, upload-time = "2025-03-08T15:30:38.416Z" }, + { url = "https://files.pythonhosted.org/packages/b7/75/050b6fd08978de85a7b480f0f220ab6a30967c0910119f3096a8dd40befc/coincurve-21.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b04778b75339c6e46deb9ae3bcfc2250fbe48d1324153e4310fc4996e135715", size = 1616974, upload-time = "2025-03-08T15:30:39.939Z" }, + { url = "https://files.pythonhosted.org/packages/d7/62/2740ba0cafebf45708633635fecadcbe582d7a3ed1ce8b4637921feceaf8/coincurve-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8efcbdcd50cc219989a2662e6c6552f455efc000a15dd6ab3ebf4f9b187f41a3", size = 1644133, upload-time = "2025-03-08T15:30:41.733Z" }, + { url = "https://files.pythonhosted.org/packages/94/14/1f27c3048c4084fa85ef65f42a4ca631f2b184336e6d9446fecec20e0a7f/coincurve-21.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6df44b4e3b7acdc1453ade52a52e3f8a5b53ecdd5a06bd200f1ec4b4e250f7d9", size = 1619918, upload-time = "2025-03-08T15:30:43.284Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/7ec3ec4c8e7764daa25767d6674cb5741ea2d9b39ff758e9918d22a4b49b/coincurve-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bcc0831f07cb75b91c35c13b1362e7b9dc76c376b27d01ff577bec52005e22a8", size = 1645797, upload-time = "2025-03-08T15:30:44.974Z" }, + { url = "https://files.pythonhosted.org/packages/fb/60/87982b7499943ab12605df7b14f6001fff331aca0881b260682461e2309d/coincurve-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:5dd7b66b83b143f3ad3861a68fc0279167a0bae44fe3931547400b7a200e90b1", size = 1329255, upload-time = "2025-03-08T15:30:46.4Z" }, + { url = "https://files.pythonhosted.org/packages/62/c0/65b60b371579570931daca8a3f67debfc1482908b8ed03432297274a27da/coincurve-21.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:78dbe439e8cb22389956a4f2f2312813b4bd0531a0b691d4f8e868c7b366555d", size = 1325973, upload-time = "2025-03-08T15:30:48.056Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/cce55adaec37a588eb24b67da8eb68926546458e12ed2c4c2a21deb93d4c/coincurve-21.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9df5ceb5de603b9caf270629996710cf5ed1d43346887bc3895a11258644b65b", size = 1391762, upload-time = "2025-03-08T15:30:49.586Z" }, + { url = "https://files.pythonhosted.org/packages/ca/7a/628a30281d246ce98aea56592e0c8e79b03a93ee8b85d688db3388130c2d/coincurve-21.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:154467858d23c48f9e5ab380433bc2625027b50617400e2984cc16f5799ab601", size = 1384921, upload-time = "2025-03-08T15:30:51.103Z" }, + { url = "https://files.pythonhosted.org/packages/61/cc/719c5da31e6ba07e438abcf962f7a365eb69a06a0621ca4f2a484f344e09/coincurve-21.0.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f57f07c44d14d939bed289cdeaba4acb986bba9f729a796b6a341eab1661eedc", size = 1606559, upload-time = "2025-03-08T15:30:53.218Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ee/dd14237013d732e7fc3248c0c33a1d36b88b5378dfa3e624a50a23fb6f19/coincurve-21.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fb03e3a388a93d31ed56a442bdec7983ea404490e21e12af76fb1dbf097082a", size = 1613684, upload-time = "2025-03-08T15:30:55.087Z" }, + { url = "https://files.pythonhosted.org/packages/f0/05/eaa7f36a03376ced1c19e0cb563341cc83fe48f5734b2effe8f16d0ee0ab/coincurve-21.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d09ba4fd9d26b00b06645fcd768c5ad44832a1fa847ebe8fb44970d3204c3cb7", size = 1617001, upload-time = "2025-03-08T15:30:57.036Z" }, + { url = "https://files.pythonhosted.org/packages/39/32/fc75f1dd914ac95eb2704425c7ca1a9f509f982e15d05e0ca895b9e6ea9c/coincurve-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1a1e7ee73bc1b3bcf14c7b0d1f44e6485785d3b53ef7b16173c36d3cefa57f93", size = 1643924, upload-time = "2025-03-08T15:30:58.737Z" }, + { url = "https://files.pythonhosted.org/packages/1a/4b/8c6e65b5755e26fc02077803879747615c1c327047328d1784bccb4ff4c3/coincurve-21.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ad05952b6edc593a874df61f1bc79db99d716ec48ba4302d699e14a419fe6f51", size = 1619964, upload-time = "2025-03-08T15:31:00.275Z" }, + { url = "https://files.pythonhosted.org/packages/64/bc/d0a743305ff9fa26e72b4c77b534d5958ec8030b3772555a7172a0c134e5/coincurve-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d2bf350ced38b73db9efa1ff8fd16a67a1cb35abb2dda50d89661b531f03fd3", size = 1645526, upload-time = "2025-03-08T15:31:01.952Z" }, + { url = "https://files.pythonhosted.org/packages/9d/44/ab082e2dc8c9a45774f1bb9961f58b43c0882b866f5c469ead932d45a35d/coincurve-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:54d9500c56d5499375e579c3917472ffcf804c3584dd79052a79974280985c74", size = 1329285, upload-time = "2025-03-08T15:31:03.591Z" }, + { url = "https://files.pythonhosted.org/packages/f3/94/407f6fc811310f15b1fc7255f436f6a9040854213beeb10093f56b5b7fd3/coincurve-21.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:773917f075ec4b94a7a742637d303a3a082616a115c36568eb6c873a8d950d18", size = 1326027, upload-time = "2025-03-08T15:31:05.318Z" }, ] [[package]] @@ -941,7 +938,7 @@ test = [ [package.metadata] requires-dist = [ - { name = "coincurve", specifier = ">=20,<21" }, + { name = "coincurve", specifier = ">=20,<22" }, { name = "cryptography", specifier = ">=45.0.1,<46" }, { name = "ethash", marker = "extra == 'optimized'", specifier = ">=1.1.0,<2" }, { name = "ethereum-rlp", specifier = ">=0.1.6,<0.2" }, @@ -1109,7 +1106,7 @@ test = [ requires-dist = [ { name = "ckzg", specifier = ">=2.1.3,<3" }, { name = "click", specifier = ">=8.1.0,<9" }, - { name = "coincurve", specifier = ">=20.0.0,<21" }, + { name = "coincurve", specifier = ">=20.0.0,<22" }, { name = "colorlog", specifier = ">=6.7.0,<7" }, { name = "eth-abi", specifier = ">=5.2.0" }, { name = "ethereum-execution", editable = "." }, diff --git a/whitelist.txt b/whitelist.txt index 958faf8957..4bb6a579cc 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -322,6 +322,7 @@ copytree cp CPUs CPython +crate create create2 create3