diff --git a/.circleci/Untitled b/.circleci/Untitled new file mode 100644 index 00000000000..30eb64ea43c --- /dev/null +++ b/.circleci/Untitled @@ -0,0 +1 @@ +c-contracts_changed \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f1a5b2f67b..fca6747cd00 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -108,6 +108,8 @@ workflows: .* c-go-cache-version << pipeline.parameters.go-cache-version >> .circleci/continue/main.yml rust/.* c-rust_files_changed true .circleci/continue/main.yml ^(?!docs/public-docs/).+ c-non_docs_changes true .circleci/continue/main.yml + (packages/contracts-bedrock|\.circleci|\.github|ops/check-changed)/.* c-contracts_changed true .circleci/continue/main.yml + ^(package\.json|mise\.toml)$ c-contracts_changed true .circleci/continue/main.yml # Docs CI — trigger on docs/public-docs/ changes .* c-default_docker_image << pipeline.parameters.default_docker_image >> .circleci/continue/docs-ci.yml diff --git a/.circleci/continue/main.yml b/.circleci/continue/main.yml index 16358bedccd..418573a4061 100644 --- a/.circleci/continue/main.yml +++ b/.circleci/continue/main.yml @@ -77,6 +77,11 @@ parameters: c-go-cache-version: type: string default: "v0.0" + # Set to true by path-filtering when contracts or rebuild-all paths change. + # When false, contract feature matrix jobs are skipped entirely. + c-contracts_changed: + type: boolean + default: false # Passthrough declarations for setup config parameters. # CircleCI forwards all explicitly-passed pipeline parameters to continuation configs. # Without these declarations, manually triggered pipelines fail with "Unexpected argument(s)". @@ -149,7 +154,7 @@ parameters: default: "v0.0" orbs: - utils: ethereum-optimism/circleci-utils@1.0.25 + utils: ethereum-optimism/circleci-utils@1.0.27 go: circleci/go@1.8.0 gcp-cli: circleci/gcp-cli@3.0.1 slack: circleci/slack@6.0.0 @@ -1513,6 +1518,19 @@ jobs: command: just update-selectors working_directory: packages/contracts-bedrock + required-contracts-ci: + docker: + - image: <> + resource_class: small + parameters: + always-succed: + description: Force always succeed (skip API gate; for contracts-feature-tests-short) + type: boolean + default: false + steps: + - utils/ci-gate: + always-succeed: << parameters.always-succed >> + contracts-bedrock-checks-fast: docker: - image: <> @@ -1673,6 +1691,20 @@ jobs: command: | go run ./ops/scripts/check-nut-locks + nut-provenance-verify: + docker: + - image: <> + resource_class: 2xlarge + steps: + - utils/checkout-with-mise: + enable-mise-cache: true + - install-contracts-dependencies + - check-changed: + patterns: op-core/nuts + - run: + name: verify NUT bundle provenance + command: ./ops/scripts/nut-provenance-verify-changed.sh + go-tests: parameters: notify: @@ -2612,6 +2644,9 @@ workflows: - check-nut-locks: context: - circleci-repo-readonly-authenticated-github-token + - nut-provenance-verify: + context: + - circleci-repo-readonly-authenticated-github-token - fuzz-golang: name: fuzz-golang-<> on_changes: <> @@ -3169,4 +3204,210 @@ workflows: - circleci-repo-readonly-authenticated-github-token - slack + contracts-feature-tests: + when: + or: + - and: + - equal: ["webhook", << pipeline.trigger_source >>] + - << pipeline.parameters.c-contracts_changed >> + - << pipeline.parameters.c-non_docs_changes >> + # Always run on develop (mirrors previous check-changed whitelist behavior) + - and: + - equal: ["webhook", << pipeline.trigger_source >>] + - equal: ["develop", << pipeline.git.branch >>] + - << pipeline.parameters.c-non_docs_changes >> + # Merge queue (merge gate). Match checkout branch. + - matches: + pattern: "^gh-readonly-queue/.*" + value: << pipeline.git.branch >> + - and: + - equal: [true, <>] + - equal: ["api", << pipeline.trigger_source >>] + jobs: + - contracts-bedrock-tests: + # Heavily fuzz any fuzz tests within added or modified test files. + name: contracts-bedrock-tests-heavy-fuzz-modified <> + test_list: git diff origin/develop...HEAD --name-only --diff-filter=AM -- './test/**/*.t.sol' | sed 's|packages/contracts-bedrock/||' + test_timeout: 1h + test_profile: ciheavy + features: <> + matrix: + parameters: + features: &features_matrix + - main + - CUSTOM_GAS_TOKEN + - OPTIMISM_PORTAL_INTEROP + - OPCM_V2 + - OPCM_V2,CUSTOM_GAS_TOKEN + - OPCM_V2,OPTIMISM_PORTAL_INTEROP + - OPCM_V2,ZK_DISPUTE_GAME + - OPCM_V2,CANNON_KONA + - OPCM_V2,SUPER_ROOT_GAMES_MIGRATION + context: + - circleci-repo-readonly-authenticated-github-token + - slack + # On PRs, run tests with lite profile for better build times. + - contracts-bedrock-tests: + name: contracts-bedrock-tests <> + test_list: find test -name "*.t.sol" + test_profile: liteci + features: <> + matrix: + parameters: + features: *features_matrix + context: + - circleci-repo-readonly-authenticated-github-token + - slack + filters: + branches: + ignore: develop + # On develop, run tests with ci profile to mirror production. + - contracts-bedrock-tests: + name: contracts-bedrock-tests-develop <> + test_list: find test -name "*.t.sol" + test_profile: ci + features: <> + matrix: + parameters: + features: *features_matrix + context: + - circleci-repo-readonly-authenticated-github-token + - slack + filters: + branches: + only: develop + - contracts-bedrock-coverage: + # Generate coverage reports. + name: contracts-bedrock-coverage <> + test_timeout: 1h + test_profile: cicoverage + features: <> + matrix: + parameters: + features: *features_matrix + context: + - circleci-repo-readonly-authenticated-github-token + - slack + # On PRs, run upgrade tests with lite profile for better build times. + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade op-mainnet <> + fork_op_chain: op + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + test_profile: liteci + features: <> + matrix: + parameters: + features: *features_matrix + context: + - circleci-repo-readonly-authenticated-github-token + - slack + filters: + branches: + ignore: develop + # On develop, run upgrade tests with ci profile to mirror production. + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade-develop op-mainnet <> + fork_op_chain: op + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + test_profile: ci + features: <> + matrix: + parameters: + features: *features_matrix + context: + - circleci-repo-readonly-authenticated-github-token + - slack + filters: + branches: + only: develop + # On PRs, run chain-specific upgrade tests with lite profile for better build times. + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade <>-mainnet + fork_op_chain: <> + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + test_profile: liteci + matrix: + parameters: + fork_op_chain: ["op", "ink", "unichain"] + context: + - circleci-repo-readonly-authenticated-github-token + - slack + filters: + branches: + ignore: develop + # On develop, run chain-specific upgrade tests with ci profile to mirror production. + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade-develop <>-mainnet + fork_op_chain: <> + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + test_profile: ci + matrix: + parameters: + fork_op_chain: ["op", "ink", "unichain"] + context: + - circleci-repo-readonly-authenticated-github-token + - slack + filters: + branches: + only: develop + # Always run L2 fork tests with ci profile + - contracts-bedrock-tests-l2-fork: + name: contracts-bedrock-tests-l2-fork op-mainnet + fork_op_chain: op-mainnet + l2_fork_rpc: https://op-mainnet-rpc.optimism.io/ + l2_fork_block_number: latest + test_profile: ci + context: + - circleci-repo-readonly-authenticated-github-token + - slack + - contracts-bedrock-checks-fast: + context: + - circleci-repo-readonly-authenticated-github-token + - slack + # ----------------------------------------------------------------------- + # required-contracts-ci: GitHub required status for this workflow. Terminal requires + # keep this job scheduled even when upstream jobs fail; ci-gate then checks they passed. + # ----------------------------------------------------------------------- + - required-contracts-ci: + requires: + - contracts-bedrock-tests main: terminal + - contracts-bedrock-tests-heavy-fuzz-modified main: terminal + - contracts-bedrock-coverage main: terminal + - contracts-bedrock-tests-upgrade op-mainnet main: terminal + - contracts-bedrock-checks-fast: terminal + context: + - circleci-api-token + + # ============================================================================ + # Required contracts CI gate (skip) — runs when no contract changes + # Produces the required-contracts-ci status so the merge queue doesn't hang. # ============================================================================ + contracts-feature-tests-short: + when: + not: # these conditions should be kept in sync with the ones in contracts-feature-tests + or: + - and: + - equal: ["webhook", << pipeline.trigger_source >>] + - << pipeline.parameters.c-contracts_changed >> + - << pipeline.parameters.c-non_docs_changes >> + # Always run on develop (mirrors previous check-changed whitelist behavior) + - and: + - equal: ["webhook", << pipeline.trigger_source >>] + - equal: ["develop", << pipeline.git.branch >>] + - << pipeline.parameters.c-non_docs_changes >> + # Merge queue (merge gate). Match checkout branch. + - matches: + pattern: "^gh-readonly-queue/.*" + value: << pipeline.git.branch >> + - and: + - equal: [true, <>] + - equal: ["api", << pipeline.trigger_source >>] + jobs: + - required-contracts-ci: + always-succed: true + context: + - circleci-api-token diff --git a/.circleci/continue/rust-ci.yml b/.circleci/continue/rust-ci.yml index f71ba9d0505..2c49689580c 100644 --- a/.circleci/continue/rust-ci.yml +++ b/.circleci/continue/rust-ci.yml @@ -4,7 +4,7 @@ version: 2.1 # This file contains all Rust CI commands, parameterized jobs, crate-specific jobs, and workflows. orbs: - utils: ethereum-optimism/circleci-utils@1.0.25 + utils: ethereum-optimism/circleci-utils@1.0.27 gcp-cli: circleci/gcp-cli@3.0.1 codecov: codecov/codecov@5.0.3 @@ -407,10 +407,6 @@ jobs: - rust-prepare-and-restore-cache: &fmt-cache-args directory: <> prefix: <>-fmt - - run: - name: Install nightly toolchain - working_directory: <> - command: just install-nightly - run: name: Check formatting working_directory: <> @@ -598,10 +594,6 @@ jobs: directory: <> prefix: <>-docs features: "all" - - run: - name: Install nightly toolchain - working_directory: <> - command: just install-nightly - run: name: Build documentation working_directory: <> @@ -701,10 +693,6 @@ jobs: directory: <> prefix: <>-udeps profile: "release" - - run: - name: Install nightly toolchain - working_directory: <> - command: just install-nightly - install-cargo-binstall - run: name: Install cargo-udeps diff --git a/.claude/skills/fix-rust-fmt/SKILL.md b/.claude/skills/fix-rust-fmt/SKILL.md index d87b23bfa92..433a1045006 100644 --- a/.claude/skills/fix-rust-fmt/SKILL.md +++ b/.claude/skills/fix-rust-fmt/SKILL.md @@ -28,16 +28,7 @@ cd && mise install This installs `rust`, `just`, and all other tools pinned in `mise.toml`. -### Step 2: Install the nightly toolchain with rustfmt - -The justfile pins a specific nightly (see `NIGHTLY` variable in `rust/justfile`). -Install it: - -```bash -cd /rust && mise exec -- just install-nightly -``` - -### Step 3: Run the formatter +### Step 2: Run the formatter ```bash cd /rust && mise exec -- just fmt-fix diff --git a/.claude/skills/vm-compat-triage/SKILL.md b/.claude/skills/vm-compat-triage/SKILL.md index 748f9e50ba9..cc1d877c2f2 100644 --- a/.claude/skills/vm-compat-triage/SKILL.md +++ b/.claude/skills/vm-compat-triage/SKILL.md @@ -17,8 +17,7 @@ Use when the `analyze-op-program-client` CI job fails on a PR. The job runs `vm- - `gh` CLI authenticated with GitHub - `jq` available -- `vm-compat` binary (install: `mise use -g ubi:ChainSafe/vm-compat@1.1.0`, or download from GitHub releases — binary name is `analyzer-linux-arm64` / `analyzer-linux-amd64`) -- `llvm-objdump` — **Linux only** (install: `sudo apt-get install -y llvm`). Not available on macOS. On macOS, use `just run-vm-compat` in the `op-program` directory which runs the analysis inside Docker. +- Docker — required for `just run-vm-compat` and `just regenerate-vm-compat-baseline`. These targets build and run the analysis inside Docker, so no local `vm-compat` or `llvm-objdump` installation is needed. If Docker is not available, ask the user to run the just target. - The PR URL or number (ask the user if not provided) ## MIPS64 Syscall Reference @@ -270,44 +269,29 @@ Only proceed if ALL findings are marked as either unreachable or acceptable (non **Do NOT manually add entries to the existing baseline.** The baseline must be regenerated from scratch so that stale entries (from code paths that no longer exist) are removed. -To regenerate: +To regenerate, use the `just regenerate-vm-compat-baseline` target from the `op-program` directory in the PR branch worktree. This requires Docker and handles everything end-to-end: -1. Run vm-compat with **no baseline** to get the complete report for the current code: +1. Runs vm-compat with **no baseline** to get the complete report +2. Normalizes the output by stripping assembly-level fields (`line`, `file`, `absPath`) that cause false positives when they change +3. Overwrites the baseline JSON file ```bash -cd op-program && vm-compat analyze \ - --with-trace=true --skip-warnings=false --format=json \ - --vm-profile-config vm-profiles/cannon-multithreaded-64.yaml \ - --report-output-path /tmp/vm-compat-full-report.json \ - ./client/cmd/main.go +cd op-program && just regenerate-vm-compat-baseline ``` -2. Normalize the output by stripping `line`, `file`, and `absPath` fields (these are assembly positions, not Go source lines, and cause false positives when they change): - -```bash -cat /tmp/vm-compat-full-report.json | jq 'walk( - if type == "object" and has("line") then del(.line) else . end | - if type == "object" and has("absPath") then del(.absPath) else . end | - if type == "object" and has("file") then del(.file) else . end -)' > op-program/compatibility-test/baseline-cannon-multithreaded-64.json -``` +If Docker is not available, ask the user to run this command. -3. This replaces the entire baseline with the current state. The old baseline is not merged — it is replaced. +This replaces the entire baseline with the current state. The old baseline is not merged — it is replaced. ### Step 7: Verify -After regenerating the baseline, re-run `vm-compat` with the new baseline to confirm zero new findings: +After regenerating the baseline, re-run vm-compat with the new baseline to confirm zero new findings: ```bash -cd op-program && vm-compat analyze \ - --with-trace=true --skip-warnings=false --format=json \ - --vm-profile-config vm-profiles/cannon-multithreaded-64.yaml \ - --baseline-report compatibility-test/baseline-cannon-multithreaded-64.json \ - --report-output-path /tmp/verify.json \ - ./client/cmd/main.go +cd op-program && just run-vm-compat ``` -If the output file contains an empty array `[]`, the baseline is complete. +If it exits successfully with no findings, the baseline is complete. If Docker is not available, ask the user to run this command. ## Notes diff --git a/.semgrep/rules/sol-rules.yaml b/.semgrep/rules/sol-rules.yaml index d8cf6f16a10..850875124cb 100644 --- a/.semgrep/rules/sol-rules.yaml +++ b/.semgrep/rules/sol-rules.yaml @@ -244,7 +244,6 @@ rules: - /packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol - /packages/contracts-bedrock/src/L2/CrossL2Inbox.sol - /packages/contracts-bedrock/src/L1/ResourceMetering.sol - - /packages/contracts-bedrock/src/L1/OPContractsManager.sol - /packages/contracts-bedrock/src/L1/DataAvailabilityChallenge.sol - id: sol-safety-use-disable-initializer @@ -320,14 +319,12 @@ rules: include: - /packages/contracts-bedrock/src exclude: - - /packages/contracts-bedrock/src/L1/OPContractsManager.sol - /packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol - /packages/contracts-bedrock/src/L1/opcm/OPContractsManagerContainer.sol - /packages/contracts-bedrock/src/L1/opcm/OPContractsManagerUtils.sol - /packages/contracts-bedrock/src/L1/opcm/OPContractsManagerUtilsCaller.sol - /packages/contracts-bedrock/src/L1/opcm/OPContractsManagerMigrator.sol - /packages/contracts-bedrock/src/L1/OptimismPortal2.sol - - /packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol - /packages/contracts-bedrock/src/L2/FeeVault.sol - /packages/contracts-bedrock/src/L2/OptimismMintableERC721.sol - /packages/contracts-bedrock/src/L2/OptimismMintableERC721Factory.sol diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4bd03412d2..68e7e7c6257 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,7 +192,6 @@ Also, all labels can be seen by visiting the [labels page][labels] When altering label names or deleting labels there are a few things you must be aware of. -- If the https://github.com/ethereum-optimism/optimism/labels/S-stale label is altered, the [close-stale](.github/workflows/close-stale.yml) workflow should be updated. - If the https://github.com/ethereum-optimism/optimism/labels/M-dependabot label is altered, the [dependabot config](.github/dependabot.yml) file should be adjusted. - Saved label filters for project boards will not automatically update. These should be updated if label names change. diff --git a/cannon/mipsevm/memory/memory.go b/cannon/mipsevm/memory/memory.go index e508d794ebb..a70a2a7bbdc 100644 --- a/cannon/mipsevm/memory/memory.go +++ b/cannon/mipsevm/memory/memory.go @@ -5,11 +5,11 @@ import ( "encoding/json" "fmt" "io" + "maps" "slices" "sort" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" - "golang.org/x/exp/maps" ) // Note: 2**12 = 4 KiB, the min phys page size in the Go runtime. @@ -334,7 +334,7 @@ func (m *Memory) Serialize(out io.Writer) error { if err := binary.Write(out, binary.BigEndian, Word(m.PageCount())); err != nil { return err } - indexes := maps.Keys(m.pageTable) + indexes := slices.Collect(maps.Keys(m.pageTable)) // iterate sorted map keys for consistent serialization slices.Sort(indexes) for _, pageIndex := range indexes { diff --git a/cannon/mipsevm/tests/evm_multithreaded64_test.go b/cannon/mipsevm/tests/evm_multithreaded64_test.go index ba96f0dd943..981b44b1e1e 100644 --- a/cannon/mipsevm/tests/evm_multithreaded64_test.go +++ b/cannon/mipsevm/tests/evm_multithreaded64_test.go @@ -4,11 +4,11 @@ package tests import ( "encoding/binary" "fmt" + "maps" "slices" "testing" "github.com/stretchr/testify/require" - "golang.org/x/exp/maps" "github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm/arch" @@ -651,7 +651,7 @@ func TestEVM_NoopSyscall64(t *testing.T) { func TestEVM_UnsupportedSyscall64(t *testing.T) { t.Parallel() - noopSyscallNums := maps.Values(NoopSyscalls64) + noopSyscallNums := slices.Collect(maps.Values(NoopSyscalls64)) unsupportedSyscalls := make([]uint32, 0, 400) for i := 5000; i < 5400; i++ { candidate := uint32(i) diff --git a/docs/ai/rust-dev.md b/docs/ai/rust-dev.md index d3b47e2212c..b01ad86a3c2 100644 --- a/docs/ai/rust-dev.md +++ b/docs/ai/rust-dev.md @@ -47,6 +47,30 @@ just test-unit just test-docs ``` +### Running op-reth E2E Tests + +The op-reth E2E tests (`rust/op-reth/tests/proofs/`) run a full devnet with op-geth (sequencer) and op-reth (validator). They require two build prerequisites: + +1. **Forge artifacts** — the devnet deploys contracts from compiled artifacts: + ```bash + cd packages/contracts-bedrock + mise exec -- just build-no-tests + ``` + +2. **op-reth release binary** — the test harness (`op-devstack/sysgo/rust_binary.go`) only searches `target/release/`, not `target/debug/`. Options: + ```bash + # Option A: let the test build it (slow first run, cached after) + RUST_JIT_BUILD=1 go test -v -run TestName ./rust/op-reth/tests/proofs/core/ + + # Option B: pre-build the binary + cd rust && just build-op-reth + ``` + +Run from the monorepo root: +```bash +mise exec -- go test -v -run TestExecutePayloadSuccess -count=1 ./rust/op-reth/tests/proofs/core/ +``` + ### Generating Prestates Kona prestates are built via Docker: @@ -74,12 +98,7 @@ Lint configuration lives in `rust/Cargo.toml` (workspace lints section), `rust/c ### Formatting Requires Nightly -Formatting uses a pinned nightly toolchain (defined as `NIGHTLY` in `rust/justfile`). If the nightly isn't installed: - -```bash -cd rust -just install-nightly -``` +Formatting uses a pinned nightly toolchain (defined as `NIGHTLY` in `rust/justfile`). It is installed via mise. Then use `just fmt-fix` to auto-format, or `just fmt-check` to verify. diff --git a/docs/public-docs/chain-operators/guides/management/fee-vaults.mdx b/docs/public-docs/chain-operators/guides/management/fee-vaults.mdx new file mode 100644 index 00000000000..1f92351ed92 --- /dev/null +++ b/docs/public-docs/chain-operators/guides/management/fee-vaults.mdx @@ -0,0 +1,121 @@ +--- +title: Fee vault operations +description: How to configure, monitor, and withdraw from fee vaults on your OP Stack chain. +--- + +This guide covers the operational aspects of managing fee vaults on your OP Stack chain. For a conceptual overview of how fee vaults work, see [Fee vaults](/op-stack/transactions/fee-vaults). + +## Prerequisites + +- Access to the **ProxyAdminOwner** account (required for configuration changes) +- An RPC endpoint for your L2 chain +- `cast` CLI tool installed ([Foundry](https://book.getfoundry.sh/getting-started/installation)) + +## Fee vault addresses + +| Vault | Address | +|-------|---------| +| `SequencerFeeVault` | `0x4200000000000000000000000000000000000011` | +| `BaseFeeVault` | `0x4200000000000000000000000000000000000019` | +| `L1FeeVault` | `0x420000000000000000000000000000000000001A` | +| `OperatorFeeVault` | `0x420000000000000000000000000000000000001B` | + +## Checking vault state + +### View current balance + +```bash +export L2_RPC= +export VAULT= + +cast balance $VAULT --rpc-url $L2_RPC +``` + +### View configuration + +```bash +# Recipient address +cast call $VAULT "recipient()" --rpc-url $L2_RPC + +# Minimum withdrawal amount (in wei) +cast call $VAULT "minWithdrawalAmount()" --rpc-url $L2_RPC + +# Withdrawal network (0 = L1, 1 = L2) +cast call $VAULT "withdrawalNetwork()" --rpc-url $L2_RPC + +# Total ETH withdrawn historically +cast call $VAULT "totalProcessed()" --rpc-url $L2_RPC +``` + +## Updating configuration + + + Configuration changes require the **ProxyAdminOwner** account. If your chain uses a multisig as the ProxyAdminOwner, these calls must be executed through the multisig. + + +### Set recipient + +```bash +export L2_RPC= +export VAULT= +export PK= + +cast send --rpc-url $L2_RPC --private-key $PK \ + $VAULT "setRecipient(address)" +``` + +### Set minimum withdrawal amount + +```bash +cast send --rpc-url $L2_RPC --private-key $PK \ + $VAULT "setMinWithdrawalAmount(uint256)" +``` + +### Set withdrawal network + +```bash +# 0 = L1, 1 = L2 +cast send --rpc-url $L2_RPC --private-key $PK \ + $VAULT "setWithdrawalNetwork(uint8)" <0_OR_1> +``` + +## Withdrawing fees + +Withdrawals are **permissionless** — anyone can trigger them once the vault balance meets the minimum threshold. + +### Trigger a withdrawal + +```bash +cast send --rpc-url $L2_RPC --private-key $PK \ + $VAULT "withdraw()" +``` + +This withdraws the **entire vault balance** to the configured recipient. + +### Withdrawal behavior by network + +- **L2 withdrawal (`withdrawalNetwork = 1`)**: Funds are transferred immediately to the recipient on L2. The transaction completes in a single step. +- **L1 withdrawal (`withdrawalNetwork = 0`)**: An L2-to-L1 withdrawal is initiated via the `L2ToL1MessagePasser`. After calling `withdraw()`, you must still: + 1. Wait for the withdrawal to be included in an L2 output root + 2. Prove the withdrawal on L1 + 3. Wait for the finalization period + 4. Finalize the withdrawal on L1 + + For details on completing L1 withdrawals, see [Withdrawal flow](/op-stack/bridging/withdrawal-flow). + +## Initial configuration during deployment + +Fee vault recipients, minimum withdrawal amounts, and withdrawal networks are configured during chain deployment with `op-deployer`. For details on setting these parameters, see the [op-deployer setup guide](/chain-operators/tutorials/create-l2-rollup/op-deployer-setup). + +## Monitoring + +It's good practice to monitor your fee vaults regularly: + +- **Vault balances**: Track balances to know when withdrawals can be triggered. +- **`totalProcessed`**: Monitor cumulative withdrawals over time to understand revenue trends. + +## Next steps + +- Read the [fee vaults explainer](/op-stack/transactions/fee-vaults) to understand how vaults work at the protocol level. +- See [Transaction Fees 101](/chain-operators/guides/management/transaction-fees-101) to learn how to tune fee parameters. +- Review [fee components and formulas](/op-stack/transactions/fees) for detailed fee calculations. diff --git a/docs/public-docs/chain-operators/guides/management/transaction-fees-101.mdx b/docs/public-docs/chain-operators/guides/management/transaction-fees-101.mdx index 630df1ecb41..79956109b5c 100644 --- a/docs/public-docs/chain-operators/guides/management/transaction-fees-101.mdx +++ b/docs/public-docs/chain-operators/guides/management/transaction-fees-101.mdx @@ -11,7 +11,7 @@ On an OP stack chain a transaction **Total Fee** is made of three main component `Total Fee = L2 Fee + L1 Fee + Operator Fee` -Fees are gathered in dedicated contract vaults that collect the different fee components (for example `BaseFeeVault`, `SequencerFeeVault`, etc.). +Fees are gathered in dedicated contract [fee vaults](/op-stack/transactions/fee-vaults) that collect the different fee components (for example `BaseFeeVault`, `SequencerFeeVault`, etc.). See the [fee vault operations guide](/chain-operators/guides/management/fee-vaults) for how to manage and withdraw from these vaults. ## 1. L2 Fee diff --git a/docs/public-docs/chain-operators/tutorials/create-l2-rollup/op-deployer-setup.mdx b/docs/public-docs/chain-operators/tutorials/create-l2-rollup/op-deployer-setup.mdx index 8f3d8707d32..84cc605df77 100644 --- a/docs/public-docs/chain-operators/tutorials/create-l2-rollup/op-deployer-setup.mdx +++ b/docs/public-docs/chain-operators/tutorials/create-l2-rollup/op-deployer-setup.mdx @@ -258,9 +258,10 @@ The intent file defines your chain's configuration. [[chains]] id = "0x000000000000000000000000000000000000000000000000000000000016de8d" - baseFeeVaultRecipient = "0x..." # base_Fee_Vault_Recipient address - l1FeeVaultRecipient = "0x..." # l1_Fee_Vault_Recipient address - sequencerFeeVaultRecipient = "0x..." # sequencer_Fee_Vault_Recipient address + baseFeeVaultRecipient = "0x..." # receives base fees + l1FeeVaultRecipient = "0x..." # receives L1 data fees + sequencerFeeVaultRecipient = "0x..." # receives priority fees (tips) + operatorFeeVaultRecipient = "0x..." # receives operator fees eip1559DenominatorCanyon = 250 eip1559Denominator = 50 eip1559Elasticity = 6 @@ -298,9 +299,18 @@ The intent file defines your chain's configuration. **Chain Configuration:** * `id`: Unique identifier for your chain - * `*FeeVaultRecipient`: Addresses receiving various protocol fees + * `*FeeVaultRecipient`: Addresses receiving protocol fees (required — deployment fails if any are set to the zero address). See [fee vaults](/op-stack/transactions/fee-vaults) for details on each vault. * `eip1559*`: Parameters for dynamic gas price calculation + **Fee Vault Optional Overrides:** + + Each vault also supports optional parameters that can be set via deploy overrides. If not specified, the following defaults apply: + + | Parameter | Default | + |-----------|---------| + | `*MinimumWithdrawalAmount` | 10 ETH | + | `*WithdrawalNetwork` | `local` (L2) | + **Chain Roles:** * `l1ProxyAdminOwner`: Can upgrade L1 contract implementations (usually same as superchain proxyAdminOwner) diff --git a/docs/public-docs/chain-operators/tutorials/migrating-permissionless.mdx b/docs/public-docs/chain-operators/tutorials/migrating-permissionless.mdx index 4715418bfd9..d326935231c 100644 --- a/docs/public-docs/chain-operators/tutorials/migrating-permissionless.mdx +++ b/docs/public-docs/chain-operators/tutorials/migrating-permissionless.mdx @@ -184,15 +184,12 @@ For detailed information about privileged roles and their security implications, ### Adding the PermissionlessDisputeGame to a chain -To enable the permissionless dispute game, you must call the `addGameType()`function on the `OPContractsManager.sol` (OPCM) contract. +To enable the permissionless dispute game, call the `upgrade()` function on the `OPContractsManagerV2` (OPCM) contract with the appropriate `DisputeGameConfig` entries. This is typically done via `op-deployer manage add-game-type-v2`. -This function adds the permissionless dispute game to the system. +The upgrade will: -This method will: - -1. Deploy the `FaultDisputeGame` contract -2. Setup the `DelayedWethProxy` for the new game -3. Reinitialize the `AnchorStateRegistry` to add the new game type. +1. Register the `FaultDisputeGame` implementation on the `DisputeGameFactory` with the correct game arguments (absolute prestate, VM, anchor state registry, delayed WETH, chain ID). +2. Set the initial bond for the new game type. See a high‐level implementation from this [docs](/chain-operators/tutorials/dispute-games) or [this superchain-ops template](https://github.com/ethereum-optimism/superchain-ops/blob/main/src/template/AddGameTypeTemplate.sol). diff --git a/docs/public-docs/docs.json b/docs/public-docs/docs.json index 93e48930468..1aacc44c020 100644 --- a/docs/public-docs/docs.json +++ b/docs/public-docs/docs.json @@ -1920,6 +1920,7 @@ "chain-operators/guides/management/key-management", "chain-operators/guides/management/operations", "chain-operators/guides/management/transaction-fees-101", + "chain-operators/guides/management/fee-vaults", "chain-operators/guides/management/troubleshooting" ] } @@ -2285,6 +2286,7 @@ "group": "Transactions", "pages": [ "/op-stack/transactions/fees", + "/op-stack/transactions/fee-vaults", "/op-stack/transactions/flashblocks", "/op-stack/transactions/forced-transaction", "/op-stack/transactions/transaction-finality", @@ -2368,6 +2370,7 @@ { "group": "Notices", "pages": [ + "notices/req-resp-cl-sync-deprecation", "notices/op-geth-deprecation", "notices/op-deployer-upgrade-deprecation", { @@ -2719,6 +2722,12 @@ "pages": [ "rust/op-alloy/glossary" ] + }, + { + "group": "op-revm", + "pages": [ + "rust/op-revm/index" + ] } ] } diff --git a/docs/public-docs/notices/op-deployer-upgrade-deprecation.mdx b/docs/public-docs/notices/op-deployer-upgrade-deprecation.mdx index 84036bd7e15..a0b3e1b63ad 100644 --- a/docs/public-docs/notices/op-deployer-upgrade-deprecation.mdx +++ b/docs/public-docs/notices/op-deployer-upgrade-deprecation.mdx @@ -30,5 +30,5 @@ If you need to upgrade beyond `op-contracts/v5.0.0`, migrate to one of the follo ## Resources * [superchain-ops upgrade guide](/chain-operators/tutorials/l1-contract-upgrades/superchain-ops-guide) -* [OP Contracts Manager reference](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OPContractsManager.sol) +* [OP Contracts Manager reference](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol) * [OPCM design documentation](https://github.com/ethereum-optimism/design-docs/blob/main/protocol/op-contracts-manager-arch.md) diff --git a/docs/public-docs/notices/req-resp-cl-sync-deprecation.mdx b/docs/public-docs/notices/req-resp-cl-sync-deprecation.mdx new file mode 100644 index 00000000000..07cd12e1b8f --- /dev/null +++ b/docs/public-docs/notices/req-resp-cl-sync-deprecation.mdx @@ -0,0 +1,40 @@ +--- +title: Deprecation of Req/Res CL P2P sync +description: The op-node Req/Res consensus-layer P2P sync client is being deprecated in favor of execution-layer syncing. +lang: en-US +content_type: notice +topic: req-resp-cl-sync-deprecation +personas: + - node-operator + - chain-operator +categories: + - infrastructure + - protocol +is_imported_content: 'false' +--- + +The `op-node` request-response (Req/Res) consensus-layer (CL) P2P sync client is being deprecated in favor of syncing through the execution layer (EL) client. + +This change simplifies `op-node` configuration, removes fragile sync logic, and relies on the execution client's native P2P sync, which is the more battle-tested path. + +## What this means + +- The Req/Res CL P2P sync **client** in `op-node` is deprecated. +- `op-node` should rely on its connected EL node, such as **op-geth** or **op-reth**, to reach the chain tip in the presence of gaps between local state and chain tip received over gossip. +- Operators should ensure their EL nodes have healthy P2P connectivity and enough peers to sync independently. +- If you still explicitly set `--syncmode.req-resp=true`, plan to remove that override. +- The Req/Res **server** path will remain temporarily to support older nodes, but it is also planned for removal later. + +## Action required + +### Node operators + +Move away from the deprecated Req/Res client path and rely on EL syncing. + +- Ensure your EL node has healthy P2P connectivity. +- Remove any explicit `--syncmode.req-resp=true` override unless you need temporary backward compatibility. +- If you are running an older release where Req/Res is still enabled by default, set `--syncmode.req-resp=false` to opt into the new behavior now. + + + Once the Req/Res client path is fully removed, nodes with unhealthy EL peer connectivity may fail to catch up to the unsafe tip. + diff --git a/docs/public-docs/op-stack/protocol/smart-contracts.mdx b/docs/public-docs/op-stack/protocol/smart-contracts.mdx index ae0acf569d4..d34299f2ee2 100644 --- a/docs/public-docs/op-stack/protocol/smart-contracts.mdx +++ b/docs/public-docs/op-stack/protocol/smart-contracts.mdx @@ -505,6 +505,7 @@ contract. If the ERC20 token is native to L1, it will be burnt. The `SequencerFeeVault` is the contract that holds any fees paid to the Sequencer during transaction processing and block production. +See [fee vaults](/op-stack/transactions/fee-vaults) for details. * **Address:** `0x4200000000000000000000000000000000000011` * **Introduced:** Legacy @@ -632,7 +633,8 @@ have the ability to upgrade any of the other predeploy contracts. The `BaseFeeVault` predeploy receives the base fees on L2. The base fee is not burnt on L2 like it is on L1. Once the contract has received a certain amount -of fees, the ETH can be withdrawn to an immutable address on L1. +of fees, the ETH can be withdrawn to a configured recipient. +See [fee vaults](/op-stack/transactions/fee-vaults) for details. * **Address:** `0x4200000000000000000000000000000000000019` * **Introduced:** Bedrock @@ -643,7 +645,8 @@ of fees, the ETH can be withdrawn to an immutable address on L1. The `L1FeeVault` predeploy receives the L1 portion of the transaction fees. Once the contract has received a certain amount of fees, the ETH can be -withdrawn to an immutable address on L1. +withdrawn to a configured recipient. +See [fee vaults](/op-stack/transactions/fee-vaults) for details. * **Address:** `0x420000000000000000000000000000000000001a` * **Introduced:** Bedrock @@ -654,7 +657,8 @@ withdrawn to an immutable address on L1. The `OperatorFeeVault` predeploy (introduced with the [Isthmus hardfork](https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/isthmus/predeploys.md#operatorfeevault)) receives the operator portion of the transaction fees. Once the contract has received a certain amount of fees, funds can be withdrawn to -the BaseFeeVault on the L2 network. +a configured recipient. +See [fee vaults](/op-stack/transactions/fee-vaults) for details. * **Address:** `0x420000000000000000000000000000000000001B` * **Introduced:** Isthmus diff --git a/docs/public-docs/op-stack/transactions/fee-vaults.mdx b/docs/public-docs/op-stack/transactions/fee-vaults.mdx new file mode 100644 index 00000000000..5c9a480fec0 --- /dev/null +++ b/docs/public-docs/op-stack/transactions/fee-vaults.mdx @@ -0,0 +1,65 @@ +--- +title: Fee vaults +description: Learn how fee vaults work on OP Stack chains, where transaction fees are collected, and how the withdrawal mechanism operates. +--- + +Fee vaults are predeploy contracts on OP Stack L2 chains that collect and hold the different components of transaction fees. Unlike Ethereum where the base fee is burned, OP Stack chains route all fees into dedicated vault contracts, allowing chain operators to withdraw and manage collected revenue. + +## Overview + +Every OP Stack chain has four fee vaults, each collecting a specific fee component: + +| Vault | Address | Collects | Introduced | +|-------|---------|----------|------------| +| `SequencerFeeVault` | `0x4200000000000000000000000000000000000011` | Priority fees (tips) | Legacy | +| `BaseFeeVault` | `0x4200000000000000000000000000000000000019` | Base fees (not burned on L2) | Bedrock | +| `L1FeeVault` | `0x420000000000000000000000000000000000001A` | L1 data fees | Bedrock | +| `OperatorFeeVault` | `0x420000000000000000000000000000000000001B` | Operator fees | Isthmus | + +### How fees flow into vaults + +When a transaction is executed on an OP Stack chain, the total fee is split across vaults: + +- **Base fee** (`gasUsed × baseFee`) goes to the `BaseFeeVault` +- **Priority fee** (`gasUsed × priorityFee`) goes to the `SequencerFeeVault` +- **L1 data fee** goes to the `L1FeeVault` +- **Operator fee** (`operatorFeeScalar × gasUsed + operatorFeeConstant`) goes to the `OperatorFeeVault` + +Each component has its own formula. For the full breakdown, see [Transaction fees](/op-stack/transactions/fees). + +## Vault configuration + +Each fee vault has three configurable parameters: + +| Parameter | Description | +|-----------|-------------| +| **recipient** | The address that receives withdrawn funds | +| **minWithdrawalAmount** | Minimum ETH balance required before a withdrawal can be triggered | +| **withdrawalNetwork** | Whether funds are withdrawn to an L1 or L2 address | + +### Withdrawal network + +The `withdrawalNetwork` parameter determines how funds are routed when withdrawn: + +- **L1 (`0`)**: Funds are sent to the recipient on L1 via the `L2ToL1MessagePasser`. This initiates a standard L2-to-L1 withdrawal that must be proven and finalized on L1. +- **L2 (`1`)**: Funds are sent directly to the recipient on L2 via a simple ETH transfer. + +## Withdrawal mechanism + +Fee vault withdrawals are **permissionless** — anyone can trigger a withdrawal by calling the `withdraw()` function on any vault. This design ensures that collected fees can always be moved to the configured recipient without relying on a specific operator. + +### How withdrawals work + +1. The caller invokes `withdraw()` on the vault contract. +2. The vault checks that its balance meets the `minWithdrawalAmount` threshold. +3. The entire vault balance is withdrawn (not a partial amount). +4. The `totalProcessed` counter is incremented. +5. Funds are routed based on `withdrawalNetwork`: + - **L2**: Direct ETH transfer to the recipient. + - **L1**: A withdrawal is initiated through the `L2ToL1MessagePasser` with a 400,000 gas limit. The withdrawal must then be [proven and finalized](/op-stack/bridging/withdrawal-flow) on L1. + +## Next steps + +- Learn how to [configure and withdraw from fee vaults](/chain-operators/guides/management/fee-vaults) as a chain operator. +- Understand [transaction fee components](/op-stack/transactions/fees) in detail. +- Review the [smart contracts reference](/op-stack/protocol/smart-contracts) for all predeploy addresses. diff --git a/docs/public-docs/op-stack/transactions/fees.mdx b/docs/public-docs/op-stack/transactions/fees.mdx index d0c75ac5e08..5afab669d1f 100644 --- a/docs/public-docs/op-stack/transactions/fees.mdx +++ b/docs/public-docs/op-stack/transactions/fees.mdx @@ -227,7 +227,7 @@ The Operator fee is a new fee component which enables more customizable fee stru ### Mechanism -The Operator fee is automatically charged for any transaction that is included in a block after the Isthmus activation. This fee is deducted directly from the address that sent the transaction and follows the same semantics as existing fees charged in the EVM. The collected operator fees are sent to the **Operator Fee Vault**, a new vault similar to existing fee vaults. +The Operator fee is automatically charged for any transaction that is included in a block after the Isthmus activation. This fee is deducted directly from the address that sent the transaction and follows the same semantics as existing fees charged in the EVM. The collected operator fees are sent to the **Operator Fee Vault**, a new vault similar to existing [fee vaults](/op-stack/transactions/fee-vaults). **Deposit transactions do not get charged operator fees.** For all deposit transactions, regardless of the operator fee parameter configuration, the operator fee is always zero. @@ -279,25 +279,12 @@ The Operator fee follows standard EVM fee semantics: Transaction pools must account for the additional operator fee when validating transactions. Transactions without sufficient balance to cover the worst-case total cost (including operator fee) will be rejected. -## Sequencer fee vault +## Fee vaults -The Sequencer fee vault collects and holds transaction fees paid to the sequencer during block production on OP Mainnet. These fees cover the cost of posting transaction data to L1, ensuring network sustainability and data availability. - -### Fee collection and distribution - -* **Purpose**: The sequencer deposits collected fees into the Sequencer fee vault. These fees reimburse the sequencer for gas costs when submitting transaction batches to L1. -* **Vault address**: The Sequencer fee vault is predeployed at the address `0x4200000000000000000000000000000000000011` on the OP Mainnet. -* **Fee usage**: Stored fees are eventually transferred to a designated recipient address (e.g., a treasury or distribution contract). - -### How it works - -1. **Fee collection**: During the processing of transactions, the sequencer collects fees from users as part of their transaction costs. These fees are primarily used to cover the gas expenses of posting transaction data to Ethereum L1. -2. **Storage**: Collected fees are deposited into the Sequencer fee vault contract. -3. **Distribution**: The fees are later distributed to the appropriate recipient, typically covering operational costs like L1 gas fees for data availability. - -This system ensures effective fee management, maintaining the security and operation of the Optimism network. +Each fee component is routed to a dedicated [fee vault](/op-stack/transactions/fee-vaults) contract on L2. Unlike Ethereum where the base fee is burned, OP Stack chains collect all fees in these vaults, allowing chain operators to withdraw and manage collected revenue. See the [fee vaults](/op-stack/transactions/fee-vaults) page for details on how vaults work and the [fee vault operations guide](/chain-operators/guides/management/fee-vaults) for managing them. ## Next steps +* Learn how [fee vaults](/op-stack/transactions/fee-vaults) collect and distribute transaction fees. * Read the [differences between Ethereum and OP Stack Chains](/op-stack/protocol/differences) guide. * Read the [L2 to L1 Transactions](/app-developers/bridging/messaging#for-l1-to-l2-transactions) guide. diff --git a/docs/public-docs/rust/index.mdx b/docs/public-docs/rust/index.mdx index fd510d8d158..9f6d4c46f35 100644 --- a/docs/public-docs/rust/index.mdx +++ b/docs/public-docs/rust/index.mdx @@ -1,6 +1,6 @@ --- title: "OP Stack Rust" -description: "Documentation for Rust implementations of the OP Stack: Kona, op-reth, and op-alloy" +description: "Documentation for Rust implementations of the OP Stack: Kona, op-reth, op-alloy, and op-revm" --- # OP Stack Rust @@ -8,7 +8,7 @@ description: "Documentation for Rust implementations of the OP Stack: Kona, op-r Rust implementations for the OP Stack, built by [OP Labs](https://www.oplabs.co/). A unified documentation site for OP Stack Rust components: **Kona** (rollup node & fault proofs), -**op-reth** (execution client), and **op-alloy** (types & providers). +**op-reth** (execution client), **op-alloy** (types & providers), and **op-revm** (OP Stack EVM). ## Quick Start @@ -32,7 +32,7 @@ op-reth node --chain base ## Components - + Modular rollup node and fault proof system. Spec-compliant, performant, and extensible with `no_std` support. @@ -42,6 +42,9 @@ op-reth node --chain base OP Stack types and providers for the Alloy ecosystem. Consensus, RPC, and network crates. + + Optimism variant of revm — deposit transactions, L1/operator fee accounting, and OP Stack precompiles. + ## Built with Kona SDK diff --git a/docs/public-docs/rust/op-revm/index.mdx b/docs/public-docs/rust/op-revm/index.mdx new file mode 100644 index 00000000000..80aea622f2c --- /dev/null +++ b/docs/public-docs/rust/op-revm/index.mdx @@ -0,0 +1,88 @@ +--- +title: "op-revm" +description: "op-revm is the Optimism variant of revm, implementing OP Stack modifications to the EVM." +--- + +`op-revm` is the Optimism variant of [revm](https://github.com/bluealloy/revm) — +the OP Stack's modifications to the Ethereum Virtual Machine, packaged as a +custom EVM built on top of the upstream `revm` framework. + +## Features + +`op-revm` extends `revm` with everything the OP Stack needs on top of vanilla +Ethereum execution: + +- **Deposit transactions** — the L1-to-L2 deposit transaction type and its + execution semantics. +- **L1 cost accounting** — `L1BlockInfo` and per-transaction L1 fee / blob fee + calculation. +- **Operator fees** — operator fee handling introduced in Isthmus and refined + in Jovian. +- **OP-specific precompiles** — including accelerated BN254 pairing. +- **OP halt reasons & transaction errors** — OP Stack–specific execution + failure modes. +- **Hardfork-aware spec selection** — `OpSpecId` selects the right behavior for + each OP Stack hardfork (Bedrock, Regolith, Canyon, Ecotone, Fjord, Granite, + Holocene, Isthmus, Jovian, …). + +## Provenance + +`op-revm` is vendored from upstream +[`bluealloy/revm`'s `crates/op-revm`](https://github.com/bluealloy/revm/tree/main/crates/op-revm) +and imported into the monorepo so it can evolve in lock-step with the rest of +the OP Stack Rust code. The upstream release history is preserved in +[`rust/op-revm/CHANGELOG.md`](https://github.com/ethereum-optimism/optimism/blob/develop/rust/op-revm/CHANGELOG.md). + +## Crate features + +The crate exposes the usual `revm` feature knobs, forwarded through to the +underlying `revm` crate: + +- `default = ["std", "c-kzg", "secp256k1", "portable", "blst"]` +- `std` — enables `std`-dependent code paths in `revm`, `alloy-primitives`, + `serde_json`, etc. +- `serde` — derives `serde` impls and forwards to `revm/serde` and + `alloy-primitives/serde`. +- `portable`, `c-kzg`, `secp256k1`, `blst`, `bn` — pass-through feature gates + for the cryptographic backends. +- `dev`, `memory_limit`, `optional_balance_check`, `optional_block_gas_limit`, + `optional_eip3541`, `optional_eip3607`, `optional_no_base_fee`, + `optional_fee_charge` — debugging and testing knobs forwarded to `revm`. + +`op-revm` supports `no_std` builds: disabling default features (or building +with `--no-default-features`) produces a crate suitable for fault-proof and +zkVM targets such as `riscv32imac-unknown-none-elf`. + +## Building & testing + +From the `rust/` workspace root: + +```bash +# Build +cargo build -p op-revm + +# Run tests with all features +cargo nextest run -p op-revm --all-features + +# no_std check (mirrors upstream's riscv32imac CI) +cargo build -p op-revm \ + --target riscv32imac-unknown-none-elf \ + --no-default-features +``` + +The `no_std` build is also exercised by `just check-no-std` and runs in CI on +every PR that touches `rust/**`. + +## Using op-revm + +`op-revm` is the EVM used by [op-reth](/rust/op-reth) for OP Stack block +execution, and by [Kona](/rust/kona/intro/overview) inside the fault-proof +program and rollup node. Most users will consume it transitively through one +of those components rather than depending on it directly; depend on `op-revm` +directly only when building a custom OP Stack execution environment (for +example, an alternate client or a zkVM proof backend). + +## License + +`op-revm` is licensed under the MIT License — see +[`rust/op-revm/LICENSE`](https://github.com/ethereum-optimism/optimism/blob/develop/rust/op-revm/LICENSE). diff --git a/go.mod b/go.mod index 9b5a6de9207..0204f0eaa07 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 - github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/raft v1.7.3 github.com/hashicorp/raft-boltdb/v2 v2.3.1 @@ -49,7 +48,6 @@ require ( github.com/multiformats/go-multiaddr v0.14.0 github.com/multiformats/go-multiaddr-dns v0.4.1 github.com/olekukonko/tablewriter v0.0.5 - github.com/pkg/errors v0.9.1 github.com/pkg/profile v1.7.0 github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_model v0.6.2 @@ -61,7 +59,6 @@ require ( go.opentelemetry.io/otel v1.40.0 go.opentelemetry.io/otel/trace v1.40.0 golang.org/x/crypto v0.46.0 - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c golang.org/x/mod v0.30.0 golang.org/x/sync v0.19.0 golang.org/x/term v0.38.0 @@ -76,6 +73,8 @@ require ( github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/grafana/pyroscope-go v1.2.7 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect ) @@ -109,7 +108,7 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect - github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect + github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/dchest/siphash v1.2.3 // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect @@ -148,7 +147,6 @@ require ( github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/go-hclog v1.6.2 // indirect github.com/hashicorp/go-immutable-radix v1.0.0 // indirect @@ -260,7 +258,6 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/fx v1.22.2 // indirect @@ -280,7 +277,7 @@ require ( lukechampine.com/blake3 v1.3.0 // indirect ) -replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101701.0-rc.3 +replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101702.1-rc.1 // replace github.com/ethereum/go-ethereum => ../op-geth diff --git a/go.sum b/go.sum index c6137907704..dae0a5eef9e 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg= -github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= +github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc= +github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4= github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -220,8 +220,8 @@ github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.4-0.20251001155152-4eb15ccedf7e h1:iy1vBIzACYUyOVyoADUwvAiq2eOPC0yVsDUdolPwQjk= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.4-0.20251001155152-4eb15ccedf7e/go.mod h1:DYj7+vYJ4cIB7zera9mv4LcAynCL5u4YVfoeUu6Wa+w= -github.com/ethereum-optimism/op-geth v1.101701.0-rc.3 h1:eQS0Y9rWG4/8/EGhZ3rh8hmKsTw+dalLwCmhSncefhA= -github.com/ethereum-optimism/op-geth v1.101701.0-rc.3/go.mod h1:Jws4nBy37/q7vgx8i/nC/dDTSYQbPIsW/ihwOAiIqHE= +github.com/ethereum-optimism/op-geth v1.101702.1-rc.1 h1:2p7pzvmAeZ6xR6pqltf3l6cwCu8HwGR4eWom3v8PwkM= +github.com/ethereum-optimism/op-geth v1.101702.1-rc.1/go.mod h1:HzvOtk7c9KwFaSxRvUBPFHGSjIjomWtw4iSXX6vruQE= github.com/ethereum-optimism/superchain-registry/validation v0.0.0-20260115192958-fb86a23cd30e h1:TO1tUcwbhIrNuea/LCsQJSQ5HDWCHdrzT/5MLC1aIU4= github.com/ethereum-optimism/superchain-registry/validation v0.0.0-20260115192958-fb86a23cd30e/go.mod h1:NZ816PzLU1TLv1RdAvYAb6KWOj4Zm5aInT0YpDVml2Y= github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls= @@ -376,9 +376,6 @@ github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY4 github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -393,8 +390,6 @@ github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9 github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0= github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= diff --git a/justfile b/justfile index 457859d4c56..5d4c4c21b0d 100644 --- a/justfile +++ b/justfile @@ -6,7 +6,7 @@ PYTHON := env('PYTHON', 'python3') TEST_TIMEOUT := env('TEST_TIMEOUT', '10m') -TEST_PKGS := "./op-alt-da/... ./op-batcher/... ./op-chain-ops/... ./op-node/... ./op-proposer/... ./op-challenger/... ./op-faucet/... ./op-dispute-mon/... ./op-conductor/... ./op-program/... ./op-service/... ./op-supervisor/... ./op-test-sequencer/... ./op-fetcher/... ./op-e2e/system/... ./op-e2e/e2eutils/... ./op-e2e/opgeth/... ./op-e2e/interop/... ./op-e2e/actions/altda ./op-e2e/actions/batcher ./op-e2e/actions/derivation ./op-e2e/actions/helpers ./op-e2e/actions/interop ./op-e2e/actions/proofs ./op-e2e/actions/proposer ./op-e2e/actions/safedb ./op-e2e/actions/sequencer ./op-e2e/actions/sync ./op-e2e/actions/upgrades ./packages/contracts-bedrock/scripts/checks/... ./op-dripper/... ./op-devstack/... ./op-deployer/pkg/deployer/artifacts/... ./op-deployer/pkg/deployer/broadcaster/... ./op-deployer/pkg/deployer/clean/... ./op-deployer/pkg/deployer/integration_test/ ./op-deployer/pkg/deployer/integration_test/cli/... ./op-deployer/pkg/deployer/standard/... ./op-deployer/pkg/deployer/state/... ./op-deployer/pkg/deployer/verify/... ./op-sync-tester/... ./op-supernode/..." +TEST_PKGS := "./op-alt-da/... ./op-batcher/... ./op-chain-ops/... ./op-node/... ./op-proposer/... ./op-challenger/... ./op-faucet/... ./op-dispute-mon/... ./op-conductor/... ./op-program/... ./op-service/... ./op-supervisor/... ./op-test-sequencer/... ./op-fetcher/... ./op-e2e/system/... ./op-e2e/e2eutils/... ./op-e2e/opgeth/... ./op-e2e/interop/... ./op-e2e/actions/altda ./op-e2e/actions/batcher ./op-e2e/actions/derivation ./op-e2e/actions/helpers ./op-e2e/actions/interop ./op-e2e/actions/proofs ./op-e2e/actions/proposer ./op-e2e/actions/safedb ./op-e2e/actions/sequencer ./op-e2e/actions/sync ./op-e2e/actions/upgrades ./packages/contracts-bedrock/scripts/checks/... ./ops/scripts/... ./op-dripper/... ./op-devstack/... ./op-deployer/pkg/deployer/artifacts/... ./op-deployer/pkg/deployer/broadcaster/... ./op-deployer/pkg/deployer/clean/... ./op-deployer/pkg/deployer/integration_test/ ./op-deployer/pkg/deployer/integration_test/cli/... ./op-deployer/pkg/deployer/standard/... ./op-deployer/pkg/deployer/state/... ./op-deployer/pkg/deployer/verify/... ./op-sync-tester/... ./op-supernode/..." FRAUD_PROOF_TEST_PKGS := "./op-e2e/faultproofs/..." @@ -352,6 +352,15 @@ build-rust-release: check-nut-locks: go run ./ops/scripts/check-nut-locks +# Snapshots current-upgrade-bundle.json as a fork's NUT bundle and updates the lock file. +nut-snapshot-for fork: + go run ./ops/scripts/nut-snapshot-for {{fork}} + +# Verifies a fork's NUT bundle was correctly built from its recorded commit. +nut-provenance-verify fork: + go run ./ops/scripts/nut-provenance-verify {{fork}} + + # Checks that TODO comments have corresponding issues. todo-checker: ./ops/scripts/todo-checker.sh diff --git a/mise.toml b/mise.toml index e4822eab208..910a9ccb94c 100644 --- a/mise.toml +++ b/mise.toml @@ -6,7 +6,7 @@ go = "1.24.13" golangci-lint = "2.8.0" gotestsum = "1.12.3" mockery = "2.53.3" -rust = "1.94.0" +rust = ["1.94.0", { version = "nightly-2026-02-20", components = "rustfmt" }] python = "3.12.0" uv = "0.5.5" jq = "1.7.1" @@ -18,6 +18,7 @@ just = "1.46.0" make = "4.4.1" svm-rs = "0.5.19" +"github:nextest-rs/nextest" = { version = "0.9.132", version_prefix = "cargo-nextest-", bin = "cargo-nextest" } # Go dependencies "go:github.com/ethereum/go-ethereum/cmd/abigen" = "1.15.10" @@ -61,7 +62,6 @@ gotestsum = "ubi:gotestyourself/gotestsum" kurtosis = "ubi:kurtosis-tech/kurtosis-cli-release-artifacts[exe=kurtosis]" mockery = "ubi:vektra/mockery" svm-rs = "ubi:alloy-rs/svm-rs[exe=svm]" - # These are disabled, but latest mise versions error if they don't have a known # install source even though it won't ever actually use that source. kontrol = "ubi:ethereum-optimism/fake-kontrol" diff --git a/op-acceptance-tests/tests/flashblocks/flashblocks_transfer_test.go b/op-acceptance-tests/tests/flashblocks/flashblocks_transfer_test.go index ef434f9fd08..34b6b1cc668 100644 --- a/op-acceptance-tests/tests/flashblocks/flashblocks_transfer_test.go +++ b/op-acceptance-tests/tests/flashblocks/flashblocks_transfer_test.go @@ -5,7 +5,6 @@ import ( "strings" "sync" "testing" - "time" "github.com/ethereum-optimism/optimism/op-devstack/devtest" "github.com/ethereum-optimism/optimism/op-devstack/presets" @@ -16,17 +15,16 @@ import ( "github.com/stretchr/testify/require" ) -// TestFlashblocksTransfer checks that a transfer gets reflected in a flashblock before the transaction is confirmed in a block +// TestFlashblocksTransfer checks that a transfer is reflected in the op-rbuilder +// flashblock stream for the same block that eventually includes the transaction. // // Expectations: // -// - There must have been a Flashblock containing a new_account_balance corresponding to Bob's -// account. This flashblock would be representative of the flashblock including Alice-to-Bob -// transaction. -// - The flashblock's time (in seconds) must be less than or equal to the Transaction's block -// time (in seconds). (Can't check the block time beyond the granularity of seconds) -// - That Flashblock's time in nanoseconds must be before the approximated transaction -// confirmation time recorded previously. +// - There must have been a Flashblock whose metadata.receipts contains Alice's transaction hash. +// This identifies the flashblock that carried Alice's transfer to Bob. +// - The transaction's confirmed block number must match the flashblock's block number. +// - If Bob's balance is reported in new_account_balances for that flashblock, it must match the +// on-chain balance at the transaction's inclusion block. func TestFlashblocksTransfer(gt *testing.T) { t := devtest.ParallelT(gt) // Example error with kona-node: @@ -51,62 +49,72 @@ func TestFlashblocksTransfer(gt *testing.T) { // Drive a couple blocks on the test sequencer so the faucet L2 funding tx has a chance to land before we rely on it. driveViaTestSequencer(t, sys, 2) + // Subscribe directly to op-rbuilder here: rollup-boost may intentionally drop + // flashblocks, but this test needs to observe the flashblock carrying Alice's + // transfer to Bob. fbClient := sources.NewFlashblockClient( - sys.L2RollupBoost.FlashblocksClient(), - t.Logger().With("stream_source", "rollup-boost"), + sys.L2OPRBuilder.FlashblocksClient(), + t.Logger().With("stream_source", "op-rbuilder"), 100, ) startClient(t, fbClient) bob := sys.Wallet.NewEOA(sys.L2EL) - txCh := make(chan *txplan.PlannedTx) + alice := sys.FunderL2.NewFundedEOA(eth.ThreeHundredthsEther) + bobAddress := bob.Address() + tx := txplan.NewPlannedTx(alice.Plan(), txplan.WithTo(&bobAddress), txplan.WithValue(eth.OneHundredthEther)) + signedTx, err := tx.Signed.Eval(t.Ctx()) + t.Require().NoError(err) + txHash := strings.ToLower(signedTx.Hash().Hex()) + + // Buffer the result so cleanup cannot deadlock if we time out before reading it. + txCh := make(chan error, 1) var wg sync.WaitGroup defer wg.Wait() wg.Add(1) go func() { defer wg.Done() - alice := sys.FunderL2.NewFundedEOA(eth.ThreeHundredthsEther) - bobAddress := bob.Address() - txCh <- alice.Transact(alice.Plan(), txplan.WithTo(&bobAddress), txplan.WithValue(eth.OneHundredthEther)) + _, err := tx.Success.Eval(t.Ctx()) + txCh <- err close(txCh) }() var blockNumber int var observedBalance *big.Int - var flashblockTime time.Time outer: for { select { case fb, ok := <-fbClient.Next(): t.Require().True(ok, "client channel closed before we found the transaction") - balanceStr, found := fb.Metadata.NewAccountBalances[strings.ToLower(bob.Address().Hex())] - if found { - flashblockTime = time.Now() + if _, found := fb.Metadata.Receipts[txHash]; found { blockNumber = fb.Metadata.BlockNumber - observedBalance, ok = new(big.Int).SetString(balanceStr[2:], 16) - t.Require().True(ok) + if balanceStr, ok := fb.Metadata.NewAccountBalances[strings.ToLower(bob.Address().Hex())]; ok { + observedBalance, ok = new(big.Int).SetString(balanceStr[2:], 16) + t.Require().True(ok) + } break outer } case <-t.Ctx().Done(): - t.Require().NoError(t.Ctx().Err(), "never found the transaction") + t.Require().NoError(t.Ctx().Err(), "never found the transaction in flashblock receipts") } } - var txBlock eth.BlockRef select { - case tx, ok := <-txCh: - t.Require().True(ok) - var err error - txBlock, err = tx.IncludedBlock.Eval(t.Ctx()) + case err := <-txCh: t.Require().NoError(err) case <-t.Ctx().Done(): t.Require().NoError(t.Ctx().Err()) } - expectedBalance, err := sys.L2EL.EthClient().BalanceAt(t.Ctx(), bob.Address(), new(big.Int).SetUint64(txBlock.Number)) + txBlock, err := tx.IncludedBlock.Eval(t.Ctx()) t.Require().NoError(err) - require.Equal(t, expectedBalance, observedBalance, "Bob's balance must be correct as per exactly what Alice transferred to them") + require.Equal(t, int(txBlock.Number), blockNumber, "the transaction's block number should be the same as the flashblock's block number") - require.Equal(t, int(txBlock.Number), blockNumber, "the transaction's block number should be the same as the flashblock's parent block number") - require.LessOrEqual(t, flashblockTime.Unix(), int64(txBlock.Time), "the transaction's block time (in seconds) should be less than or equal to the flashblock's time (in seconds)") + if observedBalance == nil { + t.Require().Fail("matched flashblock via receipts but Bob was absent from new_account_balances", "bob=%s txHash=%s", bob.Address(), txHash) + } + + expectedBalance, err := sys.L2EL.EthClient().BalanceAt(t.Ctx(), bob.Address(), new(big.Int).SetUint64(txBlock.Number)) + t.Require().NoError(err) + require.Equal(t, expectedBalance, observedBalance, "Bob's balance must match the on-chain balance at the transaction inclusion block when reported in flashblock metadata") } diff --git a/op-acceptance-tests/tests/flashblocks/utils.go b/op-acceptance-tests/tests/flashblocks/utils.go index b0d68553a28..3d289ddb688 100644 --- a/op-acceptance-tests/tests/flashblocks/utils.go +++ b/op-acceptance-tests/tests/flashblocks/utils.go @@ -3,6 +3,7 @@ package flashblocks import ( "context" "sync" + "time" "github.com/ethereum-optimism/optimism/op-devstack/devtest" "github.com/ethereum-optimism/optimism/op-devstack/presets" @@ -28,6 +29,12 @@ func driveViaTestSequencer(t devtest.T, sys *presets.SingleChainWithFlashblocks, // Ensure the sequencer EL has produced at least one unsafe block before subscribing. sys.L2EL.WaitForBlockNumber(1) + // Wait until L2 time catches up to wall clock time before relying on flashblocks. + // During startup the builder may report "unsafe block timestamp is too old" / + // "FCU arrived too late" while the sequencer is still catching up, which makes + // early flashblock receipt assertions flaky. + sys.L2EL.WaitForTime(uint64(time.Now().Unix())) + // Log the latest unsafe head and L1 origin to confirm block production before listening. head = sys.L2EL.BlockRefByLabel(eth.Unsafe) sys.Log.Info("Pre-listen unsafe head", "unsafe", head) diff --git a/op-acceptance-tests/tests/interop/proofs/fpp/fpp_test.go b/op-acceptance-tests/tests/interop/proofs/fpp/fpp_test.go index 5d3c65ad3ea..3867cb6046f 100644 --- a/op-acceptance-tests/tests/interop/proofs/fpp/fpp_test.go +++ b/op-acceptance-tests/tests/interop/proofs/fpp/fpp_test.go @@ -22,8 +22,6 @@ func TestFPP(gt *testing.T) { func TestNextSuperRootNotFound(gt *testing.T) { t := devtest.ParallelT(gt) - // TODO(#19180): Unskip this once supernode is updated. - t.Skip("Supernode does not yet return optimistic blocks until blocks are fully validated") sys := presets.NewSimpleInteropSupernodeProofs(t, presets.WithChallengerCannonKonaEnabled()) blockTime := sys.L2ChainA.Escape().RollupConfig().BlockTime diff --git a/op-acceptance-tests/tests/interop/proofs/serial/interop_fault_proofs_test.go b/op-acceptance-tests/tests/interop/proofs/serial/interop_fault_proofs_test.go index e95042b5613..89419caf1cd 100644 --- a/op-acceptance-tests/tests/interop/proofs/serial/interop_fault_proofs_test.go +++ b/op-acceptance-tests/tests/interop/proofs/serial/interop_fault_proofs_test.go @@ -12,8 +12,6 @@ import ( func TestInteropFaultProofs(gt *testing.T) { t := devtest.ParallelT(gt) - // TODO(#19180): Unskip this once supernode is updated. - t.Skip("Supernode does not yet return optimistic blocks until blocks are fully validated") sys := presets.NewSimpleInteropSupernodeProofs(t, presets.WithChallengerCannonKonaEnabled()) sfp.RunSuperFaultProofTest(t, sys) } @@ -73,3 +71,19 @@ func TestInteropFaultProofs_InvalidBlock(gt *testing.T) { sys := presets.NewSimpleInteropSupernodeProofs(t, presets.WithChallengerCannonKonaEnabled()) sfp.RunInvalidBlockTest(t, sys) } + +func TestInteropFaultProofs_DepositMessage_InvalidExecution(gt *testing.T) { + t := devtest.SerialT(gt) + sys := presets.NewSimpleInteropSupernodeProofs(t, presets.WithChallengerCannonKonaEnabled()) + sfp.RunDepositMessageInvalidExecutionTest(t, sys) +} + +func TestInteropFaultProofs_MessageExpiry(gt *testing.T) { + t := devtest.SerialT(gt) + const messageExpiryWindow = uint64(12) // 12 seconds for fast test + sys := presets.NewSimpleInteropSupernodeProofs(t, + presets.WithChallengerCannonKonaEnabled(), + presets.WithMessageExpiryWindow(messageExpiryWindow), + ) + sfp.RunMessageExpiryTest(t, sys, messageExpiryWindow) +} diff --git a/op-acceptance-tests/tests/osaka_on_l2_test.go b/op-acceptance-tests/tests/osaka_on_l2_test.go new file mode 100644 index 00000000000..770c5730550 --- /dev/null +++ b/op-acceptance-tests/tests/osaka_on_l2_test.go @@ -0,0 +1,74 @@ +package tests + +import ( + "math/big" + "testing" + + "github.com/ethereum-optimism/optimism/op-core/forks" + "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/presets" + "github.com/ethereum-optimism/optimism/op-devstack/sysgo" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" +) + +var modexpPrecompile = common.HexToAddress("0x0000000000000000000000000000000000000005") + +// buildModExpInput constructs input data for the MODEXP precompile (address 0x05). +// Format: +func buildModExpInput(base, exp, mod []byte) []byte { + input := make([]byte, 0, 96+len(base)+len(exp)+len(mod)) + input = append(input, common.LeftPadBytes(new(big.Int).SetInt64(int64(len(base))).Bytes(), 32)...) + input = append(input, common.LeftPadBytes(new(big.Int).SetInt64(int64(len(exp))).Bytes(), 32)...) + input = append(input, common.LeftPadBytes(new(big.Int).SetInt64(int64(len(mod))).Bytes(), 32)...) + input = append(input, base...) + input = append(input, exp...) + input = append(input, mod...) + return input +} + +func TestEIP7823UpperBoundModExp(gt *testing.T) { + t := devtest.ParallelT(gt) + sysgo.SkipOnOpGeth(t, "osaka is not supported in op-geth") + + karstOffset := uint64(3) + sys := presets.NewMinimal(t, presets.WithDeployerOptions(sysgo.WithKarstAtOffset(&karstOffset))) + + activationBlock := sys.L2Chain.AwaitActivation(t, forks.Karst) + t.Require().Greater(activationBlock.Number, uint64(0), "karst must not activate at genesis") + preForkBlockNum := activationBlock.Number - 1 + postForkBlockNum := activationBlock.Number + 1 + sys.L2EL.WaitForBlockNumber(postForkBlockNum) + + l2Client := sys.L2EL.EthClient() + + // Modexp input exceeding EIP-7823 limits: modulus length is 1025 bytes (limit is 1024) + oversizeMod := make([]byte, 1025) + oversizeMod[1024] = 5 + exceedingLimitInput := buildModExpInput([]byte{2}, []byte{3}, oversizeMod) + + // Pre-fork: oversized modexp input should succeed (EIP-7823 not yet active) + result, err := l2Client.Call(t.Ctx(), ethereum.CallMsg{ + To: &modexpPrecompile, + Data: exceedingLimitInput, + }, rpc.BlockNumber(preForkBlockNum)) + t.Require().NoError(err) + t.Require().Len(result, 1025, "pre-fork: modexp with oversized input should return 1025-byte result") + + // Post-fork: oversized modexp input should fail (EIP-7823 enforced) + result, err = l2Client.Call(t.Ctx(), ethereum.CallMsg{ + To: &modexpPrecompile, + Data: exceedingLimitInput, + }, rpc.BlockNumber(postForkBlockNum)) + t.Require().Error(err) + t.Require().Empty(result, "post-fork: modexp with oversized input should return empty result due to EIP-7823") + + // Post-fork: within-limit modexp input should still succeed + result, err = l2Client.Call(t.Ctx(), ethereum.CallMsg{ + To: &modexpPrecompile, + Data: buildModExpInput([]byte{2}, []byte{3}, []byte{5}), + }, rpc.BlockNumber(postForkBlockNum)) + t.Require().NoError(err) + t.Require().Equal([]byte{3}, result, "2^3 mod 5 should equal 3") +} diff --git a/op-acceptance-tests/tests/safeheaddb_elsync/safeheaddb_test.go b/op-acceptance-tests/tests/safeheaddb_elsync/safeheaddb_test.go index 3ed7357687c..a78086be0e4 100644 --- a/op-acceptance-tests/tests/safeheaddb_elsync/safeheaddb_test.go +++ b/op-acceptance-tests/tests/safeheaddb_elsync/safeheaddb_test.go @@ -12,12 +12,22 @@ import ( ) func newSingleChainMultiNodeELSync(t devtest.T) *presets.SingleChainMultiNode { - return presets.NewSingleChainMultiNode(t, + // Use WithoutCheck because the default preset sync check uses 30 attempts + // (60s) for CrossSafe matching, which is insufficient for EL Sync mode. + // EL Sync must complete the initial sync phase before derivation can start, + // so CrossSafe takes longer to advance than in CL Sync mode. + sys := presets.NewSingleChainMultiNodeWithoutCheck(t, presets.WithGlobalL2CLOption(sysgo.L2CLOptionFn(func(p devtest.T, _ sysgo.ComponentTarget, cfg *sysgo.L2CLConfig) { cfg.VerifierSyncMode = sync.ELSync cfg.SafeDBPath = p.TempDir() })), ) + // Run the initial sync check with 60 attempts (120s) to accommodate EL Sync. + dsl.CheckAll(t, + sys.L2CLB.MatchedFn(sys.L2CL, types.CrossSafe, 60), + sys.L2CLB.MatchedFn(sys.L2CL, types.LocalUnsafe, 60), + ) + return sys } func TestTruncateDatabaseOnELResync(gt *testing.T) { @@ -55,8 +65,8 @@ func TestTruncateDatabaseOnELResync(gt *testing.T) { sys.L2CLB.Start() sys.L2ELB.PeerWith(sys.L2EL) - sys.L2CLB.Matched(sys.L2CL, types.LocalSafe, 30) - sys.L2CLB.Advanced(types.LocalSafe, 1, 30) // At least one safe head db update after resync + sys.L2CLB.Matched(sys.L2CL, types.LocalSafe, 60) + sys.L2CLB.Advanced(types.LocalSafe, 1, 60) // At least one safe head db update after resync sys.L2CLB.VerifySafeHeadDatabaseMatches(sys.L2CL) } @@ -90,8 +100,8 @@ func TestNotTruncateDatabaseOnRestartWithExistingDatabase(gt *testing.T) { sys.L2CLB.Start() - sys.L2CLB.Matched(sys.L2CL, types.LocalSafe, 30) - sys.L2CLB.Advanced(types.LocalSafe, 1, 30) // At least one safe head db update after resync + sys.L2CLB.Matched(sys.L2CL, types.LocalSafe, 60) + sys.L2CLB.Advanced(types.LocalSafe, 1, 60) // At least one safe head db update after resync sys.L2CLB.VerifySafeHeadDatabaseMatches(sys.L2CL, dsl.WithMinRequiredL2Block(preRestartSafeBlock)) } diff --git a/op-acceptance-tests/tests/superfaultproofs/superfaultproofs.go b/op-acceptance-tests/tests/superfaultproofs/superfaultproofs.go index cd66695402b..43acdcd8345 100644 --- a/op-acceptance-tests/tests/superfaultproofs/superfaultproofs.go +++ b/op-acceptance-tests/tests/superfaultproofs/superfaultproofs.go @@ -1055,6 +1055,146 @@ func RunInvalidBlockTest(t devtest.T, sys *presets.SimpleInterop) { } } +// RunMessageExpiryTest verifies that when a cross-chain message expires (the +// executing message's block timestamp exceeds the init message timestamp plus +// the message expiry window), the block containing the executing message is +// replaced during consolidation. The system must be configured with a short +// message expiry window via WithMessageExpiryWindow. +// +// msgExpiryWindow is the configured message expiry window in seconds; it must +// match the value passed to WithMessageExpiryWindow when creating the system. +func RunMessageExpiryTest(t devtest.T, sys *presets.SimpleInterop, msgExpiryWindow uint64) { + t.Require().NotNil(sys.SuperRoots, "supernode is required for this test") + rng := rand.New(rand.NewSource(1234)) + + chains := orderedChains(sys) + t.Require().Len(chains, 2, "expected exactly 2 interop chains") + + aliceA := sys.FunderA.NewFundedEOA(eth.OneEther) + aliceB := aliceA.AsEL(sys.L2ELB) + sys.FunderB.Fund(aliceB, eth.OneEther) + + // Send an initiating message on chain A. + eventLogger := aliceA.DeployEventLogger() + initMsg := aliceA.SendRandomInitMessage(rng, eventLogger, 2, 10) + + // Record the init message's block timestamp. + initBlockNum := bigs.Uint64Strict(initMsg.BlockNumber()) + initTimestamp := sys.L2ChainA.TimestampForBlockNum(initBlockNum) + + // Calculate target block numbers past expiry for each chain independently. + // The message expires when: initTimestamp + expiryWindow < execTimestamp. + // Add extra blocks for safety margin. + blockTimeA := sys.L2ChainA.Escape().RollupConfig().BlockTime + blockTimeB := sys.L2ChainB.Escape().RollupConfig().BlockTime + t.Require().NotZero(blockTimeA, "block time A must be non-zero") + t.Require().NotZero(blockTimeB, "block time B must be non-zero") + + currentBlockA := sys.L2ELA.BlockRefByLabel(eth.Unsafe) + currentBlockB := sys.L2ELB.BlockRefByLabel(eth.Unsafe) + blocksNeededA := (msgExpiryWindow / blockTimeA) + 2 + blocksNeededB := (msgExpiryWindow / blockTimeB) + 2 + targetBlockA := currentBlockA.Number + blocksNeededA + targetBlockB := currentBlockB.Number + blocksNeededB + + // Wait for both chains to produce blocks past the expiry window. + sys.L2ELA.Reached(eth.Unsafe, targetBlockA, 60) + sys.L2ELB.Reached(eth.Unsafe, targetBlockB, 60) + + // Stop batcher B so we control block production on chain B. + sys.L2BatcherB.Stop() + + // Build the exec tx without submitting to the mempool. + // InteropMempoolFiltering would reject the expired message, so we + // bypass the mempool by injecting the raw tx via the test sequencer. + // This models a malicious sequencer force-including an invalid tx. + rawExecTx, execTxHash := aliceB.PrepareExecTx(initMsg) + + // Inject the expired exec message into a block on chain B via test sequencer. + parentB := sys.L2ELB.BlockRefByLabel(eth.Unsafe) + chainBID := sys.L2ChainB.ChainID() + sys.TestSequencer.SequenceBlockWithTxs(t, chainBID, parentB.Hash, [][]byte{rawExecTx}) + + // Also advance chain A by one empty block to keep timestamps aligned. + parentA := sys.L2ELA.BlockRefByLabel(eth.Unsafe) + chainAID := sys.L2ChainA.ChainID() + sys.TestSequencer.SequenceBlock(t, chainAID, parentA.Hash) + + // The injected block is the new unsafe head on chain B. + newHeadB := sys.L2ELB.BlockRefByLabel(eth.Unsafe) + execBlockNum := newHeadB.Number + + // Verify the expired exec tx is actually in the injected block before consolidation. + sys.L2ELB.AssertTxInBlock(execBlockNum, execTxHash) + + // Restart batcher B so batch data gets submitted to L1. + sys.L2BatcherB.Start() + + endTimestamp := sys.L2ChainB.TimestampForBlockNum(execBlockNum) + t.Require().Greaterf(endTimestamp, initTimestamp+msgExpiryWindow, + "exec message timestamp %d should exceed init timestamp %d + expiry window %d", + endTimestamp, initTimestamp, msgExpiryWindow) + startTimestamp := endTimestamp - 1 + + // Wait for cross-safe validation, which should replace the invalid block. + sys.SuperRoots.AwaitValidatedTimestamp(endTimestamp) + sys.L2CLB.Reached(types.CrossSafe, execBlockNum, 30) + + // Verify the expired exec tx was reorged out during consolidation. + sys.L2ELB.AssertTxNotInBlock(execBlockNum, execTxHash) + + l1HeadCurrent := latestRequiredL1(sys.SuperRoots.SuperRootAtTimestamp(endTimestamp)) + + crossSafeSuperRootEnd := superRootAtTimestamp(t, chains, endTimestamp) + + firstOptimistic := optimisticBlockAtTimestamp(t, sys.SuperRoots.QueryAPI(), chains[0].ID, endTimestamp) + secondOptimistic := optimisticBlockAtTimestamp(t, sys.SuperRoots.QueryAPI(), chains[1].ID, endTimestamp) + + start := superRootAtTimestamp(t, chains, startTimestamp) + paddingStep := func(step uint64) []byte { + return marshalTransition(start, step, firstOptimistic, secondOptimistic) + } + + preReplacementSuperRoot := eth.NewSuperV1(endTimestamp, + eth.ChainIDAndOutput{ChainID: chains[0].ID, Output: firstOptimistic.OutputRoot}, + eth.ChainIDAndOutput{ChainID: chains[1].ID, Output: secondOptimistic.OutputRoot}) + + tests := []*transitionTest{ + { + Name: "Consolidate-ExpectInvalidPendingBlock", + AgreedClaim: paddingStep(consolidateStep), + DisputedClaim: preReplacementSuperRoot.Marshal(), + DisputedTraceIndex: consolidateStep, + ExpectValid: false, + L1Head: l1HeadCurrent, + ClaimTimestamp: endTimestamp, + }, + { + Name: "Consolidate-ReplaceExpiredMessage", + AgreedClaim: paddingStep(consolidateStep), + DisputedClaim: crossSafeSuperRootEnd.Marshal(), + DisputedTraceIndex: consolidateStep, + ExpectValid: true, + L1Head: l1HeadCurrent, + ClaimTimestamp: endTimestamp, + }, + } + + challengerCfg := sys.L2ChainA.Escape().L2Challengers()[0].Config() + gameDepth := sys.DisputeGameFactory().GameImpl(gameTypes.SuperCannonKonaGameType).SplitDepth() + for _, test := range tests { + t.Run(test.Name+"-fpp", func(t devtest.T) { + runKonaInteropProgram(t, challengerCfg.CannonKona, test.L1Head.Hash, + test.AgreedClaim, crypto.Keccak256Hash(test.DisputedClaim), + test.ClaimTimestamp, test.ExpectValid) + }) + + t.Run(test.Name+"-challenger", func(t devtest.T) { + runChallengerProviderTest(t, sys.SuperRoots.QueryAPI(), gameDepth, startTimestamp, test.ClaimTimestamp, test) + }) + } +} + // RunDepositMessageTest verifies that the fault proof system correctly handles // consolidation when a cross-chain message is initiated via an L1 deposit transaction. func RunDepositMessageTest(t devtest.T, sys *presets.SimpleInterop) { @@ -1125,3 +1265,92 @@ func RunDepositMessageTest(t devtest.T, sys *presets.SimpleInterop) { }) } } + +// RunDepositMessageInvalidExecutionTest verifies that the fault proof system correctly +// detects an invalid executing message when the initiating message was sent via an L1 +// deposit transaction. The executing message uses an invalid identifier, so consolidation +// must replace the optimistic block with the cross-safe result. +func RunDepositMessageInvalidExecutionTest(t devtest.T, sys *presets.SimpleInterop) { + t.Require().NotNil(sys.SuperRoots, "supernode is required for this test") + rng := rand.New(rand.NewSource(9012)) + + chains := orderedChains(sys) + t.Require().Len(chains, 2, "expected exactly 2 interop chains") + + aliceA := sys.FunderA.NewFundedEOA(eth.OneEther) + aliceL1 := aliceA.AsEL(sys.L1EL) + sys.FunderL1.Fund(aliceL1, eth.OneEther) + aliceB := aliceA.AsEL(sys.L2ELB) + sys.FunderB.Fund(aliceB, eth.OneEther) + + eventLogger := aliceA.DeployEventLogger() + depositEOA := aliceA.ViaDepositTx(aliceL1, sys.L2ELA, sys.L2ChainA) + initMsg := depositEOA.SendRandomInitMessage(rng, eventLogger) + execMsg := aliceB.SendInvalidExecMessage(initMsg) + + endTimestamp := sys.L2ChainB.TimestampForBlockNum(bigs.Uint64Strict(execMsg.BlockNumber())) + startTimestamp := endTimestamp - 1 + + sys.SuperRoots.AwaitValidatedTimestamp(endTimestamp) + sys.L2CLB.Reached(types.CrossSafe, bigs.Uint64Strict(execMsg.BlockNumber()), 10) + sys.L2ELB.AssertExecMessageNotInBlock(execMsg) + + l1HeadCurrent := latestRequiredL1(sys.SuperRoots.SuperRootAtTimestamp(endTimestamp)) + + start := superRootAtTimestamp(t, chains, startTimestamp) + crossSafeEnd := superRootAtTimestamp(t, chains, endTimestamp) + + firstOptimistic := optimisticBlockAtTimestamp(t, sys.SuperRoots.QueryAPI(), chains[0].ID, endTimestamp) + secondOptimistic := optimisticBlockAtTimestamp(t, sys.SuperRoots.QueryAPI(), chains[1].ID, endTimestamp) + paddingStep := func(step uint64) []byte { + return marshalTransition(start, step, firstOptimistic, secondOptimistic) + } + + optimisticEnd := eth.NewSuperV1(endTimestamp, + eth.ChainIDAndOutput{ChainID: chains[0].ID, Output: firstOptimistic.OutputRoot}, + eth.ChainIDAndOutput{ChainID: chains[1].ID, Output: secondOptimistic.OutputRoot}) + + tests := []*transitionTest{ + { + Name: "Consolidate", + AgreedClaim: paddingStep(consolidateStep), + DisputedClaim: crossSafeEnd.Marshal(), + DisputedTraceIndex: consolidateStep, + ExpectValid: true, + L1Head: l1HeadCurrent, + ClaimTimestamp: endTimestamp, + }, + { + Name: "Consolidate-InvalidNoChange", + AgreedClaim: paddingStep(consolidateStep), + DisputedClaim: paddingStep(consolidateStep), + DisputedTraceIndex: consolidateStep, + ExpectValid: false, + L1Head: l1HeadCurrent, + ClaimTimestamp: endTimestamp, + }, + { + Name: "Consolidate-ExpectInvalidPendingBlock", + AgreedClaim: paddingStep(consolidateStep), + DisputedClaim: optimisticEnd.Marshal(), + DisputedTraceIndex: consolidateStep, + ExpectValid: false, + L1Head: l1HeadCurrent, + ClaimTimestamp: endTimestamp, + }, + } + + challengerCfg := sys.L2ChainA.Escape().L2Challengers()[0].Config() + gameDepth := sys.DisputeGameFactory().GameImpl(gameTypes.SuperCannonKonaGameType).SplitDepth() + for _, test := range tests { + t.Run(test.Name+"-fpp", func(t devtest.T) { + runKonaInteropProgram(t, challengerCfg.CannonKona, test.L1Head.Hash, + test.AgreedClaim, crypto.Keccak256Hash(test.DisputedClaim), + test.ClaimTimestamp, test.ExpectValid) + }) + + t.Run(test.Name+"-challenger", func(t devtest.T) { + runChallengerProviderTest(t, sys.SuperRoots.QueryAPI(), gameDepth, startTimestamp, test.ClaimTimestamp, test) + }) + } +} diff --git a/op-acceptance-tests/tests/sync/elsync/offset_el_safe/init_test.go b/op-acceptance-tests/tests/sync/elsync/offset_el_safe/init_test.go new file mode 100644 index 00000000000..02d31da988d --- /dev/null +++ b/op-acceptance-tests/tests/sync/elsync/offset_el_safe/init_test.go @@ -0,0 +1,21 @@ +package offset_el_safe + +import ( + "time" + + "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/presets" + "github.com/ethereum-optimism/optimism/op-devstack/sysgo" + nodeSync "github.com/ethereum-optimism/optimism/op-node/rollup/sync" +) + +const testOffset = 10 * time.Second + +func newOffsetELSafeSystem(t devtest.T) *presets.SingleChainMultiNode { + return presets.NewSingleChainMultiNode(t, + presets.WithGlobalL2CLOption(sysgo.L2CLOptionFn(func(_ devtest.T, _ sysgo.ComponentTarget, cfg *sysgo.L2CLConfig) { + cfg.VerifierSyncMode = nodeSync.ELSync + cfg.OffsetELSafe = testOffset + })), + ) +} diff --git a/op-acceptance-tests/tests/sync/elsync/offset_el_safe/sync_test.go b/op-acceptance-tests/tests/sync/elsync/offset_el_safe/sync_test.go new file mode 100644 index 00000000000..56ac263c669 --- /dev/null +++ b/op-acceptance-tests/tests/sync/elsync/offset_el_safe/sync_test.go @@ -0,0 +1,85 @@ +package offset_el_safe + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/dsl" + "github.com/ethereum-optimism/optimism/op-devstack/sysgo" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" +) + +// TestELSyncSafeRetractedByOffset verifies that when OffsetELSafe is configured on +// the verifier, the safe and finalized EL heads are set behind the unsafe head after +// EL sync completes. +// +// Flow: +// 1. Both nodes advance LocalSafe (ensures the CL has real finalized state). +// 2. Stop/wipe the verifier EL to force a full EL sync on restart. +// 3. Stop the batcher, then advance the sequencer's unsafe chain further. +// Stopping the batcher makes the gap permanent: derivation can never advance +// safe past the last batched block, eliminating timing flakes. +// 4. Restart verifier, peer ELs, and wait for EL sync to complete. +// 5. Assert safe/finalized are behind unsafe. +func TestELSyncSafeRetractedByOffset(gt *testing.T) { + t := devtest.ParallelT(gt) + sysgo.SkipOnOpReth(t, "not supported (peering issue)") + sys := newOffsetELSafeSystem(t) + require := t.Require() + logger := t.Logger() + + // Advance both CLs to LocalSafe so the verifier CL has valid finalized state + // before we stop it (prevents "forkchoice not initialized" after EL wipe). + dsl.CheckAll(t, + sys.L2CL.AdvancedFn(types.LocalSafe, 1, 30), + sys.L2CLB.AdvancedFn(types.LocalSafe, 1, 30)) + sys.L2CLB.Matched(sys.L2CL, types.LocalSafe, 30) + + // Stop verifier and wipe its EL to force a full EL sync on restart. + sys.L2ELB.Stop() + sys.L2CLB.Stop() + + // Stop the batcher so new sequencer blocks are unsafe-only. + // This creates a permanent gap between safe and unsafe after EL sync: + // derivation can only advance safe up to the last batched block. + sys.L2Batcher.Stop() + + // Advance sequencer further while verifier is down (unsafe only, no batches). + sys.L2CL.Advanced(types.LocalUnsafe, 3, 30) + + sys.L2ELB.Start() + sys.L2CLB.Start() + sys.L2ELB.PeerWith(sys.L2EL) + + // Wait for the verifier CL to advance LocalSafe, which confirms: + // - EL sync completed (blocks transferred via p2p) + // - CL detected completion and sent the forkchoice update (setting safe/finalized) + // - Derivation began processing old batched data + // Because the batcher is stopped, derivation can only reach the last batched + // block — the gap between safe and unsafe is permanent. + sys.L2CLB.Advanced(types.LocalSafe, 1, 30) + + unsafeHead := sys.L2ELB.BlockRefByLabel(eth.Unsafe) + safeHead := sys.L2ELB.SafeHead().BlockRef + finalizedHead := sys.L2ELB.FinalizedHead().BlockRef + + logger.Info("Verifier heads after EL sync", + "unsafe", unsafeHead.Number, + "safe", safeHead.Number, + "finalized", finalizedHead.Number) + + // Safe and finalized must be behind unsafe when offset is configured. + // The batcher is stopped, so derivation can never close the gap — these + // assertions are deterministic regardless of timing. + require.Greater(unsafeHead.Number, safeHead.Number, + "safe head should be behind unsafe head when OffsetELSafe is configured") + require.Greater(unsafeHead.Number, finalizedHead.Number, + "finalized head should be behind unsafe head when OffsetELSafe is configured") + require.GreaterOrEqual(safeHead.Number, finalizedHead.Number, + "safe head should be at or ahead of finalized head") + + retraction := unsafeHead.Number - safeHead.Number + logger.Info("Observed retraction", "blocks", retraction) + require.Greater(retraction, uint64(0), "retraction must be nonzero") +} diff --git a/op-acceptance-tests/tests/sync/elsync/reorg/sync_test.go b/op-acceptance-tests/tests/sync/elsync/reorg/sync_test.go index 325e256c30d..91438bdab0e 100644 --- a/op-acceptance-tests/tests/sync/elsync/reorg/sync_test.go +++ b/op-acceptance-tests/tests/sync/elsync/reorg/sync_test.go @@ -114,6 +114,15 @@ func TestUnsafeGapFillAfterUnsafeReorg_RestartL2CL(gt *testing.T) { // assertions.go:387: operation failed permanently after 30 attempts: expected head to reorg 0x893d77533b0ff9b37a92090679bf256d987b4535f06186ec71f29e68ddccd9a5:14, but got 0x893d77533b0ff9b37a92090679bf256d987b4535f06186ec71f29e68ddccd9a5:14 // assertions.go:387: Test: TestUnsafeGapFillAfterUnsafeReorg_RestartL2CL sysgo.SkipOnKonaNode(t, "not supported (timeout)") + // Example error with op-reth: + // + // assertions.go:387: + // Error Trace: /op-devstack/dsl/l2_el.go:430 + // /op-acceptance-tests/tests/sync/elsync/reorg/sync_test.go:218 + // Error: Received unexpected error: + // operation failed permanently after 50 attempts: expected head to match: unsafe + // Test: TestUnsafeGapFillAfterUnsafeReorg_RestartL2CL + sysgo.FlakyOnOpReth(t, "") sys := newReorgSystem(t) require := t.Require() logger := t.Logger() diff --git a/op-batcher/compressor/compressors.go b/op-batcher/compressor/compressors.go index d03d245239a..99cfd7a6451 100644 --- a/op-batcher/compressor/compressors.go +++ b/op-batcher/compressor/compressors.go @@ -1,8 +1,10 @@ package compressor import ( + "maps" + "slices" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" - "golang.org/x/exp/maps" ) type FactoryFunc func(Config) (derive.Compressor, error) @@ -26,5 +28,5 @@ var Kinds = map[string]FactoryFunc{ var KindKeys []string func init() { - KindKeys = maps.Keys(Kinds) + KindKeys = slices.Collect(maps.Keys(Kinds)) } diff --git a/op-chain-ops/addresses/contracts.go b/op-chain-ops/addresses/contracts.go index 7efecff81c4..9d6c29b2947 100644 --- a/op-chain-ops/addresses/contracts.go +++ b/op-chain-ops/addresses/contracts.go @@ -27,8 +27,6 @@ type SuperchainContracts struct { // - these contracts are shared by all OpChains that are members of the same superchain // - these contracts are not upgradable, but can be replaced by new contract releases/deployments type ImplementationsContracts struct { - OpcmImpl common.Address - OpcmContractsContainerImpl common.Address OpcmGameTypeAdderImpl common.Address OpcmDeployerImpl common.Address OpcmUpgraderImpl common.Address @@ -40,7 +38,6 @@ type ImplementationsContracts struct { OpcmContainerImpl common.Address DelayedWethImpl common.Address OptimismPortalImpl common.Address - OptimismPortalInteropImpl common.Address EthLockboxImpl common.Address PreimageOracleImpl common.Address MipsImpl common.Address diff --git a/op-chain-ops/cmd/check-prestate/main.go b/op-chain-ops/cmd/check-prestate/main.go index 47c5477ce31..b946cf62b68 100644 --- a/op-chain-ops/cmd/check-prestate/main.go +++ b/op-chain-ops/cmd/check-prestate/main.go @@ -5,7 +5,9 @@ import ( "encoding/json" "flag" "fmt" + "maps" "os" + "slices" "strings" "github.com/BurntSushi/toml" @@ -18,7 +20,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/superchain" "github.com/mattn/go-isatty" - "golang.org/x/exp/maps" ) type FPProgramType interface { @@ -65,7 +66,7 @@ func main() { chainFilter = func(chainName string) bool { return chains[chainName] } - filteredChainNames = maps.Keys(chains) + filteredChainNames = slices.Collect(maps.Keys(chains)) } prestateHash := common.HexToHash(prestateHashStr) if prestateHash == (common.Hash{}) { @@ -158,7 +159,7 @@ func main() { ExecutionClient: elCommitInfo, SuperchainRegistry: commitInfo("superchain-registry", commit, "main", "superchain"), UpToDateChains: supportedChains, - OutdatedChains: maps.Values(outdatedChains), + OutdatedChains: slices.Collect(maps.Values(outdatedChains)), MissingChains: missingChains, } encoder := json.NewEncoder(os.Stdout) diff --git a/op-chain-ops/cmd/op-run-block/main.go b/op-chain-ops/cmd/op-run-block/main.go index e5bbee03061..a8ae29c8969 100644 --- a/op-chain-ops/cmd/op-run-block/main.go +++ b/op-chain-ops/cmd/op-run-block/main.go @@ -155,11 +155,11 @@ func mainAction(c *cli.Context) error { Overrides: nil, }, outW) - witness, err := stateless.NewWitness(header, chCtx) + witness, err := stateless.NewWitness(header, chCtx, false) if err != nil { return fmt.Errorf("failed to prepare witness data collector: %w", err) } - state.StartPrefetcher("debug", witness, nil) + state.StartPrefetcher("debug", witness) defer func() { // Even if the EVM fails, try to export witness data for the state-transition up to the error. witnessDump := witness.ToExecutionWitness() out, err := json.MarshalIndent(witnessDump, "", " ") @@ -305,13 +305,12 @@ func Process(logger log.Logger, config *params.ChainConfig, chainCtx *remoteChainCtx, outW io.Writer) (*core.ProcessResult, error) { var ( receipts types.Receipts - usedGas = new(uint64) header = block.CreateGethHeader() blockHash = block.Hash blockNumber = new(big.Int).SetUint64(uint64(block.Number)) blockTime = uint64(block.Time) allLogs []*types.Log - gp = new(core.GasPool).AddGas(uint64(block.GasLimit)) + gp = core.NewGasPool(uint64(block.GasLimit)) ) // Mutate the block and state according to any hard-fork specs @@ -344,7 +343,7 @@ func Process(logger log.Logger, config *params.ChainConfig, } statedb.SetTxContext(tx.Hash(), i) - receipt, err := core.ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, blockTime, tx, usedGas, vmenv) + receipt, err := core.ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, blockTime, tx, vmenv) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -366,6 +365,6 @@ func Process(logger log.Logger, config *params.ChainConfig, Receipts: receipts, Requests: nil, Logs: allLogs, - GasUsed: *usedGas, + GasUsed: gp.Used(), }, nil } diff --git a/op-chain-ops/cmd/op-simulate/main.go b/op-chain-ops/cmd/op-simulate/main.go index 714cdd1a903..bf07506037a 100644 --- a/op-chain-ops/cmd/op-simulate/main.go +++ b/op-chain-ops/cmd/op-simulate/main.go @@ -312,8 +312,7 @@ func simulate(ctx context.Context, logger log.Logger, conf *params.ChainConfig, state.SetTxContext(tx.Hash(), 0) cCtx := &simChainContext{eng: beacon.New(ethash.NewFaker()), head: header, cfg: conf} - gp := core.GasPool(tx.Gas()) - usedGas := uint64(0) + gp := core.NewGasPool(tx.Gas()) vmConfig := vm.Config{} if doProfile { @@ -326,7 +325,7 @@ func simulate(ctx context.Context, logger log.Logger, conf *params.ChainConfig, // nil block-author, since it defaults to header.coinbase blockCtx := core.NewEVMBlockContext(header, cCtx, nil, conf, state) evm := vm.NewEVM(blockCtx, state, conf, vmConfig) - receipt, err := core.ApplyTransaction(evm, &gp, state, header, tx, &usedGas) + receipt, err := core.ApplyTransaction(evm, gp, state, header, tx) if err != nil { return fmt.Errorf("failed to apply tx: %w", err) } diff --git a/op-chain-ops/cmd/unclaimed-credits/main.go b/op-chain-ops/cmd/unclaimed-credits/main.go index 8c208264c25..0534fdd7aaa 100644 --- a/op-chain-ops/cmd/unclaimed-credits/main.go +++ b/op-chain-ops/cmd/unclaimed-credits/main.go @@ -4,8 +4,10 @@ import ( "context" "encoding/json" "fmt" + "maps" "math/big" "os" + "slices" "time" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" @@ -20,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2" - "golang.org/x/exp/maps" ) var ( @@ -130,7 +131,7 @@ func unclaimedCreditsForGame(ctx context.Context, game contracts.FaultDisputeGam players[claim.CounteredBy] = true } } - playerList := maps.Keys(players) + playerList := slices.Collect(maps.Keys(players)) credits, err := game.GetCredits(ctx, rpcblock.Latest, playerList...) if err != nil { return fmt.Errorf("failed to retrieve credits: %w", err) diff --git a/op-chain-ops/foundry/sourcefs.go b/op-chain-ops/foundry/sourcefs.go index eecf787e123..aac65ab3d2c 100644 --- a/op-chain-ops/foundry/sourcefs.go +++ b/op-chain-ops/foundry/sourcefs.go @@ -5,12 +5,12 @@ import ( "errors" "fmt" "io/fs" + "maps" "path" "path/filepath" + "slices" "strings" - "golang.org/x/exp/maps" - "github.com/ethereum-optimism/optimism/op-chain-ops/srcmap" ) @@ -122,7 +122,7 @@ func (s *SourceMapFS) ReadSourceIDs(path string, contract string, compilerVersio return nil, errors.New("no known build, unspecified compiler version") } if len(byCompilerVersion) > 1 { - return nil, fmt.Errorf("no compiler version specified, and more than one option: %s", strings.Join(maps.Keys(byCompilerVersion), ", ")) + return nil, fmt.Errorf("no compiler version specified, and more than one option: %s", strings.Join(slices.Collect(maps.Keys(byCompilerVersion)), ", ")) } // select the only remaining entry for _, v := range byCompilerVersion { @@ -140,7 +140,7 @@ func (s *SourceMapFS) ReadSourceIDs(path string, contract string, compilerVersio return nil, errors.New("no known build, unspecified profile") } if len(byProfile) > 1 { - return nil, fmt.Errorf("no profile specified, and more than one option: %s", strings.Join(maps.Keys(byProfile), ", ")) + return nil, fmt.Errorf("no profile specified, and more than one option: %s", strings.Join(slices.Collect(maps.Keys(byProfile)), ", ")) } // select the only remaining entry for _, v := range byProfile { diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index 89e1ca9399b..533e89513b1 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -1175,6 +1175,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *eth.BlockRef, l2GenesisBlockHa PectraBlobScheduleTime: d.PectraBlobScheduleTime(l1StartTime), IsthmusTime: d.IsthmusTime(l1StartTime), JovianTime: d.JovianTime(l1StartTime), + KarstTime: d.KarstTime(l1StartTime), InteropTime: d.InteropTime(l1StartTime), ProtocolVersionsAddress: d.ProtocolVersionsProxy, AltDAConfig: altDA, @@ -1241,7 +1242,6 @@ type L1Deployments struct { OptimismMintableERC20Factory common.Address `json:"OptimismMintableERC20Factory"` OptimismMintableERC20FactoryProxy common.Address `json:"OptimismMintableERC20FactoryProxy"` OptimismPortal common.Address `json:"OptimismPortal"` - OptimismPortalInterop common.Address `json:"OptimismPortalInterop"` OptimismPortalProxy common.Address `json:"OptimismPortalProxy"` ETHLockbox common.Address `json:"ETHLockbox"` ETHLockboxProxy common.Address `json:"ETHLockboxProxy"` @@ -1269,7 +1269,6 @@ func CreateL1DeploymentsFromContracts(contracts *addresses.L1Contracts) *L1Deplo OptimismMintableERC20Factory: contracts.OptimismMintableErc20FactoryImpl, OptimismMintableERC20FactoryProxy: contracts.OptimismMintableErc20FactoryProxy, OptimismPortal: contracts.OptimismPortalImpl, - OptimismPortalInterop: contracts.OptimismPortalInteropImpl, OptimismPortalProxy: contracts.OptimismPortalProxy, ETHLockbox: contracts.EthLockboxImpl, ETHLockboxProxy: contracts.EthLockboxProxy, diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index f1e99ba56af..a8c779da81e 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -3,11 +3,11 @@ package interopgen import ( "errors" "fmt" + "maps" "math/big" + "slices" "sort" - "golang.org/x/exp/maps" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" @@ -18,7 +18,7 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis/beacondeposit" "github.com/ethereum-optimism/optimism/op-chain-ops/script" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/manage" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" @@ -194,7 +194,7 @@ func DeploySuperchainToL1(l1Host *script.Host, opcmScripts *opcm.Scripts, superC ProofMaturityDelaySeconds: superCfg.Implementations.FaultProof.ProofMaturityDelaySeconds, DisputeGameFinalityDelaySeconds: superCfg.Implementations.FaultProof.DisputeGameFinalityDelaySeconds, MipsVersion: superCfg.Implementations.FaultProof.MipsVersion, - DevFeatureBitmap: deployer.OptimismPortalInteropDevFlag, + DevFeatureBitmap: devfeatures.OptimismPortalInteropFlag, FaultGameV2MaxGameDepth: big.NewInt(73), FaultGameV2SplitDepth: big.NewInt(30), FaultGameV2ClockExtension: big.NewInt(10800), @@ -271,7 +271,7 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme func MigrateInterop( l1Host *script.Host, l1GenesisTimestamp uint64, superCfg *SuperchainConfig, superDeployment *SuperchainDeployment, l2Cfgs map[string]*L2Config, l2Deployments map[string]*L2Deployment, ) (*InteropDeployment, error) { - l2ChainIDs := maps.Keys(l2Deployments) + l2ChainIDs := slices.Collect(maps.Keys(l2Deployments)) sort.Strings(l2ChainIDs) // We don't have a super root at genesis. But stub the starting anchor root anyways to facilitate super DG testing. @@ -388,7 +388,7 @@ func devFeatureBitmapForL2Genesis(multichainDepSet bool) common.Hash { // TODO(#19102): add support for L2CM var bitmap common.Hash if multichainDepSet { - bitmap = deployer.EnableDevFeature(bitmap, deployer.OptimismPortalInteropDevFlag) + bitmap = devfeatures.EnableDevFeature(bitmap, devfeatures.OptimismPortalInteropFlag) } return bitmap } diff --git a/op-chain-ops/interopgen/deployments.go b/op-chain-ops/interopgen/deployments.go index 0cfa0254082..4ea1c35b27a 100644 --- a/op-chain-ops/interopgen/deployments.go +++ b/op-chain-ops/interopgen/deployments.go @@ -23,7 +23,6 @@ type Implementations struct { OpcmContainer common.Address `json:"OPCMContainer"` DelayedWETHImpl common.Address `json:"DelayedWETHImpl"` OptimismPortalImpl common.Address `json:"OptimismPortalImpl"` - OptimismPortalInteropImpl common.Address `json:"OptimismPortalInteropImpl"` ETHLockboxImpl common.Address `json:"ETHLockboxImpl"` PreimageOracleSingleton common.Address `json:"PreimageOracleSingleton"` MipsSingleton common.Address `json:"MipsSingleton"` diff --git a/op-chain-ops/script/cheatcodes_external.go b/op-chain-ops/script/cheatcodes_external.go index c29608fe1ed..063f63ee3a5 100644 --- a/op-chain-ops/script/cheatcodes_external.go +++ b/op-chain-ops/script/cheatcodes_external.go @@ -5,14 +5,15 @@ import ( "encoding/json" "errors" "fmt" + "maps" "math/big" "path" + "slices" "strconv" "strings" "time" "github.com/BurntSushi/toml" - "golang.org/x/exp/maps" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -388,7 +389,7 @@ func lookupKeys(v any, query string) ([]string, error) { if query == "$" || query == "" { switch x := v.(type) { case map[string]any: - return maps.Keys(x), nil + return slices.Collect(maps.Keys(x)), nil default: return nil, fmt.Errorf("JSON value (Type %T) is not an object", x) } @@ -414,7 +415,7 @@ func lookupKeys(v any, query string) ([]string, error) { if trailing != "" { return nil, errors.New("cannot continue query after $ sign") } - return maps.Keys(x), nil + return slices.Collect(maps.Keys(x)), nil } data, ok := x[stringKey] if !ok { diff --git a/op-chain-ops/script/forking/db.go b/op-chain-ops/script/forking/db.go index 5efe8fddb8c..c9ba2aa6aa8 100644 --- a/op-chain-ops/script/forking/db.go +++ b/op-chain-ops/script/forking/db.go @@ -39,6 +39,10 @@ func (f *ForkDB) Snapshot() *snapshot.Tree { return nil } +func (f *ForkDB) Commit(*state.StateUpdate) error { + panic("unimplemented") +} + var _ state.Database = (*ForkDB)(nil) func NewForkDB(source ForkSource) *ForkDB { diff --git a/op-chain-ops/script/forking/reader.go b/op-chain-ops/script/forking/reader.go index 06340ae0fae..86c91adbdbf 100644 --- a/op-chain-ops/script/forking/reader.go +++ b/op-chain-ops/script/forking/reader.go @@ -1,6 +1,8 @@ package forking import ( + "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -36,12 +38,20 @@ func (f *forkStateReader) Storage(addr common.Address, slot common.Hash) (common return common.Hash(v), nil } -func (f *forkStateReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) { - return f.trie.ContractCode(addr, codeHash) +func (f *forkStateReader) Code(addr common.Address, codeHash common.Hash) []byte { + result, err := f.trie.ContractCode(addr, codeHash) + if err != nil { + panic(fmt.Errorf("get contract code for address %q and codeHash %q: %w", addr, codeHash, err)) + } + return result } -func (f *forkStateReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) { - return f.trie.ContractCodeSize(addr, codeHash) +func (f *forkStateReader) CodeSize(addr common.Address, codeHash common.Hash) int { + result, err := f.trie.ContractCodeSize(addr, codeHash) + if err != nil { + panic(fmt.Errorf("get contract code size for address %q and codeHash %q: %w", addr, codeHash, err)) + } + return result } func (f *forkStateReader) Copy() state.Reader { diff --git a/op-chain-ops/script/forking/state.go b/op-chain-ops/script/forking/state.go index 80ef0ffa8b1..284f8a8b113 100644 --- a/op-chain-ops/script/forking/state.go +++ b/op-chain-ops/script/forking/state.go @@ -44,6 +44,10 @@ type ForkableState struct { var _ VMStateDB = (*ForkableState)(nil) +func (fst *ForkableState) EmitLogsForBurnAccounts() { + panic("unimplemented") +} + func NewForkableState(base VMStateDB) *ForkableState { return &ForkableState{ selected: base, diff --git a/op-challenger/cmd/credits.go b/op-challenger/cmd/credits.go index 4d14e712914..4f3513159ff 100644 --- a/op-challenger/cmd/credits.go +++ b/op-challenger/cmd/credits.go @@ -3,7 +3,9 @@ package main import ( "context" "fmt" + "maps" "math/big" + "slices" "time" "github.com/ethereum-optimism/optimism/op-challenger/flags" @@ -17,7 +19,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v2" - "golang.org/x/exp/maps" ) func ListCredits(ctx *cli.Context) error { @@ -72,7 +73,7 @@ func listCredits(ctx context.Context, game contracts.FaultDisputeGameContract) e if err != nil { return fmt.Errorf("failed to get DelayedWETH info: %w", err) } - claimants := maps.Keys(recipients) + claimants := slices.Collect(maps.Keys(recipients)) withdrawals, err := game.GetWithdrawals(ctx, rpcblock.Latest, claimants...) if err != nil { return fmt.Errorf("failed to get withdrawals: %w", err) diff --git a/op-challenger/game/registry/oracles.go b/op-challenger/game/registry/oracles.go index 9ee10799028..ef86f00b72b 100644 --- a/op-challenger/game/registry/oracles.go +++ b/op-challenger/game/registry/oracles.go @@ -1,11 +1,12 @@ package registry import ( + "maps" + "slices" "sync" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types" "github.com/ethereum/go-ethereum/common" - "golang.org/x/exp/maps" ) type OracleRegistry struct { @@ -28,5 +29,5 @@ func (r *OracleRegistry) RegisterOracle(oracle keccakTypes.LargePreimageOracle) func (r *OracleRegistry) Oracles() []keccakTypes.LargePreimageOracle { r.l.Lock() defer r.l.Unlock() - return maps.Values(r.oracles) + return slices.Collect(maps.Values(r.oracles)) } diff --git a/op-challenger/sender/sender_test.go b/op-challenger/sender/sender_test.go index ea5f0986d39..4852ef3e664 100644 --- a/op-challenger/sender/sender_test.go +++ b/op-challenger/sender/sender_test.go @@ -3,7 +3,9 @@ package sender import ( "context" "fmt" + "maps" "math/big" + "slices" "sync" "testing" "time" @@ -16,7 +18,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" "github.com/stretchr/testify/require" - "golang.org/x/exp/maps" ) func TestSendAndWaitQueueWithMaxPending(t *testing.T) { @@ -163,7 +164,7 @@ func (s *stubTxMgr) txSuccess(candidate txmgr.TxCandidate) { ch, ok := s.sending[candidate.TxData[0]] if !ok { // Shouldn't happen if tests are well written, but double check... - panic(fmt.Sprintf("Completing unknown transaction: %v Known: %v", candidate.TxData[0], maps.Keys(s.sending))) + panic(fmt.Sprintf("Completing unknown transaction: %v Known: %v", candidate.TxData[0], slices.Collect(maps.Keys(s.sending)))) } ch <- &types.Receipt{Status: types.ReceiptStatusSuccessful} close(ch) diff --git a/op-conductor/conductor/config.go b/op-conductor/conductor/config.go index f7b45b6e6cc..8b0c181dbdb 100644 --- a/op-conductor/conductor/config.go +++ b/op-conductor/conductor/config.go @@ -6,7 +6,6 @@ import ( "time" "github.com/ethereum/go-ethereum/log" - "github.com/pkg/errors" "github.com/urfave/cli/v2" "github.com/ethereum-optimism/optimism/op-conductor/flags" @@ -135,19 +134,19 @@ func (c *Config) Check() error { return fmt.Errorf("missing rollup-boost next healthcheck URL") } if err := c.HealthCheck.Check(); err != nil { - return errors.Wrap(err, "invalid health check config") + return fmt.Errorf("invalid health check config: %w", err) } if err := c.RollupCfg.Check(); err != nil { - return errors.Wrap(err, "invalid rollup config") + return fmt.Errorf("invalid rollup config: %w", err) } if err := c.MetricsConfig.Check(); err != nil { - return errors.Wrap(err, "invalid metrics config") + return fmt.Errorf("invalid metrics config: %w", err) } if err := c.PprofConfig.Check(); err != nil { - return errors.Wrap(err, "invalid pprof config") + return fmt.Errorf("invalid pprof config: %w", err) } if err := c.RPC.Check(); err != nil { - return errors.Wrap(err, "invalid rpc config") + return fmt.Errorf("invalid rpc config: %w", err) } return nil } @@ -155,12 +154,12 @@ func (c *Config) Check() error { // NewConfig parses the Config from the provided flags or environment variables. func NewConfig(ctx *cli.Context, log log.Logger) (*Config, error) { if err := flags.CheckRequired(ctx); err != nil { - return nil, errors.Wrap(err, "missing required flags") + return nil, fmt.Errorf("missing required flags: %w", err) } rollupCfg, err := opnode.NewRollupConfigFromCLI(log, ctx) if err != nil { - return nil, errors.Wrap(err, "failed to load rollup config") + return nil, fmt.Errorf("failed to load rollup config: %w", err) } executionP2pRpcUrl := ctx.String(flags.HealthcheckExecutionP2pRPCUrl.Name) diff --git a/op-conductor/conductor/service.go b/op-conductor/conductor/service.go index 778e3b6ff09..81ad4efe31b 100644 --- a/op-conductor/conductor/service.go +++ b/op-conductor/conductor/service.go @@ -2,6 +2,7 @@ package conductor import ( "context" + "errors" "fmt" "math/rand" "net/http" @@ -12,9 +13,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" - "github.com/hashicorp/go-multierror" "github.com/hashicorp/raft" - "github.com/pkg/errors" "github.com/ethereum-optimism/optimism/op-conductor/client" "github.com/ethereum-optimism/optimism/op-conductor/consensus" @@ -58,7 +57,7 @@ func NewOpConductor( hmon health.HealthMonitor, ) (*OpConductor, error) { if err := cfg.Check(); err != nil { - return nil, errors.Wrap(err, "invalid config") + return nil, fmt.Errorf("invalid config: %w", err) } oc := &OpConductor{ @@ -95,7 +94,7 @@ func NewOpConductor( // ensure we always close the resources if we fail to initialize the conductor. closeErr := oc.Stop(ctx) if closeErr != nil { - err = multierror.Append(err, closeErr) + err = errors.Join(err, closeErr) } return nil, err } @@ -106,19 +105,19 @@ func NewOpConductor( func (c *OpConductor) init(ctx context.Context) error { c.log.Info("initializing OpConductor", "version", c.version) if err := c.initSequencerControl(ctx); err != nil { - return errors.Wrap(err, "failed to initialize sequencer control") + return fmt.Errorf("failed to initialize sequencer control: %w", err) } if err := c.initConsensus(ctx); err != nil { - return errors.Wrap(err, "failed to initialize consensus") + return fmt.Errorf("failed to initialize consensus: %w", err) } if err := c.initHealthMonitor(ctx); err != nil { - return errors.Wrap(err, "failed to initialize health monitor") + return fmt.Errorf("failed to initialize health monitor: %w", err) } if err := c.initRPCServer(ctx); err != nil { - return errors.Wrap(err, "failed to initialize rpc server") + return fmt.Errorf("failed to initialize rpc server: %w", err) } if err := c.initFlashblocksHandler(ctx); err != nil { - return errors.Wrap(err, "failed to initialize flashblocks handler") + return fmt.Errorf("failed to initialize flashblocks handler: %w", err) } return nil } @@ -130,18 +129,18 @@ func (c *OpConductor) initSequencerControl(ctx context.Context) error { ec, err := opclient.NewRPC(ctx, c.log, c.cfg.ExecutionRPC) if err != nil { - return errors.Wrap(err, "failed to create geth rpc client") + return fmt.Errorf("failed to create geth rpc client: %w", err) } execCfg := sources.L2ClientDefaultConfig(&c.cfg.RollupCfg, true) // TODO: Add metrics tracer here. tracked by https://github.com/ethereum-optimism/protocol-quest/issues/45 exec, err := sources.NewEthClient(ec, c.log, nil, &execCfg.EthClientConfig) if err != nil { - return errors.Wrap(err, "failed to create geth client") + return fmt.Errorf("failed to create geth client: %w", err) } nc, err := opclient.NewRPC(ctx, c.log, c.cfg.NodeRPC) if err != nil { - return errors.Wrap(err, "failed to create node rpc client") + return fmt.Errorf("failed to create node rpc client: %w", err) } node := sources.NewRollupClient(nc) c.ctrl = client.NewSequencerControl(exec, node) @@ -159,7 +158,7 @@ func (c *OpConductor) initSequencerControl(ctx context.Context) error { return enabled, err }) if err != nil { - return errors.Wrap(err, "failed to connect to sequencer") + return fmt.Errorf("failed to connect to sequencer: %w", err) } if !enabled { return errors.New("conductor is not enabled on sequencer, exiting...") @@ -191,7 +190,7 @@ func (c *OpConductor) initConsensus(ctx context.Context) error { cons, err := consensus.NewRaftConsensus(c.log, raftConsensusConfig) if err != nil { if !errors.Is(err, raft.ErrCantBootstrap) { - return errors.Wrap(err, "failed to create raft consensus") + return fmt.Errorf("failed to create raft consensus: %w", err) } } else if c.cfg.RaftBootstrap { c.log.Warn("Raft cluster bootstrapped, pausing conductor.") @@ -209,7 +208,7 @@ func (c *OpConductor) initHealthMonitor(ctx context.Context) error { nc, err := opclient.NewRPC(ctx, c.log, c.cfg.NodeRPC) if err != nil { - return errors.Wrap(err, "failed to create node rpc client") + return fmt.Errorf("failed to create node rpc client: %w", err) } node := sources.NewRollupClient(nc) @@ -229,7 +228,7 @@ func (c *OpConductor) initHealthMonitor(ctx context.Context) error { if c.cfg.HealthCheck.ExecutionP2pEnabled { execClient, err := dial.DialEthClientWithTimeout(ctx, 1*time.Minute, c.log, c.cfg.HealthCheck.ExecutionP2pRPCUrl) if err != nil { - return errors.Wrap(err, "failed to create execution rpc client out of the el p2p rpc url: "+c.cfg.HealthCheck.ExecutionP2pRPCUrl) + return fmt.Errorf("failed to create execution rpc client out of the el p2p rpc url %s: %w", c.cfg.HealthCheck.ExecutionP2pRPCUrl, err) } switch c.cfg.HealthCheck.ExecutionP2pCheckApi { case "net": @@ -249,7 +248,7 @@ func (c *OpConductor) initHealthMonitor(ctx context.Context) error { if c.cfg.SupervisorRPC != "" { supervisor, err = dial.DialSupervisorClientWithTimeout(ctx, c.log, c.cfg.SupervisorRPC) if err != nil { - return errors.Wrap(err, "failed to dial supervisor") + return fmt.Errorf("failed to dial supervisor: %w", err) } } @@ -294,7 +293,7 @@ func (oc *OpConductor) initRPCServer(ctx context.Context) error { if oc.cfg.RPCEnableProxy { execClient, err := dial.DialEthClientWithTimeout(ctx, 1*time.Minute, oc.log, oc.cfg.ExecutionRPC) if err != nil { - return errors.Wrap(err, "failed to create execution rpc client") + return fmt.Errorf("failed to create execution rpc client: %w", err) } executionProxy := conductorrpc.NewExecutionProxyBackend(oc.log, oc, execClient) server.AddAPI(rpc.API{ @@ -309,7 +308,7 @@ func (oc *OpConductor) initRPCServer(ctx context.Context) error { nodeClient, err := dial.DialRollupClientWithTimeout(ctx, oc.log, oc.cfg.NodeRPC) if err != nil { - return errors.Wrap(err, "failed to create node rpc client") + return fmt.Errorf("failed to create node rpc client: %w", err) } nodeProxy := conductorrpc.NewNodeProxyBackend(oc.log, oc, nodeClient) server.AddAPI(rpc.API{ @@ -344,7 +343,7 @@ func (c *OpConductor) initFlashblocksHandler(ctx context.Context) error { }, c.metrics) if err != nil { - return errors.Wrap(err, "failed to create flashblocks handler") + return fmt.Errorf("failed to create flashblocks handler: %w", err) } c.flashblocksHandler = handler @@ -429,19 +428,19 @@ func (oc *OpConductor) Start(ctx context.Context) error { oc.log.Info("starting OpConductor") if err := oc.hmon.Start(ctx); err != nil { - return errors.Wrap(err, "failed to start health monitor") + return fmt.Errorf("failed to start health monitor: %w", err) } oc.log.Info("starting JSON-RPC server") if err := oc.rpcServer.Start(); err != nil { - return errors.Wrap(err, "failed to start JSON-RPC server") + return fmt.Errorf("failed to start JSON-RPC server: %w", err) } // Start the flashblocks handler if it was initialized if oc.flashblocksHandler != nil { oc.log.Info("starting flashblocks handler") if err := oc.flashblocksHandler.Start(ctx); err != nil { - return errors.Wrap(err, "failed to start flashblocks handler") + return fmt.Errorf("failed to start flashblocks handler: %w", err) } } @@ -453,7 +452,7 @@ func (oc *OpConductor) Start(ctx context.Context) error { } metricsServer, err := opmetrics.StartServer(m.Registry(), oc.cfg.MetricsConfig.ListenAddr, oc.cfg.MetricsConfig.ListenPort) if err != nil { - return errors.Wrap(err, "failed to start metrics server") + return fmt.Errorf("failed to start metrics server: %w", err) } oc.metricsServer = metricsServer } @@ -480,7 +479,7 @@ func (oc *OpConductor) Stop(ctx context.Context) error { } oc.log.Info("stopping OpConductor") - var result *multierror.Error + var result error // close control loop oc.shutdownCancel() @@ -494,32 +493,32 @@ func (oc *OpConductor) Stop(ctx context.Context) error { if oc.rpcServer != nil { if err := oc.rpcServer.Stop(); err != nil { - result = multierror.Append(result, errors.Wrap(err, "failed to stop rpc server")) + result = errors.Join(result, fmt.Errorf("failed to stop rpc server: %w", err)) } } // stop health check if oc.hmon != nil { if err := oc.hmon.Stop(); err != nil { - result = multierror.Append(result, errors.Wrap(err, "failed to stop health monitor")) + result = errors.Join(result, fmt.Errorf("failed to stop health monitor: %w", err)) } } if oc.cons != nil { if err := oc.cons.Shutdown(); err != nil { - result = multierror.Append(result, errors.Wrap(err, "failed to shutdown consensus")) + result = errors.Join(result, fmt.Errorf("failed to shutdown consensus: %w", err)) } } if oc.metricsServer != nil { if err := oc.metricsServer.Shutdown(ctx); err != nil { - result = multierror.Append(result, errors.Wrap(err, "failed to stop metrics server")) + result = errors.Join(result, fmt.Errorf("failed to stop metrics server: %w", err)) } } - if result.ErrorOrNil() != nil { - oc.log.Error("failed to stop OpConductor", "err", result.ErrorOrNil()) - return result.ErrorOrNil() + if result != nil { + oc.log.Error("failed to stop OpConductor", "err", result) + return result } oc.stopped.Store(true) @@ -548,7 +547,7 @@ func (oc *OpConductor) Pause(ctx context.Context) error { func (oc *OpConductor) Resume(ctx context.Context) error { err := oc.updateSequencerActiveStatus() if err != nil { - return errors.Wrap(err, "cannot resume because failed to get sequencer active status") + return fmt.Errorf("cannot resume because failed to get sequencer active status: %w", err) } select { @@ -779,13 +778,13 @@ func (oc *OpConductor) action() { // 2. we're here because an healthy leader became unhealthy itself // then we should try to stop sequencing locally and transfer leadership. - var result *multierror.Error + var result error // Try to stop sequencer first, but since sequencer is not healthy, we may not be able to stop it. // In this case, it's fine to continue to try to transfer leadership to another server. This is safe because // 1. if leadership transfer succeeded, then we'll retry and enter case !status.leader && status.healthy && status.active, which will try to stop sequencer. // 2. even if the retry continues to fail and current server stays in active sequencing mode, it would be safe because our hook in op-node will prevent it from committing any new blocks to the network via p2p (if it's not leader any more) if e := oc.stopSequencer(); e != nil { - result = multierror.Append(result, e) + result = errors.Join(result, e) } // try to transfer leadership to another server despite if sequencer is stopped or not. There are 4 scenarios here: // 1. [sequencer stopped, leadership transfer succeeded] which is the happy case and we handed over sequencing to another server. @@ -793,9 +792,9 @@ func (oc *OpConductor) action() { // 3. [sequencer active, leadership transfer succeeded] we'll enter into case !status.leader && status.healthy && status.active and retry stop sequencer. // 4. [sequencer active, leadership transfer failed] we're in the same state and will retry here again. if e := oc.transferLeader(); e != nil { - result = multierror.Append(result, e) + result = errors.Join(result, e) } - err = result.ErrorOrNil() + err = result case status.leader && status.healthy && !status.active: // start sequencer err = oc.startSequencer() @@ -867,7 +866,7 @@ func (oc *OpConductor) stopSequencer() error { if strings.Contains(err.Error(), driver.ErrSequencerAlreadyStopped.Error()) { oc.log.Warn("sequencer already stopped", "err", err) } else { - return errors.Wrap(err, "failed to stop sequencer") + return fmt.Errorf("failed to stop sequencer: %w", err) } } oc.metrics.RecordStopSequencer(err == nil) @@ -918,7 +917,7 @@ func (oc *OpConductor) startSequencer() error { func (oc *OpConductor) compareUnsafeHead(ctx context.Context) (*eth.ExecutionPayloadEnvelope, eth.BlockInfo, error) { unsafeInCons, err := oc.cons.LatestUnsafePayload() if err != nil { - return nil, nil, errors.Wrap(err, "unable to retrieve unsafe head from consensus") + return nil, nil, fmt.Errorf("unable to retrieve unsafe head from consensus: %w", err) } if unsafeInCons == nil { return nil, nil, ErrNoUnsafeHead @@ -926,7 +925,7 @@ func (oc *OpConductor) compareUnsafeHead(ctx context.Context) (*eth.ExecutionPay unsafeInNode, err := oc.ctrl.LatestUnsafeBlock(ctx) if err != nil { - return unsafeInCons, nil, errors.Wrap(err, "failed to get latest unsafe block from EL during compareUnsafeHead phase") + return unsafeInCons, nil, fmt.Errorf("failed to get latest unsafe block from EL during compareUnsafeHead phase: %w", err) } oc.log.Debug("comparing unsafe head", "consensus", uint64(unsafeInCons.ExecutionPayload.BlockNumber), "node", unsafeInNode.NumberU64()) @@ -948,7 +947,7 @@ func (oc *OpConductor) compareUnsafeHead(ctx context.Context) (*eth.ExecutionPay func (oc *OpConductor) updateSequencerActiveStatus() error { active, err := oc.ctrl.SequencerActive(oc.shutdownCtx) if err != nil { - return errors.Wrap(err, "failed to get sequencer active status") + return fmt.Errorf("failed to get sequencer active status: %w", err) } oc.log.Info("sequencer active status updated", "active", active) oc.seqActive.Store(active) diff --git a/op-conductor/conductor/service_test.go b/op-conductor/conductor/service_test.go index 91b5903734a..429b9ad2446 100644 --- a/op-conductor/conductor/service_test.go +++ b/op-conductor/conductor/service_test.go @@ -16,7 +16,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" - "github.com/hashicorp/go-multierror" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" @@ -876,8 +875,9 @@ func (s *OpConductorTestSuite) TestConductorRestart() { func (s *OpConductorTestSuite) TestHandleInitError() { // This will cause an error in the init function, which should cause the conductor to stop successfully without issues. _, err := New(s.ctx, &s.cfg, s.log, s.version) - _, ok := err.(*multierror.Error) - // error should not be a multierror, this means that init failed, but Stop() succeeded, which is what we expect. + // error should not be a joined error, this means that init failed, but Stop() succeeded, which is what we expect. + type multiUnwrap interface{ Unwrap() []error } + _, ok := err.(multiUnwrap) s.False(ok) } diff --git a/op-conductor/consensus/raft.go b/op-conductor/consensus/raft.go index 7d932331b06..23b9db8b868 100644 --- a/op-conductor/consensus/raft.go +++ b/op-conductor/consensus/raft.go @@ -2,6 +2,7 @@ package consensus import ( "bytes" + "errors" "fmt" "net" "os" @@ -11,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/hashicorp/raft" boltdb "github.com/hashicorp/raft-boltdb/v2" - "github.com/pkg/errors" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -136,7 +136,7 @@ func NewRaftConsensus(log log.Logger, cfg *RaftConsensusConfig) (*RaftConsensus, // When advertiseAddr == nil, the transport will use the local address that it is bound to. transport, err := raft.NewTCPTransportWithLogger(bindAddr, advertiseAddr, maxConnPool, timeout, rc.Logger) if err != nil { - return nil, errors.Wrap(err, "failed to create raft tcp transport") + return nil, fmt.Errorf("failed to create raft tcp transport: %w", err) } log.Info("Raft server network transport is up", "addr", transport.LocalAddr()) @@ -145,7 +145,7 @@ func NewRaftConsensus(log log.Logger, cfg *RaftConsensusConfig) (*RaftConsensus, r, err := raft.NewRaft(rc, fsm, logStore, stableStore, snapshotStore, transport) if err != nil { log.Error("failed to create raft", "err", err) - return nil, errors.Wrap(err, "failed to create raft") + return nil, fmt.Errorf("failed to create raft: %w", err) } // If bootstrap = true, start raft in bootstrap mode, this will allow the current node to elect itself as leader when there's no other participants @@ -175,7 +175,7 @@ func NewRaftConsensus(log log.Logger, cfg *RaftConsensusConfig) (*RaftConsensus, if errors.Is(err, raft.ErrCantBootstrap) { log.Warn("Raft cluster already exists, skipping bootstrap") } else { - return nil, errors.Wrap(err, "failed to bootstrap raft cluster") + return nil, fmt.Errorf("failed to bootstrap raft cluster: %w", err) } } } @@ -307,12 +307,12 @@ func (rc *RaftConsensus) CommitUnsafePayload(payload *eth.ExecutionPayloadEnvelo var buf bytes.Buffer if _, err := payload.MarshalSSZ(&buf); err != nil { - return errors.Wrap(err, "failed to marshal payload envelope") + return fmt.Errorf("failed to marshal payload envelope: %w", err) } f := rc.r.Apply(buf.Bytes(), defaultTimeout) if err := f.Error(); err != nil { - return errors.Wrap(err, "failed to apply payload envelope") + return fmt.Errorf("failed to apply payload envelope: %w", err) } rc.log.Debug("unsafe payload committed", "number", uint64(payload.ExecutionPayload.BlockNumber), "hash", payload.ExecutionPayload.BlockHash.Hex()) @@ -322,7 +322,7 @@ func (rc *RaftConsensus) CommitUnsafePayload(payload *eth.ExecutionPayloadEnvelo // LatestUnsafePayload implements Consensus, it returns the latest unsafe payload from FSM in a strongly consistent fashion. func (rc *RaftConsensus) LatestUnsafePayload() (*eth.ExecutionPayloadEnvelope, error) { if err := rc.r.Barrier(defaultTimeout).Error(); err != nil { - return nil, errors.Wrap(err, "failed to apply barrier") + return nil, fmt.Errorf("failed to apply barrier: %w", err) } return rc.unsafeTracker.UnsafeHead(), nil diff --git a/op-conductor/consensus/raft_fsm_test.go b/op-conductor/consensus/raft_fsm_test.go index 3b154ceded2..d8391e247ab 100644 --- a/op-conductor/consensus/raft_fsm_test.go +++ b/op-conductor/consensus/raft_fsm_test.go @@ -2,6 +2,7 @@ package consensus import ( "bytes" + "fmt" "io" "testing" @@ -10,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/hashicorp/raft" - "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -86,7 +86,7 @@ func NewMockReadCloser(data *eth.ExecutionPayloadEnvelope) (*mockReadCloser, err var buf bytes.Buffer if _, err := data.MarshalSSZ(&buf); err != nil { - return nil, errors.Wrap(err, "failed to unmarshal execution payload envelope") + return nil, fmt.Errorf("failed to unmarshal execution payload envelope: %w", err) } mrc.buffer = buf.Bytes() diff --git a/op-core/devfeatures/devfeatures.go b/op-core/devfeatures/devfeatures.go new file mode 100644 index 00000000000..683968f69e3 --- /dev/null +++ b/op-core/devfeatures/devfeatures.go @@ -0,0 +1,53 @@ +package devfeatures + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// Development feature flag constants. +var ( + // OptimismPortalInteropFlag enables interop features in OptimismPortal2. + OptimismPortalInteropFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") + + // CannonKonaFlag enables Kona as the default cannon prover. + CannonKonaFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000010") + + // DeployV2DisputeGamesFlag enables deployment of V2 dispute game contracts. + DeployV2DisputeGamesFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000100") + + // OPCMV2Flag enables the OPContractsManagerV2 contract. + OPCMV2Flag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000010000") + + // L2CMFlag enables L2CM. + L2CMFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000100000") + + // ZKDisputeGameFlag enables the ZK dispute game system. + // TODO(#19432): Use this flag in the OPCM/OPD integration pipeline. + ZKDisputeGameFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000001000000") + + // SuperRootGamesMigrationFlag enables the super root games migration path in OPCM upgrade. + SuperRootGamesMigrationFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000010000000") +) + +// IsDevFeatureEnabled checks if a specific development feature is enabled in a feature bitmap. +// It performs a bitwise AND between the bitmap and flag to determine if the feature +// is set. This follows the same pattern as the Solidity DevFeatures library. +func IsDevFeatureEnabled(bitmap, flag common.Hash) bool { + b := new(big.Int).SetBytes(bitmap[:]) + f := new(big.Int).SetBytes(flag[:]) + + featuresIsNonZero := f.Cmp(big.NewInt(0)) != 0 + bitmapContainsFeatures := new(big.Int).And(b, f).Cmp(f) == 0 + return featuresIsNonZero && bitmapContainsFeatures +} + +// EnableDevFeature sets a specific development feature flag in a feature bitmap. +func EnableDevFeature(bitmap, flag common.Hash) common.Hash { + var result common.Hash + for i := 0; i < 32; i++ { + result[i] = bitmap[i] | flag[i] + } + return result +} diff --git a/op-deployer/pkg/deployer/devfeatures_test.go b/op-core/devfeatures/devfeatures_test.go similarity index 99% rename from op-deployer/pkg/deployer/devfeatures_test.go rename to op-core/devfeatures/devfeatures_test.go index 7c83b55a3a5..6c3f79f0a02 100644 --- a/op-deployer/pkg/deployer/devfeatures_test.go +++ b/op-core/devfeatures/devfeatures_test.go @@ -1,4 +1,4 @@ -package deployer +package devfeatures import ( "testing" diff --git a/op-core/nuts/README.md b/op-core/nuts/README.md new file mode 100644 index 00000000000..68f815ffb7f --- /dev/null +++ b/op-core/nuts/README.md @@ -0,0 +1,56 @@ +# NUT Bundles + +Network Upgrade Transaction (NUT) bundles define the L2 deposit transactions that activate a hardfork. Each bundle is a JSON file containing ordered transactions (implementation deployments, proxy upgrades, etc.) that the rollup node embeds and executes at the fork activation block. + +## Files + +| File | Purpose | +|------|---------| +| `fork_lock.toml` | Lock file mapping fork names to bundle paths, sha256 hashes, and source commits | +| `bundles/_nut_bundle.json` | Embedded bundle consumed by op-node and kona-node at fork activation | + +## Workflow + +### Generating a bundle + +```bash +cd packages/contracts-bedrock +just generate-nut-bundle +``` + +### Snapshotting a bundle for a fork + +```bash +just nut-snapshot-for +``` + +This copies `current-upgrade-bundle.json` to `op-core/nuts/bundles/_nut_bundle.json` and updates `fork_lock.toml` with the sha256 hash and the merge-base commit with `origin/develop`. + +**Important:** The recorded commit is the merge-base with develop, not HEAD. This ensures the commit survives squash-merge. Contract changes must be merged to develop in a separate PR *before* snapshotting the bundle. + + +### Verifying a bundle + +```bash +just nut-provenance-verify +``` + +Checks that: +1. The bundle file exists and its sha256 matches the lock +2. Creates a temporary worktree at the recorded commit, regenerates the bundle, and compares byte-for-byte + +Requires `forge` for the provenance check (step 2). + +### CI checks + +- **`check-nut-locks`** — Verifies all bundle hashes match their lock entries, all entries have a commit, and every `*_nut_bundle.json` file has a corresponding lock entry. Runs in CI on every PR. + +## fork_lock.toml schema + +```toml +[] +bundle = "op-core/nuts/bundles/_nut_bundle.json" # repo-relative path +hash = "sha256:" # sha256 of bundle contents +commit = "" # commit that produced the bundle +``` + diff --git a/op-core/nuts/bundles.go b/op-core/nuts/bundles.go new file mode 100644 index 00000000000..eedaf6d6d5d --- /dev/null +++ b/op-core/nuts/bundles.go @@ -0,0 +1,10 @@ +package nuts + +import ( + _ "embed" +) + +// KarstNUTBundleJSON is the embedded Karst NUT bundle. +// +//go:embed bundles/karst_nut_bundle.json +var KarstNUTBundleJSON []byte diff --git a/op-core/nuts/bundles/karst_nut_bundle.json b/op-core/nuts/bundles/karst_nut_bundle.json new file mode 100644 index 00000000000..9a75d366669 --- /dev/null +++ b/op-core/nuts/bundles/karst_nut_bundle.json @@ -0,0 +1,231 @@ +{ + "metadata": { + "version": "1.0.0" + }, + "transactions": [ + { + "data": "0x9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4608060405234801561001057600080fd5b506105b8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d5014610046578063cdcb760a14610098578063e0145f5c146100d0575b600080fd5b6100826040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b60405161008f91906103f7565b60405180910390f35b6100ab6100a6366004610440565b6100ea565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b734e59b44847b379578588920ca78fbf26c0b4956c6100ab565b8051602080830191909120604080517fff00000000000000000000000000000000000000000000000000000000000000818501527f4e59b44847b379578588920ca78fbf26c0b4956c000000000000000000000000602182015260358101869052605580820184905282518083039091018152607590910190915280519201919091206000919073ffffffffffffffffffffffffffffffffffffffff81163b156101d85760405173ffffffffffffffffffffffffffffffffffffffff8216907ffbe57d889a7f75a4e0c7da304cd158fcaddc4b925cdd9f4cfb115c0f9e48009b90600090a291506103779050565b600080734e59b44847b379578588920ca78fbf26c0b4956c73ffffffffffffffffffffffffffffffffffffffff168787604051602001610219929190610519565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526102519161053f565b6000604051808303816000865af19150503d806000811461028e576040519150601f19603f3d011682016040523d82523d6000602084013e610293565b606091505b5091509150806102a29061055b565b60601c94508115806102e057508273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b1561032257806040517fcb0fc6f700000000000000000000000000000000000000000000000000000000815260040161031991906103f7565b60405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff167f9b7318127ed899f286ea9ddd7925ed8ad24a682b6a825c3b5b3d88a3f00bc1d28860405161036a91815260200190565b60405180910390a2505050505b92915050565b60005b83811015610398578181015183820152602001610380565b838111156103a7576000848401525b50505050565b600081518084526103c581602086016020860161037d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061040a60208301846103ad565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561045357600080fd5b82359150602083013567ffffffffffffffff8082111561047257600080fd5b818501915085601f83011261048657600080fd5b81358181111561049857610498610411565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156104de576104de610411565b816040528281528860208487010111156104f757600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b8281526000825161053181602085016020870161037d565b919091016020019392505050565b6000825161055181846020870161037d565b9190910192915050565b6000815160208301517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808216935060148310156105a35780818460140360031b1b83161693505b50505091905056fea164736f6c634300080f000a", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 600000, + "intent": "ConditionalDeployer Deployment", + "to": "0x4e59b44847b379578588920cA78FbF26c0B4956C" + }, + { + "data": "0x3659cfe6000000000000000000000000906835344844979ffd3a752eaa23728d513db00b", + "from": "0x0000000000000000000000000000000000000000", + "gasLimit": 50000, + "intent": "Upgrade ConditionalDeployer Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000048a608060405234801561001057600080fd5b5061046a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c8063a6ed563e11610076578063bd02d0f51161005b578063bd02d0f51461018e578063ca446dd9146101b8578063e2a4853a1461011557600080fd5b8063a6ed563e1461018e578063abfdcced146101aa57600080fd5b80634e91db08116100a75780634e91db081461011557806354fd4d50146101275780637ae1cfca1461017057600080fd5b80630528afe2146100c357806321f8a721146100d8575b600080fd5b6100d66100d1366004610239565b6101c6565b005b6100eb6100e63660046102ae565b610229565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100d66101233660046102c7565b9055565b6101636040518060400160405280600581526020017f312e322e3200000000000000000000000000000000000000000000000000000081525081565b60405161010c91906102e9565b61017e6100e63660046102ae565b604051901515815260200161010c565b61019c6100e63660046102ae565b60405190815260200161010c565b6100d661012336600461035c565b6100d6610123366004610391565b8060005b81811015610223576102118484838181106101e7576101e76103cf565b90506040020160000135858584818110610203576102036103cf565b905060400201602001359055565b8061021b816103fe565b9150506101ca565b50505050565b6000610233825490565b92915050565b6000806020838503121561024c57600080fd5b823567ffffffffffffffff8082111561026457600080fd5b818501915085601f83011261027857600080fd5b81358181111561028757600080fd5b8660208260061b850101111561029c57600080fd5b60209290920196919550909350505050565b6000602082840312156102c057600080fd5b5035919050565b600080604083850312156102da57600080fd5b50508035926020909101359150565b600060208083528351808285015260005b81811015610316578581018301518582016040015282016102fa565b81811115610328576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6000806040838503121561036f57600080fd5b823591506020830135801515811461038657600080fd5b809150509250929050565b600080604083850312156103a457600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461038657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610456577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b506001019056fea164736f6c634300080f000a00000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 500000, + "intent": "Deploy StorageSetter Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000022d3608060405234801561001057600080fd5b5061001961001e565b6100eb565b600054600160a81b900460ff161561008c5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff600160a01b909104811610156100e9576000805460ff60a01b191660ff60a01b17905560405160ff81527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6121d880620000fb6000396000f3fe6080604052600436106101a15760003560e01c80638cbeeef2116100e1578063c4d66de81161008a578063db505d8011610064578063db505d8014610451578063ddd5a40f1461047e578063e46e245a14610494578063ecc70428146104a957600080fd5b8063c4d66de814610409578063d764ad0b14610429578063dad544e01461043c57600080fd5b8063a7119869116100bb578063a71198691461035e578063b1b1b209146103b9578063b28ade25146103e957600080fd5b80638cbeeef2146102905780639fce812c1461035e578063a4e7f8bd1461038957600080fd5b80633f827a5a1161014e5780635644cfdf116101285780635644cfdf146102fc5780635c975abb146103125780636e296e451461033257806383a740741461034757600080fd5b80633f827a5a146102685780634c1d6a691461029057806354fd4d50146102a657600080fd5b80632f7d39221161017f5780632f7d3922146102035780633dbb202b146102195780633e47158c1461022e57600080fd5b8063028f85f7146101a65780630c568498146101d95780632828d7e8146101ee575b600080fd5b3480156101b257600080fd5b506101bb601081565b60405167ffffffffffffffff90911681526020015b60405180910390f35b3480156101e557600080fd5b506101bb603f81565b3480156101fa57600080fd5b506101bb604081565b34801561020f57600080fd5b506101bb61520881565b61022c610227366004611bb4565b61050e565b005b34801561023a57600080fd5b506102436107a1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d0565b34801561027457600080fd5b5061027d600181565b60405161ffff90911681526020016101d0565b34801561029c57600080fd5b506101bb619c4081565b3480156102b257600080fd5b506102ef6040518060400160405280600581526020017f322e322e3100000000000000000000000000000000000000000000000000000081525081565b6040516101d09190611c86565b34801561030857600080fd5b506101bb61138881565b34801561031e57600080fd5b5060005b60405190151581526020016101d0565b34801561033e57600080fd5b506102436109ac565b34801561035357600080fd5b506101bb62030d4081565b34801561036a57600080fd5b5060cf5473ffffffffffffffffffffffffffffffffffffffff16610243565b34801561039557600080fd5b506103226103a4366004611c99565b60ce6020526000908152604090205460ff1681565b3480156103c557600080fd5b506103226103d4366004611c99565b60cb6020526000908152604090205460ff1681565b3480156103f557600080fd5b506101bb610404366004611ce1565b610a98565b34801561041557600080fd5b5061022c610424366004611dc1565b610b59565b61022c610437366004611dde565b610d60565b34801561044857600080fd5b50610243611645565b34801561045d57600080fd5b5060cf546102439073ffffffffffffffffffffffffffffffffffffffff1681565b34801561048a57600080fd5b506101bb61010481565b3480156104a057600080fd5b506101bb602881565b3480156104b557600080fd5b5061050060cd547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b6040519081526020016101d0565b60cf54604080516020601f86018190048102820181019092528481526106769273ffffffffffffffffffffffffffffffffffffffff169161056c91908790879081908401838280828437600092019190915250879250610a98915050565b347fd764ad0b000000000000000000000000000000000000000000000000000000006105d860cd547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b338a34898c8c6040516024016105f49796959493929190611ead565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526116c2565b8373ffffffffffffffffffffffffffffffffffffffff167fcb0f7ffd78f9aee47a248fae8db181db6eee833039123e026dcbff529522e52a3385856106fb60cd547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b8660405161070d959493929190611f0c565b60405180910390a260405134815233907f8ebb2ec2465bdb2a06a66fc37a0963af8a2a6a1479d81d56fdb8cbb98096d5469060200160405180910390a2505060cd80547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808216600101167fffff0000000000000000000000000000000000000000000000000000000000009091161790555050565b6000806107cc7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156107ef57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026108329190611f89565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061088d906060015b604051602081830303815290604052805190602001205490565b146108c4576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091526000906108e690606001610873565b905073ffffffffffffffffffffffffffffffffffffffff81161561097a578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561094f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109739190611fc6565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60cc5460009073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff215301610a7b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f43726f7373446f6d61696e4d657373656e6765723a2078446f6d61696e4d657360448201527f7361676553656e646572206973206e6f7420736574000000000000000000000060648201526084015b60405180910390fd5b5060cc5473ffffffffffffffffffffffffffffffffffffffff1690565b600080603f610aae604063ffffffff8616611fe3565b610ab89190612013565b611388619c40610acb8162030d40612061565b610ad59190612061565b610adf9190612061565b610ae99190612061565b9050600061010467ffffffffffffffff168551610b06919061208d565b9050610b44610b16601083611fe3565b610b209084612061565b67ffffffffffffffff16610b35602884611fe3565b67ffffffffffffffff16611750565b610b5090615208612061565b95945050505050565b6000547501000000000000000000000000000000000000000000900460ff1615808015610ba4575060005460017401000000000000000000000000000000000000000090910460ff16105b80610bd65750303b158015610bd6575060005474010000000000000000000000000000000000000000900460ff166001145b610c62576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610a72565b600080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790558015610ce857600080547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555b610cf0611769565b610cf9826117ec565b8015610d5c57600080547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b60f087901c60028110610e1b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f43726f7373446f6d61696e4d657373656e6765723a206f6e6c7920766572736960448201527f6f6e2030206f722031206d657373616765732061726520737570706f7274656460648201527f20617420746869732074696d6500000000000000000000000000000000000000608482015260a401610a72565b8061ffff16600003610f10576000610e6c878986868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508f9250611928915050565b600081815260cb602052604090205490915060ff1615610f0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f43726f7373446f6d61696e4d657373656e6765723a206c65676163792077697460448201527f6864726177616c20616c72656164792072656c617965640000000000000000006064820152608401610a72565b505b6000610f56898989898989898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061194792505050565b9050610f9f60cf54337fffffffffffffffffffffffffeeeeffffffffffffffffffffffffffffffffeeef0173ffffffffffffffffffffffffffffffffffffffff90811691161490565b15610fd757853414610fb357610fb36120a5565b600081815260ce602052604090205460ff1615610fd257610fd26120a5565b611129565b341561108b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605060248201527f43726f7373446f6d61696e4d657373656e6765723a2076616c7565206d75737460448201527f206265207a65726f20756e6c657373206d6573736167652069732066726f6d2060648201527f612073797374656d206164647265737300000000000000000000000000000000608482015260a401610a72565b600081815260ce602052604090205460ff16611129576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f43726f7373446f6d61696e4d657373656e6765723a206d65737361676520636160448201527f6e6e6f74206265207265706c61796564000000000000000000000000000000006064820152608401610a72565b6111328761196a565b156111e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f43726f7373446f6d61696e4d657373656e6765723a2063616e6e6f742073656e60448201527f64206d65737361676520746f20626c6f636b65642073797374656d206164647260648201527f6573730000000000000000000000000000000000000000000000000000000000608482015260a401610a72565b600081815260cb602052604090205460ff1615611284576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f43726f7373446f6d61696e4d657373656e6765723a206d65737361676520686160448201527f7320616c7265616479206265656e2072656c61796564000000000000000000006064820152608401610a72565b6112a585611296611388619c40612061565b67ffffffffffffffff166119bf565b15806112cb575060cc5473ffffffffffffffffffffffffffffffffffffffff1661dead14155b156113e457600081815260ce602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555182917f99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f91a27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff32016113dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f43726f7373446f6d61696e4d657373656e6765723a206661696c656420746f2060448201527f72656c6179206d657373616765000000000000000000000000000000000000006064820152608401610a72565b505061163c565b60cc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8a16179055600061147588619c405a61143891906120d4565b8988888080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119dd92505050565b60cc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790559050801561152b57600082815260cb602052604090205460ff16156114c8576114c86120a5565b600082815260cb602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555183917f4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c91a2611638565b600082815260ce602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555183917f99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f91a27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3201611638576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f43726f7373446f6d61696e4d657373656e6765723a206661696c656420746f2060448201527f72656c6179206d657373616765000000000000000000000000000000000000006064820152608401610a72565b5050505b50505050505050565b600061164f6107a1565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611699573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116bd9190611fc6565b905090565b6040517fc2b3e5ac0000000000000000000000000000000000000000000000000000000081527342000000000000000000000000000000000000169063c2b3e5ac908490611718908890889087906004016120eb565b6000604051808303818588803b15801561173157600080fd5b505af1158015611745573d6000803e3d6000fd5b505050505050505050565b6000818310156117605781611762565b825b9392505050565b336117726107a1565b73ffffffffffffffffffffffffffffffffffffffff16141580156117b357503361179a611645565b73ffffffffffffffffffffffffffffffffffffffff1614155b156117ea576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6000547501000000000000000000000000000000000000000000900460ff16611897576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610a72565b60cc5473ffffffffffffffffffffffffffffffffffffffff166118e15760cc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790555b60cf80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000611936858585856119f5565b805190602001209050949350505050565b6000611957878787878787611a8e565b8051906020012090509695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82163014806119b9575073ffffffffffffffffffffffffffffffffffffffff8216734200000000000000000000000000000000000016145b92915050565b600080603f83619c4001026040850201603f5a021015949350505050565b6000806000835160208501868989f195945050505050565b606084848484604051602401611a0e949392919061212a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fcbd4ece9000000000000000000000000000000000000000000000000000000001790529050949350505050565b6060868686868686604051602401611aab96959493929190612174565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd764ad0b0000000000000000000000000000000000000000000000000000000017905290509695505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114611b4f57600080fd5b50565b60008083601f840112611b6457600080fd5b50813567ffffffffffffffff811115611b7c57600080fd5b602083019150836020828501011115611b9457600080fd5b9250929050565b803563ffffffff81168114611baf57600080fd5b919050565b60008060008060608587031215611bca57600080fd5b8435611bd581611b2d565b9350602085013567ffffffffffffffff811115611bf157600080fd5b611bfd87828801611b52565b9094509250611c10905060408601611b9b565b905092959194509250565b6000815180845260005b81811015611c4157602081850181015186830182015201611c25565b81811115611c53576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006117626020830184611c1b565b600060208284031215611cab57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060408385031215611cf457600080fd5b823567ffffffffffffffff80821115611d0c57600080fd5b818501915085601f830112611d2057600080fd5b813581811115611d3257611d32611cb2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611d7857611d78611cb2565b81604052828152886020848701011115611d9157600080fd5b826020860160208301376000602084830101528096505050505050611db860208401611b9b565b90509250929050565b600060208284031215611dd357600080fd5b813561176281611b2d565b600080600080600080600060c0888a031215611df957600080fd5b873596506020880135611e0b81611b2d565b95506040880135611e1b81611b2d565b9450606088013593506080880135925060a088013567ffffffffffffffff811115611e4557600080fd5b611e518a828b01611b52565b989b979a50959850939692959293505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b878152600073ffffffffffffffffffffffffffffffffffffffff808916602084015280881660408401525085606083015263ffffffff8516608083015260c060a0830152611eff60c083018486611e64565b9998505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff86168152608060208201526000611f3c608083018688611e64565b905083604083015263ffffffff831660608301529695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611fc157611fc1611f5a565b500290565b600060208284031215611fd857600080fd5b815161176281611b2d565b600067ffffffffffffffff8083168185168183048111821515161561200a5761200a611f5a565b02949350505050565b600067ffffffffffffffff80841680612055577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b600067ffffffffffffffff80831681851680830382111561208457612084611f5a565b01949350505050565b600082198211156120a0576120a0611f5a565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000828210156120e6576120e6611f5a565b500390565b73ffffffffffffffffffffffffffffffffffffffff8416815267ffffffffffffffff83166020820152606060408201526000610b506060830184611c1b565b600073ffffffffffffffffffffffffffffffffffffffff8087168352808616602084015250608060408301526121636080830185611c1b565b905082606083015295945050505050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a08301526121bf60c0830184611c1b565b9897505050505050505056fea164736f6c634300080f000a00000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 2600000, + "intent": "Deploy L2CrossDomainMessenger Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001ec8608060405234801561001057600080fd5b50611ea8806100206000396000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806368d5dca6116100e3578063c59859181161008c578063f45e65d811610066578063f45e65d8146102fc578063f820614014610304578063fe173b971461029357600080fd5b8063c5985918146102ce578063de26c4a1146102d6578063f1c7a58b146102e957600080fd5b8063960e3a23116100bd578063960e3a23146102a1578063b3d72079146102b3578063b54501bc146102bb57600080fd5b806368d5dca6146102765780636ef25c3a146102935780638e98b1061461029957600080fd5b80632e0f2625116101455780634ef6e2241161011f5780634ef6e22414610218578063519b4bd31461022557806354fd4d501461022d57600080fd5b80632e0f2625146101f6578063313ce567146101fe57806349948e0e1461020557600080fd5b806322b90ab31161017657806322b90ab3146101d1578063275aedd2146101db578063291b0383146101ee57600080fd5b80630c18c16214610192578063105d0b81146101ad575b600080fd5b61019a61030c565b6040519081526020015b60405180910390f35b6000546101c1906301000000900460ff1681565b60405190151581526020016101a4565b6101d961042d565b005b61019a6101e93660046118fa565b6105b6565b6101d9610776565b61019a600681565b600661019a565b61019a610213366004611942565b61099e565b6000546101c19060ff1681565b61019a6109db565b6102696040518060400160405280600581526020017f312e362e3000000000000000000000000000000000000000000000000000000081525081565b6040516101a49190611a11565b61027e610a3c565b60405163ffffffff90911681526020016101a4565b4861019a565b6101d9610ac1565b6000546101c190610100900460ff1681565b6101d9610cbb565b6000546101c19062010000900460ff1681565b61027e610ec2565b61019a6102e4366004611942565b610f23565b61019a6102f73660046118fa565b61101d565b61019a6110f1565b61019a6111e4565b6000805460ff16156103a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f47617350726963654f7261636c653a206f76657268656164282920697320646560448201527f707265636174656400000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015610404573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104289190611a84565b905090565b3373deaddeaddeaddeaddeaddeaddeaddeaddead0001146104f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e2073657420697345636f746f6e6520666c6160648201527f6700000000000000000000000000000000000000000000000000000000000000608482015260a40161039c565b60005460ff1615610589576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a2045636f746f6e6520616c72656164792060448201527f6163746976650000000000000000000000000000000000000000000000000000606482015260840161039c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6000805462010000900460ff166105cf57506000919050565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16634d5d9a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610630573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106549190611a9d565b63ffffffff169050600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166316d3bc7f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e19190611ac3565b67ffffffffffffffff169050600060039054906101000a900460ff161561072a578061070d8386611b1c565b610718906064611b1c565b6107229190611b59565b949350505050565b610722620f424083860286810485148715177fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01176107699190611b71565b8281019081106000031790565b3373deaddeaddeaddeaddeaddeaddeaddeaddead00011461083f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e20736574206973497374686d757320666c6160648201527f6700000000000000000000000000000000000000000000000000000000000000608482015260a40161039c565b600054610100900460ff166108d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f47617350726963654f7261636c653a20497374686d75732063616e206f6e6c7960448201527f2062652061637469766174656420616674657220466a6f726400000000000000606482015260840161039c565b60005462010000900460ff161561096f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a20497374686d757320616c72656164792060448201527f6163746976650000000000000000000000000000000000000000000000000000606482015260840161039c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000179055565b60008054610100900460ff16156109be576109b882611245565b92915050565b60005460ff16156109d2576109b882611264565b6109b882611308565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16635cf249696040518163ffffffff1660e01b8152600401602060405180830381865afa158015610404573d6000803e3d6000fd5b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166368d5dca66040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a9d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104289190611a9d565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610b64576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e20736574206973466a6f726420666c616700606482015260840161039c565b60005460ff16610bf6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f47617350726963654f7261636c653a20466a6f72642063616e206f6e6c79206260448201527f65206163746976617465642061667465722045636f746f6e6500000000000000606482015260840161039c565b600054610100900460ff1615610c8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f47617350726963654f7261636c653a20466a6f726420616c726561647920616360448201527f7469766500000000000000000000000000000000000000000000000000000000606482015260840161039c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff16610100179055565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610d6057604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f47617350726963654f7261636c653a206f6e6c7920746865206465706f73697460448201527f6f72206163636f756e742063616e207365742069734a6f7669616e20666c6167606482015260840161039c565b60005462010000900460ff16610df8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f47617350726963654f7261636c653a204a6f7669616e2063616e206f6e6c792060448201527f62652061637469766174656420616674657220497374686d7573000000000000606482015260840161039c565b6000546301000000900460ff1615610e92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f47617350726963654f7261636c653a204a6f7669616e20616c7265616479206160448201527f6374697665000000000000000000000000000000000000000000000000000000606482015260840161039c565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffff166301000000179055565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663c59859186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a9d573d6000803e3d6000fd5b60008054610100900460ff1615610f6a57620f4240610f55610f448461145c565b51610f50906044611b59565b611779565b610f60906010611b1c565b6109b89190611b71565b6000610f75836117d8565b60005490915060ff1615610f895792915050565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061100c9190611a84565b6110169082611b59565b9392505050565b60008054610100900460ff166110b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f47617350726963654f7261636c653a206765744c314665655570706572426f7560448201527f6e64206f6e6c7920737570706f72747320466a6f726400000000000000000000606482015260840161039c565b60006110c2836044611b59565b905060006110d160ff83611b71565b6110db9083611b59565b6110e6906010611b59565b905061072281611868565b6000805460ff1615611185576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f47617350726963654f7261636c653a207363616c61722829206973206465707260448201527f6563617465640000000000000000000000000000000000000000000000000000606482015260840161039c565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16639e8c49666040518163ffffffff1660e01b8152600401602060405180830381865afa158015610404573d6000803e3d6000fd5b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663f82061406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610404573d6000803e3d6000fd5b60006109b86112538361145c565b5161125f906044611b59565b611868565b600080611270836117d8565b9050600061127c6109db565b611284610ec2565b61128f906010611bac565b63ffffffff1661129f9190611b1c565b905060006112ab6111e4565b6112b3610a3c565b63ffffffff166112c39190611b1c565b905060006112d18284611b59565b6112db9085611b1c565b90506112e96006600a611cf8565b6112f4906010611b1c565b6112fe9082611b71565b9695505050505050565b600080611314836117d8565b9050600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16639e8c49666040518163ffffffff1660e01b8152600401602060405180830381865afa158015611377573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139b9190611a84565b6113a36109db565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638b239f736040518163ffffffff1660e01b8152600401602060405180830381865afa158015611402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114269190611a84565b6114309085611b59565b61143a9190611b1c565b6114449190611b1c565b90506114526006600a611cf8565b6107229082611b71565b60606115eb565b818153600101919050565b600082840393505b838110156110165782810151828201511860001a1590930292600101611476565b825b602082106114e35782516114ae601f83611463565b52602092909201917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090910190602101611499565b81156110165782516114f86001840383611463565b520160010192915050565b60006001830392505b6101078210611544576115368360ff1661153160fd6115318760081c60e00189611463565b611463565b93506101068203915061150c565b600782106115715761156a8360ff16611531600785036115318760081c60e00189611463565b9050611016565b6107228360ff166115318560081c8560051b0187611463565b6115e38282036115c76115b784600081518060001a8160011a60081b178160021a60101b17915050919050565b639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b6180003860405139618000604051016020830180600d8551820103826002015b8181101561171e576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b9091189091528401908183039084841061167357506116ae565b600184019350611fff82116116a8578251600081901a600182901a60081b1760029190911a60101b1781036116a857506116ae565b50611617565b8383106116bc57505061171e565b600183039250858311156116da576116d78787888603611497565b96505b6116ee60098501600385016003850161146e565b91506116fb878284611503565b9650506117138461170e8684860161158a565b61158a565b91505080935061160b565b50506117308383848851850103611497565b925050506040519150618000820180820391508183526020830160005b8381101561176557828101518282015260200161174d565b506000920191825250602001604052919050565b60008061178983620cc394611b1c565b6117b3907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd763200611d04565b90506117c36064620f4240611d78565b8112156109b8576110166064620f4240611d78565b80516000908190815b8181101561185b578481815181106117fb576117fb611e34565b01602001517fff000000000000000000000000000000000000000000000000000000000000001660000361183b57611834600484611b59565b9250611849565b611846601084611b59565b92505b8061185381611e63565b9150506117e1565b5061072282610440611b59565b60008061187483611779565b905060006118806111e4565b611888610a3c565b63ffffffff166118989190611b1c565b6118a06109db565b6118a8610ec2565b6118b3906010611bac565b63ffffffff166118c39190611b1c565b6118cd9190611b59565b90506118db60066002611b1c565b6118e690600a611cf8565b6118f08284611b1c565b6107229190611b71565b60006020828403121561190c57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561195457600080fd5b813567ffffffffffffffff8082111561196c57600080fd5b818401915084601f83011261198057600080fd5b81358181111561199257611992611913565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156119d8576119d8611913565b816040528281528760208487010111156119f157600080fd5b826020860160208301376000928101602001929092525095945050505050565b600060208083528351808285015260005b81811015611a3e57858101830151858201604001528201611a22565b81811115611a50576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b600060208284031215611a9657600080fd5b5051919050565b600060208284031215611aaf57600080fd5b815163ffffffff8116811461101657600080fd5b600060208284031215611ad557600080fd5b815167ffffffffffffffff8116811461101657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611b5457611b54611aed565b500290565b60008219821115611b6c57611b6c611aed565b500190565b600082611ba7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600063ffffffff80831681851681830481118215151615611bcf57611bcf611aed565b02949350505050565b600181815b80851115611c3157817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611c1757611c17611aed565b80851615611c2457918102915b93841c9390800290611bdd565b509250929050565b600082611c48575060016109b8565b81611c55575060006109b8565b8160018114611c6b5760028114611c7557611c91565b60019150506109b8565b60ff841115611c8657611c86611aed565b50506001821b6109b8565b5060208310610133831016604e8410600b8410161715611cb4575081810a6109b8565b611cbe8383611bd8565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115611cf057611cf0611aed565b029392505050565b60006110168383611c39565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615611d3e57611d3e611aed565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615611d7257611d72611aed565b50500190565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615611db957611db9611aed565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615611df457611df4611aed565b60008712925087820587128484161615611e1057611e10611aed565b87850587128184161615611e2657611e26611aed565b505050929093029392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611e9457611e94611aed565b506001019056fea164736f6c634300080f000a000000000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 2600000, + "intent": "Deploy GasPriceOracle Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000002f3d60806040523480156200001157600080fd5b506200001c62000022565b620000e4565b600054610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161015620000e2576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b612e4980620000f46000396000f3fe6080604052600436106101485760003560e01c80635c975abb116100c0578063a3a7954811610074578063c89701a211610059578063c89701a214610459578063dad544e014610486578063e11013dd1461049b57600080fd5b8063a3a7954814610426578063c4d66de81461043957600080fd5b806387087623116100a557806387087623146103955780638f601f66146103b5578063927ede2d146103fb57600080fd5b80635c975abb146103795780637f46ddb21461027a57600080fd5b806336c717c1116101175780633e47158c116100fc5780633e47158c146102f8578063540abf731461030d57806354fd4d501461032d57600080fd5b806336c717c11461027a5780633cb747bf146102cb57600080fd5b80630166a07a1461022157806309fc8843146102415780631635f5fd1461025457806332b7006d1461026757600080fd5b3661021c576101556104ae565b6101e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f5374616e646172644272696467653a2066756e6374696f6e2063616e206f6e6c60448201527f792062652063616c6c65642066726f6d20616e20454f4100000000000000000060648201526084015b60405180910390fd5b61021a73deaddeaddeaddeaddeaddeaddeaddeaddead000033333462030d40604051806020016040528060008152506104eb565b005b600080fd5b34801561022d57600080fd5b5061021a61023c3660046127ff565b6105c6565b61021a61024f3660046128b0565b610968565b61021a610262366004612903565b610a44565b61021a610275366004612976565b610e96565b34801561028657600080fd5b5060045473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156102d757600080fd5b506003546102a19073ffffffffffffffffffffffffffffffffffffffff1681565b34801561030457600080fd5b506102a1610f75565b34801561031957600080fd5b5061021a6103283660046129ca565b611180565b34801561033957600080fd5b50604080518082018252600681527f312e31332e310000000000000000000000000000000000000000000000000000602082015290516102c29190612ab7565b34801561038557600080fd5b50604051600081526020016102c2565b3480156103a157600080fd5b5061021a6103b0366004612aca565b6111c5565b3480156103c157600080fd5b506103ed6103d0366004612b4d565b600260209081526000928352604080842090915290825290205481565b6040519081526020016102c2565b34801561040757600080fd5b5060035473ffffffffffffffffffffffffffffffffffffffff166102a1565b61021a610434366004612aca565b61129e565b34801561044557600080fd5b5061021a610454366004612b86565b6112e2565b34801561046557600080fd5b506004546102a19073ffffffffffffffffffffffffffffffffffffffff1681565b34801561049257600080fd5b506102a1611493565b61021a6104a9366004612ba3565b611510565b60003233036104bd5750600190565b333b6017036104e557604051602081016040526020600082333c5160e81c62ef010014905090565b50600090565b7fffffffffffffffffffffffff215221522152215221522152215221522153000073ffffffffffffffffffffffffffffffffffffffff87160161053a576105358585858585611559565b6105be565b60008673ffffffffffffffffffffffffffffffffffffffff1663c01e1bd66040518163ffffffff1660e01b8152600401602060405180830381865afa158015610587573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ab9190612c06565b90506105bc87828888888888611723565b505b505050505050565b60035473ffffffffffffffffffffffffffffffffffffffff1633148015610699575060048054600354604080517f6e296e45000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff938416949390921692636e296e459282820192602092908290030181865afa15801561065d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106819190612c06565b73ffffffffffffffffffffffffffffffffffffffff16145b61074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f5374616e646172644272696467653a2066756e6374696f6e2063616e206f6e6c60448201527f792062652063616c6c65642066726f6d20746865206f7468657220627269646760648201527f6500000000000000000000000000000000000000000000000000000000000000608482015260a4016101dd565b61075487611adc565b156108a2576107638787611b3e565b610815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f5374616e646172644272696467653a2077726f6e672072656d6f746520746f6b60448201527f656e20666f72204f7074696d69736d204d696e7461626c65204552433230206c60648201527f6f63616c20746f6b656e00000000000000000000000000000000000000000000608482015260a4016101dd565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8581166004830152602482018590528816906340c10f1990604401600060405180830381600087803b15801561088557600080fd5b505af1158015610899573d6000803e3d6000fd5b50505050610924565b73ffffffffffffffffffffffffffffffffffffffff8088166000908152600260209081526040808320938a16835292905220546108e0908490612c52565b73ffffffffffffffffffffffffffffffffffffffff8089166000818152600260209081526040808320948c1683529390529190912091909155610924908585611c5e565b6105bc878787878787878080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611d3292505050565b6109706104ae565b6109fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f5374616e646172644272696467653a2066756e6374696f6e2063616e206f6e6c60448201527f792062652063616c6c65642066726f6d20616e20454f4100000000000000000060648201526084016101dd565b610a3f3333348686868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061155992505050565b505050565b60035473ffffffffffffffffffffffffffffffffffffffff1633148015610b17575060048054600354604080517f6e296e45000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff938416949390921692636e296e459282820192602092908290030181865afa158015610adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aff9190612c06565b73ffffffffffffffffffffffffffffffffffffffff16145b610bc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604160248201527f5374616e646172644272696467653a2066756e6374696f6e2063616e206f6e6c60448201527f792062652063616c6c65642066726f6d20746865206f7468657220627269646760648201527f6500000000000000000000000000000000000000000000000000000000000000608482015260a4016101dd565b823414610c58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5374616e646172644272696467653a20616d6f756e742073656e7420646f657360448201527f206e6f74206d6174636820616d6f756e7420726571756972656400000000000060648201526084016101dd565b3073ffffffffffffffffffffffffffffffffffffffff851603610cfd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f5374616e646172644272696467653a2063616e6e6f742073656e6420746f207360448201527f656c66000000000000000000000000000000000000000000000000000000000060648201526084016101dd565b60035473ffffffffffffffffffffffffffffffffffffffff90811690851603610da8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f5374616e646172644272696467653a2063616e6e6f742073656e6420746f206d60448201527f657373656e67657200000000000000000000000000000000000000000000000060648201526084016101dd565b610dea85858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611dc092505050565b6000610e07855a8660405180602001604052806000815250611e61565b9050806105be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f5374616e646172644272696467653a20455448207472616e736665722066616960448201527f6c6564000000000000000000000000000000000000000000000000000000000060648201526084016101dd565b610e9e6104ae565b610f2a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f5374616e646172644272696467653a2066756e6374696f6e2063616e206f6e6c60448201527f792062652063616c6c65642066726f6d20616e20454f4100000000000000000060648201526084016101dd565b610f6e853333878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506104eb92505050565b5050505050565b600080610fa07fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff811615610fc357919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026110069190612c69565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000009190911790611061906060015b604051602081830303815290604052805190602001205490565b14611098576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091526000906110ba90606001611047565b905073ffffffffffffffffffffffffffffffffffffffff81161561114e578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611123573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111479190612c06565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105bc87873388888888888080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061172392505050565b6111cd6104ae565b611259576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f5374616e646172644272696467653a2066756e6374696f6e2063616e206f6e6c60448201527f792062652063616c6c65642066726f6d20616e20454f4100000000000000000060648201526084016101dd565b6105be86863333888888888080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061172392505050565b6105be863387878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506104eb92505050565b600054610100900460ff16158080156113025750600054600160ff909116105b8061131c5750303b15801561131c575060005460ff166001145b6113a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016101dd565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561140657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b61140e611e79565b61142c73420000000000000000000000000000000000000783611efc565b801561148f57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b600061149d610f75565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061150b9190612c06565b905090565b6115533385348686868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061155992505050565b50505050565b8234146115e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5374616e646172644272696467653a206272696467696e6720455448206d757360448201527f7420696e636c7564652073756666696369656e74204554482076616c7565000060648201526084016101dd565b6115f485858584611fe6565b60035460045460405173ffffffffffffffffffffffffffffffffffffffff92831692633dbb202b9287929116907f1635f5fd0000000000000000000000000000000000000000000000000000000090611657908b908b9086908a90602401612ca6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009485161790525160e086901b90921682526116ea92918890600401612cef565b6000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050505050505050565b34156117b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f5374616e646172644272696467653a2063616e6e6f742073656e642076616c7560448201527f650000000000000000000000000000000000000000000000000000000000000060648201526084016101dd565b6117ba87611adc565b15611908576117c98787611b3e565b61187b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f5374616e646172644272696467653a2077726f6e672072656d6f746520746f6b60448201527f656e20666f72204f7074696d69736d204d696e7461626c65204552433230206c60648201527f6f63616c20746f6b656e00000000000000000000000000000000000000000000608482015260a4016101dd565b6040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff868116600483015260248201859052881690639dc29fac90604401600060405180830381600087803b1580156118eb57600080fd5b505af11580156118ff573d6000803e3d6000fd5b5050505061199c565b61192a73ffffffffffffffffffffffffffffffffffffffff8816863086612087565b73ffffffffffffffffffffffffffffffffffffffff8088166000908152600260209081526040808320938a1683529290522054611968908490612d34565b73ffffffffffffffffffffffffffffffffffffffff8089166000908152600260209081526040808320938b16835292905220555b6119aa8787878787866120e5565b60035460045460405173ffffffffffffffffffffffffffffffffffffffff92831692633dbb202b9216907f0166a07a0000000000000000000000000000000000000000000000000000000090611a0e908b908d908c908c908c908b90602401612d4c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009485161790525160e085901b9092168252611aa192918790600401612cef565b600060405180830381600087803b158015611abb57600080fd5b505af1158015611acf573d6000803e3d6000fd5b5050505050505050505050565b6000611b08827f1d1d8b6300000000000000000000000000000000000000000000000000000000612173565b80611b385750611b38827fec4fc8e300000000000000000000000000000000000000000000000000000000612173565b92915050565b6000611b6a837f1d1d8b6300000000000000000000000000000000000000000000000000000000612173565b15611c13578273ffffffffffffffffffffffffffffffffffffffff1663c01e1bd66040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bde9190612c06565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16149050611b38565b8273ffffffffffffffffffffffffffffffffffffffff1663d6c0b2c46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bba573d6000803e3d6000fd5b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052610a3f9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612196565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fb0444523268717a02698be47d0803aa7468c00acbed2f8bd93a0459cde61dd89868686604051611daa93929190612da7565b60405180910390a46105be8686868686866122a2565b8373ffffffffffffffffffffffffffffffffffffffff1673deaddeaddeaddeaddeaddeaddeaddeaddead000073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fb0444523268717a02698be47d0803aa7468c00acbed2f8bd93a0459cde61dd89868686604051611e4d93929190612da7565b60405180910390a46115538484848461232a565b6000806000835160208501868989f195945050505050565b33611e82610f75565b73ffffffffffffffffffffffffffffffffffffffff1614158015611ec3575033611eaa611493565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611efa576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b600054610100900460ff16611f93576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016101dd565b6003805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560048054929093169116179055565b8373ffffffffffffffffffffffffffffffffffffffff1673deaddeaddeaddeaddeaddeaddeaddeaddead000073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f73d170910aba9e6d50b102db522b1dbcd796216f5128b445aa2135272886497e86868660405161207393929190612da7565b60405180910390a461155384848484612397565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526115539085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611cb0565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f73d170910aba9e6d50b102db522b1dbcd796216f5128b445aa2135272886497e86868660405161215d93929190612da7565b60405180910390a46105be8686868686866123f6565b600061217e8361246e565b801561218f575061218f83836124d2565b9392505050565b60006121f8826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166125a19092919063ffffffff16565b805190915015610a3f57808060200190518101906122169190612de5565b610a3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016101dd565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fd59c65b35445225835c83f50b6ede06a7be047d22e357073e250d9af537518cd86868660405161231a93929190612da7565b60405180910390a4505050505050565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f31b2166ff604fc5672ea5df08a78081d2bc6d746cadce880747f3643d819e83d8484604051612389929190612e07565b60405180910390a350505050565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f2849b43074093a05396b6f2a937dee8565b15a48a7b3d4bffb732a5017380af58484604051612389929190612e07565b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167f7ff126db8024424bbfd9826e8ab82ff59136289ea440b04b39a0df1b03b9cabf86868660405161231a93929190612da7565b600061249a827f01ffc9a7000000000000000000000000000000000000000000000000000000006124d2565b8015611b3857506124cb827fffffffff000000000000000000000000000000000000000000000000000000006124d2565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d9150600051905082801561258a575060208210155b80156125965750600081115b979650505050505050565b60606125b084846000856125b8565b949350505050565b60608247101561264a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016101dd565b73ffffffffffffffffffffffffffffffffffffffff85163b6126c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101dd565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516126f19190612e20565b60006040518083038185875af1925050503d806000811461272e576040519150601f19603f3d011682016040523d82523d6000602084013e612733565b606091505b50915091506125968282866060831561274d57508161218f565b82511561275d5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101dd9190612ab7565b73ffffffffffffffffffffffffffffffffffffffff811681146127b357600080fd5b50565b60008083601f8401126127c857600080fd5b50813567ffffffffffffffff8111156127e057600080fd5b6020830191508360208285010111156127f857600080fd5b9250929050565b600080600080600080600060c0888a03121561281a57600080fd5b873561282581612791565b9650602088013561283581612791565b9550604088013561284581612791565b9450606088013561285581612791565b93506080880135925060a088013567ffffffffffffffff81111561287857600080fd5b6128848a828b016127b6565b989b979a50959850939692959293505050565b803563ffffffff811681146128ab57600080fd5b919050565b6000806000604084860312156128c557600080fd5b6128ce84612897565b9250602084013567ffffffffffffffff8111156128ea57600080fd5b6128f6868287016127b6565b9497909650939450505050565b60008060008060006080868803121561291b57600080fd5b853561292681612791565b9450602086013561293681612791565b935060408601359250606086013567ffffffffffffffff81111561295957600080fd5b612965888289016127b6565b969995985093965092949392505050565b60008060008060006080868803121561298e57600080fd5b853561299981612791565b9450602086013593506129ae60408701612897565b9250606086013567ffffffffffffffff81111561295957600080fd5b600080600080600080600060c0888a0312156129e557600080fd5b87356129f081612791565b96506020880135612a0081612791565b95506040880135612a1081612791565b945060608801359350612a2560808901612897565b925060a088013567ffffffffffffffff81111561287857600080fd5b60005b83811015612a5c578181015183820152602001612a44565b838111156115535750506000910152565b60008151808452612a85816020860160208601612a41565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061218f6020830184612a6d565b60008060008060008060a08789031215612ae357600080fd5b8635612aee81612791565b95506020870135612afe81612791565b945060408701359350612b1360608801612897565b9250608087013567ffffffffffffffff811115612b2f57600080fd5b612b3b89828a016127b6565b979a9699509497509295939492505050565b60008060408385031215612b6057600080fd5b8235612b6b81612791565b91506020830135612b7b81612791565b809150509250929050565b600060208284031215612b9857600080fd5b813561218f81612791565b60008060008060608587031215612bb957600080fd5b8435612bc481612791565b9350612bd260208601612897565b9250604085013567ffffffffffffffff811115612bee57600080fd5b612bfa878288016127b6565b95989497509550505050565b600060208284031215612c1857600080fd5b815161218f81612791565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015612c6457612c64612c23565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612ca157612ca1612c23565b500290565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152612ce56080830184612a6d565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff84168152606060208201526000612d1e6060830185612a6d565b905063ffffffff83166040830152949350505050565b60008219821115612d4757612d47612c23565b500190565b600073ffffffffffffffffffffffffffffffffffffffff80891683528088166020840152808716604084015280861660608401525083608083015260c060a0830152612d9b60c0830184612a6d565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff84168152826020820152606060408201526000612ddc6060830184612a6d565b95945050505050565b600060208284031215612df757600080fd5b8151801515811461218f57600080fd5b8281526040602082015260006125b06040830184612a6d565b60008251612e32818460208701612a41565b919091019291505056fea164736f6c634300080f000a000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 3600000, + "intent": "Deploy L2StandardBridge Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000010956080604052348015600e575f80fd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b610fbf806100d65f395ff3fe6080604052600436106100f2575f3560e01c80638312f14911610087578063d0e12f9011610057578063d0e12f90146102e9578063d3e5792b14610318578063d4ff9218146100fd578063dad544e01461032c575f80fd5b80638312f1491461028257806384411d651461029757806385b5b14d146102ab578063b49dc741146102ca575f80fd5b80633e47158c116100c25780633e47158c146101af57806354fd4d50146101c357806366d003ac1461021857806382356d8a14610244575f80fd5b80630d9019e1146100fd578063307f29621461014d5780633bbed4a01461016e5780633ccfd60b1461018d575f80fd5b366100f957005b5f80fd5b348015610108575f80fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b348015610158575f80fd5b5061016c610167366004610d6d565b610340565b005b348015610179575f80fd5b5061016c610188366004610daa565b6103e6565b348015610198575f80fd5b506101a161046d565b604051908152602001610144565b3480156101ba575f80fd5b5061012361077b565b3480156101ce575f80fd5b5061020b6040518060400160405280600581526020017f312e362e3100000000000000000000000000000000000000000000000000000081525081565b6040516101449190610dc5565b348015610223575f80fd5b506002546101239073ffffffffffffffffffffffffffffffffffffffff1681565b34801561024f575f80fd5b506002546102759074010000000000000000000000000000000000000000900460ff1681565b6040516101449190610e7e565b34801561028d575f80fd5b506101a160015481565b3480156102a2575f80fd5b506101a15f5481565b3480156102b6575f80fd5b5061016c6102c5366004610e92565b610981565b3480156102d5575f80fd5b5061016c6102e4366004610ea9565b6109c7565b3480156102f4575f80fd5b5060025474010000000000000000000000000000000000000000900460ff16610275565b348015610323575f80fd5b506001546101a1565b348015610337575f80fd5b50610123610be3565b610348610c5d565b600280547401000000000000000000000000000000000000000080820460ff1692849290917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff909116908360018111156103a4576103a4610e18565b02179055507ff2ec44eb1c3b3acd547b76333eb2c4b27eee311860c57a9fdb04c95f62398fc881836040516103da929190610ee4565b60405180910390a15050565b6103ee610c5d565b6002805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f62e69886a5df0ba8ffcacbfc1388754e7abd9bde24b036354c561f1acd4e459391016103da565b5f60015447101561052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d207769746864726160648201527f77616c20616d6f756e7400000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b479050805f8082825461053e9190610f2c565b90915550506002546040805183815273ffffffffffffffffffffffffffffffffffffffff909216602083018190523383830152905190917fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba919081900360600190a16002546040517f38e04cbeb8c10f8f568618aa75be0f10b6729b8b4237743b4de20cbcde2839ee916105f39185918591339174010000000000000000000000000000000000000000900460ff1690610f3f565b60405180910390a1600160025474010000000000000000000000000000000000000000900460ff16600181111561062c5761062c610e18565b036106d0575f61063c8284610cb5565b9050806106cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4665655661756c743a206661696c656420746f2073656e642045544820746f2060448201527f4c322066656520726563697069656e74000000000000000000000000000000006064820152608401610522565b505090565b6040517fc2b3e5ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015262061a806024820152606060448201525f60648201527342000000000000000000000000000000000000169063c2b3e5ac9084906084015f604051808303818588803b158015610760575f80fd5b505af1158015610772573d5f803e3d5ffd5b50505050505090565b5f806107a57fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156107c857919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e67657200000000000081525051600261080b9190610f80565b604080513060208201525f918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000009190911790610865906060015b604051602081830303815290604052805190602001205490565b1461089c576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091525f906108bd9060600161084b565b905073ffffffffffffffffffffffffffffffffffffffff81161561094f578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610924573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109489190610f97565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610989610c5d565b600180549082905560408051828152602081018490527f895a067c78583e800418fabf3da26a9496aab2ff3429cebdf7fefa642b2e420391016103da565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610a115750825b90505f8267ffffffffffffffff166001148015610a2d5750303b155b905081158015610a3b575080155b15610a72576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610ad35784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610adb610cc8565b6002805473ffffffffffffffffffffffffffffffffffffffff8a167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117835560018a81558993927fffffffffffffffffffffff000000000000000000000000000000000000000000169091179074010000000000000000000000000000000000000000908490811115610b7357610b73610e18565b02179055508315610bd95784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f610bec61077b565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c34573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c589190610f97565b905090565b33610c66610be3565b73ffffffffffffffffffffffffffffffffffffffff1614610cb3576040517f7f12c64b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f610cc1835a84610d49565b9392505050565b33610cd161077b565b73ffffffffffffffffffffffffffffffffffffffff1614158015610d12575033610cf9610be3565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610cb3576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f805f858888f1949350505050565b803560028110610d68575f80fd5b919050565b5f60208284031215610d7d575f80fd5b610cc182610d5a565b73ffffffffffffffffffffffffffffffffffffffff81168114610da7575f80fd5b50565b5f60208284031215610dba575f80fd5b8135610cc181610d86565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60028110610e7a577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9052565b60208101610e8c8284610e45565b92915050565b5f60208284031215610ea2575f80fd5b5035919050565b5f805f60608486031215610ebb575f80fd5b8335610ec681610d86565b925060208401359150610edb60408501610d5a565b90509250925092565b60408101610ef28285610e45565b610cc16020830184610e45565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610e8c57610e8c610eff565b84815273ffffffffffffffffffffffffffffffffffffffff84811660208301528316604082015260808101610f776060830184610e45565b95945050505050565b8082028115828204841417610e8c57610e8c610eff565b5f60208284031215610fa7575f80fd5b8151610cc181610d8656fea164736f6c6343000819000a0000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1300000, + "intent": "Deploy SequencerFeeVault Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000002b6d608060405234801561001057600080fd5b5061001961001e565b6100de565b600054610100900460ff161561008a5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156100dc576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b612a80806100ed6000396000f3fe60806040523480156200001157600080fd5b5060043610620000935760003560e01c8063c4d66de81162000062578063c4d66de81462000175578063ce5ac90f146200018e578063e78cea9214620001a5578063ee9a31a214620001c657600080fd5b8063316b3739146200009857806354fd4d5014620000fb578063896f93d114620001475780638cf0629c146200015e575b600080fd5b620000d1620000a936600462000636565b60026020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620001386040518060400160405280600681526020017f312e31302e32000000000000000000000000000000000000000000000000000081525081565b604051620000f29190620006c9565b620000d162000158366004620007c0565b620001e5565b620000d16200016f3660046200083d565b620001fc565b6200018c6200018636600462000636565b6200041b565b005b620000d16200019f366004620007c0565b620005ed565b600154620000d19073ffffffffffffffffffffffffffffffffffffffff1681565b60015473ffffffffffffffffffffffffffffffffffffffff16620000d1565b6000620001f4848484620005ed565b949350505050565b600073ffffffffffffffffffffffffffffffffffffffff8516620002a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d4d696e7461626c654552433230466163746f72793a206d7560448201527f73742070726f766964652072656d6f746520746f6b656e20616464726573730060648201526084015b60405180910390fd5b600085858585604051602001620002c29493929190620008d4565b604051602081830303815290604052805190602001209050600081600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16888888886040516200031290620005fe565b620003229594939291906200092e565b8190604051809103906000f590508015801562000343573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff81811660008181526002602052604080822080547fffffffffffffffffffffffff000000000000000000000000000000000000000016948d1694851790555193945090927fceeb8e7d520d7f3b65fc11a262b91066940193b05d4f93df07cfdced0eb551cf9190a360405133815273ffffffffffffffffffffffffffffffffffffffff80891691908316907f52fe89dd5930f343d25650b62fd367bae47088bcddffd2a88350a6ecdd620cdb9060200160405180910390a39695505050505050565b600054610100900460ff16158080156200043c5750600054600160ff909116105b80620004585750303b15801562000458575060005460ff166001145b620004e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016200029e565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156200054557600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161790558015620005e957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6000620001f48484846012620001fc565b6120e0806200099483390190565b803573ffffffffffffffffffffffffffffffffffffffff811681146200063157600080fd5b919050565b6000602082840312156200064957600080fd5b62000654826200060c565b9392505050565b6000815180845260005b81811015620006835760208185018101518683018201520162000665565b8181111562000696576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006200065460208301846200065b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126200071f57600080fd5b813567ffffffffffffffff808211156200073d576200073d620006de565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715620007865762000786620006de565b81604052838152866020858801011115620007a057600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215620007d657600080fd5b620007e1846200060c565b9250602084013567ffffffffffffffff80821115620007ff57600080fd5b6200080d878388016200070d565b935060408601359150808211156200082457600080fd5b5062000833868287016200070d565b9150509250925092565b600080600080608085870312156200085457600080fd5b6200085f856200060c565b9350602085013567ffffffffffffffff808211156200087d57600080fd5b6200088b888389016200070d565b94506040870135915080821115620008a257600080fd5b50620008b1878288016200070d565b925050606085013560ff81168114620008c957600080fd5b939692955090935050565b73ffffffffffffffffffffffffffffffffffffffff851681526080602082015260006200090560808301866200065b565b82810360408401526200091981866200065b565b91505060ff8316606083015295945050505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525060a060408301526200096960a08301866200065b565b82810360608401526200097d81866200065b565b91505060ff83166080830152969550505050505056fe6101a06040523480156200001257600080fd5b50604051620020e0380380620020e0833981016040819052620000359162000215565b6040805180820190915260018152603160f81b6020820152839081908185600362000061838262000350565b50600462000070828262000350565b5050825160208085019190912083518483012060e08290526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81880181905281830187905260608201869052608082019490945230818401528151808203909301835260c0019052805194019390932091935091906080523060c05261012052505050506001600160a01b0394851661014052509390921661016052505060ff16610180526200041c565b80516001600160a01b03811681146200014357600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200017057600080fd5b81516001600160401b03808211156200018d576200018d62000148565b604051601f8301601f19908116603f01168101908282118183101715620001b857620001b862000148565b81604052838152602092508683858801011115620001d557600080fd5b600091505b83821015620001f95785820183015181830184015290820190620001da565b838211156200020b5760008385830101525b9695505050505050565b600080600080600060a086880312156200022e57600080fd5b62000239866200012b565b945062000249602087016200012b565b60408701519094506001600160401b03808211156200026757600080fd5b6200027589838a016200015e565b945060608801519150808211156200028c57600080fd5b506200029b888289016200015e565b925050608086015160ff81168114620002b357600080fd5b809150509295509295909350565b600181811c90821680620002d657607f821691505b602082108103620002f757634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200034b57600081815260208120601f850160051c81016020861015620003265750805b601f850160051c820191505b81811015620003475782815560010162000332565b5050505b505050565b81516001600160401b038111156200036c576200036c62000148565b62000384816200037d8454620002c1565b84620002fd565b602080601f831160018114620003bc5760008415620003a35750858301515b600019600386901b1c1916600185901b17855562000347565b600085815260208120601f198616915b82811015620003ed57888601518255948401946001909101908401620003cc565b50858210156200040c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e0516101005161012051610140516101605161018051611c37620004a960003960006102700152600081816103a70152818161041c0152818161064801526107aa0152600081816101d501526103cd01526000611174015260006111c30152600061119e015260006110f7015260006111210152600061114b0152611c376000f3fe608060405234801561001057600080fd5b50600436106101a35760003560e01c806370a08231116100ee578063ae1f6aaf11610097578063d6c0b2c411610071578063d6c0b2c4146103cb578063dd62ed3e14610404578063e78cea92146103a5578063ee9a31a21461041757600080fd5b8063ae1f6aaf146103a5578063c01e1bd6146103cb578063d505accf146103f157600080fd5b80639dc29fac116100c85780639dc29fac1461036c578063a457c2d71461037f578063a9059cbb1461039257600080fd5b806370a082311461031b5780637ecebe001461035157806395d89b411461036457600080fd5b8063313ce5671161015057806340c10f191161012a57806340c10f19146102b557806354fd4d50146102ca5780636afdd8501461030657600080fd5b8063313ce567146102695780633644e5151461029a57806339509351146102a257600080fd5b8063095ea7b311610181578063095ea7b31461023157806318160ddd1461024457806323b872dd1461025657600080fd5b806301ffc9a7146101a8578063033964be146101d057806306fdde031461021c575b600080fd5b6101bb6101b636600461194b565b61043e565b60405190151581526020015b60405180910390f35b6101f77f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c7565b61022461052f565b6040516101c7919061198d565b6101bb61023f366004611a29565b6105c1565b6002545b6040519081526020016101c7565b6101bb610264366004611a53565b6105db565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016101c7565b6102486105ff565b6101bb6102b0366004611a29565b61060e565b6102c86102c3366004611a29565b610630565b005b6102246040518060400160405280600581526020017f312e342e3100000000000000000000000000000000000000000000000000000081525081565b6e22d473030f116ddee9f6b43ac78ba36101f7565b610248610329366004611a8f565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61024861035f366004611a8f565b610758565b610224610783565b6102c861037a366004611a29565b610792565b6101bb61038d366004611a29565b6108a9565b6101bb6103a0366004611a29565b610956565b7f00000000000000000000000000000000000000000000000000000000000000006101f7565b7f00000000000000000000000000000000000000000000000000000000000000006101f7565b6102c86103ff366004611aaa565b610964565b610248610412366004611b1d565b610b23565b6101f77f000000000000000000000000000000000000000000000000000000000000000081565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007f1d1d8b63000000000000000000000000000000000000000000000000000000007fec4fc8e3000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000085168314806104f757507fffffffff00000000000000000000000000000000000000000000000000000000858116908316145b8061052657507fffffffff00000000000000000000000000000000000000000000000000000000858116908216145b95945050505050565b60606003805461053e90611b50565b80601f016020809104026020016040519081016040528092919081815260200182805461056a90611b50565b80156105b75780601f1061058c576101008083540402835291602001916105b7565b820191906000526020600020905b81548152906001019060200180831161059a57829003601f168201915b5050505050905090565b6000336105cf818585610bc4565b60019150505b92915050565b6000336105e9858285610d78565b6105f4858585610e2a565b506001949350505050565b60006106096110dd565b905090565b6000336105cf8185856106218383610b23565b61062b9190611bcc565b610bc4565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146106fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f4f7074696d69736d4d696e7461626c6545524332303a206f6e6c79206272696460448201527f67652063616e206d696e7420616e64206275726e00000000000000000000000060648201526084015b60405180910390fd5b6107048282611211565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161074c91815260200190565b60405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260408120546105d5565b60606004805461053e90611b50565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610857576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f4f7074696d69736d4d696e7461626c6545524332303a206f6e6c79206272696460448201527f67652063616e206d696e7420616e64206275726e00000000000000000000000060648201526084016106f1565b6108618282611331565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161074c91815260200190565b600033816108b78286610b23565b905083811015610949576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016106f1565b6105f48286868403610bc4565b6000336105cf818585610e2a565b834211156109ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e6500000060448201526064016106f1565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886109fd8c611516565b60408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610a658261154b565b90506000610a75828787876115b4565b90508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610b0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e6174757265000060448201526064016106f1565b610b178a8a8a610bc4565b50505050505050505050565b60007fffffffffffffffffffffffffffffffffffdd2b8cfcf0ee922116094bc538745d73ffffffffffffffffffffffffffffffffffffffff831601610b8957507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6105d5565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600160209081526040808320938616835292905220545b9392505050565b73ffffffffffffffffffffffffffffffffffffffff8316610c66576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff8216610d09576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000610d848484610b23565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e245781811015610e17576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016106f1565b610e248484848403610bc4565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316610ecd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff8216610f70576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015611026576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526020819052604080822085850390559185168152908120805484929061106a908490611bcc565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516110d091815260200190565b60405180910390a3610e24565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614801561114357507f000000000000000000000000000000000000000000000000000000000000000046145b1561116d57507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b73ffffffffffffffffffffffffffffffffffffffff821661128e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016106f1565b80600260008282546112a09190611bcc565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600090815260208190526040812080548392906112da908490611bcc565b909155505060405181815273ffffffffffffffffffffffffffffffffffffffff8316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff82166113d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff82166000908152602081905260409020548181101561148a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016106f1565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604081208383039055600280548492906114c6908490611be4565b909155505060405182815260009073ffffffffffffffffffffffffffffffffffffffff8516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610d6b565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604090208054600181018255905b50919050565b60006105d56115586110dd565b836040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006115c5878787876115dc565b915091506115d2816116f4565b5095945050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561161357506000905060036116eb565b8460ff16601b1415801561162b57508460ff16601c14155b1561163c57506000905060046116eb565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611690573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166116e4576000600192509250506116eb565b9150600090505b94509492505050565b600081600481111561170857611708611bfb565b036117105750565b600181600481111561172457611724611bfb565b0361178b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016106f1565b600281600481111561179f5761179f611bfb565b03611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016106f1565b600381600481111561181a5761181a611bfb565b036118a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f756500000000000000000000000000000000000000000000000000000000000060648201526084016106f1565b60048160048111156118bb576118bb611bfb565b03611948576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c60448201527f756500000000000000000000000000000000000000000000000000000000000060648201526084016106f1565b50565b60006020828403121561195d57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610bbd57600080fd5b600060208083528351808285015260005b818110156119ba5785810183015185820160400152820161199e565b818111156119cc576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611a2457600080fd5b919050565b60008060408385031215611a3c57600080fd5b611a4583611a00565b946020939093013593505050565b600080600060608486031215611a6857600080fd5b611a7184611a00565b9250611a7f60208501611a00565b9150604084013590509250925092565b600060208284031215611aa157600080fd5b610bbd82611a00565b600080600080600080600060e0888a031215611ac557600080fd5b611ace88611a00565b9650611adc60208901611a00565b95506040880135945060608801359350608088013560ff81168114611b0057600080fd5b9699959850939692959460a0840135945060c09093013592915050565b60008060408385031215611b3057600080fd5b611b3983611a00565b9150611b4760208401611a00565b90509250929050565b600181811c90821680611b6457607f821691505b602082108103611545577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115611bdf57611bdf611b9d565b500190565b600082821015611bf657611bf6611b9d565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000aa164736f6c634300080f000a00000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 3600000, + "intent": "Deploy OptimismMintableERC20Factory Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001aaa608060405234801561001057600080fd5b5061001961001e565b6100de565b600054610100900460ff161561008a5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156100dc576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6119bd806100ed6000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c80637f46ddb211610081578063c4d66de81161005b578063c4d66de8146101fa578063c89701a21461020d578063dad544e01461022d57600080fd5b80637f46ddb2146101ab578063927ede2d146101c9578063aa557452146101e757600080fd5b806354fd4d50116100b257806354fd4d50146101405780635c975abb14610189578063761f44931461019857600080fd5b80633687011a146100d95780633cb747bf146100ee5780633e47158c14610138575b600080fd5b6100ec6100e73660046115d7565b610235565b005b60015461010e9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010e6102e6565b61017c6040518060400160405280600681526020017f312e31302e31000000000000000000000000000000000000000000000000000081525081565b60405161012f91906116c5565b6040516000815260200161012f565b6100ec6101a63660046116d8565b6104f1565b60025473ffffffffffffffffffffffffffffffffffffffff1661010e565b60015473ffffffffffffffffffffffffffffffffffffffff1661010e565b6100ec6101f5366004611770565b610a0f565b6100ec6102083660046117e7565b610acb565b60025461010e9073ffffffffffffffffffffffffffffffffffffffff1681565b61010e610c7c565b61023d610cf9565b6102ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f4552433732314272696467653a206163636f756e74206973206e6f742065787460448201527f65726e616c6c79206f776e65640000000000000000000000000000000000000060648201526084015b60405180910390fd5b6102de8686333388888888610d36565b505050505050565b6000806103117fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff81161561033457919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026103779190611804565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e67657200000000000091909117906103d2906060015b604051602081830303815290604052805190602001205490565b14610409576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805130602082015260019181019190915260009061042b906060016103b8565b905073ffffffffffffffffffffffffffffffffffffffff8116156104bf578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610494573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b89190611868565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015473ffffffffffffffffffffffffffffffffffffffff16331480156105c65750600254600154604080517f6e296e45000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff9384169390921691636e296e45916004808201926020929091908290030181865afa15801561058a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ae9190611868565b73ffffffffffffffffffffffffffffffffffffffff16145b610652576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4552433732314272696467653a2066756e6374696f6e2063616e206f6e6c792060448201527f62652063616c6c65642066726f6d20746865206f74686572206272696467650060648201526084016102c5565b3073ffffffffffffffffffffffffffffffffffffffff8816036106f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f4c324552433732314272696467653a206c6f63616c20746f6b656e2063616e6e60448201527f6f742062652073656c660000000000000000000000000000000000000000000060648201526084016102c5565b610721877faecafc230000000000000000000000000000000000000000000000000000000061128c565b6107ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603660248201527f4c324552433732314272696467653a206c6f63616c20746f6b656e20696e746560448201527f7266616365206973206e6f7420636f6d706c69616e740000000000000000000060648201526084016102c5565b8673ffffffffffffffffffffffffffffffffffffffff1663d6c0b2c46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061081c9190611868565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146108fc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4c324552433732314272696467653a2077726f6e672072656d6f746520746f6b60448201527f656e20666f72204f7074696d69736d204d696e7461626c65204552433732312060648201527f6c6f63616c20746f6b656e000000000000000000000000000000000000000000608482015260a4016102c5565b6040517fa144819400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301526024820185905288169063a144819490604401600060405180830381600087803b15801561096c57600080fd5b505af1158015610980573d6000803e3d6000fd5b505050508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff167f1f39bf6707b5d608453e0ae4c067b562bcc4c85c0f562ef5d2c774d2e7f131ac878787876040516109fe94939291906118ce565b60405180910390a450505050505050565b73ffffffffffffffffffffffffffffffffffffffff8516610ab2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4552433732314272696467653a206e667420726563697069656e742063616e6e60448201527f6f7420626520616464726573732830290000000000000000000000000000000060648201526084016102c5565b610ac28787338888888888610d36565b50505050505050565b600054610100900460ff1615808015610aeb5750600054600160ff909116105b80610b055750303b158015610b05575060005460ff166001145b610b91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016102c5565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610bef57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610bf76112af565b610c1573420000000000000000000000000000000000000783611332565b8015610c7857600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6000610c866102e6565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf49190611868565b905090565b6000323303610d085750600190565b333b601703610d3057604051602081016040526020600082333c5160e81c62ef010014905090565b50600090565b73ffffffffffffffffffffffffffffffffffffffff8716610dd9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603160248201527f4c324552433732314272696467653a2072656d6f746520746f6b656e2063616e60448201527f6e6f74206265206164647265737328302900000000000000000000000000000060648201526084016102c5565b6040517f6352211e0000000000000000000000000000000000000000000000000000000081526004810185905273ffffffffffffffffffffffffffffffffffffffff891690636352211e90602401602060405180830381865afa158015610e44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e689190611868565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610f22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f4c324552433732314272696467653a205769746864726177616c206973206e6f60448201527f74206265696e6720696e69746961746564206279204e4654206f776e6572000060648201526084016102c5565b60008873ffffffffffffffffffffffffffffffffffffffff1663d6c0b2c46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f939190611868565b90508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611050576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4c324552433732314272696467653a2072656d6f746520746f6b656e20646f6560448201527f73206e6f74206d6174636820676976656e2076616c756500000000000000000060648201526084016102c5565b6040517f9dc29fac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152602482018790528a1690639dc29fac90604401600060405180830381600087803b1580156110c057600080fd5b505af11580156110d4573d6000803e3d6000fd5b505050506000818a89898988886040516024016110f7979695949392919061190e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f761f44930000000000000000000000000000000000000000000000000000000017905260015460025491517f3dbb202b00000000000000000000000000000000000000000000000000000000815292935073ffffffffffffffffffffffffffffffffffffffff90811692633dbb202b926111cc92169085908a9060040161196b565b600060405180830381600087803b1580156111e657600080fd5b505af11580156111fa573d6000803e3d6000fd5b505050508773ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167fb7460e2a880f256ebef3406116ff3eee0cee51ebccdc2a40698f87ebb2e9c1a58a8a898960405161127894939291906118ce565b60405180910390a450505050505050505050565b60006112978361141c565b80156112a857506112a88383611481565b9392505050565b336112b86102e6565b73ffffffffffffffffffffffffffffffffffffffff16141580156112f95750336112e0610c7c565b73ffffffffffffffffffffffffffffffffffffffff1614155b15611330576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b600054610100900460ff166113c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016102c5565b6001805473ffffffffffffffffffffffffffffffffffffffff9384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560028054929093169116179055565b6000611448827f01ffc9a700000000000000000000000000000000000000000000000000000000611481565b801561147b5750611479827fffffffff00000000000000000000000000000000000000000000000000000000611481565b155b92915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015611539575060208210155b80156115455750600081115b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461157257600080fd5b50565b803563ffffffff8116811461158957600080fd5b919050565b60008083601f8401126115a057600080fd5b50813567ffffffffffffffff8111156115b857600080fd5b6020830191508360208285010111156115d057600080fd5b9250929050565b60008060008060008060a087890312156115f057600080fd5b86356115fb81611550565b9550602087013561160b81611550565b94506040870135935061162060608801611575565b9250608087013567ffffffffffffffff81111561163c57600080fd5b61164889828a0161158e565b979a9699509497509295939492505050565b6000815180845260005b8181101561168057602081850181015186830182015201611664565b81811115611692576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006112a8602083018461165a565b600080600080600080600060c0888a0312156116f357600080fd5b87356116fe81611550565b9650602088013561170e81611550565b9550604088013561171e81611550565b9450606088013561172e81611550565b93506080880135925060a088013567ffffffffffffffff81111561175157600080fd5b61175d8a828b0161158e565b989b979a50959850939692959293505050565b600080600080600080600060c0888a03121561178b57600080fd5b873561179681611550565b965060208801356117a681611550565b955060408801356117b681611550565b9450606088013593506117cb60808901611575565b925060a088013567ffffffffffffffff81111561175157600080fd5b6000602082840312156117f957600080fd5b81356112a881611550565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611863577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b60006020828403121561187a57600080fd5b81516112a881611550565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000611904606083018486611885565b9695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808a1683528089166020840152808816604084015280871660608401525084608083015260c060a083015261195e60c083018486611885565b9998505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260606020820152600061199a606083018561165a565b905063ffffffff8316604083015294935050505056fea164736f6c634300080f000a00000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1900000, + "intent": "Deploy L2ERC721Bridge Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000cad608060405234801561001057600080fd5b50610c8d806100206000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80635cf24969116100f9578063c598591811610097578063e591b28211610071578063e591b28214610469578063e81b2c6d14610483578063f82061401461048c578063fe3d57101461049557600080fd5b8063c598591814610408578063d844471514610428578063dad544e01461046157600080fd5b80638381f58a116100d35780638381f58a146103c25780638b239f73146103d65780639e8c4966146103df578063b80777ea146103e857600080fd5b80635cf249691461038957806364ca23ef1461039257806368d5dca6146103a657600080fd5b80634397dfef1161016657806347af267b1161014057806347af267b146102ba5780634d5d9a2a146102dd57806354fd4d501461030e578063550fcdc91461035057600080fd5b80634397dfef14610277578063440a5e201461029f57806346a4d780146102a757600080fd5b806316d3bc7f116101a257806316d3bc7f14610202578063213268491461022f5780633db6be2b146102425780633e47158c1461024a57600080fd5b8063015d8eb9146101c9578063098999be146101de57806309bd5a60146101e6575b600080fd5b6101dc6101d7366004610ae1565b6104c6565b005b6101dc610605565b6101ef60025481565b6040519081526020015b60405180910390f35b6008546102169067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101f9565b60005b60405190151581526020016101f9565b6101dc610618565b610252610642565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f9565b6040805173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee815260126020820152016101f9565b6101dc61084d565b6101dc6102b5366004610b53565b6108a4565b6102326102c8366004610b53565b60096020526000908152604090205460ff1681565b6008546102f99068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016101f9565b60408051808201909152600581527f312e392e3000000000000000000000000000000000000000000000000000000060208201525b6040516101f99190610b6c565b60408051808201909152600381527f45544800000000000000000000000000000000000000000000000000000000006020820152610343565b6101ef60015481565b6003546102169067ffffffffffffffff1681565b6003546102f99068010000000000000000900463ffffffff1681565b6000546102169067ffffffffffffffff1681565b6101ef60055481565b6101ef60065481565b6000546102169068010000000000000000900467ffffffffffffffff1681565b6003546102f9906c01000000000000000000000000900463ffffffff1681565b60408051808201909152600581527f45746865720000000000000000000000000000000000000000000000000000006020820152610343565b6102526108b9565b73deaddeaddeaddeaddeaddeaddeaddeaddead0001610252565b6101ef60045481565b6101ef60075481565b6008546104b3906c01000000000000000000000000900461ffff1681565b60405161ffff90911681526020016101f9565b3373deaddeaddeaddeaddeaddeaddeaddeaddead00011461056d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4c31426c6f636b3a206f6e6c7920746865206465706f7369746f72206163636f60448201527f756e742063616e20736574204c3120626c6f636b2076616c7565730000000000606482015260840160405180910390fd5b6000805467ffffffffffffffff98891668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116998916999099179890981790975560019490945560029290925560038054919094167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009190911617909255600491909155600555600655565b61060d61084d565b60a43560a01c600855565b61062061084d565b6dffff00000000000000000000000060b03560901c1660a43560a01c17600855565b60008061066d7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff81161561069057919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026106d39190610bdf565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061072e906060015b604051602081830303815290604052805190602001205490565b14610765576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805130602082015260019181019190915260009061078790606001610714565b905073ffffffffffffffffffffffffffffffffffffffff81161561081b578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108149190610c43565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73deaddeaddeaddeaddeaddeaddeaddeaddead000133811461087757633cc50b456000526004601cfd5b60043560801c60035560143560801c60005560243560015560443560075560643560025560843560045550565b6108ad33610936565b6108b681610a13565b50565b60006108c3610642565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561090d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109319190610c43565b905090565b73ffffffffffffffffffffffffffffffffffffffff811673deaddeaddeaddeaddeaddeaddeaddeaddead000114806109a057506109716108b9565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b806109dd57506109ae610642565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6108b6576040517fbe9d7ca600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526009602052604090205460ff1615610a5c576040517f4f45326000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526009602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091559051909183917fb876f6594132c89891d2fd198e925e999be741ec809abb58bfe9b966876cc06c9190a350565b803567ffffffffffffffff81168114610adc57600080fd5b919050565b600080600080600080600080610100898b031215610afe57600080fd5b610b0789610ac4565b9750610b1560208a01610ac4565b96506040890135955060608901359450610b3160808a01610ac4565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600060208284031215610b6557600080fd5b5035919050565b600060208083528351808285015260005b81811015610b9957858101830151858201604001528201610b7d565b81811115610bab576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610c3e577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b600060208284031215610c5557600080fd5b815173ffffffffffffffffffffffffffffffffffffffff81168114610c7957600080fd5b939250505056fea164736f6c634300080f000a00000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 750000, + "intent": "Deploy L1Block Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006e9608060405234801561001057600080fd5b506106c9806100206000396000f3fe6080604052600436106100695760003560e01c806382e3702d1161004357806382e3702d14610120578063c2b3e5ac14610160578063ecc704281461017357600080fd5b80633f827a5a1461009257806344df8e70146100bf57806354fd4d50146100d457600080fd5b3661008d5761008b33620186a0604051806020016040528060008152506101d8565b005b600080fd5b34801561009e57600080fd5b506100a7600181565b60405161ffff90911681526020015b60405180910390f35b3480156100cb57600080fd5b5061008b61039c565b3480156100e057600080fd5b50604080518082018252600581527f312e322e30000000000000000000000000000000000000000000000000000000602082015290516100b691906104c7565b34801561012c57600080fd5b5061015061013b3660046104e1565b60006020819052908152604090205460ff1681565b60405190151581526020016100b6565b61008b61016e366004610529565b6101d8565b34801561017f57600080fd5b506101ca6001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b6040519081526020016100b6565b600061026e6040518060c001604052806102326001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b815233602082015273ffffffffffffffffffffffffffffffffffffffff871660408201523460608201526080810186905260a0018490526103d4565b600081815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055905073ffffffffffffffffffffffffffffffffffffffff8416336103096001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b7f02a52367d10742d8032712c1bb8e0144ff1ec5ffda1ed7d70bb05a27449550543487878760405161033e949392919061062d565b60405180910390a45050600180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8082168301167fffff0000000000000000000000000000000000000000000000000000000000009091161790555050565b476103a681610421565b60405181907f7967de617a5ac1cc7eba2d6f37570a0135afa950d8bb77cdd35f0d0b4e85a16f90600090a250565b80516020808301516040808501516060860151608087015160a0880151935160009761040497909695910161065d565b604051602081830303815290604052805190602001209050919050565b8060405161042e90610450565b6040518091039082f090508015801561044b573d6000803e3d6000fd5b505050565b6008806106b583390190565b6000815180845260005b8181101561048257602081850181015186830182015201610466565b81811115610494576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006104da602083018461045c565b9392505050565b6000602082840312156104f357600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561053e57600080fd5b833573ffffffffffffffffffffffffffffffffffffffff8116811461056257600080fd5b925060208401359150604084013567ffffffffffffffff8082111561058657600080fd5b818601915086601f83011261059a57600080fd5b8135818111156105ac576105ac6104fa565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156105f2576105f26104fa565b8160405282815289602084870101111561060b57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b84815283602082015260806040820152600061064c608083018561045c565b905082606083015295945050505050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a08301526106a860c083018461045c565b9897505050505050505056fe608060405230fffea164736f6c634300080f000a0000000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 650000, + "intent": "Deploy L2ToL1MessagePasser Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000003f8d608060405234801561001057600080fd5b5061001961001e565b6100de565b600154610100900460ff161561008a5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60015460ff90811610156100dc576001805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b613ea0806100ed6000396000f3fe60806040523480156200001157600080fd5b5060043610620000cd5760003560e01c8063d2382242116200007f578063dad544e01162000062578063dad544e014620001d6578063e78cea9214620001e0578063ee9a31a2146200020757600080fd5b8063d238224214620001b5578063d97df65214620001bf57600080fd5b80635572acae11620000b45780635572acae14620001525780637d1d0c5b1462000189578063cd6dc687146200019c57600080fd5b80633e47158c14620000d257806354fd4d501462000106575b600080fd5b620000dc6200022c565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b620001436040518060400160405280600581526020017f312e352e3100000000000000000000000000000000000000000000000000000081525081565b604051620000fd9190620009ad565b6200017862000163366004620009ef565b60006020819052908152604090205460ff1681565b6040519015158152602001620000fd565b6002545b604051908152602001620000fd565b620001b3620001ad36600462000a0f565b62000443565b005b6200018d60025481565b620000dc620001d036600462000b20565b62000628565b620000dc62000826565b600154620000dc9062010000900473ffffffffffffffffffffffffffffffffffffffff1681565b60015462010000900473ffffffffffffffffffffffffffffffffffffffff16620000dc565b600080620002587fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156200027c57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000815250516002620002c1919062000b9f565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e67657200000000000091909117906200031d906060015b604051602081830303815290604052805190602001205490565b1462000355576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051306020820152600191810191909152600090620003799060600162000303565b905073ffffffffffffffffffffffffffffffffffffffff81161562000411578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200040a919062000c04565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154610100900460ff16158080156200046157506001805460ff16105b806200047c5750303b1580156200047c57506001805460ff16145b6200050e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168117905580156200056c57600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b62000576620008a8565b600180547fffffffffffffffffffff0000000000000000000000000000000000000000ffff166201000073ffffffffffffffffffffffffffffffffffffffff861602179055600282905580156200062357600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1681556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050565b600073ffffffffffffffffffffffffffffffffffffffff8416620006f6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526044602482018190527f4f7074696d69736d4d696e7461626c65455243373231466163746f72793a204c908201527f3120746f6b656e20616464726573732063616e6e6f742062652061646472657360648201527f7328302900000000000000000000000000000000000000000000000000000000608482015260a40162000505565b60008484846040516020016200070f9392919062000c24565b604051602081830303815290604052805190602001209050600081600160029054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600254888888604051620007619062000931565b6200077195949392919062000c73565b8190604051809103906000f590508015801562000792573d6000803e3d6000fd5b5073ffffffffffffffffffffffffffffffffffffffff8181166000818152602081815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905590513381529394509189169290917fe72783bb8e0ca31286b85278da59684dd814df9762a52f0837f89edd1483b299910160405180910390a395945050505050565b6000620008326200022c565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200087d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620008a3919062000c04565b905090565b33620008b36200022c565b73ffffffffffffffffffffffffffffffffffffffff1614158015620008f7575033620008de62000826565b73ffffffffffffffffffffffffffffffffffffffff1614155b156200092f576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6131bf8062000cd583390190565b6000815180845260005b81811015620009675760208185018101518683018201520162000949565b818111156200097a576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000620009c260208301846200093f565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81168114620009ec57600080fd5b50565b60006020828403121562000a0257600080fd5b8135620009c281620009c9565b6000806040838503121562000a2357600080fd5b823562000a3081620009c9565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011262000a7f57600080fd5b813567ffffffffffffffff8082111562000a9d5762000a9d62000a3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171562000ae65762000ae662000a3e565b8160405283815286602085880101111562000b0057600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121562000b3657600080fd5b833562000b4381620009c9565b9250602084013567ffffffffffffffff8082111562000b6157600080fd5b62000b6f8783880162000a6d565b9350604086013591508082111562000b8657600080fd5b5062000b958682870162000a6d565b9150509250925092565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161562000bff577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b60006020828403121562000c1757600080fd5b8151620009c281620009c9565b73ffffffffffffffffffffffffffffffffffffffff8416815260606020820152600062000c5560608301856200093f565b828103604084015262000c6981856200093f565b9695505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835286602084015280861660408401525060a0606083015262000cb460a08301856200093f565b828103608084015262000cc881856200093f565b9897505050505050505056fe60e06040523480156200001157600080fd5b50604051620031bf380380620031bf83398101604081905262000034916200062d565b8181600062000044838262000756565b50600162000053828262000756565b5050506001600160a01b038516620000d85760405162461bcd60e51b815260206004820152603360248201527f4f7074696d69736d4d696e7461626c654552433732313a20627269646765206360448201527f616e6e6f7420626520616464726573732830290000000000000000000000000060648201526084015b60405180910390fd5b83600003620001505760405162461bcd60e51b815260206004820152603660248201527f4f7074696d69736d4d696e7461626c654552433732313a2072656d6f7465206360448201527f6861696e2069642063616e6e6f74206265207a65726f000000000000000000006064820152608401620000cf565b6001600160a01b038316620001ce5760405162461bcd60e51b815260206004820152603960248201527f4f7074696d69736d4d696e7461626c654552433732313a2072656d6f7465207460448201527f6f6b656e2063616e6e6f742062652061646472657373283029000000000000006064820152608401620000cf565b60808490526001600160a01b0383811660a081905290861660c0526200020290601462000256602090811b62000eed17901c565b62000218856200041660201b620011301760201c565b6040516020016200022b92919062000822565b604051602081830303815290604052600a90816200024a919062000756565b50505050505062000993565b6060600062000267836002620008ac565b62000274906002620008ce565b6001600160401b038111156200028e576200028e62000553565b6040519080825280601f01601f191660200182016040528015620002b9576020820181803683370190505b509050600360fc1b81600081518110620002d757620002d7620008e9565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110620003095762000309620008e9565b60200101906001600160f81b031916908160001a90535060006200032f846002620008ac565b6200033c906001620008ce565b90505b6001811115620003be576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110620003745762000374620008e9565b1a60f81b8282815181106200038d576200038d620008e9565b60200101906001600160f81b031916908160001a90535060049490941c93620003b681620008ff565b90506200033f565b5083156200040f5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401620000cf565b9392505050565b6060816000036200043e5750506040805180820190915260018152600360fc1b602082015290565b8160005b81156200046e5780620004558162000919565b9150620004669050600a836200094b565b915062000442565b6000816001600160401b038111156200048b576200048b62000553565b6040519080825280601f01601f191660200182016040528015620004b6576020820181803683370190505b5090505b84156200052e57620004ce60018362000962565b9150620004dd600a866200097c565b620004ea906030620008ce565b60f81b818381518110620005025762000502620008e9565b60200101906001600160f81b031916908160001a90535062000526600a866200094b565b9450620004ba565b949350505050565b80516001600160a01b03811681146200054e57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620005865781810151838201526020016200056c565b8381111562000596576000848401525b50505050565b600082601f830112620005ae57600080fd5b81516001600160401b0380821115620005cb57620005cb62000553565b604051601f8301601f19908116603f01168101908282118183101715620005f657620005f662000553565b816040528381528660208588010111156200061057600080fd5b6200062384602083016020890162000569565b9695505050505050565b600080600080600060a086880312156200064657600080fd5b620006518662000536565b945060208601519350620006686040870162000536565b60608701519093506001600160401b03808211156200068657600080fd5b6200069489838a016200059c565b93506080880151915080821115620006ab57600080fd5b50620006ba888289016200059c565b9150509295509295909350565b600181811c90821680620006dc57607f821691505b602082108103620006fd57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200075157600081815260208120601f850160051c810160208610156200072c5750805b601f850160051c820191505b818110156200074d5782815560010162000738565b5050505b505050565b81516001600160401b0381111562000772576200077262000553565b6200078a81620007838454620006c7565b8462000703565b602080601f831160018114620007c25760008415620007a95750858301515b600019600386901b1c1916600185901b1785556200074d565b600085815260208120601f198616915b82811015620007f357888601518255948401946001909101908401620007d2565b5085821015620008125787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6832ba3432b932bab69d60b91b8152600083516200084881600985016020880162000569565b600160fe1b60099184019182015283516200086b81600a84016020880162000569565b712f746f6b656e5552493f75696e743235363d60701b600a9290910191820152601c01949350505050565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615620008c957620008c962000896565b500290565b60008219821115620008e457620008e462000896565b500190565b634e487b7160e01b600052603260045260246000fd5b60008162000911576200091162000896565b506000190190565b6000600182016200092e576200092e62000896565b5060010190565b634e487b7160e01b600052601260045260246000fd5b6000826200095d576200095d62000935565b500490565b60008282101562000977576200097762000896565b500390565b6000826200098e576200098e62000935565b500690565b60805160a05160c0516127d9620009e6600039600081816103e20152818161047a01528181610b210152610c430152600081816101e001526103bc015260008181610329015261040801526127d96000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80637d1d0c5b116100ee578063c87b56dd11610097578063e78cea9211610071578063e78cea92146103e0578063e951819614610406578063e985e9c51461042c578063ee9a31a21461047557600080fd5b8063c87b56dd1461039f578063d547cfb7146103b2578063d6c0b2c4146103ba57600080fd5b8063a1448194116100c8578063a144819414610366578063a22cb46514610379578063b88d4fde1461038c57600080fd5b80637d1d0c5b1461032457806395d89b411461034b5780639dc29fac1461035357600080fd5b806323b872dd1161015b5780634f6ccce7116101355780634f6ccce7146102af57806354fd4d50146102c25780636352211e146102fe57806370a082311461031157600080fd5b806323b872dd146102765780632f745c591461028957806342842e0e1461029c57600080fd5b8063081812fc1161018c578063081812fc1461023c578063095ea7b31461024f57806318160ddd1461026457600080fd5b806301ffc9a7146101b3578063033964be146101db57806306fdde0314610227575b600080fd5b6101c66101c1366004612226565b61049c565b60405190151581526020015b60405180910390f35b6102027f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d2565b61022f6104fa565b6040516101d291906122b9565b61020261024a3660046122cc565b61058c565b61026261025d36600461230e565b6105c0565b005b6008545b6040519081526020016101d2565b610262610284366004612338565b610751565b61026861029736600461230e565b6107f2565b6102626102aa366004612338565b6108c1565b6102686102bd3660046122cc565b6108dc565b61022f6040518060400160405280600581526020017f312e332e3200000000000000000000000000000000000000000000000000000081525081565b61020261030c3660046122cc565b61099a565b61026861031f366004612374565b610a2c565b6102687f000000000000000000000000000000000000000000000000000000000000000081565b61022f610afa565b61026261036136600461230e565b610b09565b61026261037436600461230e565b610c2b565b61026261038736600461238f565b610d42565b61026261039a3660046123fa565b610d51565b61022f6103ad3660046122cc565b610df9565b61022f610e5f565b7f0000000000000000000000000000000000000000000000000000000000000000610202565b7f0000000000000000000000000000000000000000000000000000000000000000610202565b7f0000000000000000000000000000000000000000000000000000000000000000610268565b6101c661043a3660046124f4565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260056020908152604080832093909416825291909152205460ff1690565b6102027f000000000000000000000000000000000000000000000000000000000000000081565b60007faecafc23000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083168114806104f357506104f38361126d565b9392505050565b60606000805461050990612527565b80601f016020809104026020016040519081016040528092919081815260200182805461053590612527565b80156105825780601f1061055757610100808354040283529160200191610582565b820191906000526020600020905b81548152906001019060200180831161056557829003601f168201915b5050505050905090565b6000610597826112c3565b5060009081526004602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b60006105cb8261099a565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361068d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560448201527f720000000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614806106b657506106b6813361043a565b610742576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206e6f7220617070726f76656420666f7220616c6c00006064820152608401610684565b61074c8383611351565b505050565b61075b33826113f1565b6107e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206e6f7220617070726f7665640000000000000000000000000000000000006064820152608401610684565b61074c8383836114b0565b60006107fd83610a2c565b821061088b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201527f74206f6620626f756e64730000000000000000000000000000000000000000006064820152608401610684565b5073ffffffffffffffffffffffffffffffffffffffff919091166000908152600660209081526040808320938352929052205490565b61074c83838360405180602001604052806000815250610d51565b60006108e760085490565b8210610975576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201527f7574206f6620626f756e647300000000000000000000000000000000000000006064820152608401610684565b600882815481106109885761098861257a565b90600052602060002001549050919050565b60008181526002602052604081205473ffffffffffffffffffffffffffffffffffffffff1680610a26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610684565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff8216610ad1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f74206120766160448201527f6c6964206f776e657200000000000000000000000000000000000000000000006064820152608401610684565b5073ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205490565b60606001805461050990612527565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610bce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4f7074696d69736d4d696e7461626c654552433732313a206f6e6c792062726960448201527f6467652063616e2063616c6c20746869732066756e6374696f6e0000000000006064820152608401610684565b610bd781611722565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca582604051610c1f91815260200190565b60405180910390a25050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610cf0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4f7074696d69736d4d696e7461626c654552433732313a206f6e6c792062726960448201527f6467652063616e2063616c6c20746869732066756e6374696f6e0000000000006064820152608401610684565b610cfa82826117fb565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d412139688582604051610c1f91815260200190565b610d4d338383611815565b5050565b610d5b33836113f1565b610de7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201527f72206e6f7220617070726f7665640000000000000000000000000000000000006064820152608401610684565b610df384848484611942565b50505050565b6060610e04826112c3565b6000610e0e6119e5565b90506000815111610e2e57604051806020016040528060008152506104f3565b80610e3884611130565b604051602001610e499291906125a9565b6040516020818303038152906040529392505050565b600a8054610e6c90612527565b80601f0160208091040260200160405190810160405280929190818152602001828054610e9890612527565b8015610ee55780601f10610eba57610100808354040283529160200191610ee5565b820191906000526020600020905b815481529060010190602001808311610ec857829003601f168201915b505050505081565b60606000610efc836002612607565b610f07906002612644565b67ffffffffffffffff811115610f1f57610f1f6123cb565b6040519080825280601f01601f191660200182016040528015610f49576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110610f8057610f8061257a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110610fe357610fe361257a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600061101f846002612607565b61102a906001612644565b90505b60018111156110c7577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061106b5761106b61257a565b1a60f81b8282815181106110815761108161257a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c936110c08161265c565b905061102d565b5083156104f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610684565b60608160000361117357505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b811561119d578061118781612691565b91506111969050600a836126f8565b9150611177565b60008167ffffffffffffffff8111156111b8576111b86123cb565b6040519080825280601f01601f1916602001820160405280156111e2576020820181803683370190505b5090505b8415611265576111f760018361270c565b9150611204600a86612723565b61120f906030612644565b60f81b8183815181106112245761122461257a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535061125e600a866126f8565b94506111e6565b949350505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f780e9d63000000000000000000000000000000000000000000000000000000001480610a265750610a26826119f4565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1661134e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606401610684565b50565b600081815260046020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841690811790915581906113ab8261099a565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806113fd8361099a565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16148061146b575073ffffffffffffffffffffffffffffffffffffffff80821660009081526005602090815260408083209388168352929052205460ff165b8061126557508373ffffffffffffffffffffffffffffffffffffffff166114918461058c565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b8273ffffffffffffffffffffffffffffffffffffffff166114d08261099a565b73ffffffffffffffffffffffffffffffffffffffff1614611573576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201527f6f776e65720000000000000000000000000000000000000000000000000000006064820152608401610684565b73ffffffffffffffffffffffffffffffffffffffff8216611615576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610684565b611620838383611ad7565b61162b600082611351565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260036020526040812080546001929061166190849061270c565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600090815260036020526040812080546001929061169c908490612644565b909155505060008181526002602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b600061172d8261099a565b905061173b81600084611ad7565b611746600083611351565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260036020526040812080546001929061177c90849061270c565b909155505060008281526002602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555183919073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b610d4d828260405180602001604052806000815250611bdd565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036118aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610684565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526005602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b61194d8484846114b0565b61195984848484611c80565b610df3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610684565b6060600a805461050990612527565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd000000000000000000000000000000000000000000000000000000001480611a8757507fffffffff0000000000000000000000000000000000000000000000000000000082167f5b5e139f00000000000000000000000000000000000000000000000000000000145b80610a2657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610a26565b73ffffffffffffffffffffffffffffffffffffffff8316611b3f57611b3a81600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611b7c565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614611b7c57611b7c8382611e73565b73ffffffffffffffffffffffffffffffffffffffff8216611ba05761074c81611f2a565b8273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161461074c5761074c8282611fd9565b611be7838361202a565b611bf46000848484611c80565b61074c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610684565b600073ffffffffffffffffffffffffffffffffffffffff84163b15611e68576040517f150b7a0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063150b7a0290611cf7903390899088908890600401612737565b6020604051808303816000875af1925050508015611d50575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252611d4d91810190612780565b60015b611e1d573d808015611d7e576040519150601f19603f3d011682016040523d82523d6000602084013e611d83565b606091505b508051600003611e15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560448201527f63656976657220696d706c656d656e74657200000000000000000000000000006064820152608401610684565b805181602001fd5b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a0200000000000000000000000000000000000000000000000000000000149050611265565b506001949350505050565b60006001611e8084610a2c565b611e8a919061270c565b600083815260076020526040902054909150808214611eea5773ffffffffffffffffffffffffffffffffffffffff841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b50600091825260076020908152604080842084905573ffffffffffffffffffffffffffffffffffffffff9094168352600681528383209183525290812055565b600854600090611f3c9060019061270c565b60008381526009602052604081205460088054939450909284908110611f6457611f6461257a565b906000526020600020015490508060088381548110611f8557611f8561257a565b6000918252602080832090910192909255828152600990915260408082208490558582528120556008805480611fbd57611fbd61279d565b6001900381819060005260206000200160009055905550505050565b6000611fe483610a2c565b73ffffffffffffffffffffffffffffffffffffffff9093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b73ffffffffffffffffffffffffffffffffffffffff82166120a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610684565b60008181526002602052604090205473ffffffffffffffffffffffffffffffffffffffff1615612133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610684565b61213f60008383611ad7565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600360205260408120805460019290612175908490612644565b909155505060008181526002602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461134e57600080fd5b60006020828403121561223857600080fd5b81356104f3816121f8565b60005b8381101561225e578181015183820152602001612246565b83811115610df35750506000910152565b60008151808452612287816020860160208601612243565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006104f3602083018461226f565b6000602082840312156122de57600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461230957600080fd5b919050565b6000806040838503121561232157600080fd5b61232a836122e5565b946020939093013593505050565b60008060006060848603121561234d57600080fd5b612356846122e5565b9250612364602085016122e5565b9150604084013590509250925092565b60006020828403121561238657600080fd5b6104f3826122e5565b600080604083850312156123a257600080fd5b6123ab836122e5565b9150602083013580151581146123c057600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000806080858703121561241057600080fd5b612419856122e5565b9350612427602086016122e5565b925060408501359150606085013567ffffffffffffffff8082111561244b57600080fd5b818701915087601f83011261245f57600080fd5b813581811115612471576124716123cb565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156124b7576124b76123cb565b816040528281528a60208487010111156124d057600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806040838503121561250757600080fd5b612510836122e5565b915061251e602084016122e5565b90509250929050565b600181811c9082168061253b57607f821691505b602082108103612574577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600083516125bb818460208801612243565b8351908301906125cf818360208801612243565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561263f5761263f6125d8565b500290565b60008219821115612657576126576125d8565b500190565b60008161266b5761266b6125d8565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036126c2576126c26125d8565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082612707576127076126c9565b500490565b60008282101561271e5761271e6125d8565b500390565b600082612732576127326126c9565b500690565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525083604083015260806060830152612776608083018461226f565b9695505050505050565b60006020828403121561279257600080fd5b81516104f3816121f8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c634300080f000aa164736f6c634300080f000a00000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 4900000, + "intent": "Deploy OptimismMintableERC721Factory Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001c9d608060405234801561001057600080fd5b50600061001c3361002b565b6100258161002b565b5061007b565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b611c138061008a6000396000f3fe6080604052600436106101445760003560e01c80637eff275e116100c057806399a88ec411610074578063b794726211610059578063b7947262146103c8578063f2fde38b14610403578063f3b7dead1461042357600080fd5b806399a88ec4146103885780639b2ea4bd146103a857600080fd5b80638d52d4a0116100a55780638d52d4a01461032a5780638da5cb5b1461034a5780639623609d1461037557600080fd5b80637eff275e146102ea578063860f7cda1461030a57600080fd5b80633ab76e9f116101175780636bd9f516116100fc5780636bd9f51614610278578063715018a6146102b55780637c36f37e146102ca57600080fd5b80633ab76e9f1461020257806354fd4d501461022f57600080fd5b80630652b57a1461014957806307c8f7b01461016b578063204e1c7a1461018b578063238181ae146101d5575b600080fd5b34801561015557600080fd5b50610169610164366004611490565b610443565b005b34801561017757600080fd5b506101696101863660046114ad565b610492565b34801561019757600080fd5b506101ab6101a6366004611490565b6104e4565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101e157600080fd5b506101f56101f0366004611490565b61070a565b6040516101cc9190611545565b34801561020e57600080fd5b506003546101ab9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561023b57600080fd5b506101f56040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b34801561028457600080fd5b506102a8610293366004611490565b60016020526000908152604090205460ff1681565b6040516101cc9190611587565b3480156102c157600080fd5b506101696107a4565b3480156102d657600080fd5b506101696102e5366004611490565b6107b8565b3480156102f657600080fd5b506101696103053660046115c8565b6109b0565b34801561031657600080fd5b50610169610325366004611723565b610b63565b34801561033657600080fd5b50610169610345366004611773565b610b9a565b34801561035657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101ab565b6101696103833660046117a5565b610c0e565b34801561039457600080fd5b506101696103a33660046115c8565b610e25565b3480156103b457600080fd5b506101696103c336600461181b565b6110b5565b3480156103d457600080fd5b5060035474010000000000000000000000000000000000000000900460ff1660405190151581526020016101cc565b34801561040f57600080fd5b5061016961041e366004611490565b61114b565b34801561042f57600080fd5b506101ab61043e366004611490565b611202565b61044b611378565b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b61049a611378565b6003805491151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff909216919091179055565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604081205460ff168181600281111561052057610520611558565b0361059b578273ffffffffffffffffffffffffffffffffffffffff16635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610570573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105949190611862565b9392505050565b60018160028111156105af576105af611558565b036105ff578273ffffffffffffffffffffffffffffffffffffffff1663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa158015610570573d6000803e3d6000fd5b600281600281111561061357610613611558565b0361069d5760035473ffffffffffffffffffffffffffffffffffffffff8481166000908152600260205260409081902090517fbf40fac1000000000000000000000000000000000000000000000000000000008152919092169163bf40fac19161068091906004016118cc565b602060405180830381865afa158015610570573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f50726f787941646d696e3a20756e6b6e6f776e2070726f78792074797065000060448201526064015b60405180910390fd5b50919050565b600260205260009081526040902080546107239061187f565b80601f016020809104026020016040519081016040528092919081815260200182805461074f9061187f565b801561079c5780601f106107715761010080835404028352916020019161079c565b820191906000526020600020905b81548152906001019060200180831161077f57829003601f168201915b505050505081565b6107ac611378565b6107b660006113f9565b565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610805576040517fcde661e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff163b60000361086e576040517fe1e56d9d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016106fb565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd55ec697000000000000000000000000000000000000000000000000000000001790529051600091829173ffffffffffffffffffffffffffffffffffffffff8516916108ec91611975565b600060405180830381855af49150503d8060008114610927576040519150601f19603f3d011682016040523d82523d6000602084013e61092c565b606091505b50915091508161096a57806040517f1c0a89cc0000000000000000000000000000000000000000000000000000000081526004016106fb9190611545565b60405173ffffffffffffffffffffffffffffffffffffffff8416907f14e22d69ea30aab5b2220164345b33bdb5125e9c77a7d5fe12e23a1c691bd13990600090a2505050565b6109b8611378565b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081205460ff16908160028111156109f4576109f4611558565b03610a80576040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152841690638f283970906024015b600060405180830381600087803b158015610a6357600080fd5b505af1158015610a77573d6000803e3d6000fd5b50505050505050565b6001816002811115610a9457610a94611558565b03610aed576040517f13af403500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301528416906313af403590602401610a49565b6002816002811115610b0157610b01611558565b0361069d576003546040517ff2fde38b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529091169063f2fde38b90602401610a49565b505050565b610b6b611378565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600260205260409020610b5e82826119d7565b610ba2611378565b73ffffffffffffffffffffffffffffffffffffffff82166000908152600160208190526040909120805483927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836002811115610c0557610c05611558565b02179055505050565b610c16611378565b73ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081205460ff1690816002811115610c5257610c52611558565b03610d18576040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851690634f1ef286903490610cad9087908790600401611af1565b60006040518083038185885af1158015610ccb573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610d129190810190611b28565b50610e1f565b610d228484610e25565b60008473ffffffffffffffffffffffffffffffffffffffff163484604051610d4a9190611975565b60006040518083038185875af1925050503d8060008114610d87576040519150601f19603f3d011682016040523d82523d6000602084013e610d8c565b606091505b5050905080610e1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f50726f787941646d696e3a2063616c6c20746f2070726f78792061667465722060448201527f75706772616465206661696c656400000000000000000000000000000000000060648201526084016106fb565b505b50505050565b610e2d611378565b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604081205460ff1690816002811115610e6957610e69611558565b03610ec2576040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152841690633659cfe690602401610a49565b6001816002811115610ed657610ed6611558565b03610f55576040517f9b0b0fda0000000000000000000000000000000000000000000000000000000081527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc600482015273ffffffffffffffffffffffffffffffffffffffff8381166024830152841690639b0b0fda90604401610a49565b6002816002811115610f6957610f69611558565b036110ad5773ffffffffffffffffffffffffffffffffffffffff831660009081526002602052604081208054610f9e9061187f565b80601f0160208091040260200160405190810160405280929190818152602001828054610fca9061187f565b80156110175780601f10610fec57610100808354040283529160200191611017565b820191906000526020600020905b815481529060010190602001808311610ffa57829003601f168201915b50506003546040517f9b2ea4bd00000000000000000000000000000000000000000000000000000000815294955073ffffffffffffffffffffffffffffffffffffffff1693639b2ea4bd935061107592508591508790600401611b9f565b600060405180830381600087803b15801561108f57600080fd5b505af11580156110a3573d6000803e3d6000fd5b5050505050505050565b610b5e611bd7565b6110bd611378565b6003546040517f9b2ea4bd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690639b2ea4bd906111159085908590600401611b9f565b600060405180830381600087803b15801561112f57600080fd5b505af1158015611143573d6000803e3d6000fd5b505050505050565b611153611378565b73ffffffffffffffffffffffffffffffffffffffff81166111f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016106fb565b6111ff816113f9565b50565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604081205460ff168181600281111561123e5761123e611558565b0361128e578273ffffffffffffffffffffffffffffffffffffffff1663f851a4406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610570573d6000803e3d6000fd5b60018160028111156112a2576112a2611558565b036112f2578273ffffffffffffffffffffffffffffffffffffffff1663893d20e86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610570573d6000803e3d6000fd5b600281600281111561130657611306611558565b0361069d57600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610570573d6000803e3d6000fd5b60005473ffffffffffffffffffffffffffffffffffffffff1633146107b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106fb565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146111ff57600080fd5b6000602082840312156114a257600080fd5b81356105948161146e565b6000602082840312156114bf57600080fd5b8135801515811461059457600080fd5b60005b838110156114ea5781810151838201526020016114d2565b83811115610e1f5750506000910152565b600081518084526115138160208601602086016114cf565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061059460208301846114fb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208101600383106115c2577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b600080604083850312156115db57600080fd5b82356115e68161146e565b915060208301356115f68161146e565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561167757611677611601565b604052919050565b600067ffffffffffffffff82111561169957611699611601565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006116d86116d38461167f565b611630565b90508281528383830111156116ec57600080fd5b828260208301376000602084830101529392505050565b600082601f83011261171457600080fd5b610594838335602085016116c5565b6000806040838503121561173657600080fd5b82356117418161146e565b9150602083013567ffffffffffffffff81111561175d57600080fd5b61176985828601611703565b9150509250929050565b6000806040838503121561178657600080fd5b82356117918161146e565b91506020830135600381106115f657600080fd5b6000806000606084860312156117ba57600080fd5b83356117c58161146e565b925060208401356117d58161146e565b9150604084013567ffffffffffffffff8111156117f157600080fd5b8401601f8101861361180257600080fd5b611811868235602084016116c5565b9150509250925092565b6000806040838503121561182e57600080fd5b823567ffffffffffffffff81111561184557600080fd5b61185185828601611703565b92505060208301356115f68161146e565b60006020828403121561187457600080fd5b81516105948161146e565b600181811c9082168061189357607f821691505b602082108103610704577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006020808352600084546118e08161187f565b80848701526040600180841660008114611901576001811461193957611967565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550611967565b896000528660002060005b8581101561195f5781548b8201860152908301908801611944565b8a0184019650505b509398975050505050505050565b600082516119878184602087016114cf565b9190910192915050565b601f821115610b5e57600081815260208120601f850160051c810160208610156119b85750805b601f850160051c820191505b81811015611143578281556001016119c4565b815167ffffffffffffffff8111156119f1576119f1611601565b611a05816119ff845461187f565b84611991565b602080601f831160018114611a585760008415611a225750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555611143565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015611aa557888601518255948401946001909101908401611a86565b5085821015611ae157878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201526000611b2060408301846114fb565b949350505050565b600060208284031215611b3a57600080fd5b815167ffffffffffffffff811115611b5157600080fd5b8201601f81018413611b6257600080fd5b8051611b706116d38261167f565b818152856020838501011115611b8557600080fd5b611b968260208301602086016114cf565b95945050505050565b604081526000611bb260408301856114fb565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fdfea164736f6c634300080f000a000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 2400000, + "intent": "Deploy L2ProxyAdmin Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000108a6080604052348015600e575f80fd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b610fb4806100d65f395ff3fe6080604052600436106100e7575f3560e01c806382356d8a11610087578063b49dc74111610057578063b49dc741146102bf578063d0e12f90146102de578063d3e5792b1461030d578063dad544e014610321575f80fd5b806382356d8a146102395780638312f1491461027757806384411d651461028c57806385b5b14d146102a0575f80fd5b80633ccfd60b116100c25780633ccfd60b146101825780633e47158c146101a457806354fd4d50146101b857806366d003ac1461020d575f80fd5b80630d9019e1146100f2578063307f2962146101425780633bbed4a014610163575f80fd5b366100ee57005b5f80fd5b3480156100fd575f80fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561014d575f80fd5b5061016161015c366004610d62565b610335565b005b34801561016e575f80fd5b5061016161017d366004610d9f565b6103db565b34801561018d575f80fd5b50610196610462565b604051908152602001610139565b3480156101af575f80fd5b50610118610770565b3480156101c3575f80fd5b506102006040518060400160405280600581526020017f312e362e3100000000000000000000000000000000000000000000000000000081525081565b6040516101399190610dba565b348015610218575f80fd5b506002546101189073ffffffffffffffffffffffffffffffffffffffff1681565b348015610244575f80fd5b5060025461026a9074010000000000000000000000000000000000000000900460ff1681565b6040516101399190610e73565b348015610282575f80fd5b5061019660015481565b348015610297575f80fd5b506101965f5481565b3480156102ab575f80fd5b506101616102ba366004610e87565b610976565b3480156102ca575f80fd5b506101616102d9366004610e9e565b6109bc565b3480156102e9575f80fd5b5060025474010000000000000000000000000000000000000000900460ff1661026a565b348015610318575f80fd5b50600154610196565b34801561032c575f80fd5b50610118610bd8565b61033d610c52565b600280547401000000000000000000000000000000000000000080820460ff1692849290917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9091169083600181111561039957610399610e0d565b02179055507ff2ec44eb1c3b3acd547b76333eb2c4b27eee311860c57a9fdb04c95f62398fc881836040516103cf929190610ed9565b60405180910390a15050565b6103e3610c52565b6002805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f62e69886a5df0ba8ffcacbfc1388754e7abd9bde24b036354c561f1acd4e459391016103cf565b5f600154471015610520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d207769746864726160648201527f77616c20616d6f756e7400000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b479050805f808282546105339190610f21565b90915550506002546040805183815273ffffffffffffffffffffffffffffffffffffffff909216602083018190523383830152905190917fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba919081900360600190a16002546040517f38e04cbeb8c10f8f568618aa75be0f10b6729b8b4237743b4de20cbcde2839ee916105e89185918591339174010000000000000000000000000000000000000000900460ff1690610f34565b60405180910390a1600160025474010000000000000000000000000000000000000000900460ff16600181111561062157610621610e0d565b036106c5575f6106318284610caa565b9050806106c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4665655661756c743a206661696c656420746f2073656e642045544820746f2060448201527f4c322066656520726563697069656e74000000000000000000000000000000006064820152608401610517565b505090565b6040517fc2b3e5ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015262061a806024820152606060448201525f60648201527342000000000000000000000000000000000000169063c2b3e5ac9084906084015f604051808303818588803b158015610755575f80fd5b505af1158015610767573d5f803e3d5ffd5b50505050505090565b5f8061079a7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156107bd57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026108009190610f75565b604080513060208201525f918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061085a906060015b604051602081830303815290604052805190602001205490565b14610891576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091525f906108b290606001610840565b905073ffffffffffffffffffffffffffffffffffffffff811615610944578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610919573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093d9190610f8c565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097e610c52565b600180549082905560408051828152602081018490527f895a067c78583e800418fabf3da26a9496aab2ff3429cebdf7fefa642b2e420391016103cf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610a065750825b90505f8267ffffffffffffffff166001148015610a225750303b155b905081158015610a30575080155b15610a67576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610ac85784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610ad0610cbd565b6002805473ffffffffffffffffffffffffffffffffffffffff8a167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117835560018a81558993927fffffffffffffffffffffff000000000000000000000000000000000000000000169091179074010000000000000000000000000000000000000000908490811115610b6857610b68610e0d565b02179055508315610bce5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f610be1610770565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c29573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c4d9190610f8c565b905090565b33610c5b610bd8565b73ffffffffffffffffffffffffffffffffffffffff1614610ca8576040517f7f12c64b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f610cb6835a84610d3e565b9392505050565b33610cc6610770565b73ffffffffffffffffffffffffffffffffffffffff1614158015610d07575033610cee610bd8565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610ca8576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f805f858888f1949350505050565b803560028110610d5d575f80fd5b919050565b5f60208284031215610d72575f80fd5b610cb682610d4f565b73ffffffffffffffffffffffffffffffffffffffff81168114610d9c575f80fd5b50565b5f60208284031215610daf575f80fd5b8135610cb681610d7b565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60028110610e6f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9052565b60208101610e818284610e3a565b92915050565b5f60208284031215610e97575f80fd5b5035919050565b5f805f60608486031215610eb0575f80fd5b8335610ebb81610d7b565b925060208401359150610ed060408501610d4f565b90509250925092565b60408101610ee78285610e3a565b610cb66020830184610e3a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610e8157610e81610ef4565b84815273ffffffffffffffffffffffffffffffffffffffff84811660208301528316604082015260808101610f6c6060830184610e3a565b95945050505050565b8082028115828204841417610e8157610e81610ef4565b5f60208284031215610f9c575f80fd5b8151610cb681610d7b56fea164736f6c6343000819000a00000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1300000, + "intent": "Deploy BaseFeeVault Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000108a6080604052348015600e575f80fd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b610fb4806100d65f395ff3fe6080604052600436106100e7575f3560e01c806382356d8a11610087578063b49dc74111610057578063b49dc741146102bf578063d0e12f90146102de578063d3e5792b1461030d578063dad544e014610321575f80fd5b806382356d8a146102395780638312f1491461027757806384411d651461028c57806385b5b14d146102a0575f80fd5b80633ccfd60b116100c25780633ccfd60b146101825780633e47158c146101a457806354fd4d50146101b857806366d003ac1461020d575f80fd5b80630d9019e1146100f2578063307f2962146101425780633bbed4a014610163575f80fd5b366100ee57005b5f80fd5b3480156100fd575f80fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561014d575f80fd5b5061016161015c366004610d62565b610335565b005b34801561016e575f80fd5b5061016161017d366004610d9f565b6103db565b34801561018d575f80fd5b50610196610462565b604051908152602001610139565b3480156101af575f80fd5b50610118610770565b3480156101c3575f80fd5b506102006040518060400160405280600581526020017f312e362e3100000000000000000000000000000000000000000000000000000081525081565b6040516101399190610dba565b348015610218575f80fd5b506002546101189073ffffffffffffffffffffffffffffffffffffffff1681565b348015610244575f80fd5b5060025461026a9074010000000000000000000000000000000000000000900460ff1681565b6040516101399190610e73565b348015610282575f80fd5b5061019660015481565b348015610297575f80fd5b506101965f5481565b3480156102ab575f80fd5b506101616102ba366004610e87565b610976565b3480156102ca575f80fd5b506101616102d9366004610e9e565b6109bc565b3480156102e9575f80fd5b5060025474010000000000000000000000000000000000000000900460ff1661026a565b348015610318575f80fd5b50600154610196565b34801561032c575f80fd5b50610118610bd8565b61033d610c52565b600280547401000000000000000000000000000000000000000080820460ff1692849290917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9091169083600181111561039957610399610e0d565b02179055507ff2ec44eb1c3b3acd547b76333eb2c4b27eee311860c57a9fdb04c95f62398fc881836040516103cf929190610ed9565b60405180910390a15050565b6103e3610c52565b6002805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f62e69886a5df0ba8ffcacbfc1388754e7abd9bde24b036354c561f1acd4e459391016103cf565b5f600154471015610520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d207769746864726160648201527f77616c20616d6f756e7400000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b479050805f808282546105339190610f21565b90915550506002546040805183815273ffffffffffffffffffffffffffffffffffffffff909216602083018190523383830152905190917fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba919081900360600190a16002546040517f38e04cbeb8c10f8f568618aa75be0f10b6729b8b4237743b4de20cbcde2839ee916105e89185918591339174010000000000000000000000000000000000000000900460ff1690610f34565b60405180910390a1600160025474010000000000000000000000000000000000000000900460ff16600181111561062157610621610e0d565b036106c5575f6106318284610caa565b9050806106c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4665655661756c743a206661696c656420746f2073656e642045544820746f2060448201527f4c322066656520726563697069656e74000000000000000000000000000000006064820152608401610517565b505090565b6040517fc2b3e5ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015262061a806024820152606060448201525f60648201527342000000000000000000000000000000000000169063c2b3e5ac9084906084015f604051808303818588803b158015610755575f80fd5b505af1158015610767573d5f803e3d5ffd5b50505050505090565b5f8061079a7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156107bd57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026108009190610f75565b604080513060208201525f918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061085a906060015b604051602081830303815290604052805190602001205490565b14610891576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091525f906108b290606001610840565b905073ffffffffffffffffffffffffffffffffffffffff811615610944578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610919573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093d9190610f8c565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097e610c52565b600180549082905560408051828152602081018490527f895a067c78583e800418fabf3da26a9496aab2ff3429cebdf7fefa642b2e420391016103cf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610a065750825b90505f8267ffffffffffffffff166001148015610a225750303b155b905081158015610a30575080155b15610a67576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610ac85784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610ad0610cbd565b6002805473ffffffffffffffffffffffffffffffffffffffff8a167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117835560018a81558993927fffffffffffffffffffffff000000000000000000000000000000000000000000169091179074010000000000000000000000000000000000000000908490811115610b6857610b68610e0d565b02179055508315610bce5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f610be1610770565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c29573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c4d9190610f8c565b905090565b33610c5b610bd8565b73ffffffffffffffffffffffffffffffffffffffff1614610ca8576040517f7f12c64b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f610cb6835a84610d3e565b9392505050565b33610cc6610770565b73ffffffffffffffffffffffffffffffffffffffff1614158015610d07575033610cee610bd8565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610ca8576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f805f858888f1949350505050565b803560028110610d5d575f80fd5b919050565b5f60208284031215610d72575f80fd5b610cb682610d4f565b73ffffffffffffffffffffffffffffffffffffffff81168114610d9c575f80fd5b50565b5f60208284031215610daf575f80fd5b8135610cb681610d7b565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60028110610e6f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9052565b60208101610e818284610e3a565b92915050565b5f60208284031215610e97575f80fd5b5035919050565b5f805f60608486031215610eb0575f80fd5b8335610ebb81610d7b565b925060208401359150610ed060408501610d4f565b90509250925092565b60408101610ee78285610e3a565b610cb66020830184610e3a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610e8157610e81610ef4565b84815273ffffffffffffffffffffffffffffffffffffffff84811660208301528316604082015260808101610f6c6060830184610e3a565b95945050505050565b8082028115828204841417610e8157610e81610ef4565b5f60208284031215610f9c575f80fd5b8151610cb681610d7b56fea164736f6c6343000819000a00000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 50000, + "intent": "Deploy L1FeeVault Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000108a6080604052348015600e575f80fd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b610fb4806100d65f395ff3fe6080604052600436106100e7575f3560e01c806382356d8a11610087578063b49dc74111610057578063b49dc741146102bf578063d0e12f90146102de578063d3e5792b1461030d578063dad544e014610321575f80fd5b806382356d8a146102395780638312f1491461027757806384411d651461028c57806385b5b14d146102a0575f80fd5b80633ccfd60b116100c25780633ccfd60b146101825780633e47158c146101a457806354fd4d50146101b857806366d003ac1461020d575f80fd5b80630d9019e1146100f2578063307f2962146101425780633bbed4a014610163575f80fd5b366100ee57005b5f80fd5b3480156100fd575f80fd5b5060025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561014d575f80fd5b5061016161015c366004610d62565b610335565b005b34801561016e575f80fd5b5061016161017d366004610d9f565b6103db565b34801561018d575f80fd5b50610196610462565b604051908152602001610139565b3480156101af575f80fd5b50610118610770565b3480156101c3575f80fd5b506102006040518060400160405280600581526020017f312e312e3100000000000000000000000000000000000000000000000000000081525081565b6040516101399190610dba565b348015610218575f80fd5b506002546101189073ffffffffffffffffffffffffffffffffffffffff1681565b348015610244575f80fd5b5060025461026a9074010000000000000000000000000000000000000000900460ff1681565b6040516101399190610e73565b348015610282575f80fd5b5061019660015481565b348015610297575f80fd5b506101965f5481565b3480156102ab575f80fd5b506101616102ba366004610e87565b610976565b3480156102ca575f80fd5b506101616102d9366004610e9e565b6109bc565b3480156102e9575f80fd5b5060025474010000000000000000000000000000000000000000900460ff1661026a565b348015610318575f80fd5b50600154610196565b34801561032c575f80fd5b50610118610bd8565b61033d610c52565b600280547401000000000000000000000000000000000000000080820460ff1692849290917fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff9091169083600181111561039957610399610e0d565b02179055507ff2ec44eb1c3b3acd547b76333eb2c4b27eee311860c57a9fdb04c95f62398fc881836040516103cf929190610ed9565b60405180910390a15050565b6103e3610c52565b6002805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f62e69886a5df0ba8ffcacbfc1388754e7abd9bde24b036354c561f1acd4e459391016103cf565b5f600154471015610520576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f4665655661756c743a207769746864726177616c20616d6f756e74206d75737460448201527f2062652067726561746572207468616e206d696e696d756d207769746864726160648201527f77616c20616d6f756e7400000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b479050805f808282546105339190610f21565b90915550506002546040805183815273ffffffffffffffffffffffffffffffffffffffff909216602083018190523383830152905190917fc8a211cc64b6ed1b50595a9fcb1932b6d1e5a6e8ef15b60e5b1f988ea9086bba919081900360600190a16002546040517f38e04cbeb8c10f8f568618aa75be0f10b6729b8b4237743b4de20cbcde2839ee916105e89185918591339174010000000000000000000000000000000000000000900460ff1690610f34565b60405180910390a1600160025474010000000000000000000000000000000000000000900460ff16600181111561062157610621610e0d565b036106c5575f6106318284610caa565b9050806106c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4665655661756c743a206661696c656420746f2073656e642045544820746f2060448201527f4c322066656520726563697069656e74000000000000000000000000000000006064820152608401610517565b505090565b6040517fc2b3e5ac00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015262061a806024820152606060448201525f60648201527342000000000000000000000000000000000000169063c2b3e5ac9084906084015f604051808303818588803b158015610755575f80fd5b505af1158015610767573d5f803e3d5ffd5b50505050505090565b5f8061079a7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156107bd57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026108009190610f75565b604080513060208201525f918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061085a906060015b604051602081830303815290604052805190602001205490565b14610891576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091525f906108b290606001610840565b905073ffffffffffffffffffffffffffffffffffffffff811615610944578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610919573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093d9190610f8c565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097e610c52565b600180549082905560408051828152602081018490527f895a067c78583e800418fabf3da26a9496aab2ff3429cebdf7fefa642b2e420391016103cf565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610a065750825b90505f8267ffffffffffffffff166001148015610a225750303b155b905081158015610a30575080155b15610a67576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610ac85784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610ad0610cbd565b6002805473ffffffffffffffffffffffffffffffffffffffff8a167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117835560018a81558993927fffffffffffffffffffffff000000000000000000000000000000000000000000169091179074010000000000000000000000000000000000000000908490811115610b6857610b68610e0d565b02179055508315610bce5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b5f610be1610770565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c29573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c4d9190610f8c565b905090565b33610c5b610bd8565b73ffffffffffffffffffffffffffffffffffffffff1614610ca8576040517f7f12c64b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f610cb6835a84610d3e565b9392505050565b33610cc6610770565b73ffffffffffffffffffffffffffffffffffffffff1614158015610d07575033610cee610bd8565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610ca8576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f805f858888f1949350505050565b803560028110610d5d575f80fd5b919050565b5f60208284031215610d72575f80fd5b610cb682610d4f565b73ffffffffffffffffffffffffffffffffffffffff81168114610d9c575f80fd5b50565b5f60208284031215610daf575f80fd5b8135610cb681610d7b565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60028110610e6f577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9052565b60208101610e818284610e3a565b92915050565b5f60208284031215610e97575f80fd5b5035919050565b5f805f60608486031215610eb0575f80fd5b8335610ebb81610d7b565b925060208401359150610ed060408501610d4f565b90509250925092565b60408101610ee78285610e3a565b610cb66020830184610e3a565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b80820180821115610e8157610e81610ef4565b84815273ffffffffffffffffffffffffffffffffffffffff84811660208301528316604082015260808101610f6c6060830184610e3a565b95945050505050565b8082028115828204841417610e8157610e81610ef4565b5f60208284031215610f9c575f80fd5b8151610cb681610d7b56fea164736f6c6343000819000a00000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1300000, + "intent": "Deploy OperatorFeeVault Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000081e608060405234801561001057600080fd5b506107fe806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d501461004657806360d7a27814610098578063a2ea7c6e146100b9575b600080fd5b6100826040518060400160405280600c81526020017f312e332e312d626574612e32000000000000000000000000000000000000000081525081565b60405161008f9190610473565b60405180910390f35b6100ab6100a636600461048d565b6100d9565b60405190815260200161008f565b6100cc6100c736600461053f565b61029d565b60405161008f9190610558565b60008060405180608001604052806000801b81526020018573ffffffffffffffffffffffffffffffffffffffff168152602001841515815260200187878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201829052509390945250929350915061015b9050826103c5565b600081815260208190526040902054909150156101a4576040517f23369fa600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80825260008181526020818152604091829020845181559084015160018201805493860151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00000000000000000000000000000000000000000090941673ffffffffffffffffffffffffffffffffffffffff9092169190911792909217909155606083015183919060028201906102409082610682565b509050503373ffffffffffffffffffffffffffffffffffffffff16817fd0b86852e21f9e5fa4bc3b0cff9757ffe243d50c4b43968a42202153d651ea5e8460405161028b9190610558565b60405180910390a39695505050505050565b604080516080810182526000808252602082018190529181019190915260608082015260008281526020818152604091829020825160808101845281548152600182015473ffffffffffffffffffffffffffffffffffffffff8116938201939093527401000000000000000000000000000000000000000090920460ff1615159282019290925260028201805491929160608401919061033c906105e0565b80601f0160208091040260200160405190810160405280929190818152602001828054610368906105e0565b80156103b55780601f1061038a576101008083540402835291602001916103b5565b820191906000526020600020905b81548152906001019060200180831161039857829003601f168201915b5050505050815250509050919050565b60008160600151826020015183604001516040516020016103e89392919061079c565b604051602081830303815290604052805190602001209050919050565b60005b83811015610420578181015183820152602001610408565b50506000910152565b60008151808452610441816020860160208601610405565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006104866020830184610429565b9392505050565b600080600080606085870312156104a357600080fd5b843567ffffffffffffffff808211156104bb57600080fd5b818701915087601f8301126104cf57600080fd5b8135818111156104de57600080fd5b8860208285010111156104f057600080fd5b6020928301965094505085013573ffffffffffffffffffffffffffffffffffffffff8116811461051f57600080fd5b91506040850135801515811461053457600080fd5b939692955090935050565b60006020828403121561055157600080fd5b5035919050565b602081528151602082015273ffffffffffffffffffffffffffffffffffffffff6020830151166040820152604082015115156060820152600060608301516080808401526105a960a0840182610429565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600181811c908216806105f457607f821691505b60208210810361062d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f82111561067d57600081815260208120601f850160051c8101602086101561065a5750805b601f850160051c820191505b8181101561067957828155600101610666565b5050505b505050565b815167ffffffffffffffff81111561069c5761069c6105b1565b6106b0816106aa84546105e0565b84610633565b602080601f83116001811461070357600084156106cd5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610679565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561075057888601518255948401946001909101908401610731565b508582101561078c57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600084516107ae818460208901610405565b60609490941b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190930190815290151560f81b60148201526015019291505056fea164736f6c6343000813000a0000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 700000, + "intent": "Deploy SchemaRegistry Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000046c961016060405234801561001157600080fd5b50604080518082018252600381526245415360e81b60208083019182528351808501855260058152640312e332e360dc1b908201529151812060e08190527f6a08c3e203132c561752255a4d52ffae85bb9c5d33cb3291520dea1b843563896101008190524660a081815286517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818801819052818901959095526060810193909352608080840192909252308382018190528751808503909201825260c093840190975280519501949094209093529290915261012091909152516101405260805160a05160c05160e05161010051610120516101405161457e61014b600039600061073701526000612784015260006127d3015260006127ae01526000612707015260006127310152600061275b015261457e6000f3fe60806040526004361061018b5760003560e01c806395411525116100d6578063d45c44351161007f578063ed24911d11610059578063ed24911d146104fd578063f10b5cc814610512578063f17325e71461054157600080fd5b8063d45c443514610467578063e30bb5631461049e578063e71ff365146104dd57600080fd5b8063b469318d116100b0578063b469318d146103ba578063b83010d314610414578063cf190f341461044757600080fd5b80639541152514610367578063a3112a641461037a578063a6d4dbc7146103a757600080fd5b806344adc90e116101385780634d003070116101125780634d003070146102de57806354fd4d50146102fe57806379f7573a1461034757600080fd5b806344adc90e1461029857806346926267146102b85780634cb7e9e5146102cb57600080fd5b806317d7de7c1161016957806317d7de7c146102205780632d0335ab146102425780633c0427151461028557600080fd5b80630eabf6601461019057806312b11a17146101a557806313893f61146101e7575b600080fd5b6101a361019e3660046134c8565b610554565b005b3480156101b157600080fd5b507ffeb2925a02bae3dae48d424a0437a2b6ac939aa9230ddc55a1a76f065d9880765b6040519081526020015b60405180910390f35b3480156101f357600080fd5b506102076102023660046134c8565b6106eb565b60405167ffffffffffffffff90911681526020016101de565b34801561022c57600080fd5b50610235610730565b6040516101de9190613578565b34801561024e57600080fd5b506101d461025d3660046135bd565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101d46102933660046135da565b610760565b6102ab6102a63660046134c8565b610863565b6040516101de9190613615565b6101a36102c6366004613659565b6109e4565b6101a36102d93660046134c8565b610a68565b3480156102ea57600080fd5b506102076102f9366004613671565b610b4b565b34801561030a57600080fd5b506102356040518060400160405280600c81526020017f312e342e312d626574612e33000000000000000000000000000000000000000081525081565b34801561035357600080fd5b506101a3610362366004613671565b610b58565b6102ab6103753660046134c8565b610bef565b34801561038657600080fd5b5061039a610395366004613671565b610e62565b6040516101de9190613771565b6101a36103b5366004613784565b611025565b3480156103c657600080fd5b506102076103d5366004613797565b73ffffffffffffffffffffffffffffffffffffffff919091166000908152603460209081526040808320938352929052205467ffffffffffffffff1690565b34801561042057600080fd5b507fb5d556f07587ec0f08cf386545cc4362c702a001650c2058002615ee5c9d1e756101d4565b34801561045357600080fd5b50610207610462366004613671565b6110ca565b34801561047357600080fd5b50610207610482366004613671565b60009081526033602052604090205467ffffffffffffffff1690565b3480156104aa57600080fd5b506104cd6104b9366004613671565b600090815260326020526040902054151590565b60405190151581526020016101de565b3480156104e957600080fd5b506102076104f83660046134c8565b6110d8565b34801561050957600080fd5b506101d4611110565b34801561051e57600080fd5b5060405173420000000000000000000000000000000000002081526020016101de565b6101d461054f3660046137c3565b61111a565b348160005b818110156106e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82018114600086868481811061059a5761059a6137fe565b90506020028101906105ac919061382d565b6105b590613ac3565b60208101518051919250908015806105d257508260400151518114155b15610609576040517f947d5a8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156106ad576106a56040518060a001604052808660000151815260200185848151811061063e5761063e6137fe565b6020026020010151815260200186604001518481518110610661576106616137fe565b60200260200101518152602001866060015173ffffffffffffffffffffffffffffffffffffffff168152602001866080015167ffffffffffffffff168152506111d8565b60010161060c565b506106c383600001518385606001518a886113e9565b6106cd9088613bed565b9650505050506106dd8160010190565b9050610559565b5050505050565b60004282825b818110156107245761071c3387878481811061070f5761070f6137fe565b9050602002013585611a18565b6001016106f1565b50909150505b92915050565b606061075b7f0000000000000000000000000000000000000000000000000000000000000000611b17565b905090565b600061077361076e83613d22565b611ca5565b604080516001808252818301909252600091816020015b6040805160c081018252600080825260208083018290529282018190526060808301829052608083015260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161078a5790505090506107f86020840184613d9d565b61080190613dd1565b81600081518110610814576108146137fe565b602090810291909101015261083d83358261083560c0870160a088016135bd565b346001611e2f565b60200151600081518110610853576108536137fe565b6020026020010151915050919050565b60608160008167ffffffffffffffff8111156108815761088161386b565b6040519080825280602002602001820160405280156108b457816020015b606081526020019060019003908161089f5790505b509050600034815b848110156109ce577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85018114368989848181106108fc576108fc6137fe565b905060200281019061090e9190613ddd565b905061091d6020820182613e11565b9050600003610958576040517f947d5a8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061097d823561096c6020850185613e11565b61097591613e79565b338887611e2f565b805190915061098c9086613bed565b945080602001518785815181106109a5576109a56137fe565b6020026020010181905250806020015151860195505050506109c78160010190565b90506108bc565b506109d98383612541565b979650505050505050565b604080516001808252818301909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816109fb579050509050610a3636839003830160208401613eed565b81600081518110610a4957610a496137fe565b6020908102919091010152610a63823582333460016113e9565b505050565b348160005b818110156106e4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201811436868684818110610aad57610aad6137fe565b9050602002810190610abf9190613ddd565b9050610b2c8135610ad36020840184613f09565b808060200260200160405190810160405280939291908181526020016000905b82821015610b1f57610b1060408302860136819003810190613eed565b81526020019060010190610af3565b50505050503388866113e9565b610b369086613bed565b94505050610b448160010190565b9050610a6d565b60004261072a838261262b565b33600090815260208190526040902054808211610ba1576040517f756688fe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152602081815260409182902084905581518381529081018490527f57b09af877df9068fd60a69d7b21f5576b8b38955812d6ae4ac52942f1e38fb7910160405180910390a15050565b60608160008167ffffffffffffffff811115610c0d57610c0d61386b565b604051908082528060200260200182016040528015610c4057816020015b6060815260200190600190039081610c2b5790505b509050600034815b848110156109ce577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8501811436898984818110610c8857610c886137fe565b9050602002810190610c9a919061382d565b9050366000610cac6020840184613e11565b909250905080801580610ccd5750610cc76040850185613f71565b90508114155b15610d04576040517f947d5a8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610de557610ddd6040518060a0016040528087600001358152602001868685818110610d3957610d396137fe565b9050602002810190610d4b9190613d9d565b610d5490613dd1565b8152602001610d666040890189613f71565b85818110610d7657610d766137fe565b905060600201803603810190610d8c9190613fd8565b8152602001610da16080890160608a016135bd565b73ffffffffffffffffffffffffffffffffffffffff168152602001610dcc60a0890160808a01613ff4565b67ffffffffffffffff169052611ca5565b600101610d07565b506000610e0e8535610df78587613e79565b610e076080890160608a016135bd565b8b8a611e2f565b8051909150610e1d9089613bed565b975080602001518a8881518110610e3657610e366137fe565b602002602001018190525080602001515189019850505050505050610e5b8160010190565b9050610c48565b604080516101408101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820183905261010082019290925261012081019190915260008281526032602090815260409182902082516101408101845281548152600182015492810192909252600281015467ffffffffffffffff808216948401949094526801000000000000000081048416606084015270010000000000000000000000000000000090049092166080820152600382015460a0820152600482015473ffffffffffffffffffffffffffffffffffffffff90811660c0830152600583015490811660e083015274010000000000000000000000000000000000000000900460ff16151561010082015260068201805491929161012084019190610f9c9061400f565b80601f0160208091040260200160405190810160405280929190818152602001828054610fc89061400f565b80156110155780601f10610fea57610100808354040283529160200191611015565b820191906000526020600020905b815481529060010190602001808311610ff857829003601f168201915b5050505050815250509050919050565b61103c6110373683900383018361405c565b6111d8565b604080516001808252818301909252600091816020015b604080518082019091526000808252602082015281526020019060019003908161105357905050905061108e36839003830160208401613eed565b816000815181106110a1576110a16137fe565b6020908102919091010152610a638235826110c260e0860160c087016135bd565b3460016113e9565b60004261072a338483611a18565b60004282825b81811015610724576111088686838181106110fb576110fb6137fe565b905060200201358461262b565b6001016110de565b600061075b6126ed565b604080516001808252818301909252600091829190816020015b6040805160c081018252600080825260208083018290529282018190526060808301829052608083015260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816111345790505090506111a26020840184613d9d565b6111ab90613dd1565b816000815181106111be576111be6137fe565b602090810291909101015261083d83358233346001611e2f565b608081015167ffffffffffffffff161580159061120c57504267ffffffffffffffff16816080015167ffffffffffffffff16105b15611243576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808201516040808401516060850151855184518587015173ffffffffffffffffffffffffffffffffffffffff84166000908152978890529487208054969794969495611337957fb5d556f07587ec0f08cf386545cc4362c702a001650c2058002615ee5c9d1e7595949392886112ba836140ca565b909155506080808c015160408051602081019990995273ffffffffffffffffffffffffffffffffffffffff9097169688019690965260608701949094529285019190915260a084015260c083015267ffffffffffffffff1660e0820152610100015b60405160208183030381529060405280519060200120612821565b90506113ad84606001518284602001518560400151866000015160405160200161139993929190928352602083019190915260f81b7fff0000000000000000000000000000000000000000000000000000000000000016604082015260410190565b604051602081830303815290604052612834565b6113e3576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b6040517fa2ea7c6e0000000000000000000000000000000000000000000000000000000081526004810186905260009081907342000000000000000000000000000000000000209063a2ea7c6e90602401600060405180830381865afa158015611457573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261149d9190810190614102565b80519091506114d8576040517fbf37b20e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855160008167ffffffffffffffff8111156114f5576114f561386b565b60405190808252806020026020018201604052801561159457816020015b60408051610140810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e0830182905261010083019190915261012082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816115135790505b50905060008267ffffffffffffffff8111156115b2576115b261386b565b6040519080825280602002602001820160405280156115db578160200160208202803683370190505b50905060005b838110156119fa5760008a82815181106115fd576115fd6137fe565b6020908102919091018101518051600090815260329092526040909120805491925090611656576040517fc5723b5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8c816001015414611693576040517fbf37b20e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600581015473ffffffffffffffffffffffffffffffffffffffff8c81169116146116e9576040517f4ca8886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600581015474010000000000000000000000000000000000000000900460ff1661173f576040517f157bd4c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002810154700100000000000000000000000000000000900467ffffffffffffffff1615611799576040517f905e710700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b426002820180547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff811670010000000000000000000000000000000067ffffffffffffffff948516810291821793849055604080516101408101825287548152600188015460208201529386169286169290921791830191909152680100000000000000008304841660608301529091049091166080820152600382015460a0820152600482015473ffffffffffffffffffffffffffffffffffffffff90811660c0830152600583015490811660e083015274010000000000000000000000000000000000000000900460ff16151561010082015260068201805483916101208401916118a59061400f565b80601f01602080910402602001604051908101604052809291908181526020018280546118d19061400f565b801561191e5780601f106118f35761010080835404028352916020019161191e565b820191906000526020600020905b81548152906001019060200180831161190157829003601f168201915b505050505081525050858481518110611939576119396137fe565b6020026020010181905250816020015184848151811061195b5761195b6137fe565b6020026020010181815250508c8b73ffffffffffffffffffffffffffffffffffffffff16868581518110611991576119916137fe565b602002602001015160c0015173ffffffffffffffffffffffffffffffffffffffff167ff930a6e2523c9cc298691873087a740550b8fc85a0680830414c148ed927f61585600001516040516119e891815260200190565b60405180910390a450506001016115e1565b50611a0a84838360018b8b612a03565b9a9950505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff83166000908152603460209081526040808320858452918290529091205467ffffffffffffffff1615611a8c576040517fec9d6eeb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526020829052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff861690811790915590519091859173ffffffffffffffffffffffffffffffffffffffff8816917f92a1f7a41a7c585a8b09e25b195e225b1d43248daca46b0faf9e0792777a222991a450505050565b604080516020808252818301909252606091600091906020820181803683370190505090506000805b6020811015611be2576000858260208110611b5d57611b5d6137fe565b1a60f81b90507fff000000000000000000000000000000000000000000000000000000000000008116600003611b935750611be2565b80848481518110611ba657611ba66137fe565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505060019182019101611b40565b5060008167ffffffffffffffff811115611bfe57611bfe61386b565b6040519080825280601f01601f191660200182016040528015611c28576020820181803683370190505b50905060005b82811015611c9c57838181518110611c4857611c486137fe565b602001015160f81c60f81b828281518110611c6557611c656137fe565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101611c2e565b50949350505050565b608081015167ffffffffffffffff1615801590611cd957504267ffffffffffffffff16816080015167ffffffffffffffff16105b15611d10576040517f1ab7da6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808201516040808401516060808601518651855186880151868801519488015160808901518051908b012060a08a015173ffffffffffffffffffffffffffffffffffffffff871660009081529b8c9052988b2080549a9b989a9899611337997ffeb2925a02bae3dae48d424a0437a2b6ac939aa9230ddc55a1a76f065d988076999493928c611da0836140ca565b919050558e6080015160405160200161131c9b9a999897969594939291909a8b5273ffffffffffffffffffffffffffffffffffffffff998a1660208c015260408b019890985295909716606089015267ffffffffffffffff938416608089015291151560a088015260c087015260e0860152610100850193909352610120840152166101408201526101600190565b60408051808201909152600081526060602082015284516040805180820190915260008152606060208201528167ffffffffffffffff811115611e7457611e7461386b565b604051908082528060200260200182016040528015611e9d578160200160208202803683370190505b5060208201526040517fa2ea7c6e000000000000000000000000000000000000000000000000000000008152600481018990526000907342000000000000000000000000000000000000209063a2ea7c6e90602401600060405180830381865afa158015611f0f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611f559190810190614102565b8051909150611f90576040517fbf37b20e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008367ffffffffffffffff811115611fab57611fab61386b565b60405190808252806020026020018201604052801561204a57816020015b60408051610140810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e0830182905261010083019190915261012082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181611fc95790505b50905060008467ffffffffffffffff8111156120685761206861386b565b604051908082528060200260200182016040528015612091578160200160208202803683370190505b50905060005b858110156125205760008b82815181106120b3576120b36137fe565b60200260200101519050600067ffffffffffffffff16816020015167ffffffffffffffff16141580156120fe57504267ffffffffffffffff16816020015167ffffffffffffffff1611155b15612135576040517f08e8b93700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8460400151158015612148575080604001515b1561217f576040517f157bd4c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040518061014001604052806000801b81526020018f81526020016121a34290565b67ffffffffffffffff168152602001836020015167ffffffffffffffff168152602001600067ffffffffffffffff16815260200183606001518152602001836000015173ffffffffffffffffffffffffffffffffffffffff1681526020018d73ffffffffffffffffffffffffffffffffffffffff16815260200183604001511515815260200183608001518152509050600080600090505b6122458382612df4565b600081815260326020526040902054909250156122645760010161223b565b81835260008281526032602090815260409182902085518155908501516001820155908401516002820180546060870151608088015167ffffffffffffffff908116700100000000000000000000000000000000027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff92821668010000000000000000027fffffffffffffffffffffffffffffffff000000000000000000000000000000009094169190951617919091171691909117905560a0840151600382015560c084015160048201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff000000000000000000000000000000000000000090911617905560e0850151600583018054610100880151151574010000000000000000000000000000000000000000027fffffffffffffffffffffff000000000000000000000000000000000000000000909116929093169190911791909117905561012084015184919060068201906123e49082614228565b50505060608401511561243b57606084015160009081526032602052604090205461243b576040517fc5723b5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8287868151811061244e5761244e6137fe565b60200260200101819052508360a00151868681518110612470576124706137fe565b6020026020010181815250508189602001518681518110612493576124936137fe565b6020026020010181815250508f8e73ffffffffffffffffffffffffffffffffffffffff16856000015173ffffffffffffffffffffffffffffffffffffffff167f8bf46bf4cfd674fa735a3d63ec1c9ad4153f033c290341f3a588b75685141b358560405161250391815260200190565b60405180910390a4505050506125198160010190565b9050612097565b5061253083838360008c8c612a03565b845250919998505050505050505050565b606060008267ffffffffffffffff81111561255e5761255e61386b565b604051908082528060200260200182016040528015612587578160200160208202803683370190505b508451909150600090815b818110156126205760008782815181106125ae576125ae6137fe565b6020026020010151905060008151905060005b8181101561260c578281815181106125db576125db6137fe565b60200260200101518787815181106125f5576125f56137fe565b6020908102919091010152600195860195016125c1565b5050506126198160010190565b9050612592565b509195945050505050565b60008281526033602052604090205467ffffffffffffffff161561267b576040517f2e26794600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526033602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff85169081179091559051909184917f5aafceeb1c7ad58e4a84898bdee37c02c0fc46e7d24e6b60e8209449f183459f9190a35050565b60003073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614801561275357507f000000000000000000000000000000000000000000000000000000000000000046145b1561277d57507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b600061072a61282e6126ed565b83612e53565b60008060006128438585612e95565b9092509050600081600481111561285c5761285c614342565b14801561289457508573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b156128a4576001925050506129fc565b6000808773ffffffffffffffffffffffffffffffffffffffff16631626ba7e60e01b88886040516024016128d9929190614371565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516129629190614392565b600060405180830381855afa9150503d806000811461299d576040519150601f19603f3d011682016040523d82523d6000602084013e6129a2565b606091505b50915091508180156129b5575080516020145b80156129f5575080517f1626ba7e00000000000000000000000000000000000000000000000000000000906129f390830160209081019084016143a4565b145b9450505050505b9392505050565b84516000906001819003612a5b57612a538888600081518110612a2857612a286137fe565b602002602001015188600081518110612a4357612a436137fe565b6020026020010151888888612eda565b915050612dea565b602088015173ffffffffffffffffffffffffffffffffffffffff8116612afc5760005b82811015612ae157878181518110612a9857612a986137fe565b6020026020010151600014612ad9576040517f1574f9f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600101612a7e565b508315612af157612af1856131f9565b600092505050612dea565b6000808273ffffffffffffffffffffffffffffffffffffffff1663ce46e0466040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6e91906143bd565b905060005b84811015612c2b5760008a8281518110612b8f57612b8f6137fe565b6020026020010151905080600003612ba75750612c23565b82612bde576040517f1574f9f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b88811115612c18576040517f1101129400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b978890039792909201915b600101612b73565b508715612d06576040517f88e5b2d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416906388e5b2d9908490612c88908e908e906004016143da565b60206040518083038185885af1158015612ca6573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612ccb91906143bd565b612d01576040517fbf2f3a8b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612dd5565b6040517f91db0b7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416906391db0b7e908490612d5c908e908e906004016143da565b60206040518083038185885af1158015612d7a573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612d9f91906143bd565b612dd5576040517fe8bee83900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8515612de457612de4876131f9565b50925050505b9695505050505050565b60208083015160c084015160e0850151604080870151606088015161010089015160a08a01516101208b01519451600099612e3599989796918c9101614493565b60405160208183030381529060405280519060200120905092915050565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526022810183905260428101829052600090606201612e35565b6000808251604103612ecb5760208301516040840151606085015160001a612ebf8782858561320c565b94509450505050612ed3565b506000905060025b9250929050565b602086015160009073ffffffffffffffffffffffffffffffffffffffff8116612f4e578515612f35576040517f1574f9f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215612f4457612f44846131f9565b6000915050612dea565b8515613039578073ffffffffffffffffffffffffffffffffffffffff1663ce46e0466040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fc391906143bd565b612ff9576040517f1574f9f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83861115613033576040517f1101129400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85840393505b8415613111576040517fe49617e100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063e49617e1908890613093908b90600401613771565b60206040518083038185885af11580156130b1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906130d691906143bd565b61310c576040517fccf3bb2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6131de565b6040517fe60c350500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063e60c3505908890613165908b90600401613771565b60206040518083038185885af1158015613183573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906131a891906143bd565b6131de576040517fbd8ba84d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82156131ed576131ed846131f9565b50939695505050505050565b8015613209576132093382613324565b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613243575060009050600361331b565b8460ff16601b1415801561325b57508460ff16601c14155b1561326c575060009050600461331b565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156132c0573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81166133145760006001925092505061331b565b9150600090505b94509492505050565b80471015613393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d80600081146133ed576040519150601f19603f3d011682016040523d82523d6000602084013e6133f2565b606091505b5050905080610a63576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161338a565b60008083601f84011261349557600080fd5b50813567ffffffffffffffff8111156134ad57600080fd5b6020830191508360208260051b8501011115612ed357600080fd5b600080602083850312156134db57600080fd5b823567ffffffffffffffff8111156134f257600080fd5b6134fe85828601613483565b90969095509350505050565b60005b8381101561352557818101518382015260200161350d565b50506000910152565b6000815180845261354681602086016020860161350a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006129fc602083018461352e565b73ffffffffffffffffffffffffffffffffffffffff8116811461320957600080fd5b80356135b88161358b565b919050565b6000602082840312156135cf57600080fd5b81356129fc8161358b565b6000602082840312156135ec57600080fd5b813567ffffffffffffffff81111561360357600080fd5b820160e081850312156129fc57600080fd5b6020808252825182820181905260009190848201906040850190845b8181101561364d57835183529284019291840191600101613631565b50909695505050505050565b60006060828403121561366b57600080fd5b50919050565b60006020828403121561368357600080fd5b5035919050565b6000610140825184526020830151602085015260408301516136b8604086018267ffffffffffffffff169052565b5060608301516136d4606086018267ffffffffffffffff169052565b5060808301516136f0608086018267ffffffffffffffff169052565b5060a083015160a085015260c083015161372260c086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161374a60e086018273ffffffffffffffffffffffffffffffffffffffff169052565b506101008381015115159085015261012080840151818601839052612dea8387018261352e565b6020815260006129fc602083018461368a565b6000610100828403121561366b57600080fd5b600080604083850312156137aa57600080fd5b82356137b58161358b565b946020939093013593505050565b6000602082840312156137d557600080fd5b813567ffffffffffffffff8111156137ec57600080fd5b8201604081850312156129fc57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6183360301811261386157600080fd5b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156138bd576138bd61386b565b60405290565b60405160c0810167ffffffffffffffff811182821017156138bd576138bd61386b565b6040516080810167ffffffffffffffff811182821017156138bd576138bd61386b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156139505761395061386b565b604052919050565b600067ffffffffffffffff8211156139725761397261386b565b5060051b60200190565b60006040828403121561398e57600080fd5b6040516040810181811067ffffffffffffffff821117156139b1576139b161386b565b604052823581526020928301359281019290925250919050565b6000606082840312156139dd57600080fd5b6040516060810181811067ffffffffffffffff82111715613a0057613a0061386b565b604052905080823560ff81168114613a1757600080fd5b8082525060208301356020820152604083013560408201525092915050565b600082601f830112613a4757600080fd5b81356020613a5c613a5783613958565b613909565b82815260609283028501820192828201919087851115613a7b57600080fd5b8387015b85811015613a9e57613a9189826139cb565b8452928401928101613a7f565b5090979650505050505050565b803567ffffffffffffffff811681146135b857600080fd5b600060a08236031215613ad557600080fd5b613add61389a565b8235815260208084013567ffffffffffffffff80821115613afd57600080fd5b9085019036601f830112613b1057600080fd5b8135613b1e613a5782613958565b81815260069190911b83018401908481019036831115613b3d57600080fd5b938501935b82851015613b6657613b54368661397c565b82528582019150604085019450613b42565b80868801525050506040860135925080831115613b8257600080fd5b5050613b9036828601613a36565b604083015250613ba2606084016135ad565b6060820152613bb360808401613aab565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561072a5761072a613bbe565b801515811461320957600080fd5b600067ffffffffffffffff821115613c2857613c2861386b565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600060c08284031215613c6657600080fd5b613c6e6138c3565b90508135613c7b8161358b565b81526020613c8a838201613aab565b818301526040830135613c9c81613c00565b604083015260608381013590830152608083013567ffffffffffffffff811115613cc557600080fd5b8301601f81018513613cd657600080fd5b8035613ce4613a5782613c0e565b8181528684838501011115613cf857600080fd5b818484018583013760008483830101528060808601525050505060a082013560a082015292915050565b600060e08236031215613d3457600080fd5b613d3c61389a565b82358152602083013567ffffffffffffffff811115613d5a57600080fd5b613d6636828601613c54565b602083015250613d7936604085016139cb565b604082015260a0830135613d8c8161358b565b6060820152613bb360c08401613aab565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261386157600080fd5b600061072a3683613c54565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261386157600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613e4657600080fd5b83018035915067ffffffffffffffff821115613e6157600080fd5b6020019150600581901b3603821315612ed357600080fd5b6000613e87613a5784613958565b80848252602080830192508560051b850136811115613ea557600080fd5b855b81811015613ee157803567ffffffffffffffff811115613ec75760008081fd5b613ed336828a01613c54565b865250938201938201613ea7565b50919695505050505050565b600060408284031215613eff57600080fd5b6129fc838361397c565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613f3e57600080fd5b83018035915067ffffffffffffffff821115613f5957600080fd5b6020019150600681901b3603821315612ed357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112613fa657600080fd5b83018035915067ffffffffffffffff821115613fc157600080fd5b6020019150606081023603821315612ed357600080fd5b600060608284031215613fea57600080fd5b6129fc83836139cb565b60006020828403121561400657600080fd5b6129fc82613aab565b600181811c9082168061402357607f821691505b60208210810361366b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000610100828403121561406f57600080fd5b61407761389a565b82358152614088846020850161397c565b602082015261409a84606085016139cb565b604082015260c08301356140ad8161358b565b60608201526140be60e08401613aab565b60808201529392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036140fb576140fb613bbe565b5060010190565b6000602080838503121561411557600080fd5b825167ffffffffffffffff8082111561412d57600080fd5b908401906080828703121561414157600080fd5b6141496138e6565b825181528383015161415a8161358b565b81850152604083015161416c81613c00565b604082015260608301518281111561418357600080fd5b80840193505086601f84011261419857600080fd5b825191506141a8613a5783613c0e565b82815287858486010111156141bc57600080fd5b6141cb8386830187870161350a565b60608201529695505050505050565b601f821115610a6357600081815260208120601f850160051c810160208610156142015750805b601f850160051c820191505b818110156142205782815560010161420d565b505050505050565b815167ffffffffffffffff8111156142425761424261386b565b61425681614250845461400f565b846141da565b602080601f8311600181146142a957600084156142735750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555614220565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156142f6578886015182559484019460019091019084016142d7565b508582101561433257878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b82815260406020820152600061438a604083018461352e565b949350505050565b6000825161386181846020870161350a565b6000602082840312156143b657600080fd5b5051919050565b6000602082840312156143cf57600080fd5b81516129fc81613c00565b6000604082016040835280855180835260608501915060608160051b8601019250602080880160005b8381101561444f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa088870301855261443d86835161368a565b95509382019390820190600101614403565b50508584038187015286518085528782019482019350915060005b828110156144865784518452938101939281019260010161446a565b5091979650505050505050565b89815260007fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808b60601b166020840152808a60601b166034840152507fffffffffffffffff000000000000000000000000000000000000000000000000808960c01b166048840152808860c01b1660508401525085151560f81b6058830152846059830152835161452c81607985016020880161350a565b80830190507fffffffff000000000000000000000000000000000000000000000000000000008460e01b166079820152607d81019150509a995050505050505050505056fea164736f6c6343000813000a0000000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 5800000, + "intent": "Deploy EAS Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000017586080604052348015600e575f80fd5b5060156019565b60d4565b5f54610100900460ff161560835760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b5f5460ff908116101560d2575f805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b611677806100e15f395ff3fe6080604052600436106100bb575f3560e01c80637dfbd04911610071578063c4d66de81161004c578063c4d66de8146102f6578063d61a398b14610315578063dad544e014610346575f80fd5b80637dfbd049146102ac5780637fc81bb7146102c3578063b87ea8d4146102e2575f80fd5b8063394d2731116100a1578063394d2731146101f65780633e47158c1461021e57806354fd4d5014610257575f80fd5b80630a7617b3146101735780630c0544a314610194575f80fd5b3661016f573373ffffffffffffffffffffffffffffffffffffffff7f21346dddac42cc163a6523eefc19df981df7352c870dc3b0b17a6a92fc6fe8135c1614610130576040517f14885cf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805134815247602082018190529133917f213e72af0d3613bd643cff3059f872c1015e6541624e37872bf95eefbaf220a8910160405180910390a2005b5f80fd5b34801561017e575f80fd5b5061019261018d3660046112a1565b61035a565b005b34801561019f575f80fd5b506001546101d09070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1681565b6040516fffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b348015610201575f80fd5b506001546101d0906fffffffffffffffffffffffffffffffff1681565b348015610229575f80fd5b5061023261051c565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101ed565b348015610262575f80fd5b5061029f6040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b6040516101ed91906112bc565b3480156102b7575f80fd5b506101d06301e1338081565b3480156102ce575f80fd5b506101926102dd36600461130f565b610722565b3480156102ed575f80fd5b50610192610915565b348015610301575f80fd5b506101926103103660046112a1565b610ced565b348015610320575f80fd5b505f546102329062010000900473ffffffffffffffffffffffffffffffffffffffff1681565b348015610351575f80fd5b50610232610ee9565b73420000000000000000000000000000000000001873ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103b7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103db919061133e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461043f576040517f38bac74200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811661048c576040517f99c6ec0800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805473ffffffffffffffffffffffffffffffffffffffff838116620100008181027fffffffffffffffffffff0000000000000000000000000000000000000000ffff85161790945560408051949093049091168084526020840191909152917f16417cc372deec0caee5f52e2ad77a5f07b4591fd56b4ff31b6e20f817d4daeb91015b60405180910390a15050565b5f806105467fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff81161561056957919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026105ac9190611386565b604080513060208201525f918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000009190911790610606906060015b604051602081830303815290604052805190602001205490565b1461063d576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091525f9061065e906060016105ec565b905073ffffffffffffffffffffffffffffffffffffffff8116156106f0578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106e9919061133e565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73420000000000000000000000000000000000001873ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561077f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107a3919061133e565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610807576040517f38bac74200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806fffffffffffffffffffffffffffffffff165f03610852576040517fcf85916100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6301e133806fffffffffffffffffffffffffffffffff821611156108a2576040517f30b9f35e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180546fffffffffffffffffffffffffffffffff8381167001000000000000000000000000000000008181028385161790945560408051949093049091168084526020840191909152917f4492086b630ed3846eec0979dd87a71c814ceb1c6dab80ab81e3450b21e4de289101610510565b60015461094a906fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008204811691166113a3565b6fffffffffffffffffffffffffffffffff16421015610995576040517f1e4a9f3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000016426fffffffffffffffffffffffffffffffff161790555f6109ef734200000000000000000000000000000000000011610f63565b90505f610a0f734200000000000000000000000000000000000019610f63565b90505f610a2f73420000000000000000000000000000000000001a610f63565b90505f610a4f73420000000000000000000000000000000000001b610f63565b9050610a5a5f6111b0565b5f8282610a6786886113d3565b610a7191906113d3565b610a7b91906113d3565b9050805f03610ab6576040517fc8972e5200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f80546040517f54e7f42d000000000000000000000000000000000000000000000000000000008152600481018890526024810187905260448101859052606481018690526201000090910473ffffffffffffffffffffffffffffffffffffffff16906354e7f42d906084015f60405180830381865afa158015610b3c573d5f803e3d5ffd5b505050506040513d5f823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610b81919081019061148b565b80519091505f819003610bc0576040517f763970d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805b82811015610c70575f848281518110610bde57610bde61155a565b6020026020010151602001519050805f03610bf95750610c68565b5f610c20868481518110610c0f57610c0f61155a565b60200260200101515f0151836111d6565b905080610c59576040517fd68d1b1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c6382856113d3565b935050505b600101610bc3565b50838114610caa576040517f9c01eac000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f73f9a13241a1848ec157967f3a85601709353e616f1f2605d818c0f2d21774df8385604051610cdb929190611587565b60405180910390a15050505050505050565b5f54610100900460ff1615808015610d0b57505f54600160ff909116105b80610d245750303b158015610d2457505f5460ff166001145b610db4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a6564000000000000000000000000000000000000606482015260840160405180910390fd5b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610e10575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610e186111e9565b5f80547fffffffffffffffffffff0000000000000000000000000000000000000000ffff166201000073ffffffffffffffffffffffffffffffffffffffff85160217905572015180000000000000000000000000000000006fffffffffffffffffffffffffffffffff4216176001558015610ee5575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610510565b5050565b5f610ef261051c565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f3a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f5e919061133e565b905090565b5f60018273ffffffffffffffffffffffffffffffffffffffff166382356d8a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610faf573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fd39190611622565b6001811115610fe457610fe46115f5565b1461101b576040517fb4726cbe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff166366d003ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561107b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061109f919061133e565b73ffffffffffffffffffffffffffffffffffffffff16146110ec576040517fc3380cef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b476110f6836111b0565b8273ffffffffffffffffffffffffffffffffffffffff16633ccfd60b6040518163ffffffff1660e01b81526004016020604051808303815f875af1158015611140573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111649190611640565b915047826111728383611657565b146111a9576040517f87c91c5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050919050565b807f21346dddac42cc163a6523eefc19df981df7352c870dc3b0b17a6a92fc6fe8135d50565b5f6111e2835a8461126c565b9392505050565b336111f261051c565b73ffffffffffffffffffffffffffffffffffffffff161415801561123357503361121a610ee9565b73ffffffffffffffffffffffffffffffffffffffff1614155b1561126a576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f805f805f858888f1949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461129e575f80fd5b50565b5f602082840312156112b1575f80fd5b81356111e28161127d565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f6020828403121561131f575f80fd5b81356fffffffffffffffffffffffffffffffff811681146111e2575f80fd5b5f6020828403121561134e575f80fd5b81516111e28161127d565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808202811582820484141761139d5761139d611359565b92915050565b6fffffffffffffffffffffffffffffffff8181168382160190808211156113cc576113cc611359565b5092915050565b8082018082111561139d5761139d611359565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040805190810167ffffffffffffffff81118282101715611436576114366113e6565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611483576114836113e6565b604052919050565b5f602080838503121561149c575f80fd5b825167ffffffffffffffff808211156114b3575f80fd5b818501915085601f8301126114c6575f80fd5b8151818111156114d8576114d86113e6565b6114e6848260051b0161143c565b818152848101925060069190911b830184019087821115611505575f80fd5b928401925b8184101561154f5760408489031215611521575f80fd5b611529611413565b84516115348161127d565b8152848601518682015283526040909301929184019161150a565b979650505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b604080825283518282018190525f91906020906060850190828801855b828110156115df578151805173ffffffffffffffffffffffffffffffffffffffff1685528501518585015292850192908401906001016115a4565b5050508093505050508260208301529392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f60208284031215611632575f80fd5b8151600281106111e2575f80fd5b5f60208284031215611650575f80fd5b5051919050565b8181038181111561139d5761139d61135956fea164736f6c6343000819000a0000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1700000, + "intent": "Deploy FeeSplitter Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000005d8608060405234801561001057600080fd5b506105b8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d5014610046578063cdcb760a14610098578063e0145f5c146100d0575b600080fd5b6100826040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b60405161008f91906103f7565b60405180910390f35b6100ab6100a6366004610440565b6100ea565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b734e59b44847b379578588920ca78fbf26c0b4956c6100ab565b8051602080830191909120604080517fff00000000000000000000000000000000000000000000000000000000000000818501527f4e59b44847b379578588920ca78fbf26c0b4956c000000000000000000000000602182015260358101869052605580820184905282518083039091018152607590910190915280519201919091206000919073ffffffffffffffffffffffffffffffffffffffff81163b156101d85760405173ffffffffffffffffffffffffffffffffffffffff8216907ffbe57d889a7f75a4e0c7da304cd158fcaddc4b925cdd9f4cfb115c0f9e48009b90600090a291506103779050565b600080734e59b44847b379578588920ca78fbf26c0b4956c73ffffffffffffffffffffffffffffffffffffffff168787604051602001610219929190610519565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526102519161053f565b6000604051808303816000865af19150503d806000811461028e576040519150601f19603f3d011682016040523d82523d6000602084013e610293565b606091505b5091509150806102a29061055b565b60601c94508115806102e057508273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b1561032257806040517fcb0fc6f700000000000000000000000000000000000000000000000000000000815260040161031991906103f7565b60405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff167f9b7318127ed899f286ea9ddd7925ed8ad24a682b6a825c3b5b3d88a3f00bc1d28860405161036a91815260200190565b60405180910390a2505050505b92915050565b60005b83811015610398578181015183820152602001610380565b838111156103a7576000848401525b50505050565b600081518084526103c581602086016020860161037d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061040a60208301846103ad565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806040838503121561045357600080fd5b82359150602083013567ffffffffffffffff8082111561047257600080fd5b818501915085601f83011261048657600080fd5b81358181111561049857610498610411565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156104de576104de610411565b816040528281528860208487010111156104f757600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b8281526000825161053181602085016020870161037d565b919091016020019392505050565b6000825161055181846020870161037d565b9190910192915050565b6000815160208301517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000808216935060148310156105a35780818460140360031b1b83161693505b50505091905056fea164736f6c634300080f000a0000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 600000, + "intent": "Deploy ConditionalDeployer Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000283608060405234801561001057600080fd5b50610263806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80631bec9d65146100515780631d8a4e921461006657806354fd4d501461009b57806378ecabce146100e4575b600080fd5b61006461005f3660046101ca565b610107565b005b7fc8bc8f9195cfb2d040744aac63412d02ffc186ea9bd519039edc4666ee9032bc546040519081526020015b60405180910390f35b6100d76040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b60405161009291906101e3565b6100f76100f23660046101ca565b610178565b6040519015158152602001610092565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610154576040517fee37fa8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fc8bc8f9195cfb2d040744aac63412d02ffc186ea9bd519039edc4666ee9032bc55565b60006101ab6101a57fc8bc8f9195cfb2d040744aac63412d02ffc186ea9bd519039edc4666ee9032bc5490565b836101b1565b92915050565b600081158015906101c3575081828416145b9392505050565b6000602082840312156101dc57600080fd5b5035919050565b600060208083528351808285015260005b81811015610210578581018301518582016040015282016101f4565b81811115610222576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fea164736f6c634300080f000a0000000000000000000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 300000, + "intent": "Deploy L2DevFeatureFlags Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000069e6080604052348015600e575f80fd5b506106828061001c5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c8063331b637f1461004357806354fd4d5014610069578063ab4d6f75146100b2575b5f80fd5b610056610051366004610512565b6100c7565b6040519081526020015b60405180910390f35b6100a56040518060400160405280600581526020017f312e302e3200000000000000000000000000000000000000000000000000000081525081565b604051610060919061053b565b6100c56100c036600461058e565b61039e565b005b5f67ffffffffffffffff801683602001511115610110576040517fd1f79e8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604083015163ffffffff1015610152576040517f94338eba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606083015167ffffffffffffffff1015610198576040517f596a19a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82516040515f916101dd91859060200160609290921b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168252601482015260340190565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00181528282528051602091820120878201516060890151898501515f9487018590527fffffffffffffffff00000000000000000000000000000000000000000000000060c084811b8216602c8a015283901b1660348801527fffffffff0000000000000000000000000000000000000000000000000000000060e082901b16603c88015292965090949093919291016040516020818303038152906040526102ac906105bc565b90505f85826040516020016102cb929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828252805160209182012060808d01519184018190529183015291505f90606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0300000000000000000000000000000000000000000000000000000000000000179a9950505050505050505050565b5f6103b76103b136859003850185610601565b836100c7565b90505f6103c38261043b565b509050806103fd576040517fe3c0081600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b827f5c37832d2e8d10e346e55ad62071a6a2f9fa5130614ef2ec6617555c6f467ba78560405161042d9190610622565b60405180910390a250505050565b5f805a835491505a6103e891031115939092509050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610475575f80fd5b919050565b5f60a0828403121561048a575f80fd5b60405160a0810181811067ffffffffffffffff821117156104d2577f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040529050806104e183610452565b8152602083013560208201526040830135604082015260608301356060820152608083013560808201525092915050565b5f8060c08385031215610523575f80fd5b61052d848461047a565b9460a0939093013593505050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f8082840360c08112156105a0575f80fd5b60a08112156105ad575f80fd5b50919360a08501359350915050565b805160208083015191908110156105fb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8160200360031b1b821691505b50919050565b5f60a08284031215610611575f80fd5b61061b838361047a565b9392505050565b60a0810173ffffffffffffffffffffffffffffffffffffffff61064484610452565b168252602083013560208301526040830135604083015260608301356060830152608083013560808301529291505056fea164736f6c6343000819000a0000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 600000, + "intent": "Deploy CrossL2Inbox Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000011836080604052348015600e575f80fd5b506111678061001c5f395ff3fe6080604052600436106100b8575f3560e01c80637056f41f11610071578063b1b1b2091161004c578063b1b1b20914610228578063bc294d7d14610266578063ecc7042814610291575f80fd5b80637056f41f146101b65780637936cbee146101d55780638d1d298f14610215575f80fd5b806352617f3c116100a157806352617f3c1461011c57806354fd4d50146101425780636b0c3c5e14610197575f80fd5b806324794462146100bc57806338ffde18146100e3575b5f80fd5b3480156100c7575f80fd5b506100d06102c5565b6040519081526020015b60405180910390f35b3480156100ee575f80fd5b506100f7610344565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100da565b348015610127575f80fd5b5061012f5f81565b60405161ffff90911681526020016100da565b34801561014d575f80fd5b5061018a6040518060400160405280600581526020017f312e332e3000000000000000000000000000000000000000000000000000000081525081565b6040516100da9190610c7e565b3480156101a2575f80fd5b506100d06101b1366004610d00565b6103c3565b3480156101c1575f80fd5b506100d06101d0366004610d77565b6104ae565b3480156101e0575f80fd5b506101e96106ba565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016100da565b61018a610223366004610dcf565b61075e565b348015610233575f80fd5b50610256610242366004610e25565b5f6020819052908152604090205460ff1681565b60405190151581526020016100da565b348015610271575f80fd5b506100d0610280366004610e25565b60026020525f908152604090205481565b34801561029c575f80fd5b506001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff166100d0565b5f7ff13569814868ede994184d5a425471fb19e869768a33421cb701a2ba3d420c0a5c61031e576040517fbca35af600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee75c90565b5f7ff13569814868ede994184d5a425471fb19e869768a33421cb701a2ba3d420c0a5c61039d576040517fbca35af600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507fb83444d07072b122e2e72a669ce32857d892345c19856f4e7142d06a167ab3f35c90565b5f610407874688888888888080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610ae192505050565b5f878152600260205260409020549091508114610450576040517f6eca2e4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b858473ffffffffffffffffffffffffffffffffffffffff16887f382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f32088878760405161049c93929190610e3c565b60405180910390a49695505050505050565b5f4685036104e8576040517f8ed9a95d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffbdffffffffffffffffffffffffffffffffffffdd73ffffffffffffffffffffffffffffffffffffffff851601610557576040517f4faa250900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6105816001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b90506105c6864683338989898080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610ae192505050565b5f828152600260205260408120829055600180549294507dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909216919061060a83610ea5565b91906101000a8154817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02191690837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16021790555050808573ffffffffffffffffffffffffffffffffffffffff16877f382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f3203388886040516106a993929190610e3c565b60405180910390a450949350505050565b5f807ff13569814868ede994184d5a425471fb19e869768a33421cb701a2ba3d420c0a5c610714576040517fbca35af600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50507fb83444d07072b122e2e72a669ce32857d892345c19856f4e7142d06a167ab3f35c907f711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee75c90565b60607ff13569814868ede994184d5a425471fb19e869768a33421cb701a2ba3d420c0a5c156107b9576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60017ff13569814868ede994184d5a425471fb19e869768a33421cb701a2ba3d420c0a5d7342000000000000000000000000000000000000236107ff6020860186610f06565b73ffffffffffffffffffffffffffffffffffffffff161461084c576040517f7987c15700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73420000000000000000000000000000000000002273ffffffffffffffffffffffffffffffffffffffff1663ab4d6f7585858560405161088d929190610f21565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1682526108cc9291600401610f30565b5f604051808303815f87803b1580156108e3575f80fd5b505af11580156108f5573d5f803e3d5ffd5b505050505f805f805f6109088888610b1f565b9450945094509450945046851461094b576040517f31ac221100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60808901355f61095f878387878a88610ae1565b5f8181526020819052604090205490915060ff16156109aa576040517f9ca9480b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556109ea8285610be8565b5f8673ffffffffffffffffffffffffffffffffffffffff163485604051610a119190610f89565b5f6040518083038185875af1925050503d805f8114610a4b576040519150601f19603f3d011682016040523d82523d5f602084013e610a50565b606091505b509950905080610a6257885189602001fd5b8186847fc270d73e26d2d39dee7ef92093555927e344e243415547ecc350b2b5385b68a28c80519060200120604051610a9d91815260200190565b60405180910390a4610aaf5f80610be8565b50505050505050505f7ff13569814868ede994184d5a425471fb19e869768a33421cb701a2ba3d420c0a5d9392505050565b5f868686868686604051602001610afd96959493929190610f9f565b6040516020818303038152906040528051906020012090509695505050505050565b5f808080606081610b33602082898b610ff5565b810190610b409190610e25565b90507f382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f3208114610b9b576040517fdf1eb58600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ba960806020898b610ff5565b810190610bb6919061101c565b91975095509350610bca876080818b610ff5565b810190610bd7919061107e565b969995985093965092949392505050565b817f711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee75d807fb83444d07072b122e2e72a669ce32857d892345c19856f4e7142d06a167ab3f35d5050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f610c906020830184610c32565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610cb8575f80fd5b50565b5f8083601f840112610ccb575f80fd5b50813567ffffffffffffffff811115610ce2575f80fd5b602083019150836020828501011115610cf9575f80fd5b9250929050565b5f805f805f8060a08789031215610d15575f80fd5b86359550602087013594506040870135610d2e81610c97565b93506060870135610d3e81610c97565b9250608087013567ffffffffffffffff811115610d59575f80fd5b610d6589828a01610cbb565b979a9699509497509295939492505050565b5f805f8060608587031215610d8a575f80fd5b843593506020850135610d9c81610c97565b9250604085013567ffffffffffffffff811115610db7575f80fd5b610dc387828801610cbb565b95989497509550505050565b5f805f83850360c0811215610de2575f80fd5b60a0811215610def575f80fd5b5083925060a084013567ffffffffffffffff811115610e0c575f80fd5b610e1886828701610cbb565b9497909650939450505050565b5f60208284031215610e35575f80fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8416815260406020820152816040820152818360608301375f818301606090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010192915050565b5f7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808316818103610efc577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b6001019392505050565b5f60208284031215610f16575f80fd5b8135610c9081610c97565b818382375f9101908152919050565b60c081018335610f3f81610c97565b73ffffffffffffffffffffffffffffffffffffffff1682526020848101359083015260408085013590830152606080850135908301526080938401359382019390935260a0015290565b5f82518060208501845e5f920191825250919050565b8681528560208201528460408201525f73ffffffffffffffffffffffffffffffffffffffff808616606084015280851660808401525060c060a0830152610fe960c0830184610c32565b98975050505050505050565b5f8085851115611003575f80fd5b8386111561100f575f80fd5b5050820193919092039150565b5f805f6060848603121561102e575f80fd5b83359250602084013561104081610c97565b929592945050506040919091013590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f806040838503121561108f575f80fd5b823561109a81610c97565b9150602083013567ffffffffffffffff808211156110b6575f80fd5b818501915085601f8301126110c9575f80fd5b8135818111156110db576110db611051565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561112157611121611051565b81604052828152886020848701011115611139575f80fd5b826020860160208301375f602084830101528095505050505050925092905056fea164736f6c6343000819000a0000000000000000000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1500000, + "intent": "Deploy L2ToL2CrossDomainMessenger Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000007ab608060405234801561001057600080fd5b5061078b806100206000396000f3fe6080604052600436106100345760003560e01c80634f0edcc91461003957806354fd4d501461005b57806364a197f3146100ba575b600080fd5b34801561004557600080fd5b506100596100543660046105ae565b6100db565b005b34801561006757600080fd5b506100a46040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b6040516100b1919061065a565b60405180910390f35b6100cd6100c8366004610674565b610340565b6040519081526020016100b1565b3373420000000000000000000000000000000000002314610128576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008073420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff16637936cbee6040518163ffffffff1660e01b81526004016040805180830381865afa158015610189573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ad91906106a0565b909250905073ffffffffffffffffffffffffffffffffffffffff82163014610201576040517fbc22e2aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa0712d68000000000000000000000000000000000000000000000000000000008152600481018490527342000000000000000000000000000000000000259063a0712d6890602401600060405180830381600087803b15801561026757600080fd5b505af115801561027b573d6000803e3d6000fd5b50505050828460405161028d9061057d565b73ffffffffffffffffffffffffffffffffffffffff90911681526020016040518091039082f09050801580156102c7573d6000803e3d6000fd5b50508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fe5479bb8ebad3b9ac81f55f424a6289cf0a54ff2641708f41dcb2b26f264d3598584604051610331929190918252602082015260400190565b60405180910390a35050505050565b600073ffffffffffffffffffffffffffffffffffffffff831661038f576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73420000000000000000000000000000000000002573ffffffffffffffffffffffffffffffffffffffff166344df8e70346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156103eb57600080fd5b505af11580156103ff573d6000803e3d6000fd5b50506040805133602482015273ffffffffffffffffffffffffffffffffffffffff881660448201523460648083019190915282518083039091018152608490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f4f0edcc90000000000000000000000000000000000000000000000000000000017905290517f7056f41f0000000000000000000000000000000000000000000000000000000081527342000000000000000000000000000000000000239450637056f41f93506104de9250869130916004016106ce565b6020604051808303816000875af11580156104fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610521919061070c565b604080513481526020810185905291925073ffffffffffffffffffffffffffffffffffffffff85169133917fed98a2ff78833375c368471a747cdf0633024dde3f870feb08a934ac5be83402910160405180910390a392915050565b60598061072683390190565b73ffffffffffffffffffffffffffffffffffffffff811681146105ab57600080fd5b50565b6000806000606084860312156105c357600080fd5b83356105ce81610589565b925060208401356105de81610589565b929592945050506040919091013590565b6000815180845260005b81811015610615576020818501810151868301820152016105f9565b81811115610627576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061066d60208301846105ef565b9392505050565b6000806040838503121561068757600080fd5b823561069281610589565b946020939093013593505050565b600080604083850312156106b357600080fd5b82516106be81610589565b6020939093015192949293505050565b83815273ffffffffffffffffffffffffffffffffffffffff8316602082015260606040820152600061070360608301846105ef565b95945050505050565b60006020828403121561071e57600080fd5b505191905056fe608060405260405160593803806059833981016040819052601e91602a565b806001600160a01b0316ff5b600060208284031215603b57600080fd5b81516001600160a01b0381168114605157600080fd5b939250505056fea164736f6c634300080f000a000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 700000, + "intent": "Deploy SuperchainETHBridge Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003b3608060405234801561001057600080fd5b50610393806100206000396000f3fe60806040526004361061003f5760003560e01c806344df8e701461004457806354fd4d501461004e578063a0712d68146100ad578063b60d4288146100cd575b600080fd5b61004c6100d5565b005b34801561005a57600080fd5b506100976040518060400160405280600581526020017f312e312e3000000000000000000000000000000000000000000000000000000081525081565b6040516100a491906102a1565b60405180910390f35b3480156100b957600080fd5b5061004c6100c8366004610314565b61015a565b61004c610229565b3373420000000000000000000000000000000000002414610122576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405134815233907f875e07afd7ce17c6531b1a6b7b34829dcd8b7e6639448afbd6a8e29fa1422b82906020015b60405180910390a2565b33734200000000000000000000000000000000000024146101a7576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80336040516101b590610295565b73ffffffffffffffffffffffffffffffffffffffff90911681526020016040518091039082f09050801580156101ef573d6000803e3d6000fd5b505060405181815233907f85719716ac5bd2744ae7ed3d16702129383049b97123b506320e7a5826ebbbba9060200160405180910390a250565b34600003610263576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405134815233907fbb9e497a5b82d1a37f9496dd70c6efb97ba0d98c66c3422d05010105d063359890602001610150565b60598061032e83390190565b600060208083528351808285015260005b818110156102ce578581018301518582016040015282016102b2565b818111156102e0576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006020828403121561032657600080fd5b503591905056fe608060405260405160593803806059833981016040819052601e91602a565b806001600160a01b0316ff5b600060208284031215603b57600080fd5b81516001600160a01b0381168114605157600080fd5b939250505056fea164736f6c634300080f000a00000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 400000, + "intent": "Deploy ETHLiquidity Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000010b1608060405234801561001057600080fd5b50611091806100206000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80635cf24969116100f9578063c598591811610097578063e591b28211610071578063e591b28214610434578063e81b2c6d1461044e578063f820614014610457578063fe3d57101461046057600080fd5b8063c598591814610404578063d844471514610424578063dad544e01461042c57600080fd5b80638381f58a116100d35780638381f58a146103be5780638b239f73146103d25780639e8c4966146103db578063b80777ea146103e457600080fd5b80635cf249691461038557806364ca23ef1461038e57806368d5dca6146103a257600080fd5b80634397dfef1161016657806347af267b1161014057806347af267b146103145780634d5d9a2a1461033757806354fd4d5014610368578063550fcdc91461037d57600080fd5b80634397dfef146102c3578063440a5e20146102f957806346a4d7801461030157600080fd5b806316d3bc7f116101a257806316d3bc7f14610202578063213268491461022f5780633db6be2b1461028e5780633e47158c1461029657600080fd5b8063015d8eb9146101c9578063098999be146101de57806309bd5a60146101e6575b600080fd5b6101dc6101d7366004610d9c565b610491565b005b6101dc6105d1565b6101ef60025481565b6040519081526020015b60405180910390f35b6008546102169067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101f9565b7f435553544f4d5f4741535f544f4b454e0000000000000000000000000000000060005260096020527f4ad9936a67aeb1898ef7b848aecdf71a1f8999fbf63ff2f5b5691cb14bedfe4d5460ff165b60405190151581526020016101f9565b6101dc6105e4565b61029e61060e565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101f9565b6102cb610819565b6040805173ffffffffffffffffffffffffffffffffffffffff909316835260ff9091166020830152016101f9565b6101dc610880565b6101dc61030f366004610e0e565b6108d7565b61027e610322366004610e0e565b60096020526000908152604090205460ff1681565b6008546103539068010000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016101f9565b6103706108ec565b6040516101f99190610e57565b61037061094c565b6101ef60015481565b6003546102169067ffffffffffffffff1681565b6003546103539068010000000000000000900463ffffffff1681565b6000546102169067ffffffffffffffff1681565b6101ef60055481565b6101ef60065481565b6000546102169068010000000000000000900467ffffffffffffffff1681565b600354610353906c01000000000000000000000000900463ffffffff1681565b610370610a88565b61029e610b79565b73deaddeaddeaddeaddeaddeaddeaddeaddead000161029e565b6101ef60045481565b6101ef60075481565b60085461047e906c01000000000000000000000000900461ffff1681565b60405161ffff90911681526020016101f9565b3373deaddeaddeaddeaddeaddeaddeaddeaddead000114610539576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4c31426c6f636b3a206f6e6c7920746865206465706f7369746f72206163636f60448201527f756e742063616e20736574204c3120626c6f636b2076616c756573000000000060648201526084015b60405180910390fd5b6000805467ffffffffffffffff98891668010000000000000000027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909116998916999099179890981790975560019490945560029290925560038054919094167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009190911617909255600491909155600555600655565b6105d9610880565b60a43560a01c600855565b6105ec610880565b6dffff00000000000000000000000060b03560901c1660a43560a01c17600855565b6000806106397fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff81161561065c57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e67657200000000000081525051600261069f9190610ea8565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e67657200000000000091909117906106fa906060015b604051602081830303815290604052805190602001205490565b14610731576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051306020820152600191810191909152600090610753906060016106e0565b905073ffffffffffffffffffffffffffffffffffffffff8116156107e7578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e09190610f0c565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4c31426c6f636b4347543a20646570726563617465640000000000000000000060448201526000908190606401610530565b73deaddeaddeaddeaddeaddeaddeaddeaddead00013381146108aa57633cc50b456000526004601cfd5b60043560801c60035560143560801c60005560243560015560443560075560643560025560843560045550565b6108e033610bf1565b6108e981610cce565b50565b606061092860408051808201909152600581527f312e392e30000000000000000000000000000000000000000000000000000000602082015290565b6040516020016109389190610f49565b604051602081830303815290604052905090565b60606109a27f435553544f4d5f4741535f544f4b454e0000000000000000000000000000000060005260096020527f4ad9936a67aeb1898ef7b848aecdf71a1f8999fbf63ff2f5b5691cb14bedfe4d5460ff1690565b6109de575060408051808201909152600381527f4554480000000000000000000000000000000000000000000000000000000000602082015290565b73420000000000000000000000000000000000002a73ffffffffffffffffffffffffffffffffffffffff1663550fcdc96040518163ffffffff1660e01b8152600401600060405180830381865afa158015610a3d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a839190810190610fb9565b905090565b6060610ade7f435553544f4d5f4741535f544f4b454e0000000000000000000000000000000060005260096020527f4ad9936a67aeb1898ef7b848aecdf71a1f8999fbf63ff2f5b5691cb14bedfe4d5460ff1690565b610b1a575060408051808201909152600581527f4574686572000000000000000000000000000000000000000000000000000000602082015290565b73420000000000000000000000000000000000002a73ffffffffffffffffffffffffffffffffffffffff1663d84447156040518163ffffffff1660e01b8152600401600060405180830381865afa158015610a3d573d6000803e3d6000fd5b6000610b8361060e565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a839190610f0c565b73ffffffffffffffffffffffffffffffffffffffff811673deaddeaddeaddeaddeaddeaddeaddeaddead00011480610c5b5750610c2c610b79565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b80610c985750610c6961060e565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6108e9576040517fbe9d7ca600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526009602052604090205460ff1615610d17576040517f4f45326000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526009602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091559051909183917fb876f6594132c89891d2fd198e925e999be741ec809abb58bfe9b966876cc06c9190a350565b803567ffffffffffffffff81168114610d9757600080fd5b919050565b600080600080600080600080610100898b031215610db957600080fd5b610dc289610d7f565b9750610dd060208a01610d7f565b96506040890135955060608901359450610dec60808a01610d7f565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600060208284031215610e2057600080fd5b5035919050565b60005b83811015610e42578181015183820152602001610e2a565b83811115610e51576000848401525b50505050565b6020815260008251806020840152610e76816040850160208701610e27565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610f07577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b600060208284031215610f1e57600080fd5b815173ffffffffffffffffffffffffffffffffffffffff81168114610f4257600080fd5b9392505050565b60008251610f5b818460208701610e27565b7f2b637573746f6d2d6761732d746f6b656e2e3100000000000000000000000000920191825250601301919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060208284031215610fcb57600080fd5b815167ffffffffffffffff80821115610fe357600080fd5b818401915084601f830112610ff757600080fd5b81518181111561100957611009610f8a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561104f5761104f610f8a565b8160405282815287602084870101111561106857600080fd5b611079836020830160208801610e27565b97965050505050505056fea164736f6c634300080f000a000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1100000, + "intent": "Deploy L1BlockCGT Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000862608060405234801561001057600080fd5b50610842806100206000396000f3fe6080604052600436106100695760003560e01c806382e3702d1161004357806382e3702d146100f6578063c2b3e5ac14610136578063ecc704281461014957600080fd5b80633f827a5a1461009257806344df8e70146100bf57806354fd4d50146100d457600080fd5b3661008d5761008b33620186a0604051806020016040528060008152506101ae565b005b600080fd5b34801561009e57600080fd5b506100a7600181565b60405161ffff90911681526020015b60405180910390f35b3480156100cb57600080fd5b5061008b610284565b3480156100e057600080fd5b506100e96102bc565b6040516100b691906105dd565b34801561010257600080fd5b506101266101113660046105f7565b60006020819052908152604090205460ff1681565b60405190151581526020016100b6565b61008b61014436600461063f565b6101ae565b34801561015557600080fd5b506101a06001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b6040519081526020016100b6565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663213268496040518163ffffffff1660e01b8152600401602060405180830381865afa15801561020d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102319190610743565b801561023d5750600034115b15610274576040517fcdfaa11100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61027f83838361031c565b505050565b4761028e816104e0565b60405181907f7967de617a5ac1cc7eba2d6f37570a0135afa950d8bb77cdd35f0d0b4e85a16f90600090a250565b60606102f860408051808201909152600581527f312e322e30000000000000000000000000000000000000000000000000000000602082015290565b6040516020016103089190610765565b604051602081830303815290604052905090565b60006103b26040518060c001604052806103766001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b815233602082015273ffffffffffffffffffffffffffffffffffffffff871660408201523460608201526080810186905260a00184905261050a565b600081815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055905073ffffffffffffffffffffffffffffffffffffffff84163361044d6001547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001790565b7f02a52367d10742d8032712c1bb8e0144ff1ec5ffda1ed7d70bb05a27449550543487878760405161048294939291906107a6565b60405180910390a45050600180547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8082168301167fffff0000000000000000000000000000000000000000000000000000000000009091161790555050565b806040516104ed90610557565b6040518091039082f090508015801561027f573d6000803e3d6000fd5b80516020808301516040808501516060860151608087015160a0880151935160009761053a9790969591016107d6565b604051602081830303815290604052805190602001209050919050565b60088061082e83390190565b60005b8381101561057e578181015183820152602001610566565b8381111561058d576000848401525b50505050565b600081518084526105ab816020860160208601610563565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006105f06020830184610593565b9392505050565b60006020828403121561060957600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561065457600080fd5b833573ffffffffffffffffffffffffffffffffffffffff8116811461067857600080fd5b925060208401359150604084013567ffffffffffffffff8082111561069c57600080fd5b818601915086601f8301126106b057600080fd5b8135818111156106c2576106c2610610565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561070857610708610610565b8160405282815289602084870101111561072157600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561075557600080fd5b815180151581146105f057600080fd5b60008251610777818460208701610563565b7f2b637573746f6d2d6761732d746f6b656e000000000000000000000000000000920191825250601101919050565b8481528360208201526080604082015260006107c56080830185610593565b905082606083015295945050505050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261082160c0830184610593565b9897505050505050505056fe608060405230fffea164736f6c634300080f000a000000000000000000000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 750000, + "intent": "Deploy L2ToL1MessagePasserCGT Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000146a608060405234801561001057600080fd5b5061001961001e565b6100de565b600054610100900460ff161561008a5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811610156100dc576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b61137d806100ed6000396000f3fe6080604052600436106100dd5760003560e01c80638da5cb5b1161007f578063d844471511610059578063d844471514610256578063dad544e01461026b578063f2fde38b14610280578063f46eccc4146102a057600080fd5b80638da5cb5b146101eb5780639065714714610216578063c6f69fbb1461023657600080fd5b806344df8e70116100bb57806344df8e701461016357806354fd4d501461016b578063550fcdc9146101c1578063715018a6146101d657600080fd5b80630c984832146100e25780633e47158c1461010457806340c10f1914610143575b600080fd5b3480156100ee57600080fd5b506101026100fd366004610ec7565b6102e0565b005b34801561011057600080fd5b5061011961035f565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561014f57600080fd5b5061010261015e366004610eeb565b61056a565b6101026106cc565b34801561017757600080fd5b506101b46040518060400160405280600581526020017f312e302e3100000000000000000000000000000000000000000000000000000081525081565b60405161013a9190610f17565b3480156101cd57600080fd5b506101b46107c2565b3480156101e257600080fd5b50610102610850565b3480156101f757600080fd5b5060335473ffffffffffffffffffffffffffffffffffffffff16610119565b34801561022257600080fd5b50610102610231366004611064565b610864565b34801561024257600080fd5b50610102610251366004610ec7565b610a29565b34801561026257600080fd5b506101b4610aa5565b34801561027757600080fd5b50610119610ab2565b34801561028c57600080fd5b5061010261029b366004610ec7565b610b2f565b3480156102ac57600080fd5b506102d06102bb366004610ec7565b60656020526000908152604090205460ff1681565b604051901515815260200161013a565b6102e8610be6565b73ffffffffffffffffffffffffffffffffffffffff811660008181526065602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f83b05b6735acd4b85e3bded8e72c851d1a87718f81e3c8e6f0c9d9a2baa88e469190a250565b60008061038a7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b905073ffffffffffffffffffffffffffffffffffffffff8116156103ad57919050565b6040518060400160405280601a81526020017f4f564d5f4c3143726f7373446f6d61696e4d657373656e6765720000000000008152505160026103f091906110da565b604080513060208201526000918101919091527f4f564d5f4c3143726f7373446f6d61696e4d657373656e676572000000000000919091179061044b906060015b604051602081830303815290604052805190602001205490565b14610482576040517f54e433cd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080513060208201526001918101919091526000906104a490606001610431565b905073ffffffffffffffffffffffffffffffffffffffff811615610538578073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610531919061113e565b9250505090565b6040517f332144db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526065602052604090205460ff166105b3576040517f5fbc4ede00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2e1a7d4d0000000000000000000000000000000000000000000000000000000081526004810182905273420000000000000000000000000000000000002990632e1a7d4d90602401600060405180830381600087803b15801561061957600080fd5b505af115801561062d573d6000803e3d6000fd5b50505050808260405161063f90610e99565b73ffffffffffffffffffffffffffffffffffffffff90911681526020016040518091039082f0905080158015610679573d6000803e3d6000fd5b505060405181815273ffffffffffffffffffffffffffffffffffffffff83169033907fec89d80a36947288037745287dde87d62cd8c141d5323130b3d26d97d84004c79060200160405180910390a35050565b3360009081526065602052604090205460ff16610715576040517f5fbc4ede00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73420000000000000000000000000000000000002973ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561077157600080fd5b505af1158015610785573d6000803e3d6000fd5b50506040513481523393507f875e07afd7ce17c6531b1a6b7b34829dcd8b7e6639448afbd6a8e29fa1422b829250602001905060405180910390a2565b606780546107cf9061115b565b80601f01602080910402602001604051908101604052809291908181526020018280546107fb9061115b565b80156108485780601f1061081d57610100808354040283529160200191610848565b820191906000526020600020905b81548152906001019060200180831161082b57829003601f168201915b505050505081565b610858610be6565b6108626000610c67565b565b600054610100900460ff16158080156108845750600054600160ff909116105b8061089e5750303b15801561089e575060005460ff166001145b61092f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561098d57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b610995610cde565b61099d610d5f565b6109a684610b2f565b60666109b284826111fd565b5060676109bf83826111fd565b508015610a2357600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b610a31610be6565b73ffffffffffffffffffffffffffffffffffffffff811660008181526065602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fdf6bf03dfab5b4ccec3ba95544b98d7ecc9d4b9293d8673e86cb6edb5ac0cb629190a250565b606680546107cf9061115b565b6000610abc61035f565b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b2a919061113e565b905090565b610b37610be6565b73ffffffffffffffffffffffffffffffffffffffff8116610bda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610926565b610be381610c67565b50565b60335473ffffffffffffffffffffffffffffffffffffffff163314610862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610926565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b33610ce761035f565b73ffffffffffffffffffffffffffffffffffffffff1614158015610d28575033610d0f610ab2565b73ffffffffffffffffffffffffffffffffffffffff1614155b15610862576040517fc4050a2600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600054610100900460ff16610df6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610926565b610862600054610100900460ff16610e90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610926565b61086233610c67565b60598061131883390190565b73ffffffffffffffffffffffffffffffffffffffff81168114610be357600080fd5b600060208284031215610ed957600080fd5b8135610ee481610ea5565b9392505050565b60008060408385031215610efe57600080fd5b8235610f0981610ea5565b946020939093013593505050565b600060208083528351808285015260005b81811015610f4457858101830151858201604001528201610f28565b81811115610f56576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610fca57600080fd5b813567ffffffffffffffff80821115610fe557610fe5610f8a565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561102b5761102b610f8a565b8160405283815286602085880101111561104457600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561107957600080fd5b833561108481610ea5565b9250602084013567ffffffffffffffff808211156110a157600080fd5b6110ad87838801610fb9565b935060408601359150808211156110c357600080fd5b506110d086828701610fb9565b9150509250925092565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611139577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500290565b60006020828403121561115057600080fd5b8151610ee481610ea5565b600181811c9082168061116f57607f821691505b6020821081036111a8577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156111f857600081815260208120601f850160051c810160208610156111d55750805b601f850160051c820191505b818110156111f4578281556001016111e1565b5050505b505050565b815167ffffffffffffffff81111561121757611217610f8a565b61122b81611225845461115b565b846111ae565b602080601f83116001811461127e57600084156112485750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556111f4565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156112cb578886015182559484019460019091019084016112ac565b508582101561130757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b0190555056fe608060405260405160593803806059833981016040819052601e91602a565b806001600160a01b0316ff5b600060208284031215603b57600080fd5b81516001600160a01b0381168114605157600080fd5b939250505056fea164736f6c634300080f000a00000000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 1400000, + "intent": "Deploy LiquidityController Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca40000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000036d608060405234801561001057600080fd5b5061034d806100206000396000f3fe6080604052600436106100345760003560e01c80632e1a7d4d1461003957806354fd4d501461005b578063d0e30db0146100ba575b600080fd5b34801561004557600080fd5b5061005961005436600461025b565b6100c2565b005b34801561006757600080fd5b506100a46040518060400160405280600581526020017f312e302e3000000000000000000000000000000000000000000000000000000081525081565b6040516100b19190610274565b60405180910390f35b6100596101cb565b3373420000000000000000000000000000000000002a1461010f576040517f565369fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b47811115610149576040517f7b7f21e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80336040516101579061024f565b73ffffffffffffffffffffffffffffffffffffffff90911681526020016040518091039082f0905080158015610191573d6000803e3d6000fd5b505060405181815233907fb1cce8684b4ffa8667b4577654e61ee3480d661ee9c27522ac80e211f6bd4d259060200160405180910390a250565b3373420000000000000000000000000000000000002a14610218576040517f565369fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405134815233907f7ff07ce9a287649537e4b012e45cf012d90228b12e2b56bb03515a6b5436fcdf9060200160405180910390a2565b6059806102e883390190565b60006020828403121561026d57600080fd5b5035919050565b600060208083528351808285015260005b818110156102a157858101830151858201604001528201610285565b818111156102b3576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201604001939250505056fe608060405260405160593803806059833981016040819052601e91602a565b806001600160a01b0316ff5b600060208284031215603b57600080fd5b81516001600160a01b0381168114605157600080fd5b939250505056fea164736f6c634300080f000a00000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 400000, + "intent": "Deploy NativeAssetLiquidity Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0x3659cfe6000000000000000000000000893c2ceeb71d38514daf67728d3ff1b213fc4b5f", + "from": "0x0000000000000000000000000000000000000000", + "gasLimit": 50000, + "intent": "Upgrade L2ProxyAdmin Implementation", + "to": "0x4200000000000000000000000000000000000018" + }, + { + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000003f6c6104006040523480156200001257600080fd5b5060405162003c0c38038062003c0c8339810160408190526200003591620001d4565b30608090815281516001600160a01b0390811660a09081526020840151821660e09081526040850151831660c0908152606086015184166101009081529486015184166101209081529286015184166101409081529086015184166101609081529186015184166101809081529486015184166101a09081529286015184166101c09081529086015184166101e09081529186015184166102009081529486015184166102209081529286015184166102409081529086015184166102609081529186015184166102809081529486015184166102a09081529286015184166102c09081529086015184166102e090815291860151841661030090815294860151841661032090815292860151841661034090815290860151841661036052908501518316610380529284015182166103a05283015181166103c052910151166103e05262000407565b60405161036081016001600160401b0381118282101715620001b157634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b0381168114620001cf57600080fd5b919050565b60006103608284031215620001e857600080fd5b620001f26200017f565b620001fd83620001b7565b81526200020d60208401620001b7565b60208201526200022060408401620001b7565b60408201526200023360608401620001b7565b60608201526200024660808401620001b7565b60808201526200025960a08401620001b7565b60a08201526200026c60c08401620001b7565b60c08201526200027f60e08401620001b7565b60e082015261010062000294818501620001b7565b90820152610120620002a8848201620001b7565b90820152610140620002bc848201620001b7565b90820152610160620002d0848201620001b7565b90820152610180620002e4848201620001b7565b908201526101a0620002f8848201620001b7565b908201526101c06200030c848201620001b7565b908201526101e062000320848201620001b7565b9082015261020062000334848201620001b7565b9082015261022062000348848201620001b7565b908201526102406200035c848201620001b7565b9082015261026062000370848201620001b7565b9082015261028062000384848201620001b7565b908201526102a062000398848201620001b7565b908201526102c0620003ac848201620001b7565b908201526102e0620003c0848201620001b7565b90820152610300620003d4848201620001b7565b90820152610320620003e8848201620001b7565b90820152610340620003fc848201620001b7565b908201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161036051610380516103a0516103c0516103e0516135c26200064a600039600081816104d801526119530152600081816104af0152611b10015260008181610486015261145301526000818161045d015261130501526000818161043401526113ec01526000818161040b0152611a560152600081816103e20152611a180152600081816103b901526119da015260008181610390015261199c0152600081816103670152611ad201526000818161033e0152611a9401526000818161031501526116e90152600081816102ec01526116690152600081816102c301526115ea01526000818161029a0152611915015260008181610271015261120501526000818161024801526118d701526000818161021f01526118b10152600081816101f601526117c00152600081816101ce015261179a0152600081816101a601526110e101526000818161017e015261116d01526000818161015601526114cd01526000818161012e0152610fdb01526000818160df0152610ed5015260008181610107015261175201526000818160ba01528181610ef701528181610ffd015281816111030152818161118f015281816112270152818161132701528181611475015281816114ef0152818161160c0152818161168b015261170b0152600061052b01526135c26000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d5014610046578063615f64fe14610098578063d55ec6971461050a575b600080fd5b6100826040518060400160405280600581526020017f312e352e3000000000000000000000000000000000000000000000000000000081525081565b60405161008f9190612fd5565b60405180910390f35b604080516103608101825273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116828401527f0000000000000000000000000000000000000000000000000000000000000000811660608301527f0000000000000000000000000000000000000000000000000000000000000000811660808301527f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f0000000000000000000000000000000000000000000000000000000000000000811660e08301527f000000000000000000000000000000000000000000000000000000000000000081166101008301527f000000000000000000000000000000000000000000000000000000000000000081166101208301527f000000000000000000000000000000000000000000000000000000000000000081166101408301527f000000000000000000000000000000000000000000000000000000000000000081166101608301527f000000000000000000000000000000000000000000000000000000000000000081166101808301527f000000000000000000000000000000000000000000000000000000000000000081166101a08301527f000000000000000000000000000000000000000000000000000000000000000081166101c08301527f000000000000000000000000000000000000000000000000000000000000000081166101e08301527f000000000000000000000000000000000000000000000000000000000000000081166102008301527f000000000000000000000000000000000000000000000000000000000000000081166102208301527f000000000000000000000000000000000000000000000000000000000000000081166102408301527f000000000000000000000000000000000000000000000000000000000000000081166102608301527f000000000000000000000000000000000000000000000000000000000000000081166102808301527f000000000000000000000000000000000000000000000000000000000000000081166102a08301527f000000000000000000000000000000000000000000000000000000000000000081166102c08301527f000000000000000000000000000000000000000000000000000000000000000081166102e08301527f000000000000000000000000000000000000000000000000000000000000000081166103008301527f000000000000000000000000000000000000000000000000000000000000000081166103208301527f000000000000000000000000000000000000000000000000000000000000000016610340820152905161008f9190612fe8565b610512610514565b005b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610583576040517fada337cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061058d61059b565b905061059881610e96565b50565b6105a3612e1f565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663213268496040518163ffffffff1660e01b8152600401602060405180830381865afa158015610602573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106269190613236565b15156101608201526040517f47af267b0000000000000000000000000000000000000000000000000000000081527f494e5445524f50000000000000000000000000000000000000000000000000006004820152734200000000000000000000000000000000000015906347af267b90602401602060405180830381865afa9250505080156106f0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526106ed91810190613236565b60015b61070157600061018082015261070a565b15156101808201525b806101800151801561072357506107216001611b34565b155b1561075a576040517fa27dcc8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051806020016040528073420000000000000000000000000000000000000773ffffffffffffffffffffffffffffffffffffffff16639fce812c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e8919061327a565b73ffffffffffffffffffffffffffffffffffffffff16905281526040805160208082018084527f7f46ddb200000000000000000000000000000000000000000000000000000000905291519091829173420000000000000000000000000000000000001091637f46ddb29160248086019291908187030181865afa158015610874573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610898919061327a565b73ffffffffffffffffffffffffffffffffffffffff168152508160200181905250604051806020016040528073420000000000000000000000000000000000001473ffffffffffffffffffffffffffffffffffffffff16637f46ddb26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610947919061327a565b73ffffffffffffffffffffffffffffffffffffffff168152508160400181905250604051806020016040528073420000000000000000000000000000000000001273ffffffffffffffffffffffffffffffffffffffff1663ee9a31a26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f6919061327a565b73ffffffffffffffffffffffffffffffffffffffff1690526060820152604080518082018083527fee9a31a2000000000000000000000000000000000000000000000000000000009052905181907342000000000000000000000000000000000000179063ee9a31a2906044808501916020918187030181865afa158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa6919061327a565b73ffffffffffffffffffffffffffffffffffffffff16815260200173420000000000000000000000000000000000001773ffffffffffffffffffffffffffffffffffffffff16637d1d0c5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b449190613297565b90526080820152610b68734200000000000000000000000000000000000011611c97565b60a0820152610b8a734200000000000000000000000000000000000019611c97565b60c0820152610bac73420000000000000000000000000000000000001a611c97565b60e0820152610bce73420000000000000000000000000000000000001b611c97565b61010082015261016081015115610dbe57600073420000000000000000000000000000000000002a905060405180606001604052808273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c72919061327a565b73ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1663d84447156040518163ffffffff1660e01b8152600401600060405180830381865afa158015610cd8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610d1e9190810190613371565b81526020018273ffffffffffffffffffffffffffffffffffffffff1663550fcdc96040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610db49190810190613371565b9052610120830152505b600073420000000000000000000000000000000000002b73ffffffffffffffffffffffffffffffffffffffff1663d61a398b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610e59575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610e569181019061327a565b60015b610e6557506000610e68565b90505b604080516020810190915273ffffffffffffffffffffffffffffffffffffffff909116815261014082015290565b80515160405173ffffffffffffffffffffffffffffffffffffffff9091166024820152610f9990734200000000000000000000000000000000000007907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de80000000000000000000000000000000000000000000000000000000017905260006014611e78565b60208101515160405173ffffffffffffffffffffffffffffffffffffffff909116602482015261109f90734200000000000000000000000000000000000010907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de800000000000000000000000000000000000000000000000000000000179052600080611e78565b60408082015151905173ffffffffffffffffffffffffffffffffffffffff909116602482015261112b90734200000000000000000000000000000000000014907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b60608101515160405173ffffffffffffffffffffffffffffffffffffffff90911660248201526111b790734200000000000000000000000000000000000012907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b6080810151805160209091015160405173ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526112c990734200000000000000000000000000000000000017907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fcd6dc6870000000000000000000000000000000000000000000000000000000017905260016000611e78565b80610160015115611410576101208101518051602082015160409283015192516113d29373420000000000000000000000000000000000002a937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361135493906024016133c2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9065714700000000000000000000000000000000000000000000000000000000179052600080611e78565b6114107342000000000000000000000000000000000000297f00000000000000000000000000000000000000000000000000000000000000006125d4565b6101408101515160405173ffffffffffffffffffffffffffffffffffffffff909116602482015261149d9073420000000000000000000000000000000000002b907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b60a08101518051602082015160409283015192516115ba93734200000000000000000000000000000000000011937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb49dc741000000000000000000000000000000000000000000000000000000001790527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006000611e78565b60c081015180516020820151604092830151925161163993734200000000000000000000000000000000000019937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b60e08101518051602082015160409283015192516116b89373420000000000000000000000000000000000001a937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b6101008101518051602082015160409283015192516117389373420000000000000000000000000000000000001b937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b61177673420000000000000000000000000000000000000f7f00000000000000000000000000000000000000000000000000000000000000006125d4565b6117e47342000000000000000000000000000000000000158261016001516117be577f00000000000000000000000000000000000000000000000000000000000000006125d4565b7f00000000000000000000000000000000000000000000000000000000000000006125d4565b8061016001511561188d576040517f46a4d7800000000000000000000000000000000000000000000000000000000081527f435553544f4d5f4741535f544f4b454e000000000000000000000000000000006004820152734200000000000000000000000000000000000015906346a4d78090602401600060405180830381600087803b15801561187457600080fd5b505af1158015611888573d6000803e3d6000fd5b505050505b6118fb7342000000000000000000000000000000000000168261016001516118d5577f00000000000000000000000000000000000000000000000000000000000000006125d4565b7f00000000000000000000000000000000000000000000000000000000000000006125d4565b6119397342000000000000000000000000000000000000187f00000000000000000000000000000000000000000000000000000000000000006125d4565b61197773420000000000000000000000000000000000002d7f00000000000000000000000000000000000000000000000000000000000000006125d4565b80610180015115611a7a576119c07342000000000000000000000000000000000000227f00000000000000000000000000000000000000000000000000000000000000006125d4565b6119fe7342000000000000000000000000000000000000237f00000000000000000000000000000000000000000000000000000000000000006125d4565b611a3c7342000000000000000000000000000000000000247f00000000000000000000000000000000000000000000000000000000000000006125d4565b611a7a7342000000000000000000000000000000000000257f00000000000000000000000000000000000000000000000000000000000000006125d4565b611ab87342000000000000000000000000000000000000207f00000000000000000000000000000000000000000000000000000000000000006125d4565b611af67342000000000000000000000000000000000000217f00000000000000000000000000000000000000000000000000000000000000006125d4565b61059873420000000000000000000000000000000000002c7f00000000000000000000000000000000000000000000000000000000000000006125d4565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000002d600482015260009081907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa158015611bb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd9919061327a565b90508073ffffffffffffffffffffffffffffffffffffffff163b600003611c035750600092915050565b6040517f78ecabce0000000000000000000000000000000000000000000000000000000081526004810184905273420000000000000000000000000000000000002d906378ecabce90602401602060405180830381865afa158015611c6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c909190613236565b9392505050565b611cb860408051606081018252600080825260208201819052909182015290565b60008273ffffffffffffffffffffffffffffffffffffffff1663d0e12f906040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611d3f575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252611d3c918101906134a5565b60015b611d4b57506000611d4e565b90505b600083905060405180606001604052808273ffffffffffffffffffffffffffffffffffffffff16630d9019e16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611da9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dcd919061327a565b73ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1663d3e5792b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e579190613297565b8152602001836001811115611e6e57611e6e61340d565b9052949350505050565b611e81866128a7565b611ed4576040517fc3fe4a6600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024015b60405180910390fd5b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526000907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa158015611f55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f79919061327a565b905073ffffffffffffffffffffffffffffffffffffffff81163b158015906120c657506120c68773ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015611fea573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526120309190810190613371565b8773ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa15801561207b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526120c19190810190613371565b6128f0565b15612115576040517ff8ce5d1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88166004820152602401611ecb565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152881690633659cfe690602401600060405180830381600087803b15801561217e57600080fd5b505af1158015612192573d6000803e3d6000fd5b507ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009250505083811480156121c9575060ff831615155b15612200576040517f10415a2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808414612394576040517fa6ed563e0000000000000000000000000000000000000000000000000000000081526004810185905260009073ffffffffffffffffffffffffffffffffffffffff8a169063a6ed563e90602401602060405180830381865afa158015612275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122999190613297565b90506122a68460016134f5565b6122b49060ff16600861351a565b81901c60ff16156122f1576040517fc996d78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061230160ff8616600861351a565b6040517f4e91db080000000000000000000000000000000000000000000000000000000081526004810188905260ff90911b198381166024830152915073ffffffffffffffffffffffffffffffffffffffff8b1690634e91db0890604401600060405180830381600087803b15801561237957600080fd5b505af115801561238d573d6000803e3d6000fd5b5050505050505b6040517fa6ed563e0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff8a169063a6ed563e90602401602060405180830381865afa158015612402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124269190613297565b905060ff604082901c1615612467576040517fc996d78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f4e91db08000000000000000000000000000000000000000000000000000000008152600481018390527fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008216602482015273ffffffffffffffffffffffffffffffffffffffff8a1690634e91db0890604401600060405180830381600087803b1580156124f757600080fd5b505af115801561250b573d6000803e3d6000fd5b50506040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c169250634f1ef2869150612563908b908a90600401613557565b6000604051808303816000875af1158015612582573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526125c89190810190613371565b50505050505050505050565b6125dd826128a7565b61262b576040517fc3fe4a6600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401611ecb565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526000907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa1580156126ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d0919061327a565b905073ffffffffffffffffffffffffffffffffffffffff81163b158015906127d257506127d28373ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015612741573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526127879190810190613371565b8373ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa15801561207b573d6000803e3d6000fd5b15612821576040517ff8ce5d1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401611ecb565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152841690633659cfe690602401600060405180830381600087803b15801561288a57600080fd5b505af115801561289e573d6000803e3d6000fd5b50505050505050565b60007208400000000000000000000000000000000000600b83901c721fffffffffffffffffffffffffffffffffffff161480156128ea57506128e882612916565b155b92915050565b60006128fc838361297e565b158015611c90575061290e83836129ce565b159392505050565b600073ffffffffffffffffffffffffffffffffffffffff821673420000000000000000000000000000000000004214806128ea575073ffffffffffffffffffffffffffffffffffffffff82167342000000000000000000000000000000000000061492915050565b60008061298a84612a43565b9050600061299784612a43565b805183519192501480156129b2575080602001518260200151145b80156129c5575080604001518260400151145b95945050505050565b6000806129da84612a43565b905060006129e784612a43565b805183519192501180612a0d575080518251148015612a0d575080602001518260200151105b806129c5575080518251148015612a2b575080602001518260200151145b80156129c55750604090810151910151109392505050565b612a6760405180606001604052806000815260200160008152602001600081525090565b6000612aa8836040518060400160405280600181526020017f2e00000000000000000000000000000000000000000000000000000000000000815250612c0f565b9050600381511015612ae6576040517f9eda858c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612b4182600281518110612afe57612afe613586565b60200260200101516040518060400160405280600181526020017f2d00000000000000000000000000000000000000000000000000000000000000815250612c0f565b90506000612b9e82600081518110612b5b57612b5b613586565b60200260200101516040518060400160405280600181526020017f2b00000000000000000000000000000000000000000000000000000000000000815250612c0f565b90506040518060600160405280612bce85600081518110612bc157612bc1613586565b6020026020010151612cba565b8152602001612be985600181518110612bc157612bc1613586565b8152602001612c0483600081518110612bc157612bc1613586565b905295945050505050565b60606000612c1d8484612d32565b9050601f1960208201600183510160051b81018651838201526001845101845260005b825160608452818114612c855760405182820380825286601f8201165b8b850181015183820152870180612c5d5750600082820160200152603f018616810160405284525b875160209490940193019050818310612c4057505050508091508251612cb357602081019150600281510382525b5092915050565b80516000907f1999999999999999999999999999999999999999999999999999999999999999825b600181019050603060ff82870151160382851185600a028281019650600983118188108317171586029550505050828110612ce257505080612d2c5763101827966000526004601cfd5b50919050565b606082518251818111612e17576020850194506020840193506020604051019250846001828488010301600060208410612d6b57508286205b601f841660200360031b87515b8951818118831c612dcd578315612dab5783878c2014612dab5760018b019a50848b10612da55750612ddc565b50612d78565b858b038952998601996020909801978615612dcd57848b10612da55750612ddc565b5060018a019950838a10612d78575b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08189030160051c8152602090970190525050505b505092915050565b604080516101c08101825260006101a08201818152825282516020808201855282825280840191909152835180820185528281528385015283518082018552828152606084015283518085019094528184528301529060808201908152602001612ea060408051606081018252600080825260208201819052909182015290565b8152602001612ec660408051606081018252600080825260208201819052909182015290565b8152602001612eec60408051606081018252600080825260208201819052909182015290565b8152602001612f1260408051606081018252600080825260208201819052909182015290565b8152604080516060808201835260008252602082810182905292820152910190815260408051602081810190925260008152910190815260006020820181905260409091015290565b60005b83811015612f76578181015183820152602001612f5e565b83811115612f85576000848401525b50505050565b60008151808452612fa3816020860160208601612f5b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611c906020830184612f8b565b815173ffffffffffffffffffffffffffffffffffffffff1681526103608101602083015161302e602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151613056604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50606083015161307e606084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060808301516130a6608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a08301516130ce60a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c08301516130f660c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161311e60e084018273ffffffffffffffffffffffffffffffffffffffff169052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff90811691840191909152610120808501518216908401526101408085015182169084015261016080850151821690840152610180808501518216908401526101a0808501518216908401526101c0808501518216908401526101e08085015182169084015261020080850151821690840152610220808501518216908401526102408085015182169084015261026080850151821690840152610280808501518216908401526102a0808501518216908401526102c0808501518216908401526102e0808501518216908401526103008085015182169084015261032080850151821690840152610340808501519182168185015290612e17565b60006020828403121561324857600080fd5b81518015158114611c9057600080fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461059857600080fd5b60006020828403121561328c57600080fd5b8151611c9081613258565b6000602082840312156132a957600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff808411156132fa576132fa6132b0565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715613340576133406132b0565b8160405280935085815286868601111561335957600080fd5b613367866020830187612f5b565b5050509392505050565b60006020828403121561338357600080fd5b815167ffffffffffffffff81111561339a57600080fd5b8201601f810184136133ab57600080fd5b6133ba848251602084016132df565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff841681526060602082015260006133f16060830185612f8b565b82810360408401526134038185612f8b565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff84168152602081018390526060810160028310613497577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b826040830152949350505050565b6000602082840312156134b757600080fd5b815160028110611c9057600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600060ff821660ff84168060ff03821115613512576135126134c6565b019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613552576135526134c6565b500290565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006133ba6040830184612f8b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a0000000000000000000000002a5a3eabb9fd571a3af0299eebdf8eaafe29a914000000000000000000000000250af3f400cf8aac8d410c90f1ba3968dd87df96000000000000000000000000547d0fba434877d7237d511cf87fabe2ee26b152000000000000000000000000ac8538a2e6a1f5dfbb1c4b8bd97cefb2997824a8000000000000000000000000b178cdaa8336f25624a63c049edb5af7ca36c2da000000000000000000000000c053fc0155bf8bda5b568af53276e538f0ea4d58000000000000000000000000716ead0cf3e7ff86a02d4f8cb41a6d14922fa8330000000000000000000000006a97c5d55a21265326150efe12fc30fb21cbff56000000000000000000000000a0734858ba5085ff6db493021a0f8c54605c2cda00000000000000000000000027e51b2254433a3284d9ba73ea551c397db2a124000000000000000000000000a0f4ffff79a0a3e039fcbef738751efba8e84f96000000000000000000000000f43862b9d814bb4504158ceccb0b74b31265e4ee000000000000000000000000893c2ceeb71d38514daf67728d3ff1b213fc4b5f000000000000000000000000f7bed7215eef1003fac426682cf2edeb958569f7000000000000000000000000f7bed7215eef1003fac426682cf2edeb958569f7000000000000000000000000eddf416c7159387cc6df3015700f79cfb891137300000000000000000000000070de55bc0bfbc52c5d0cca1da5816c2428886a34000000000000000000000000bec660b456b84a081e90af29be43385bda5bf7b600000000000000000000000093a8a7a9c98cb998d88dba3373a6c7f8ee2e8a4600000000000000000000000037dc2fe754052a9fac35f17282599fafbeb9f423000000000000000000000000784f1fae11f1c3a9c413423fe1b370a3636b8d560000000000000000000000002f76618143d9d2731c56778192d3893864b423d7000000000000000000000000dda87ef358082ab3f4ba8982290c671efdc4d1590000000000000000000000008256398a687e740006098445b05d5ca46b7be21e0000000000000000000000008684ccc5bf484ec242dbc7119004a83533934a79000000000000000000000000906835344844979ffd3a752eaa23728d513db00b000000000000000000000000e35b194efc4907f383b7e3b87f4c2c339ce239f60000000000000000000000000000000000000000", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 4500000, + "intent": "Deploy L2ContractsManager Implementation", + "to": "0x420000000000000000000000000000000000002C" + }, + { + "data": "0x7c36f37e000000000000000000000000a723d436b320015ebead5e589c49e03131b80ee3", + "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", + "gasLimit": 3000000, + "intent": "L2ProxyAdmin Upgrade Predeploys", + "to": "0x4200000000000000000000000000000000000018" + } + ] +} \ No newline at end of file diff --git a/op-core/nuts/fork_lock.toml b/op-core/nuts/fork_lock.toml index b1205cb2089..1ef213f0044 100644 --- a/op-core/nuts/fork_lock.toml +++ b/op-core/nuts/fork_lock.toml @@ -1,6 +1,10 @@ -# NUT Bundle Fork Lock -# To update a locked bundle, update both the bundle file and this hash in the same PR. +# To update a fork's bundle, run: just nut-snapshot-for +# Contract changes must be merged to develop before snapshotting. [karst] -bundle = "op-node/rollup/derive/karst_nut_bundle.json" -hash = "sha256:b9c610d09ca05ab24ef84ea38e4f563d71401f592f9eff13fa97dac879bee600" + bundle = "op-core/nuts/bundles/karst_nut_bundle.json" + hash = "sha256:6145f900384f0aa2cdc63f2267a747b8c958bf1c09bacce8c29037c3eaa75d44" + commit = "cba7aba0c98aae22720b21c3a023990a486cb6e0" + +# REVIEWER NOTE: Changes to this file affect which NUT bundles are embedded +# into op-node and kona-node for hardfork activations. Review carefully. diff --git a/op-core/nuts/lock.go b/op-core/nuts/lock.go new file mode 100644 index 00000000000..b95e3660c79 --- /dev/null +++ b/op-core/nuts/lock.go @@ -0,0 +1,74 @@ +package nuts + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/BurntSushi/toml" + + opservice "github.com/ethereum-optimism/optimism/op-service" +) + +// ForkLockEntry represents a single fork's entry in fork_lock.toml. +type ForkLockEntry struct { + Bundle string `toml:"bundle"` + Hash string `toml:"hash"` + Commit string `toml:"commit"` +} + +// ForkLock is the full contents of fork_lock.toml, keyed by fork name. +type ForkLock map[string]ForkLockEntry + +// LockFilePath returns the absolute path to fork_lock.toml relative to the given directory. +func LockFilePath(dir string) (string, error) { + root, err := opservice.FindMonorepoRoot(dir) + if err != nil { + return "", fmt.Errorf("finding monorepo root: %w", err) + } + return filepath.Join(root, "op-core", "nuts", "fork_lock.toml"), nil +} + +// ReadLockFile reads and parses fork_lock.toml from the monorepo root. +func ReadLockFile(dir string) (ForkLock, string, error) { + lockPath, err := LockFilePath(dir) + if err != nil { + return nil, "", err + } + var locks ForkLock + if _, err := toml.DecodeFile(lockPath, &locks); err != nil { + return nil, "", fmt.Errorf("reading fork lock file: %w", err) + } + return locks, lockPath, nil +} + +// WriteLockFile writes fork_lock.toml back to disk with a header comment. +func WriteLockFile(lockPath string, locks ForkLock) error { + f, err := os.Create(lockPath) + if err != nil { + return fmt.Errorf("opening fork lock file for writing: %w", err) + } + defer f.Close() + + _, err = fmt.Fprint(f, `# To update a fork's bundle, run: just nut-snapshot-for +# Contract changes must be merged to develop before snapshotting. + +`) + if err != nil { + return err + } + + enc := toml.NewEncoder(f) + if err := enc.Encode(locks); err != nil { + return fmt.Errorf("writing fork lock file: %w", err) + } + + _, err = fmt.Fprint(f, ` +# REVIEWER NOTE: Changes to this file affect which NUT bundles are embedded +# into op-node and kona-node for hardfork activations. Review carefully. +`) + if err != nil { + return err + } + return nil +} diff --git a/op-deployer/pkg/deployer/broadcaster/keyed.go b/op-deployer/pkg/deployer/broadcaster/keyed.go index b856490a7be..5857adcbffc 100644 --- a/op-deployer/pkg/deployer/broadcaster/keyed.go +++ b/op-deployer/pkg/deployer/broadcaster/keyed.go @@ -2,6 +2,7 @@ package broadcaster import ( "context" + "errors" "fmt" "math/big" "sync" @@ -19,7 +20,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" - "github.com/hashicorp/go-multierror" ) const ( @@ -128,7 +128,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ) } - var txErr *multierror.Error + var txErr error var completed int for i, fut := range futures { bcastRes := <-fut @@ -143,7 +143,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er if bcastRes.Receipt.Status == 0 { failErr := fmt.Errorf("transaction failed: %s", outRes.Receipt.TxHash.String()) - txErr = multierror.Append(txErr, failErr) + txErr = errors.Join(txErr, failErr) outRes.Err = failErr t.lgr.Error( "transaction failed on chain", @@ -165,7 +165,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er ) } } else { - txErr = multierror.Append(txErr, bcastRes.Err) + txErr = errors.Join(txErr, bcastRes.Err) outRes.Err = bcastRes.Err t.lgr.Error( "transaction failed", @@ -178,7 +178,7 @@ func (t *KeyedBroadcaster) Broadcast(ctx context.Context) ([]BroadcastResult, er results[i] = outRes } - return results, txErr.ErrorOrNil() + return results, txErr } func (t *KeyedBroadcaster) broadcast(ctx context.Context, bcast script.Broadcast, blockGasLimit uint64) (<-chan txmgr.SendResponse, common.Hash) { diff --git a/op-deployer/pkg/deployer/devfeatures.go b/op-deployer/pkg/deployer/devfeatures.go deleted file mode 100644 index 9705cfd5e0f..00000000000 --- a/op-deployer/pkg/deployer/devfeatures.go +++ /dev/null @@ -1,54 +0,0 @@ -package deployer - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// Development feature flag constants that mirror the solidity DevFeatures library. -// These use a 32 byte bitmap for easy integration between op-deployer and contracts. -var ( - // OptimismPortalInteropDevFlag enables the OptimismPortalInterop contract. - OptimismPortalInteropDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") - - // CannonKonaDevFlag enables Kona as the default cannon prover. - CannonKonaDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000010") - - // DeployV2DisputeGamesDevFlag enables deployment of V2 dispute game contracts. - DeployV2DisputeGamesDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000100") - - // OPCMV2DevFlag enables the OPContractsManagerV2 contract. - OPCMV2DevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000010000") - - // L2CMDevFlag enables L2CM. - L2CMDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000100000") - - // ZKDisputeGameDevFlag enables the ZK dispute game system (ZKDisputeGame). - // TODO(#19432): Use this flag in the OPCM/OPD integration pipeline. - ZKDisputeGameDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000001000000") - - // SuperRootGamesMigrationDevFlag enables the super root games migration path in OPCM upgrade. - SuperRootGamesMigrationDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000010000000") -) - -// IsDevFeatureEnabled checks if a specific development feature is enabled in a feature bitmap. -// It performs a bitwise AND operation between the bitmap and the feature flag to determine -// if the feature is enabled. This follows the same pattern as the solidity DevFeatures library. -func IsDevFeatureEnabled(bitmap, flag common.Hash) bool { - b := new(big.Int).SetBytes(bitmap[:]) - f := new(big.Int).SetBytes(flag[:]) - - featuresIsNonZero := f.Cmp(big.NewInt(0)) != 0 - bitmapContainsFeatures := new(big.Int).And(b, f).Cmp(f) == 0 - return featuresIsNonZero && bitmapContainsFeatures -} - -// EnableDevFeature enables a specific development feature in a feature bitmap -func EnableDevFeature(bitmap, flag common.Hash) common.Hash { - var result common.Hash - for i := 0; i < 32; i++ { - result[i] = bitmap[i] | flag[i] - } - return result -} diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index ad195bbc9bd..41139a9f885 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-chain-ops/foundry" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/pipeline" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" @@ -171,7 +172,7 @@ func TestEndToEndBootstrapApplyWithUpgrade(t *testing.T) { devFeature common.Hash }{ // "default" (non-V2) test case removed: v1 OPCM was deleted. - {"opcm-v2", deployer.OPCMV2DevFlag}, + {"opcm-v2", devfeatures.OPCMV2Flag}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -219,8 +220,8 @@ func TestEndToEndBootstrapApplyWithUpgrade(t *testing.T) { FaultGameClockExtension: standard.DisputeClockExtension, FaultGameMaxClockDuration: standard.DisputeMaxClockDuration, } - if deployer.IsDevFeatureEnabled(tt.devFeature, deployer.OPCMV2DevFlag) { - cfg.DevFeatureBitmap = deployer.OPCMV2DevFlag + if devfeatures.IsDevFeatureEnabled(tt.devFeature, devfeatures.OPCMV2Flag) { + cfg.DevFeatureBitmap = devfeatures.OPCMV2Flag } runEndToEndBootstrapAndApplyUpgradeTest(t, afactsFS, cfg) @@ -364,7 +365,7 @@ func TestEndToEndApply(t *testing.T) { intent, st := shared.NewIntent(t, l1ChainID, dk, l2ChainID1, loc, loc, testCustomGasLimit) intent.GlobalDeployOverrides = map[string]any{ - "devFeatureBitmap": deployer.L2CMDevFlag, + "devFeatureBitmap": devfeatures.L2CMFlag, } require.NoError(t, deployer.ApplyPipeline(ctx, deployer.ApplyPipelineOpts{ @@ -402,7 +403,7 @@ func TestEndToEndApply(t *testing.T) { // Enable OPCMV2 dev flag intent.GlobalDeployOverrides = map[string]any{ - "devFeatureBitmap": deployer.OPCMV2DevFlag, + "devFeatureBitmap": devfeatures.OPCMV2Flag, } require.NoError(t, deployer.ApplyPipeline( @@ -432,12 +433,9 @@ func TestEndToEndApply(t *testing.T) { require.NotEmpty(t, opcmV2Code, "OPCMV2 should have code deployed") // Verify that the dev feature bitmap is set to OPCMV2 - require.Equal(t, deployer.OPCMV2DevFlag, intent.GlobalDeployOverrides["devFeatureBitmap"]) + require.Equal(t, devfeatures.OPCMV2Flag, intent.GlobalDeployOverrides["devFeatureBitmap"]) - // OpcmImpl is populated with the v2 address for downstream compat (v1 deleted) - require.Equal(t, st.ImplementationsDeployment.OpcmV2Impl, st.ImplementationsDeployment.OpcmImpl, "OpcmImpl should equal OpcmV2Impl") - // V1 sub-contract addresses are zero (v1 deleted, deprecated output fields) - require.Equal(t, common.Address{}, st.ImplementationsDeployment.OpcmContractsContainerImpl, "OPCM container implementation should be zero") + require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmV2Impl, "OpcmV2Impl should be set") require.Equal(t, common.Address{}, st.ImplementationsDeployment.OpcmGameTypeAdderImpl, "OPCM game type adder implementation should be zero") require.Equal(t, common.Address{}, st.ImplementationsDeployment.OpcmDeployerImpl, "OPCM deployer implementation should be zero") require.Equal(t, common.Address{}, st.ImplementationsDeployment.OpcmUpgraderImpl, "OPCM upgrader implementation should be zero") @@ -867,7 +865,7 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat require.NoError(t, err) opcmAddress := impls.Opcm - if deployer.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, deployer.OPCMV2DevFlag) { + if devfeatures.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, devfeatures.OPCMV2Flag) { opcmAddress = impls.OpcmV2 } @@ -896,7 +894,7 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat // Then run the OPCM upgrade t.Run("upgrade opcm", func(t *testing.T) { - if deployer.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, deployer.OPCMV2DevFlag) { + if devfeatures.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, devfeatures.OPCMV2Flag) { t.Skip("Skipping OPCM upgrade for OPCM V2") return } @@ -918,7 +916,7 @@ func runEndToEndBootstrapAndApplyUpgradeTest(t *testing.T, afactsFS foundry.Stat require.NoError(t, err, "OPCM upgrade should succeed") }) t.Run("upgrade opcm v2", func(t *testing.T) { - if !deployer.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, deployer.OPCMV2DevFlag) { + if !devfeatures.IsDevFeatureEnabled(implementationsConfig.DevFeatureBitmap, devfeatures.OPCMV2Flag) { t.Skip("Skipping OPCM V2 upgrade for non-OPCM V2 dev feature") return } @@ -1206,7 +1204,6 @@ func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, int implAddrs := []addrTuple{ {"DelayedWethImpl", st.ImplementationsDeployment.DelayedWethImpl}, {"OptimismPortalImpl", st.ImplementationsDeployment.OptimismPortalImpl}, - {"OptimismPortalInteropImpl", st.ImplementationsDeployment.OptimismPortalInteropImpl}, {"SystemConfigImpl", st.ImplementationsDeployment.SystemConfigImpl}, {"L1CrossDomainMessengerImpl", st.ImplementationsDeployment.L1CrossDomainMessengerImpl}, {"L1ERC721BridgeImpl", st.ImplementationsDeployment.L1Erc721BridgeImpl}, diff --git a/op-deployer/pkg/deployer/integration_test/cli/deploy_scripts_forge_test.go b/op-deployer/pkg/deployer/integration_test/cli/deploy_scripts_forge_test.go index 10be55f0c9a..196bad36de1 100644 --- a/op-deployer/pkg/deployer/integration_test/cli/deploy_scripts_forge_test.go +++ b/op-deployer/pkg/deployer/integration_test/cli/deploy_scripts_forge_test.go @@ -293,7 +293,6 @@ func TestDeployScriptsForge(t *testing.T) { // PrivateKey not required for read-only operations } output, err := opcm.ReadSuperchainDeploymentViaForge(forgeEnv, opcm.ReadSuperchainDeploymentInput{ - OpcmAddress: common.Address{}, // OPCM v2 flow - use SuperchainConfigProxy SuperchainConfigProxy: superchainOutput.SuperchainConfigProxy, }) require.NoError(t, err) @@ -303,10 +302,5 @@ func TestDeployScriptsForge(t *testing.T) { require.Equal(t, superchainOutput.SuperchainProxyAdmin, output.SuperchainProxyAdmin) require.NotEqual(t, common.Address{}, output.Guardian) require.NotEqual(t, common.Address{}, output.SuperchainProxyAdminOwner) - - // For OPCM v2, ProtocolVersions fields should be zero - require.Equal(t, common.Address{}, output.ProtocolVersionsProxy) - require.Equal(t, common.Address{}, output.ProtocolVersionsImpl) - require.Equal(t, common.Address{}, output.ProtocolVersionsOwner) }) } diff --git a/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go b/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go index 5de808c8a7f..8ddd7e89a6f 100644 --- a/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go +++ b/op-deployer/pkg/deployer/integration_test/cli/manage_add_game_type_v2_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/integration_test/shared" @@ -288,7 +289,7 @@ func deployDependencies(t *testing.T, runner *CLITestRunner) deployedChain { // Ensure we are using OPCM V2 intent.GlobalDeployOverrides = map[string]any{ - "devFeatureBitmap": deployer.OPCMV2DevFlag, + "devFeatureBitmap": devfeatures.OPCMV2Flag, } // Deploy using ApplyPipeline with live target diff --git a/op-deployer/pkg/deployer/integration_test/cli/migrate_test.go b/op-deployer/pkg/deployer/integration_test/cli/migrate_test.go index dbc973b6338..ee91d66c683 100644 --- a/op-deployer/pkg/deployer/integration_test/cli/migrate_test.go +++ b/op-deployer/pkg/deployer/integration_test/cli/migrate_test.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/addresses" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/bootstrap" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/integration_test/shared" @@ -141,7 +141,7 @@ func TestCLIMigrateV1(t *testing.T) { ChallengePeriodSeconds: standard.ChallengePeriodSeconds, ProofMaturityDelaySeconds: standard.ProofMaturityDelaySeconds, DisputeGameFinalityDelaySeconds: standard.DisputeGameFinalityDelaySeconds, - DevFeatureBitmap: deployer.EnableDevFeature(common.Hash{}, deployer.OptimismPortalInteropDevFlag), + DevFeatureBitmap: devfeatures.EnableDevFeature(common.Hash{}, devfeatures.OptimismPortalInteropFlag), SuperchainConfigProxy: superchainOut.SuperchainConfigProxy, ProtocolVersionsProxy: superchainOut.ProtocolVersionsProxy, SuperchainProxyAdmin: superchainOut.SuperchainProxyAdmin, @@ -221,7 +221,6 @@ func TestCLIMigrateV1(t *testing.T) { // Set implementations deployment addresses if st.ImplementationsDeployment == nil { st.ImplementationsDeployment = &addresses.ImplementationsContracts{ - OpcmImpl: impls.OpcmV2, // v1 deleted; populate with v2 for downstream compat OpcmV2Impl: impls.OpcmV2, OptimismPortalImpl: impls.OptimismPortalImpl, DelayedWethImpl: impls.DelayedWETHImpl, @@ -348,7 +347,7 @@ func TestCLIMigrateV2(t *testing.T) { }) require.NoError(t, err, "Failed to deploy superchain contracts") - devFeatureBitmap := deployer.EnableDevFeature(deployer.OPCMV2DevFlag, deployer.OptimismPortalInteropDevFlag) + devFeatureBitmap := devfeatures.EnableDevFeature(devfeatures.OPCMV2Flag, devfeatures.OptimismPortalInteropFlag) // Deploy OPCM V2 implementations (with OPCMV2DevFlag) cfg := bootstrap.ImplementationsConfig{ @@ -442,12 +441,10 @@ func TestCLIMigrateV2(t *testing.T) { // Set implementations deployment addresses if st.ImplementationsDeployment == nil { st.ImplementationsDeployment = &addresses.ImplementationsContracts{ - OpcmImpl: impls.OpcmV2, // v1 deleted; populate with v2 for downstream compat OpcmV2Impl: impls.OpcmV2, OpcmContainerImpl: impls.OpcmContainer, OpcmUtilsImpl: impls.OpcmUtils, OpcmMigratorImpl: impls.OpcmMigrator, - OptimismPortalInteropImpl: impls.OptimismPortalInteropImpl, OptimismPortalImpl: impls.OptimismPortalImpl, DelayedWethImpl: impls.DelayedWETHImpl, EthLockboxImpl: impls.ETHLockboxImpl, diff --git a/op-deployer/pkg/deployer/manage/migrate_test.go b/op-deployer/pkg/deployer/manage/migrate_test.go index f709ee9caa7..1e7c74c3027 100644 --- a/op-deployer/pkg/deployer/manage/migrate_test.go +++ b/op-deployer/pkg/deployer/manage/migrate_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/integration_test/shared" @@ -53,7 +54,7 @@ func TestInteropMigration(t *testing.T) { name string devFeature common.Hash }{ - {"opcm-v2", deployer.OPCMV2DevFlag}, + {"opcm-v2", devfeatures.OPCMV2Flag}, } for _, tt := range tests { @@ -62,7 +63,7 @@ func TestInteropMigration(t *testing.T) { intent, st := shared.NewIntent(t, l1ChainID, dk, l2ChainID, loc, loc, 30_000_000) // Set dev features for this test - devBitmap := deployer.EnableDevFeature(tt.devFeature, deployer.OptimismPortalInteropDevFlag) + devBitmap := devfeatures.EnableDevFeature(tt.devFeature, devfeatures.OptimismPortalInteropFlag) intent.GlobalDeployOverrides = map[string]any{ "devFeatureBitmap": devBitmap, } @@ -97,14 +98,14 @@ func TestInteropMigration(t *testing.T) { require.NoError(t, err) var opcmAddr common.Address - if deployer.IsDevFeatureEnabled(tt.devFeature, deployer.OPCMV2DevFlag) { + if devfeatures.IsDevFeatureEnabled(tt.devFeature, devfeatures.OPCMV2Flag) { require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmV2Impl, "OPCM V2 address should be set") opcmAddr = st.ImplementationsDeployment.OpcmV2Impl t.Logf("OPCM V2: %s", opcmAddr.Hex()) } else { - require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmImpl, "OPCM V1 address should be set") - opcmAddr = st.ImplementationsDeployment.OpcmImpl - t.Logf("OPCM V1: %s", opcmAddr.Hex()) + require.NotEqual(t, common.Address{}, st.ImplementationsDeployment.OpcmV2Impl, "OPCM V2 address should be set") + opcmAddr = st.ImplementationsDeployment.OpcmV2Impl + t.Logf("OPCM V2: %s", opcmAddr.Hex()) } // Deploy DummyCaller at l1ProxyAdminOwner for the OPCM @@ -123,7 +124,7 @@ func TestInteropMigration(t *testing.T) { var input InteropMigrationInput - if deployer.IsDevFeatureEnabled(tt.devFeature, deployer.OPCMV2DevFlag) { + if devfeatures.IsDevFeatureEnabled(tt.devFeature, devfeatures.OPCMV2Flag) { // OPCM V2 path // Note: No need to call upgradeChainV2 since ApplyPipeline already deploys a fully initialized chain diff --git a/op-deployer/pkg/deployer/opcm/implementations.go b/op-deployer/pkg/deployer/opcm/implementations.go index 3864104f0e5..f1b5a8609d9 100644 --- a/op-deployer/pkg/deployer/opcm/implementations.go +++ b/op-deployer/pkg/deployer/opcm/implementations.go @@ -42,7 +42,6 @@ type DeployImplementationsOutput struct { OpcmContainer common.Address `json:"opcmContainerAddress"` DelayedWETHImpl common.Address `json:"delayedWETHImplAddress"` OptimismPortalImpl common.Address `json:"optimismPortalImplAddress"` - OptimismPortalInteropImpl common.Address `json:"optimismPortalInteropImplAddress"` ETHLockboxImpl common.Address `json:"ethLockboxImplAddress" abi:"ethLockboxImpl"` PreimageOracleSingleton common.Address `json:"preimageOracleSingletonAddress"` MipsSingleton common.Address `json:"mipsSingletonAddress"` diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 89cef911c5f..7222261cd87 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -99,7 +99,6 @@ type ReadImplementationAddressesInput struct { type ReadImplementationAddressesOutput struct { DelayedWETH common.Address OptimismPortal common.Address - OptimismPortalInterop common.Address EthLockbox common.Address `evm:"ethLockbox"` SystemConfig common.Address AnchorStateRegistry common.Address diff --git a/op-deployer/pkg/deployer/opcm/read_superchain_deployment.go b/op-deployer/pkg/deployer/opcm/read_superchain_deployment.go index ae84e916b6a..6a56c8fb5d6 100644 --- a/op-deployer/pkg/deployer/opcm/read_superchain_deployment.go +++ b/op-deployer/pkg/deployer/opcm/read_superchain_deployment.go @@ -9,18 +9,10 @@ import ( ) type ReadSuperchainDeploymentInput struct { - OpcmAddress common.Address // TODO(#18612): Remove OpcmAddress field when OPCMv1 gets deprecated SuperchainConfigProxy common.Address } type ReadSuperchainDeploymentOutput struct { - // TODO(#18612): Remove ProtocolVersions fields when OPCMv1 gets deprecated - ProtocolVersionsImpl common.Address `abi:"protocolVersionsImpl"` - ProtocolVersionsProxy common.Address `abi:"protocolVersionsProxy"` - ProtocolVersionsOwner common.Address `abi:"protocolVersionsOwner"` - RecommendedProtocolVersion common.Hash `abi:"recommendedProtocolVersion"` - RequiredProtocolVersion common.Hash `abi:"requiredProtocolVersion"` - SuperchainConfigImpl common.Address `abi:"superchainConfigImpl"` SuperchainConfigProxy common.Address `abi:"superchainConfigProxy"` SuperchainProxyAdmin common.Address `abi:"superchainProxyAdmin"` diff --git a/op-deployer/pkg/deployer/pipeline/implementations.go b/op-deployer/pkg/deployer/pipeline/implementations.go index e04109c37de..e7a8072fd7c 100644 --- a/op-deployer/pkg/deployer/pipeline/implementations.go +++ b/op-deployer/pkg/deployer/pipeline/implementations.go @@ -83,7 +83,6 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro } st.ImplementationsDeployment = &addresses.ImplementationsContracts{ - OpcmImpl: dio.OpcmV2, // v1 deleted; populate with v2 for downstream compat OpcmGameTypeAdderImpl: dio.OpcmGameTypeAdder, OpcmDeployerImpl: dio.OpcmDeployer, OpcmUpgraderImpl: dio.OpcmUpgrader, @@ -93,7 +92,6 @@ func DeployImplementations(env *Env, intent *state.Intent, st *state.State) erro OpcmContainerImpl: dio.OpcmContainer, DelayedWethImpl: dio.DelayedWETHImpl, OptimismPortalImpl: dio.OptimismPortalImpl, - OptimismPortalInteropImpl: dio.OptimismPortalInteropImpl, EthLockboxImpl: dio.ETHLockboxImpl, PreimageOracleImpl: dio.PreimageOracleSingleton, MipsImpl: dio.MipsSingleton, diff --git a/op-deployer/pkg/deployer/pipeline/init.go b/op-deployer/pkg/deployer/pipeline/init.go index 326e541abe5..239220a9ccf 100644 --- a/op-deployer/pkg/deployer/pipeline/init.go +++ b/op-deployer/pkg/deployer/pipeline/init.go @@ -62,7 +62,6 @@ func InitLiveStrategy(ctx context.Context, env *Env, intent *state.Intent, st *s if hasPredeployedOPCM && st.ImplementationsDeployment == nil { st.ImplementationsDeployment = &addresses.ImplementationsContracts{ - OpcmImpl: opcmAddr, OpcmV2Impl: opcmAddr, } } @@ -143,11 +142,8 @@ func immutableErr(field string, was, is any) error { return fmt.Errorf("%s is immutable: was %v, is %v", field, was, is) } -// TODO(#18612): Remove OPCMAddress field when OPCMv1 gets deprecated -// TODO(#18612): Remove ProtocolVersions fields when OPCMv1 gets deprecated func PopulateSuperchainState(env *Env, opcmAddr common.Address, superchainConfigProxy common.Address) (*addresses.SuperchainContracts, *addresses.SuperchainRoles, error) { input := opcm.ReadSuperchainDeploymentInput{ - OpcmAddress: opcmAddr, SuperchainConfigProxy: superchainConfigProxy, } @@ -180,13 +176,10 @@ func PopulateSuperchainState(env *Env, opcmAddr common.Address, superchainConfig SuperchainProxyAdminImpl: out.SuperchainProxyAdmin, SuperchainConfigProxy: out.SuperchainConfigProxy, SuperchainConfigImpl: out.SuperchainConfigImpl, - ProtocolVersionsProxy: out.ProtocolVersionsProxy, - ProtocolVersionsImpl: out.ProtocolVersionsImpl, } roles := &addresses.SuperchainRoles{ SuperchainProxyAdminOwner: out.SuperchainProxyAdminOwner, SuperchainGuardian: out.Guardian, - ProtocolVersionsOwner: out.ProtocolVersionsOwner, } return deployment, roles, nil } diff --git a/op-deployer/pkg/deployer/pipeline/init_test.go b/op-deployer/pkg/deployer/pipeline/init_test.go index 40e291f8ce1..feeee5f215a 100644 --- a/op-deployer/pkg/deployer/pipeline/init_test.go +++ b/op-deployer/pkg/deployer/pipeline/init_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ethereum-optimism/optimism/op-chain-ops/addresses" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" @@ -129,7 +130,7 @@ func TestInitLiveStrategy_OPCMReuseLogicSepolia(t *testing.T) { require.NotNil(t, st.ImplementationsDeployment) require.NotNil(t, st.SuperchainRoles) require.Equal(t, *expDeployment, *st.SuperchainDeployment) - require.Equal(t, opcmAddr, st.ImplementationsDeployment.OpcmImpl) + require.Equal(t, opcmAddr, st.ImplementationsDeployment.OpcmV2Impl) // OPCMv1 removed — ProtocolVersionsOwner is no longer returned by the script. // Check the fields that are still populated. require.Equal(t, stdSuperchainRoles.SuperchainProxyAdminOwner, st.SuperchainRoles.SuperchainProxyAdminOwner) @@ -449,7 +450,7 @@ func TestInitLiveStrategy_OPCMV2WithSuperchainConfigProxy(t *testing.T) { require.NoError(t, err) // Set opcmV2Enabled flag via devFeatureBitmap - opcmV2Flag := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000010000") + opcmV2Flag := devfeatures.OPCMV2Flag intent := &state.Intent{ ConfigType: state.IntentTypeStandard, L1ChainID: l1ChainID, @@ -519,7 +520,7 @@ func TestInitLiveStrategy_OPCMV2WithSuperchainConfigProxyAndRoles_reverts(t *tes require.NoError(t, err) // Set opcmV2Enabled flag via devFeatureBitmap - opcmV2Flag := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000010000") + opcmV2Flag := devfeatures.OPCMV2Flag intent := &state.Intent{ ConfigType: state.IntentTypeStandard, L1ChainID: l1ChainID, @@ -747,7 +748,7 @@ func TestInitLiveStrategy_FlowSelection_OPCMV1(t *testing.T) { // Verify ImplementationsDeployment was set require.NotNil(t, st.ImplementationsDeployment) - require.Equal(t, opcmAddr, st.ImplementationsDeployment.OpcmImpl) + require.Equal(t, opcmAddr, st.ImplementationsDeployment.OpcmV2Impl) } // Validates that the correct flow is chosen when @@ -788,7 +789,7 @@ func TestInitLiveStrategy_FlowSelection_OPCMV2(t *testing.T) { require.NoError(t, err) // Set opcmV2Enabled flag via devFeatureBitmap - opcmV2Flag := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000010000") + opcmV2Flag := devfeatures.OPCMV2Flag intent := &state.Intent{ ConfigType: state.IntentTypeStandard, L1ChainID: l1ChainID, diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis.go b/op-deployer/pkg/deployer/pipeline/l2genesis.go index 851268084d6..90f75084558 100644 --- a/op-deployer/pkg/deployer/pipeline/l2genesis.go +++ b/op-deployer/pkg/deployer/pipeline/l2genesis.go @@ -5,6 +5,7 @@ import ( "math/big" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" @@ -214,9 +215,7 @@ func buildDevFeatureBitmap(intent *state.Intent) (common.Hash, error) { devFeatureBitmap = common.HexToHash(v) } - // TODO(#19151): Replace the hex literal with deployer.OptimismPortalInteropDevFlag when import cycles are fixed. - interopFeatureFlag := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") - interopBitEnabled := isDevFeatureEnabled(devFeatureBitmap, interopFeatureFlag) + interopBitEnabled := devfeatures.IsDevFeatureEnabled(devFeatureBitmap, devfeatures.OptimismPortalInteropFlag) if intent.UseInterop != interopBitEnabled { return common.Hash{}, fmt.Errorf("interop feature in devFeatureBitmap does not match the UseInterop intent flag") diff --git a/op-deployer/pkg/deployer/pipeline/l2genesis_test.go b/op-deployer/pkg/deployer/pipeline/l2genesis_test.go index 9603c7b9f5c..8c22ae03029 100644 --- a/op-deployer/pkg/deployer/pipeline/l2genesis_test.go +++ b/op-deployer/pkg/deployer/pipeline/l2genesis_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" @@ -15,8 +16,7 @@ import ( ) func TestBuildDevFeatureBitmap(t *testing.T) { - // TODO(#19151): Replace the hex literal with deployer.OptimismPortalInteropDevFlag when import cycles are fixed. - interopBit := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") + interopBit := devfeatures.OptimismPortalInteropFlag tests := []struct { name string diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 9828bda5e1b..c4b5d24d54a 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -92,7 +92,6 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm st.ImplementationsDeployment.DelayedWethImpl = impls.DelayedWETH st.ImplementationsDeployment.OptimismPortalImpl = impls.OptimismPortal - st.ImplementationsDeployment.OptimismPortalInteropImpl = impls.OptimismPortalInterop st.ImplementationsDeployment.EthLockboxImpl = impls.EthLockbox st.ImplementationsDeployment.SystemConfigImpl = impls.SystemConfig st.ImplementationsDeployment.AnchorStateRegistryImpl = impls.AnchorStateRegistry @@ -203,15 +202,3 @@ func shouldDeployOPChain(st *state.State, chainID common.Hash) bool { return true } - -// TODO(#19151): Remove this function when we fix import cycles. -// isDevFeatureEnabled checks if a specific development feature is enabled in a feature bitmap. -// This mirrors the function in devfeatures.go to avoid import cycles. -func isDevFeatureEnabled(bitmap, flag common.Hash) bool { - b := new(big.Int).SetBytes(bitmap[:]) - f := new(big.Int).SetBytes(flag[:]) - - featuresIsNonZero := f.Cmp(big.NewInt(0)) != 0 - bitmapContainsFeatures := new(big.Int).And(b, f).Cmp(f) == 0 - return featuresIsNonZero && bitmapContainsFeatures -} diff --git a/op-devstack/dsl/bridge.go b/op-devstack/dsl/bridge.go index b1bd6a88596..7f6aa80c317 100644 --- a/op-devstack/dsl/bridge.go +++ b/op-devstack/dsl/bridge.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "strings" "time" "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" @@ -129,15 +128,11 @@ func (b *StandardBridge) PortalVersion() string { } func (b *StandardBridge) UsesSuperRoots() bool { - // Only interop contracts have SuperRootsActive functionality - version := b.PortalVersion() - if !strings.HasSuffix(version, "+interop") { - return false - } - - superRootsActive, err := contractio.Read(b.l1Portal.SuperRootsActive(), b.ctx) - b.require.NoError(err, "Failed to read super roots active") - return superRootsActive + gameType := gameTypes.GameType(b.RespectedGameType()) + return gameType == gameTypes.SuperCannonGameType || + gameType == gameTypes.SuperPermissionedGameType || + gameType == gameTypes.SuperAsteriscKonaGameType || + gameType == gameTypes.SuperCannonKonaGameType } type Deposit struct { diff --git a/op-devstack/dsl/eoa.go b/op-devstack/dsl/eoa.go index fea2835b17a..ef7d3286b5b 100644 --- a/op-devstack/dsl/eoa.go +++ b/op-devstack/dsl/eoa.go @@ -337,6 +337,21 @@ func (u *EOA) SendExecMessage(initMsg *InitMessage, opts ...ExecMessageOpt) *Exe } } +// PrepareExecTx builds and signs an executing-message transaction referencing +// the given init message, but does NOT submit it. Returns the raw signed +// transaction bytes and tx hash. The raw bytes are suitable for injection via +// TestSequencer.SequenceBlockWithTxs, bypassing mempool filtering. +func (u *EOA) PrepareExecTx(initMsg *InitMessage) (rawTx []byte, txHash common.Hash) { + tx := txintent.NewIntent[*txintent.ExecTrigger, *txintent.InteropOutput](u.Plan()) + tx.Content.DependOn(&initMsg.Tx.Result) + tx.Content.Fn(txintent.ExecuteIndexed(predeploys.CrossL2InboxAddr, &initMsg.Tx.Result, 0)) + signedTx, err := tx.PlannedTx.Signed.Eval(u.ctx) + u.require.NoError(err, "failed to sign exec tx") + rawBytes, err := signedTx.MarshalBinary() + u.require.NoError(err, "failed to marshal exec tx") + return rawBytes, signedTx.Hash() +} + // SendInvalidExecMessage sends an executing message with an invalid identifier. // The log index is incremented to reference a non-existent log. func (u *EOA) SendInvalidExecMessage(initMsg *InitMessage) *ExecMessage { diff --git a/op-devstack/dsl/l2_el.go b/op-devstack/dsl/l2_el.go index b3417065f63..16adfa359be 100644 --- a/op-devstack/dsl/l2_el.go +++ b/op-devstack/dsl/l2_el.go @@ -263,33 +263,87 @@ func (el *L2ELNode) WaitL1OriginHash(label eth.BlockLabel, target eth.BlockID, a })) } -// VerifyWithdrawalHashChangedIn verifies that the withdrawal hash changed between the parent and current block -// This is used to verify that the withdrawal hash changed in the block where the withdrawal was initiated +// VerifyWithdrawalHashChangedIn verifies that the withdrawal hash changed between the parent and current block. +// This is used to verify that the withdrawal hash changed in the block where the withdrawal was initiated. +// +// Some EL backends, such as op-reth, can briefly lag in serving historical proofs for a block that has +// already been inserted. Retry until the proof backend catches up instead of failing immediately. func (el *L2ELNode) VerifyWithdrawalHashChangedIn(blockHash common.Hash) { l2Client := el.inner.L2EthClient() - postBlockWithdrawalInfo, err := l2Client.InfoByHash(el.ctx, blockHash) - el.require.NoError(err, "failed to get post-withdrawal block info") + el.require.Eventually(func() bool { + postBlockWithdrawalInfo, err := l2Client.InfoByHash(el.ctx, blockHash) + if err != nil { + el.log.Debug("Waiting for post-withdrawal block info", "blockHash", blockHash, "err", err) + return false + } + if postBlockWithdrawalInfo.WithdrawalsRoot() == nil { + err = fmt.Errorf("post-withdrawal block %s has no withdrawals root", blockHash) + el.log.Debug("Waiting for post-withdrawal withdrawals root", "blockHash", blockHash, "err", err) + return false + } - parentBlockInfo, err := l2Client.InfoByHash(el.ctx, postBlockWithdrawalInfo.ParentHash()) - el.require.NoError(err, "failed to get parent block info") + parentBlockInfo, err := l2Client.InfoByHash(el.ctx, postBlockWithdrawalInfo.ParentHash()) + if err != nil { + el.log.Debug("Waiting for parent block info", "blockHash", postBlockWithdrawalInfo.ParentHash(), "err", err) + return false + } + if parentBlockInfo.WithdrawalsRoot() == nil { + err = fmt.Errorf("parent block %s has no withdrawals root", postBlockWithdrawalInfo.ParentHash()) + el.log.Debug("Waiting for parent withdrawals root", "blockHash", postBlockWithdrawalInfo.ParentHash(), "err", err) + return false + } - postProof, err := l2Client.GetProof(el.ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, blockHash.String()) - el.require.NoError(err, "failed to get post-withdrawal storage proof") + postProof, err := l2Client.GetProof(el.ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, blockHash.String()) + if err != nil { + el.log.Debug("Waiting for post-withdrawal storage proof", "blockHash", blockHash, "err", err) + return false + } - parentProof, err := l2Client.GetProof(el.ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, postBlockWithdrawalInfo.ParentHash().String()) - el.require.NoError(err, "failed to get parent storage proof") + parentProof, err := l2Client.GetProof(el.ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, postBlockWithdrawalInfo.ParentHash().String()) + if err != nil { + el.log.Debug("Waiting for parent storage proof", "blockHash", postBlockWithdrawalInfo.ParentHash(), "err", err) + return false + } + + if parentProof.StorageHash == postProof.StorageHash { + err = fmt.Errorf("withdrawal hash did not change between parent %s and current %s", postBlockWithdrawalInfo.ParentHash(), blockHash) + el.log.Debug("Waiting for withdrawal hash change", + "parentBlock", postBlockWithdrawalInfo.ParentHash(), + "currentBlock", blockHash, + "storageRoot", postProof.StorageHash, + "err", err) + return false + } - el.require.NotEqual(parentProof.StorageHash, postProof.StorageHash, "withdrawal hash should have changed between parent and current block") + if postProof.StorageHash != *postBlockWithdrawalInfo.WithdrawalsRoot() { + err = fmt.Errorf("post-withdrawal storage root mismatch: proof=%s header=%s", postProof.StorageHash, *postBlockWithdrawalInfo.WithdrawalsRoot()) + el.log.Debug("Waiting for post-withdrawal storage root to match header", + "blockHash", blockHash, + "proofStorageRoot", postProof.StorageHash, + "headerWithdrawalsRoot", *postBlockWithdrawalInfo.WithdrawalsRoot(), + "err", err) + return false + } + + if parentProof.StorageHash != *parentBlockInfo.WithdrawalsRoot() { + err = fmt.Errorf("parent storage root mismatch: proof=%s header=%s", parentProof.StorageHash, *parentBlockInfo.WithdrawalsRoot()) + el.log.Debug("Waiting for parent storage root to match header", + "blockHash", postBlockWithdrawalInfo.ParentHash(), + "proofStorageRoot", parentProof.StorageHash, + "headerWithdrawalsRoot", *parentBlockInfo.WithdrawalsRoot(), + "err", err) + return false + } - el.require.Equal(postProof.StorageHash, *postBlockWithdrawalInfo.WithdrawalsRoot(), "post-withdrawal storage root should match block header withdrawal root") - el.require.Equal(parentProof.StorageHash, *parentBlockInfo.WithdrawalsRoot(), "parent storage root should match block header withdrawal root") + el.log.Info("Withdrawal hash verification successful", + "parentBlock", postBlockWithdrawalInfo.ParentHash(), + "currentBlock", blockHash, + "parentStorageRoot", parentProof.StorageHash, + "currentStorageRoot", postProof.StorageHash) - el.log.Info("Withdrawal hash verification successful", - "parentBlock", postBlockWithdrawalInfo.ParentHash(), - "currentBlock", blockHash, - "parentStorageRoot", parentProof.StorageHash, - "currentStorageRoot", postProof.StorageHash) + return true + }, 30*time.Second, 200*time.Millisecond, "withdrawal proof data did not become available in time") } func (el *L2ELNode) Stop() { diff --git a/op-devstack/presets/option_validation.go b/op-devstack/presets/option_validation.go index 2c62760eb48..b8effeb6392 100644 --- a/op-devstack/presets/option_validation.go +++ b/op-devstack/presets/option_validation.go @@ -26,6 +26,7 @@ const ( optionKindRequireInteropNotAtGen optionKindAfterBuild optionKindProofValidation + optionKindMessageExpiryWindow ) const allOptionKinds = optionKindDeployer | @@ -42,7 +43,8 @@ const allOptionKinds = optionKindDeployer | optionKindMaxSequencingWindow | optionKindRequireInteropNotAtGen | optionKindAfterBuild | - optionKindProofValidation + optionKindProofValidation | + optionKindMessageExpiryWindow var optionKindLabels = []struct { kind optionKinds @@ -63,6 +65,7 @@ var optionKindLabels = []struct { {kind: optionKindRequireInteropNotAtGen, label: "interop-not-at-genesis"}, {kind: optionKindAfterBuild, label: "after-build hooks"}, {kind: optionKindProofValidation, label: "proof-validation hooks"}, + {kind: optionKindMessageExpiryWindow, label: "message expiry window"}, } func (k optionKinds) String() string { @@ -150,7 +153,8 @@ const simpleInteropSuperProofsPresetSupportedOptionKinds = optionKindDeployer | const supernodeProofsPresetSupportedOptionKinds = optionKindDeployer | optionKindBatcher | optionKindChallengerCannonKona | - optionKindL1EL + optionKindL1EL | + optionKindMessageExpiryWindow const twoL2SupernodePresetSupportedOptionKinds = optionKindDeployer | optionKindL1EL diff --git a/op-devstack/presets/options.go b/op-devstack/presets/options.go index 37dceaaacac..2ccb911c77b 100644 --- a/op-devstack/presets/options.go +++ b/op-devstack/presets/options.go @@ -275,6 +275,19 @@ func WithRequireInteropNotAtGenesis() Option { } } +// WithMessageExpiryWindow configures the message expiry window (in seconds) +// used by the dependency set. This controls how long cross-chain messages +// remain valid before they expire. +func WithMessageExpiryWindow(window uint64) Option { + return option{ + kinds: optionKindMessageExpiryWindow, + applyFn: func(cfg *sysgo.PresetConfig) { + v := window + cfg.MessageExpiryWindow = &v + }, + } +} + // WithL2BlockTimes configures per-chain L2 block times via the deployer. // The blockTimes map keys are L2 chain IDs and values are the desired block // time in seconds for that chain. diff --git a/op-devstack/shared/challenger/challenger.go b/op-devstack/shared/challenger/challenger.go index 42bfafd221f..2d0c74f6d09 100644 --- a/op-devstack/shared/challenger/challenger.go +++ b/op-devstack/shared/challenger/challenger.go @@ -1,6 +1,7 @@ package challenger import ( + "context" "crypto/ecdsa" "encoding/json" "errors" @@ -12,11 +13,13 @@ import ( "github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" + "github.com/ethereum-optimism/optimism/op-devstack/shared/rustbin" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/crypto" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/log" ) type PrestateVariant string @@ -28,10 +31,10 @@ const ( InteropVariantNext PrestateVariant = "interopNext" ) -type Option func(cfg *config.Config) error +type Option func(ctx context.Context, cfg *config.Config) error func WithDepset(ds *depset.StaticConfigDependencySet) Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { b, err := ds.MarshalJSON() if err != nil { return fmt.Errorf("failed to marshal dependency set config: %w", err) @@ -48,7 +51,7 @@ func WithDepset(ds *depset.StaticConfigDependencySet) Option { } func WithPrivKey(key *ecdsa.PrivateKey) Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.TxMgrConfig.PrivateKey = crypto.EncodePrivKeyToString(key) return nil } @@ -71,7 +74,7 @@ func applyCannonConfig(c *config.Config, rollupCfgs []*rollup.Config, l1Genesis return nil } -func applyCannonKonaConfig(c *config.Config, rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis, interop bool) error { +func applyCannonKonaConfig(ctx context.Context, c *config.Config, rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis, interop bool) error { root, err := findMonorepoRoot() if err != nil { return err @@ -79,7 +82,15 @@ func applyCannonKonaConfig(c *config.Config, rollupCfgs []*rollup.Config, l1Gene if err := applyVmConfig(root, &c.CannonKona, c.Datadir, rollupCfgs, l1Genesis, l2Geneses); err != nil { return err } - c.CannonKona.Server = root + "rust/target/release/kona-host" + konaHostBin, err := rustbin.Spec{ + SrcDir: "rust/kona", + Package: "kona-host", + Binary: "kona-host", + }.EnsureExists(ctx, log.NewLogger(log.DiscardHandler())) + if err != nil { + return fmt.Errorf("kona-host binary: %w", err) + } + c.CannonKona.Server = konaHostBin if interop { c.CannonKonaAbsolutePreState = root + "rust/kona/prestate-artifacts-cannon-interop/prestate.bin.gz" } else { @@ -132,74 +143,74 @@ func applyVmConfig(root string, c *vm.Config, dataDir string, rollupCfgs []*roll } func WithFactoryAddress(addr common.Address) Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameFactoryAddress = addr return nil } } func WithCannonConfig(rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis, prestateVariant PrestateVariant) Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { return applyCannonConfig(c, rollupCfgs, l1Genesis, l2Geneses, prestateVariant) } } func WithCannonKonaConfig(rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis) Option { - return func(c *config.Config) error { - return applyCannonKonaConfig(c, rollupCfgs, l1Genesis, l2Geneses, false) + return func(ctx context.Context, c *config.Config) error { + return applyCannonKonaConfig(ctx, c, rollupCfgs, l1Genesis, l2Geneses, false) } } func WithCannonKonaInteropConfig(rollupCfgs []*rollup.Config, l1Genesis *core.Genesis, l2Geneses []*core.Genesis) Option { - return func(c *config.Config) error { - return applyCannonKonaConfig(c, rollupCfgs, l1Genesis, l2Geneses, true) + return func(ctx context.Context, c *config.Config) error { + return applyCannonKonaConfig(ctx, c, rollupCfgs, l1Genesis, l2Geneses, true) } } func WithCannonGameType() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.CannonGameType) return nil } } func WithCannonKonaGameType() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.CannonKonaGameType) return nil } } func WithPermissionedGameType() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.PermissionedGameType) return nil } } func WithSuperCannonGameType() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.SuperCannonGameType) return nil } } func WithSuperCannonKonaGameType() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.SuperCannonKonaGameType) return nil } } func WithSuperPermissionedGameType() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.SuperPermissionedGameType) return nil } } func WithFastGames() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.GameTypes = append(c.GameTypes, gameTypes.FastGameType) return nil } @@ -210,29 +221,29 @@ func WithFastGames() Option { // time by avoiding full block re-derivation and re-execution. // Requires op-reth or execution client with debug_executePayload support. func WithExperimentalWitnessEndpoint() Option { - return func(c *config.Config) error { + return func(_ context.Context, c *config.Config) error { c.CannonKona.EnableExperimentalWitnessEndpoint = true return nil } } -func NewInteropChallengerConfig(dir string, l1Endpoint string, l1Beacon string, supervisorEndpoint string, l2Endpoints []string, options ...Option) (*config.Config, error) { +func NewInteropChallengerConfig(ctx context.Context, dir string, l1Endpoint string, l1Beacon string, supervisorEndpoint string, l2Endpoints []string, options ...Option) (*config.Config, error) { cfg := config.NewInteropConfig(common.Address{}, l1Endpoint, l1Beacon, supervisorEndpoint, l2Endpoints, dir) - if err := applyCommonChallengerOpts(&cfg, options...); err != nil { + if err := applyCommonChallengerOpts(ctx, &cfg, options...); err != nil { return nil, err } return &cfg, nil } -func NewPreInteropChallengerConfig(dir string, l1Endpoint string, l1Beacon string, rollupEndpoint string, l2Endpoint string, options ...Option) (*config.Config, error) { +func NewPreInteropChallengerConfig(ctx context.Context, dir string, l1Endpoint string, l1Beacon string, rollupEndpoint string, l2Endpoint string, options ...Option) (*config.Config, error) { cfg := config.NewConfig(common.Address{}, l1Endpoint, l1Beacon, rollupEndpoint, l2Endpoint, dir) - if err := applyCommonChallengerOpts(&cfg, options...); err != nil { + if err := applyCommonChallengerOpts(ctx, &cfg, options...); err != nil { return nil, err } return &cfg, nil } -func applyCommonChallengerOpts(cfg *config.Config, options ...Option) error { +func applyCommonChallengerOpts(ctx context.Context, cfg *config.Config, options ...Option) error { cfg.Cannon.L2Custom = true cfg.CannonKona.L2Custom = true // The devnet can't set the absolute prestate output root because the contracts are deployed in L1 genesis @@ -247,7 +258,7 @@ func applyCommonChallengerOpts(cfg *config.Config, options ...Option) error { cfg.MetricsConfig.Enabled = false cfg.PollInterval = time.Second for _, option := range options { - if err := option(cfg); err != nil { + if err := option(ctx, cfg); err != nil { return err } } diff --git a/op-devstack/sysgo/rust_binary.go b/op-devstack/shared/rustbin/rust_binary.go similarity index 76% rename from op-devstack/sysgo/rust_binary.go rename to op-devstack/shared/rustbin/rust_binary.go index 7546ecd1db2..ca625ebc375 100644 --- a/op-devstack/sysgo/rust_binary.go +++ b/op-devstack/shared/rustbin/rust_binary.go @@ -1,4 +1,4 @@ -package sysgo +package rustbin import ( "context" @@ -10,40 +10,41 @@ import ( "sort" "strings" - "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum/go-ethereum/log" + opservice "github.com/ethereum-optimism/optimism/op-service" ) -// RustBinarySpec describes a Rust binary to be built and located. -type RustBinarySpec struct { +// Spec describes a Rust binary to be built and located. +type Spec struct { SrcDir string // directory name relative to monorepo root, e.g. "rollup-boost" Package string // cargo package name, e.g. "rollup-boost" Binary string // binary name, e.g. "rollup-boost" } -// EnsureRustBinary locates or builds a Rust binary as needed. +// EnsureExists locates or builds a Rust binary as needed. // // Env var overrides (suffix derived from binary name, e.g. "rollup-boost" -> "ROLLUP_BOOST"): // - RUST_BINARY_PATH_: absolute path to pre-built binary (skips build, must exist) // - RUST_SRC_DIR_: overrides SrcDir (absolute path to cargo project root) // // Build behavior: -// - RUST_JIT_BUILD=1: runs cargo build --release (letting cargo handle rebuild detection) +// - RUST_JIT_BUILD=1: runs cargo build in debug mode (letting cargo handle rebuild detection) // - Otherwise: only checks binary exists, errors if missing -func EnsureRustBinary(p devtest.CommonT, spec RustBinarySpec) (string, error) { - envSuffix := toEnvVarSuffix(spec.Binary) +func (s Spec) EnsureExists(ctx context.Context, logger log.Logger) (string, error) { + envSuffix := toEnvVarSuffix(s.Binary) // Check for explicit binary path override if pathOverride := os.Getenv("RUST_BINARY_PATH_" + envSuffix); pathOverride != "" { if _, err := os.Stat(pathOverride); os.IsNotExist(err) { - return "", fmt.Errorf("%s binary not found at overridden path %s", spec.Binary, pathOverride) + return "", fmt.Errorf("%s binary not found at overridden path %s", s.Binary, pathOverride) } - p.Logger().Info("Using overridden binary path", "binary", spec.Binary, "path", pathOverride) + logger.Info("Using overridden binary path", "binary", s.Binary, "path", pathOverride) return pathOverride, nil } // Determine source root - srcRoot, err := resolveSrcRoot(spec.SrcDir, envSuffix) + srcRoot, err := resolveSrcRoot(s.SrcDir, envSuffix) if err != nil { return "", err } @@ -51,15 +52,16 @@ func EnsureRustBinary(p devtest.CommonT, spec RustBinarySpec) (string, error) { jitBuild := os.Getenv("RUST_JIT_BUILD") != "" if jitBuild { - p.Logger().Info("Building Rust binary (JIT)", "binary", spec.Binary, "dir", srcRoot) - if err := buildRustBinary(p.Ctx(), srcRoot, spec.Package, spec.Binary); err != nil { + logger.Info("Building Rust binary (JIT)", "binary", s.Binary, "dir", srcRoot) + if err := buildRustBinary(ctx, srcRoot, s.Package, s.Binary); err != nil { return "", err } } - binaryPath, err := resolveBuiltRustBinaryPath(srcRoot, spec.Binary) + binaryPath, err := resolveBuiltRustBinaryPath(srcRoot, s.Binary) if err != nil { - return "", fmt.Errorf("%s binary not found; run 'just build-rust-debug' before the test or set RUST_JIT_BUILD=1: %w", spec.Binary, err) + return "", fmt.Errorf("%s binary not found; run 'cd %s && just build-%s-debug' (or just build-%s for release) or set RUST_JIT_BUILD=1: %w", + s.Binary, s.SrcDir, s.Binary, s.Binary, err) } return binaryPath, nil } @@ -88,7 +90,7 @@ func toEnvVarSuffix(binary string) string { } func buildRustBinary(ctx context.Context, root, pkg, bin string) error { - cmd := exec.CommandContext(ctx, "cargo", "build", "--release", "-p", pkg, "--bin", bin) + cmd := exec.CommandContext(ctx, "cargo", "build", "-p", pkg, "--bin", bin) cmd.Dir = root cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -107,6 +109,7 @@ func resolveBuiltRustBinaryPath(srcRoot, binary string) (string, error) { candidates := []string{ filepath.Join(targetDir, "release", binary), + filepath.Join(targetDir, "debug", binary), } globMatches, err := filepath.Glob(filepath.Join(targetDir, "*", "release", binary)) if err == nil { diff --git a/op-devstack/sysgo/deployer.go b/op-devstack/sysgo/deployer.go index b883d759afc..908927006d5 100644 --- a/op-devstack/sysgo/deployer.go +++ b/op-devstack/sysgo/deployer.go @@ -7,6 +7,7 @@ import ( "time" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" opforks "github.com/ethereum-optimism/optimism/op-core/forks" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" @@ -59,6 +60,14 @@ func WithDefaultBPOBlobSchedule(_ devtest.T, _ devkeys.Keys, builder intentbuild }) } +func WithKarstAtOffset(offset *uint64) DeployerOption { + return func(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) { + for _, l2Cfg := range builder.L2s() { + l2Cfg.WithForkAtOffset(opforks.Karst, offset) + } + } +} + func WithJovianAtGenesis(p devtest.T, _ devkeys.Keys, builder intentbuilder.Builder) { for _, l2Cfg := range builder.L2s() { l2Cfg.WithForkAtGenesis(opforks.Jovian) @@ -260,8 +269,8 @@ func WithDevFeatureEnabled(flag common.Hash) DeployerOption { if currentValue != nil { bitmap = currentValue.(common.Hash) } - builder.WithGlobalOverride(devFeatureBitmapKey, deployer.EnableDevFeature(bitmap, flag)) - if flag == deployer.OptimismPortalInteropDevFlag { + builder.WithGlobalOverride(devFeatureBitmapKey, devfeatures.EnableDevFeature(bitmap, flag)) + if flag == devfeatures.OptimismPortalInteropFlag { builder.WithUseInterop(true) } } diff --git a/op-devstack/sysgo/engine_client.go b/op-devstack/sysgo/engine_client.go index 325acb7cf29..be49f998ea1 100644 --- a/op-devstack/sysgo/engine_client.go +++ b/op-devstack/sysgo/engine_client.go @@ -28,20 +28,20 @@ func dialEngine(ctx context.Context, endpoint string, jwtSecret [32]byte) (*engi var _ geth.EngineAPI = (*engineClient)(nil) -func (e *engineClient) forkchoiceUpdated(fs engine.ForkchoiceStateV1, pa *engine.PayloadAttributes, method string) (engine.ForkChoiceResponse, error) { +func (e *engineClient) forkchoiceUpdated(ctx context.Context, fs engine.ForkchoiceStateV1, pa *engine.PayloadAttributes, method string) (engine.ForkChoiceResponse, error) { var x engine.ForkChoiceResponse - if err := e.inner.CallContext(context.Background(), &x, method, fs, pa); err != nil { + if err := e.inner.CallContext(ctx, &x, method, fs, pa); err != nil { return engine.ForkChoiceResponse{}, err } return x, nil } -func (e *engineClient) ForkchoiceUpdatedV2(fs engine.ForkchoiceStateV1, pa *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { - return e.forkchoiceUpdated(fs, pa, "engine_forkchoiceUpdatedV2") +func (e *engineClient) ForkchoiceUpdatedV2(ctx context.Context, fs engine.ForkchoiceStateV1, pa *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { + return e.forkchoiceUpdated(ctx, fs, pa, "engine_forkchoiceUpdatedV2") } -func (e *engineClient) ForkchoiceUpdatedV3(fs engine.ForkchoiceStateV1, pa *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { - return e.forkchoiceUpdated(fs, pa, "engine_forkchoiceUpdatedV3") +func (e *engineClient) ForkchoiceUpdatedV3(ctx context.Context, fs engine.ForkchoiceStateV1, pa *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { + return e.forkchoiceUpdated(ctx, fs, pa, "engine_forkchoiceUpdatedV3") } func (e *engineClient) getPayload(id engine.PayloadID, method string) (*engine.ExecutionPayloadEnvelope, error) { diff --git a/op-devstack/sysgo/l2_cl.go b/op-devstack/sysgo/l2_cl.go index 009e55d85c3..8945523c9ed 100644 --- a/op-devstack/sysgo/l2_cl.go +++ b/op-devstack/sysgo/l2_cl.go @@ -1,6 +1,8 @@ package sysgo import ( + "time" + "github.com/ethereum-optimism/optimism/op-devstack/devtest" "github.com/ethereum-optimism/optimism/op-devstack/stack" nodeSync "github.com/ethereum-optimism/optimism/op-node/rollup/sync" @@ -35,6 +37,9 @@ type L2CLConfig struct { NoDiscovery bool FollowSource string + + // OffsetELSafe retracts safe and finalized from the EL-sync tip by floor(OffsetELSafe / L2BlockTime) blocks. + OffsetELSafe time.Duration } func L2CLSequencer() L2CLOption { @@ -63,7 +68,7 @@ func DefaultL2CLConfig() *L2CLConfig { IsSequencer: false, IndexingMode: false, EnableReqRespSync: true, - UseReqRespSync: true, + UseReqRespSync: false, NoDiscovery: false, FollowSource: "", } diff --git a/op-devstack/sysgo/mixed_runtime.go b/op-devstack/sysgo/mixed_runtime.go index 614cbdb0fd0..3acabc582f3 100644 --- a/op-devstack/sysgo/mixed_runtime.go +++ b/op-devstack/sysgo/mixed_runtime.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/shared/rustbin" "github.com/ethereum-optimism/optimism/op-faucet/faucet" "github.com/ethereum-optimism/optimism/op-service/endpoint" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -43,8 +44,9 @@ import ( type MixedL2ELKind string const ( - MixedL2ELOpGeth MixedL2ELKind = "op-geth" - MixedL2ELOpReth MixedL2ELKind = "op-reth" + MixedL2ELOpGeth MixedL2ELKind = "op-geth" + MixedL2ELOpReth MixedL2ELKind = "op-reth-proof-v1" + MixedL2ELOpRethV2 MixedL2ELKind = "op-reth-proof-v2" ) type MixedL2CLKind string @@ -54,6 +56,13 @@ const ( MixedL2CLKona MixedL2CLKind = "kona-node" ) +// SkipOnOpGeth skips the test when the L2 execution layer is op-geth +func SkipOnOpGeth(t devtest.T, reason string) { + if devstackL2ELKind() == MixedL2ELOpGeth { + t.Skipf("skipping on op-geth: %s", reason) + } +} + // SkipOnOpReth skips the test when the L2 execution layer is op-reth func SkipOnOpReth(t devtest.T, reason string) { if devstackL2ELKind() == MixedL2ELOpReth { @@ -160,7 +169,9 @@ func NewMixedSingleChainRuntime(t devtest.T, cfg MixedSingleChainPresetConfig) * case MixedL2ELOpGeth: el = startL2ELNode(t, l2Net, jwtPath, jwtSecret, spec.ELKey, identity) case MixedL2ELOpReth: - el = startMixedOpRethNode(t, l2Net, spec.ELKey, jwtPath, jwtSecret, metricsRegistrar) + el = startMixedOpRethNode(t, l2Net, spec.ELKey, jwtPath, jwtSecret, metricsRegistrar, "v1") + case MixedL2ELOpRethV2: + el = startMixedOpRethNode(t, l2Net, spec.ELKey, jwtPath, jwtSecret, metricsRegistrar, "v2") default: require.FailNowf("unsupported EL kind", "unsupported mixed EL kind %q", spec.ELKind) } @@ -271,6 +282,7 @@ func startMixedOpRethNode( jwtPath string, jwtSecret [32]byte, metricsRegistrar L2MetricsRegistrar, + storageVersion string, ) *OpReth { tempDir := t.TempDir() @@ -287,11 +299,11 @@ func startMixedOpRethNode( tempP2PPath := filepath.Join(tempDir, "p2pkey.txt") - execPath, err := EnsureRustBinary(t, RustBinarySpec{ + execPath, err := rustbin.Spec{ SrcDir: "rust", Package: "op-reth", Binary: "op-reth", - }) + }.EnsureExists(t.Ctx(), t.Logger()) t.Require().NoError(err, "op-reth binary not available (build with 'just build-rust-release' or set RUST_JIT_BUILD=1)") args := []string{ @@ -346,6 +358,7 @@ func startMixedOpRethNode( "--datadir=" + dataDirPath, "--chain=" + chainConfigPath, "--proofs-history.storage-path=" + proofHistoryDir, + "--proofs-history.storage-version=" + storageVersion, } initOut, initErr := exec.Command(execPath, initProofsArgs...).CombinedOutput() t.Require().NoError(initErr, "must init op-reth proof history: %s", string(initOut)) @@ -356,6 +369,7 @@ func startMixedOpRethNode( "--proofs-history.window=10000", "--proofs-history.prune-interval=1m", "--proofs-history.storage-path="+proofHistoryDir, + "--proofs-history.storage-version="+storageVersion, ) l2EL := &OpReth{ @@ -448,11 +462,11 @@ func startMixedKonaNode( envVars = append(envVars, "KONA_NODE_MODE=Validator") } - execPath, err := EnsureRustBinary(t, RustBinarySpec{ + execPath, err := rustbin.Spec{ SrcDir: "rust/kona", Package: "kona-node", Binary: "kona-node", - }) + }.EnsureExists(t.Ctx(), t.Logger()) t.Require().NoError(err, "prepare kona-node binary") t.Require().NotEmpty(execPath, "kona-node binary path resolved") diff --git a/op-devstack/sysgo/multichain_proofs.go b/op-devstack/sysgo/multichain_proofs.go index db14e6f3eb7..a1e31b36bd9 100644 --- a/op-devstack/sysgo/multichain_proofs.go +++ b/op-devstack/sysgo/multichain_proofs.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" opchallenger "github.com/ethereum-optimism/optimism/op-challenger" challengermetrics "github.com/ethereum-optimism/optimism/op-challenger/metrics" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-devstack/devtest" sharedchallenger "github.com/ethereum-optimism/optimism/op-devstack/shared/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/setuputils" @@ -30,7 +30,7 @@ import ( func withSuperProofsDeployerFeature(cfg PresetConfig) PresetConfig { cfg.DeployerOptions = append([]DeployerOption{ - WithDevFeatureEnabled(deployer.OptimismPortalInteropDevFlag), + WithDevFeatureEnabled(devfeatures.OptimismPortalInteropFlag), }, cfg.DeployerOptions...) return cfg } @@ -289,6 +289,7 @@ func startInteropChallenger( ) } cfg, err := sharedchallenger.NewInteropChallengerConfig( + t.Ctx(), t.TempDir(), l1EL.UserRPC(), l1CL.beaconHTTPAddr, diff --git a/op-devstack/sysgo/multichain_supernode_runtime.go b/op-devstack/sysgo/multichain_supernode_runtime.go index 6579df4d38a..f96c6ccf55b 100644 --- a/op-devstack/sysgo/multichain_supernode_runtime.go +++ b/op-devstack/sysgo/multichain_supernode_runtime.go @@ -14,8 +14,8 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" opforks "github.com/ethereum-optimism/optimism/op-core/forks" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" "github.com/ethereum-optimism/optimism/op-devstack/devtest" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/intentbuilder" faucetConfig "github.com/ethereum-optimism/optimism/op-faucet/config" @@ -129,15 +129,30 @@ func newSingleChainSupernodeRuntimeWithConfig(t devtest.T, interopAtGenesis bool depSetStatic = cast } + if cfg.MessageExpiryWindow != nil && depSetStatic != nil { + var overrideErr error + depSetStatic, overrideErr = depset.NewStaticConfigDependencySetWithMessageExpiryOverride( + depSetStatic.Dependencies(), *cfg.MessageExpiryWindow) + require.NoError(overrideErr, "failed to override message expiry window") + } + supernode, l2CL := startSingleChainSharedSupernode(t, l1Net, l1EL, l1CL, l2Net, l2EL, depSetStatic, jwtSecret, interopAtGenesis) l2Batcher := startMinimalBatcher(t, keys, l2Net, l1EL, l2CL, l2EL, cfg.BatcherOptions...) l2Proposer := startMinimalProposer(t, keys, l2Net, l1EL, l2CL, cfg.ProposerOptions...) faucetService := startFaucets(t, keys, l1Net.ChainID(), l2Net.ChainID(), l1EL.UserRPC(), l2EL.UserRPC()) + // Use the potentially-overridden depSetStatic if available. + var runtimeDepSet depset.DependencySet + if depSetStatic != nil { + runtimeDepSet = depSetStatic + } else { + runtimeDepSet = depSet + } + return &MultiChainRuntime{ Keys: keys, Migration: migration, - DependencySet: depSet, + DependencySet: runtimeDepSet, L1Network: l1Net, L1EL: l1EL, L1CL: l1CL, @@ -190,6 +205,13 @@ func newTwoL2SupernodeRuntimeWithConfig(t devtest.T, enableInterop bool, delaySe depSet = cast } + if cfg.MessageExpiryWindow != nil && depSet != nil { + var err error + depSet, err = depset.NewStaticConfigDependencySetWithMessageExpiryOverride( + depSet.Dependencies(), *cfg.MessageExpiryWindow) + require.NoError(err, "failed to override message expiry window") + } + supernode, l2ACL, l2BCL := startTwoL2SharedSupernode( t, l1Net, @@ -215,10 +237,19 @@ func newTwoL2SupernodeRuntimeWithConfig(t devtest.T, enableInterop bool, delaySe l2BNet.ChainID(): l2BEL.UserRPC(), }) + // Use the potentially-overridden depSet (e.g. with custom message expiry window) + // if available; otherwise fall back to the original from the world builder. + var runtimeDepSet depset.DependencySet + if depSet != nil { + runtimeDepSet = depSet + } else { + runtimeDepSet = wb.outFullCfgSet.DependencySet + } + return &MultiChainRuntime{ Keys: keys, Migration: newInteropMigrationState(wb), - DependencySet: wb.outFullCfgSet.DependencySet, + DependencySet: runtimeDepSet, L1Network: l1Net, L1EL: l1EL, L1CL: l1CL, @@ -261,7 +292,7 @@ func buildTwoL2RuntimeWorld(t devtest.T, keys devkeys.Keys, enableInterop bool, applyConfigPrefundedL2(t, keys, DefaultL1ID, DefaultL2BID, wb.builder) if enableInterop { deployerOpts = append([]DeployerOption{ - WithDevFeatureEnabled(deployer.OptimismPortalInteropDevFlag), + WithDevFeatureEnabled(devfeatures.OptimismPortalInteropFlag), }, deployerOpts...) for _, l2Cfg := range wb.builder.L2s() { l2Cfg.WithForkAtGenesis(opforks.Interop) @@ -303,7 +334,7 @@ func l2NetworkFromWorldBuilder(t devtest.T, wb *worldBuilder, l1ChainID, l2Chain genesis: l2Genesis, rollupCfg: l2RollupCfg, deployment: l2Dep, - opcmImpl: wb.output.ImplementationsDeployment.OpcmImpl, + opcmImpl: wb.output.ImplementationsDeployment.OpcmV2Impl, mipsImpl: wb.output.ImplementationsDeployment.MipsImpl, keys: keys, } diff --git a/op-devstack/sysgo/multichain_supervisor_runtime.go b/op-devstack/sysgo/multichain_supervisor_runtime.go index 3cf9092ef1a..cd30b86934d 100644 --- a/op-devstack/sysgo/multichain_supervisor_runtime.go +++ b/op-devstack/sysgo/multichain_supervisor_runtime.go @@ -11,8 +11,9 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/shared/rustbin" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/client" "github.com/ethereum-optimism/optimism/op-service/clock" @@ -39,7 +40,7 @@ func NewSingleChainInteropRuntimeWithConfig(t devtest.T, cfg PresetConfig) *Mult require.NoError(err, "failed to derive dev keys from mnemonic") cfg.DeployerOptions = append([]DeployerOption{ - WithDevFeatureEnabled(deployer.OptimismPortalInteropDevFlag), + WithDevFeatureEnabled(devfeatures.OptimismPortalInteropFlag), }, cfg.DeployerOptions...) migration, l1Net, l2Net, depSet, fullCfgSet := buildSingleChainWorldWithInteropAndState(t, keys, true, cfg.LocalContractArtifactsPath, cfg.DeployerOptions...) validateSimpleInteropPresetConfig(t, cfg, l2Net) @@ -112,7 +113,7 @@ func NewSimpleInteropRuntimeWithConfig(t devtest.T, cfg PresetConfig) *MultiChai require.NoError(err, "failed to derive dev keys from mnemonic") cfg.DeployerOptions = append([]DeployerOption{ - WithDevFeatureEnabled(deployer.OptimismPortalInteropDevFlag), + WithDevFeatureEnabled(devfeatures.OptimismPortalInteropFlag), }, cfg.DeployerOptions...) migration, l1Net, l2ANet, l2BNet, fullCfgSet := buildTwoL2WorldWithState(t, keys, true, cfg.LocalContractArtifactsPath, cfg.DeployerOptions...) validateSimpleInteropPresetConfig(t, cfg, l2ANet, l2BNet) @@ -381,11 +382,11 @@ func startKonaSupervisor( require.NoError(os.WriteFile(filePath, rollupData, 0o644)) } - execPath, err := EnsureRustBinary(t, RustBinarySpec{ + execPath, err := rustbin.Spec{ SrcDir: "rust/kona", Package: "kona-supervisor", Binary: "kona-supervisor", - }) + }.EnsureExists(t.Ctx(), t.Logger()) require.NoError(err, "prepare kona-supervisor binary") require.NotEmpty(execPath, "kona-supervisor binary path resolved") diff --git a/op-devstack/sysgo/op_rbuilder.go b/op-devstack/sysgo/op_rbuilder.go index 0692f96743a..6030f35aaf6 100644 --- a/op-devstack/sysgo/op_rbuilder.go +++ b/op-devstack/sysgo/op_rbuilder.go @@ -9,11 +9,13 @@ import ( "strconv" "strings" "sync" + "time" "github.com/ethereum/go-ethereum/log" yaml "gopkg.in/yaml.v3" "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/shared/rustbin" "github.com/ethereum-optimism/optimism/op-devstack/stack" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -79,6 +81,8 @@ type OPRBuilderNodeConfig struct { AuthRPCAddr string AuthRPCPort int + ChainBlockTime time.Duration + // P2P P2PPort int P2PAddr string @@ -118,6 +122,7 @@ func DefaultOPRbuilderNodeConfig() *OPRBuilderNodeConfig { P2PAddr: "127.0.0.1", P2PPort: 0, P2PNodeKeyHex: "", + ChainBlockTime: time.Second * 2, StaticPeers: nil, TrustedPeers: nil, Full: true, @@ -245,6 +250,8 @@ func (cfg *OPRBuilderNodeConfig) LaunchSpec(p devtest.CommonT) (args []string, e args = append(args, "--rules.config-path="+cfg.RulesConfigPath) } + chainBlockTimeArg := "--rollup.chain-block-time=" + strconv.FormatInt(cfg.ChainBlockTime.Milliseconds(), 10) + args = append(args, chainBlockTimeArg) args = append(args, cfg.ExtraArgs...) return args, env @@ -402,11 +409,11 @@ func (b *OPRBuilderNode) Start() { b.sub = NewSubProcess(b.p, stdOut, stdErr) - execPath, err := EnsureRustBinary(b.p, RustBinarySpec{ + execPath, err := rustbin.Spec{ SrcDir: "op-rbuilder", Package: "op-rbuilder", Binary: "op-rbuilder", - }) + }.EnsureExists(b.p.Ctx(), b.p.Logger()) b.p.Require().NoError(err, "prepare op-rbuilder binary") b.p.Require().NotEmpty(execPath, "op-rbuilder binary path resolved") diff --git a/op-devstack/sysgo/preset_config.go b/op-devstack/sysgo/preset_config.go index 7cacaa963b2..d6f8ee1f7ee 100644 --- a/op-devstack/sysgo/preset_config.go +++ b/op-devstack/sysgo/preset_config.go @@ -20,6 +20,7 @@ type PresetConfig struct { EnableTimeTravel bool MaxSequencingWindow *uint64 RequireInteropNotAtGen bool + MessageExpiryWindow *uint64 } func NewPresetConfig() PresetConfig { diff --git a/op-devstack/sysgo/rollup_boost.go b/op-devstack/sysgo/rollup_boost.go index b59ca46cb65..534afd0429e 100644 --- a/op-devstack/sysgo/rollup_boost.go +++ b/op-devstack/sysgo/rollup_boost.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/shared/rustbin" "github.com/ethereum-optimism/optimism/op-devstack/stack" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/logpipe" @@ -104,11 +105,11 @@ func (r *RollupBoostNode) Start() { r.sub = NewSubProcess(r.p, stdOut, stdErr) - execPath, err := EnsureRustBinary(r.p, RustBinarySpec{ + execPath, err := rustbin.Spec{ SrcDir: "rollup-boost", Package: "rollup-boost", Binary: "rollup-boost", - }) + }.EnsureExists(r.p.Ctx(), r.p.Logger()) r.p.Require().NoError(err, "prepare rollup-boost binary") r.p.Require().NotEmpty(execPath, "rollup-boost binary path resolved") diff --git a/op-devstack/sysgo/singlechain_build.go b/op-devstack/sysgo/singlechain_build.go index 23c03735a4e..1560f4ee158 100644 --- a/op-devstack/sysgo/singlechain_build.go +++ b/op-devstack/sysgo/singlechain_build.go @@ -98,7 +98,7 @@ func buildSingleChainWorld(t devtest.T, keys devkeys.Keys, localContractArtifact genesis: wb.outL2Genesis[l2ID], rollupCfg: wb.outL2RollupCfg[l2ID], deployment: wb.outL2Deployment[l2ID], - opcmImpl: wb.output.ImplementationsDeployment.OpcmImpl, + opcmImpl: wb.output.ImplementationsDeployment.OpcmV2Impl, mipsImpl: wb.output.ImplementationsDeployment.MipsImpl, keys: keys, } @@ -156,8 +156,10 @@ func startL2ELForKey(t devtest.T, l2Net *L2Network, jwtPath string, jwtSecret [3 switch devstackL2ELKind() { case MixedL2ELOpGeth: return startL2ELNode(t, l2Net, jwtPath, jwtSecret, key, identity) - default: // op-reth - return startMixedOpRethNode(t, l2Net, key, jwtPath, jwtSecret, nil) + case MixedL2ELOpRethV2: + return startMixedOpRethNode(t, l2Net, key, jwtPath, jwtSecret, nil, "v2") + default: // op-reth v1 + return startMixedOpRethNode(t, l2Net, key, jwtPath, jwtSecret, nil, "v1") } } @@ -415,6 +417,7 @@ func startL2CLNode( SupportsPostFinalizationELSync: false, L2FollowSourceEndpoint: cfg.FollowSource, NeedInitialResetEngine: false, + OffsetELSafe: cfg.OffsetELSafe, }, ConfigPersistence: config.DisabledConfigPersistence{}, Metrics: opmetrics.CLIConfig{}, diff --git a/op-devstack/sysgo/singlechain_flashblocks.go b/op-devstack/sysgo/singlechain_flashblocks.go index c184d06a5c3..449b19d1186 100644 --- a/op-devstack/sysgo/singlechain_flashblocks.go +++ b/op-devstack/sysgo/singlechain_flashblocks.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum-optimism/optimism/op-devstack/devtest" @@ -68,6 +69,7 @@ func startBuilderEL(t devtest.T, l2Net *L2Network, jwtPath string, identity *ELN require.NoError(os.WriteFile(chainConfigPath, data, 0o644), "must write op-rbuilder genesis file") cfg := DefaultOPRbuilderNodeConfig() + cfg.ChainBlockTime = time.Second * time.Duration(l2Net.rollupCfg.BlockTime) cfg.AuthRPCJWTPath = jwtPath cfg.Chain = chainConfigPath cfg.P2PAddr = "127.0.0.1" diff --git a/op-devstack/sysgo/singlechain_interop.go b/op-devstack/sysgo/singlechain_interop.go index 17c62d78377..c36391ca3f5 100644 --- a/op-devstack/sysgo/singlechain_interop.go +++ b/op-devstack/sysgo/singlechain_interop.go @@ -2,13 +2,13 @@ package sysgo import ( "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-core/devfeatures" "github.com/ethereum-optimism/optimism/op-devstack/devtest" ) func newSingleChainInteropWorldNoSupervisor(t devtest.T, keys devkeys.Keys, cfg PresetConfig) singleChainRuntimeWorld { cfg.DeployerOptions = append([]DeployerOption{ - WithDevFeatureEnabled(deployer.OptimismPortalInteropDevFlag), + WithDevFeatureEnabled(devfeatures.OptimismPortalInteropFlag), }, cfg.DeployerOptions...) l1Net, l2Net, depSet, fullCfgSet := buildSingleChainWorldWithInterop(t, keys, true, cfg.LocalContractArtifactsPath, cfg.DeployerOptions...) return singleChainRuntimeWorld{ diff --git a/op-devstack/sysgo/singlechain_runtime.go b/op-devstack/sysgo/singlechain_runtime.go index 7d954056469..ec8bb03436c 100644 --- a/op-devstack/sysgo/singlechain_runtime.go +++ b/op-devstack/sysgo/singlechain_runtime.go @@ -348,6 +348,7 @@ func startMinimalChallenger( ) } cfg, err := sharedchallenger.NewPreInteropChallengerConfig( + t.Ctx(), t.TempDir(), l1EL.UserRPC(), l1CL.beaconHTTPAddr, diff --git a/op-devstack/sysgo/world.go b/op-devstack/sysgo/world.go index 00a3c7ecbfb..4cc9c5ebd13 100644 --- a/op-devstack/sysgo/world.go +++ b/op-devstack/sysgo/world.go @@ -55,7 +55,7 @@ func newInteropMigrationState(wb *worldBuilder) *interopMigrationState { return nil } state := &interopMigrationState{ - opcmImpl: wb.output.ImplementationsDeployment.OpcmImpl, + opcmImpl: wb.output.ImplementationsDeployment.OpcmV2Impl, superchainConfigAddr: wb.outSuperchainDeployment.SuperchainConfigAddr(), l2Deployments: make(map[eth.ChainID]*L2Deployment, len(wb.outL2Deployment)), } @@ -93,7 +93,7 @@ func buildSingleChainWorldWithInteropAndState(t devtest.T, keys devkeys.Keys, in genesis: wb.outL2Genesis[l2ID], rollupCfg: wb.outL2RollupCfg[l2ID], deployment: wb.outL2Deployment[l2ID], - opcmImpl: wb.output.ImplementationsDeployment.OpcmImpl, + opcmImpl: wb.output.ImplementationsDeployment.OpcmV2Impl, mipsImpl: wb.output.ImplementationsDeployment.MipsImpl, keys: keys, } @@ -136,7 +136,7 @@ func buildTwoL2WorldWithState(t devtest.T, keys devkeys.Keys, interopAtGenesis b genesis: l2ANet, rollupCfg: wb.outL2RollupCfg[DefaultL2AID], deployment: wb.outL2Deployment[DefaultL2AID], - opcmImpl: wb.output.ImplementationsDeployment.OpcmImpl, + opcmImpl: wb.output.ImplementationsDeployment.OpcmV2Impl, mipsImpl: wb.output.ImplementationsDeployment.MipsImpl, keys: keys, } @@ -147,7 +147,7 @@ func buildTwoL2WorldWithState(t devtest.T, keys devkeys.Keys, interopAtGenesis b genesis: l2BNet, rollupCfg: wb.outL2RollupCfg[DefaultL2BID], deployment: wb.outL2Deployment[DefaultL2BID], - opcmImpl: wb.output.ImplementationsDeployment.OpcmImpl, + opcmImpl: wb.output.ImplementationsDeployment.OpcmV2Impl, mipsImpl: wb.output.ImplementationsDeployment.MipsImpl, keys: keys, } diff --git a/op-dispute-mon/mon/extract/bond_enricher.go b/op-dispute-mon/mon/extract/bond_enricher.go index ed367812ba9..60cf2017962 100644 --- a/op-dispute-mon/mon/extract/bond_enricher.go +++ b/op-dispute-mon/mon/extract/bond_enricher.go @@ -4,13 +4,14 @@ import ( "context" "errors" "fmt" + "maps" "math/big" + "slices" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" - "golang.org/x/exp/maps" ) var _ Enricher = (*BondEnricher)(nil) @@ -29,7 +30,7 @@ func NewBondEnricher() *BondEnricher { } func (b *BondEnricher) Enrich(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error { - recipientAddrs := maps.Keys(game.Recipients) + recipientAddrs := slices.Collect(maps.Keys(game.Recipients)) credits, err := caller.GetCredits(ctx, block, recipientAddrs...) if err != nil { return err diff --git a/op-dispute-mon/mon/extract/extractor.go b/op-dispute-mon/mon/extract/extractor.go index a4cbb597b43..22510f17b1a 100644 --- a/op-dispute-mon/mon/extract/extractor.go +++ b/op-dispute-mon/mon/extract/extractor.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "maps" + "slices" "sync" "sync/atomic" @@ -13,7 +15,6 @@ import ( "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "golang.org/x/exp/maps" ) var ( @@ -125,7 +126,7 @@ func (e *Extractor) enrichGames(ctx context.Context, blockHash common.Hash, game updatedGameData[enrichedGame.Proxy] = enrichedGame } e.latestGameData = updatedGameData - return maps.Values(updatedGameData), int(ignored.Load()), int(failed.Load()) + return slices.Collect(maps.Values(updatedGameData)), int(ignored.Load()), int(failed.Load()) } func (e *Extractor) enrichGame(ctx context.Context, blockHash common.Hash, game gameTypes.GameMetadata) (*monTypes.EnrichedGameData, error) { diff --git a/op-dispute-mon/mon/extract/withdrawals_enricher.go b/op-dispute-mon/mon/extract/withdrawals_enricher.go index ea94adbfebb..a923f94a63d 100644 --- a/op-dispute-mon/mon/extract/withdrawals_enricher.go +++ b/op-dispute-mon/mon/extract/withdrawals_enricher.go @@ -4,12 +4,13 @@ import ( "context" "errors" "fmt" + "maps" + "slices" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" - "golang.org/x/exp/maps" ) var ErrIncorrectWithdrawalsCount = errors.New("incorrect withdrawals count") @@ -23,7 +24,7 @@ func NewWithdrawalsEnricher() *WithdrawalsEnricher { } func (w *WithdrawalsEnricher) Enrich(ctx context.Context, block rpcblock.Block, caller GameCaller, game *monTypes.EnrichedGameData) error { - recipients := maps.Keys(game.Recipients) + recipients := slices.Collect(maps.Keys(game.Recipients)) withdrawals, err := caller.GetWithdrawals(ctx, block, recipients...) if err != nil { return fmt.Errorf("failed to fetch withdrawals: %w", err) diff --git a/op-e2e/actions/helpers/l1_miner.go b/op-e2e/actions/helpers/l1_miner.go index ee37e89f5f5..e69269490df 100644 --- a/op-e2e/actions/helpers/l1_miner.go +++ b/op-e2e/actions/helpers/l1_miner.go @@ -137,7 +137,7 @@ func (s *L1Miner) ActL1StartBlock(timeDelta uint64) Action { s.pendingIndices = make(map[common.Address]uint64) s.l1BuildingBlobSidecars = make([]*types.BlobTxSidecar, 0) - s.L1GasPool = new(core.GasPool).AddGas(header.GasLimit) + s.L1GasPool = core.NewGasPool(header.GasLimit) } } @@ -181,15 +181,15 @@ func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) *types.Receipt { if tx.Gas() > s.l1BuildingHeader.GasLimit { t.Fatalf("tx consumes %d gas, more than available in L1 block %d", tx.Gas(), s.l1BuildingHeader.GasLimit) } - if tx.Gas() > uint64(*s.L1GasPool) { - t.InvalidAction("action takes too much gas: %d, only have %d", tx.Gas(), uint64(*s.L1GasPool)) + if tx.Gas() > s.L1GasPool.Gas() { + t.InvalidAction("action takes too much gas: %d, only have %d", tx.Gas(), s.L1GasPool.Gas()) return nil } s.l1BuildingState.SetTxContext(tx.Hash(), len(s.L1Transactions)) blockCtx := core.NewEVMBlockContext(s.l1BuildingHeader, s.l1Chain, nil, s.l1Cfg.Config, s.l1BuildingState) evm := vm.NewEVM(blockCtx, s.l1BuildingState, s.l1Cfg.Config, *s.l1Chain.GetVMConfig()) receipt, err := core.ApplyTransaction( - evm, s.L1GasPool, s.l1BuildingState, s.l1BuildingHeader, tx.WithoutBlobTxSidecar(), &s.l1BuildingHeader.GasUsed) + evm, s.L1GasPool, s.l1BuildingState, s.l1BuildingHeader, tx.WithoutBlobTxSidecar()) if err != nil { s.l1TxFailed = append(s.l1TxFailed, tx) t.Fatalf("failed to apply transaction to L1 block (tx %d): %v", len(s.L1Transactions), err) @@ -259,7 +259,7 @@ func (s *L1Miner) ActL1EndBlock(t Testing) *types.Block { } s.l1Building = false - s.l1BuildingHeader.GasUsed = s.l1BuildingHeader.GasLimit - uint64(*s.L1GasPool) + s.l1BuildingHeader.GasUsed = s.l1BuildingHeader.GasLimit - s.L1GasPool.Gas() s.l1BuildingHeader.Root = s.l1BuildingState.IntermediateRoot(s.l1Cfg.Config.IsEIP158(s.l1BuildingHeader.Number)) var withdrawals []*types.Withdrawal diff --git a/op-e2e/actions/proofs/batcher_change_test.go b/op-e2e/actions/proofs/batcher_change_test.go new file mode 100644 index 00000000000..5298493b980 --- /dev/null +++ b/op-e2e/actions/proofs/batcher_change_test.go @@ -0,0 +1,64 @@ +package proofs_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" +) + +// Test_ProgramAction_BatcherChangeWithinChannelTimeout verifies that after a +// pipeline reset, the system config is loaded from the walked-back L2 block +// (channel_timeout L1 blocks behind the safe head's L1 origin), not from the +// safe head itself. +// +// Scenario: +// 1. Batcher A submits a batch. +// 2. The batcher address is changed from A to B on L1. +// 3. Batcher B submits a batch. +// 4. The fault proof program re-derives the chain from scratch. During reset, +// the pipeline walks back by channel_timeout and should find the old system +// config (batcher A). If it incorrectly uses the safe head's config +// (batcher B), it rejects batcher A's batch and derivation diverges. +func Test_ProgramAction_BatcherChangeWithinChannelTimeout(gt *testing.T) { + matrix := helpers.NewMatrix[any]() + matrix.AddDefaultTestCases( + nil, + helpers.NewForkMatrix(helpers.Granite, helpers.Jovian), + testBatcherChangeWithinChannelTimeout, + ) + matrix.Run(gt) +} + +func testBatcherChangeWithinChannelTimeout(gt *testing.T, testCfg *helpers.TestCfg[any]) { + t := actionsHelpers.NewDefaultTesting(gt) + env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg()) + + miner := env.Miner + sequencer := env.Sequencer + + // Step 1: Batcher A (default) submits a batch. + miner.ActEmptyBlock(t) + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + safeAfterA := env.BatchMineAndSync(t) + require.Greater(t, safeAfterA.Number, uint64(0), "safe head should advance after batcher A's batch") + + // Step 2: Change batcher from A to B (Bob) on L1 and replace env.Batcher. + env.RotateBatcher(t, env.Dp.Secrets.Bob) + + // Step 3: Build L2 blocks adopting the batcher change, submit with batcher B. + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + safeAfterB := env.BatchMineAndSync(t) + require.Greater(t, safeAfterB.Number, safeAfterA.Number, "safe head should advance after batcher B's batch") + + // Step 4: Run the fault proof program. This re-derives the chain from + // scratch, triggering a pipeline reset. The pipeline must walk back by + // channel_timeout and use batcher A's system config for the initial + // derivation window. If it uses batcher B's config, batcher A's batch + // is rejected and derivation produces a different (shorter) safe chain. + env.RunFaultProofProgram(t, safeAfterB.Number, testCfg.CheckResult, testCfg.InputParams...) +} diff --git a/op-e2e/actions/proofs/helpers/env.go b/op-e2e/actions/proofs/helpers/env.go index f708c924cf3..43fbe51d4d7 100644 --- a/op-e2e/actions/proofs/helpers/env.go +++ b/op-e2e/actions/proofs/helpers/env.go @@ -1,26 +1,29 @@ package helpers import ( + "crypto/ecdsa" "math/rand" + altda "github.com/ethereum-optimism/optimism/op-alt-da" + batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-core/forks" + "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" + "github.com/ethereum-optimism/optimism/op-e2e/bindings" e2ecfg "github.com/ethereum-optimism/optimism/op-e2e/config" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-program/client/boot" - "github.com/ethereum/go-ethereum/params" - - altda "github.com/ethereum-optimism/optimism/op-alt-da" - batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" - "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" - "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-program/host/config" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/require" ) @@ -236,12 +239,40 @@ func NewOpProgramCfg( return dfault } +const L1BlockTime = 12 + +// RotateBatcher updates the on-chain batcher address in the SystemConfig contract and replaces +// env.Batcher with a new one using the given key. The L1 transaction is mined in a new L1 block. +func (env *L2FaultProofEnv) RotateBatcher(t helpers.Testing, newBatcherKey *ecdsa.PrivateKey) { + t.Helper() + newAddr := crypto.PubkeyToAddress(newBatcherKey.PublicKey) + + sysCfgContract, err := bindings.NewSystemConfig(env.Sd.RollupCfg.L1SystemConfigAddress, env.Miner.EthClient()) + require.NoError(t, err) + sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(env.Dp.Secrets.Deployer, env.Sd.RollupCfg.L1ChainID) + require.NoError(t, err) + _, err = sysCfgContract.SetBatcherHash(sysCfgOwner, eth.AddressAsLeftPaddedHash(newAddr)) + require.NoError(t, err) + + env.Miner.ActL1StartBlock(L1BlockTime)(t) + env.Miner.ActL1IncludeTx(env.Dp.Addresses.Deployer)(t) + env.Miner.ActL1EndBlock(t) + + batcherCfg := NewBatcherCfg() + batcherCfg.BatcherKey = newBatcherKey + env.Batcher = helpers.NewL2Batcher( + env.log, env.Sd.RollupCfg, batcherCfg, + env.Sequencer.RollupClient(), env.Miner.EthClient(), + env.Engine.EthClient(), env.engCl, + ) +} + // BatchAndMine batches the current unsafe chain to L1 and mines the L1 block containing the // batcher transaction. func (env *L2FaultProofEnv) BatchAndMine(t helpers.Testing) { t.Helper() env.Batcher.ActSubmitAll(t) - env.Miner.ActL1StartBlock(12)(t) + env.Miner.ActL1StartBlock(L1BlockTime)(t) env.Miner.ActL1IncludeTxByHash(env.Batcher.LastSubmitted.Hash())(t) env.Miner.ActL1EndBlock(t) } diff --git a/op-e2e/actions/proofs/holocene_activation_test.go b/op-e2e/actions/proofs/holocene_activation_test.go index 96ddda59891..c2e5e3ab6a7 100644 --- a/op-e2e/actions/proofs/holocene_activation_test.go +++ b/op-e2e/actions/proofs/holocene_activation_test.go @@ -6,9 +6,7 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" actionsHelpers "github.com/ethereum-optimism/optimism/op-e2e/actions/helpers" "github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers" - "github.com/ethereum-optimism/optimism/op-program/client/claim" "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/require" ) @@ -17,11 +15,11 @@ func Test_ProgramAction_HoloceneActivation(gt *testing.T) { runHoloceneDerivationTest := func(gt *testing.T, testCfg *helpers.TestCfg[any]) { t := actionsHelpers.NewDefaultTesting(gt) + const holoceneOffset = 14 // Define override to activate Holocene 14 seconds after genesis var setHoloceneTime = func(dc *genesis.DeployConfig) { - fourteen := hexutil.Uint64(14) - dc.L2GenesisHoloceneTimeOffset = &fourteen + dc.L2GenesisHoloceneTimeOffset = ptr(hexutil.Uint64(holoceneOffset)) } env := helpers.NewL2FaultProofEnv(t, testCfg, helpers.NewTestParams(), helpers.NewBatcherCfg(), setHoloceneTime) @@ -89,11 +87,9 @@ func Test_ProgramAction_HoloceneActivation(gt *testing.T) { env.Sequencer.ActL2PipelineFull(t) l2SafeHead := env.Sequencer.L2Safe() - t.Log(l2SafeHead.Time) + t.Log("Safe head", "time", l2SafeHead.Time) require.EqualValues(t, uint64(0), l2SafeHead.Number) // channel should be dropped, so no safe head progression - if uint64(0) == l2SafeHead.Number { - t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) - } + t.Log("Safe head progressed as expected", "number", l2SafeHead.Number) // Log assertions filters := []string{ @@ -106,25 +102,21 @@ func Test_ProgramAction_HoloceneActivation(gt *testing.T) { recs := env.Logs.FindLogs(testlog.NewMessageContainsFilter(filter), testlog.NewAttributesFilter("role", "sequencer")) require.Len(t, recs, 1, "searching for %d instances of '%s' in logs from role %s", 1, filter, "sequencer") } - env.RunFaultProofProgramFromGenesis(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + + // Now make sure the safe head progresses over the activation boundary so proofs tests aren't trivial over the genesis block. + env.BatchMineAndSync(t) + l2SafeHead = env.Sequencer.L2Safe() + t.Log("Safe head", "time", l2SafeHead.Time) + require.EqualValues(t, uint64(holoceneOffset), l2SafeHead.Number) + t.Log("Safe head progressed as expected", "number", l2SafeHead.Number) + env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) } matrix := helpers.NewMatrix[any]() - defer matrix.Run(gt) - - matrix.AddTestCase( - "HonestClaim-HoloceneActivation", - nil, - helpers.NewForkMatrix(helpers.Granite), - runHoloceneDerivationTest, - helpers.ExpectNoError(), - ) - matrix.AddTestCase( - "JunkClaim-HoloceneActivation", + matrix.AddDefaultTestCases( nil, helpers.NewForkMatrix(helpers.Granite), runHoloceneDerivationTest, - helpers.ExpectError(claim.ErrClaimNotValid), - helpers.WithL2Claim(common.HexToHash("0xdeadbeef")), ) + matrix.Run(gt) } diff --git a/op-e2e/actions/proofs/holocene_batches_test.go b/op-e2e/actions/proofs/holocene_batches_test.go index 778e2b62527..273372d8b44 100644 --- a/op-e2e/actions/proofs/holocene_batches_test.go +++ b/op-e2e/actions/proofs/holocene_batches_test.go @@ -130,7 +130,15 @@ func Test_ProgramAction_HoloceneBatches(gt *testing.T) { testCfg.Custom.RequireExpectedProgressAndLogs(t, l2SafeHead, isHolocene, env.Engine, env.Logs) t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) - env.RunFaultProofProgramFromGenesis(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + // Run the fault proof program on a non-trivial block. When safe head is 0 because + // the derivation pipeline correctly dropped disordered batches, we skip the proof — + // rebatching would gloss over the problematic range and not test the drop behavior. + // TODO(#20050): run FPP over the genesis range and assert derivation produces no new blocks. + if l2SafeHead.Number > 0 { + env.RunFaultProofProgramFromGenesis(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + } else { + t.Log("Skipping fault proof program: safe head is at genesis due to dropped batches") + } } matrix := helpers.NewMatrix[testCase]() diff --git a/op-e2e/actions/proofs/holocene_frame_test.go b/op-e2e/actions/proofs/holocene_frame_test.go index b643ff79eb6..ea537e93c48 100644 --- a/op-e2e/actions/proofs/holocene_frame_test.go +++ b/op-e2e/actions/proofs/holocene_frame_test.go @@ -113,7 +113,15 @@ func Test_ProgramAction_HoloceneFrames(gt *testing.T) { testCfg.Custom.RequireExpectedProgressAndLogs(t, l2SafeHead, isHolocene, env.Engine, env.Logs) t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) - env.RunFaultProofProgramFromGenesis(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + // Run the fault proof program on a non-trivial block. When safe head is 0 because + // the derivation pipeline correctly dropped disordered frames, we skip the proof — + // rebatching would gloss over the problematic range and not test the drop behavior. + // TODO(#20050): run FPP over the genesis range and assert derivation produces no new blocks. + if l2SafeHead.Number > 0 { + env.RunFaultProofProgramFromGenesis(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + } else { + t.Log("Skipping fault proof program: safe head is at genesis due to dropped frames") + } } matrix := helpers.NewMatrix[testCase]() diff --git a/op-e2e/actions/proofs/holocene_invalid_batch_test.go b/op-e2e/actions/proofs/holocene_invalid_batch_test.go index 1cfee05271e..894fe4bc180 100644 --- a/op-e2e/actions/proofs/holocene_invalid_batch_test.go +++ b/op-e2e/actions/proofs/holocene_invalid_batch_test.go @@ -239,8 +239,14 @@ func Test_ProgramAction_HoloceneInvalidBatch(gt *testing.T) { testCfg.Custom.RequireExpectedProgressAndLogs(t, l2SafeHead, isHolocene, env.Engine, env.Logs) t.Log("Safe head progressed as expected", "l2SafeHeadNumber", l2SafeHead.Number) - if safeHeadNumber := l2SafeHead.Number; safeHeadNumber > 0 { - env.RunFaultProofProgram(t, safeHeadNumber, testCfg.CheckResult, testCfg.InputParams...) + // Run the fault proof program on a non-trivial block. When safe head is 0 due to + // intentionally invalid block contents (e.g. over-advanced L1 origin, sequencer drift breach), + // rebatching produces the same invalid result, so skip the proof in those cases. + // The Holocene variants of these tests DO advance the safe head and run the proof. + if l2SafeHead.Number > 0 { + env.RunFaultProofProgram(t, l2SafeHead.Number, testCfg.CheckResult, testCfg.InputParams...) + } else { + t.Log("Skipping fault proof program: safe head is at genesis due to intentionally invalid block contents") } } diff --git a/op-e2e/actions/sync/sync_test.go b/op-e2e/actions/sync/sync_test.go index 5854e27c817..24226c9049e 100644 --- a/op-e2e/actions/sync/sync_test.go +++ b/op-e2e/actions/sync/sync_test.go @@ -712,6 +712,14 @@ func TestELSync(gt *testing.T) { } func PrepareELSyncedNode(t actionsHelpers.Testing, miner *actionsHelpers.L1Miner, sequencer *actionsHelpers.L2Sequencer, seqEng *actionsHelpers.L2Engine, verifier *actionsHelpers.L2Verifier, verEng *actionsHelpers.L2Engine, seqEngCl *sources.EngineClient, batcher *actionsHelpers.L2Batcher, dp *e2eutils.DeployParams) { + PrepareELSyncedNodeAndCheck(t, miner, sequencer, seqEng, verifier, verEng, seqEngCl, batcher, dp, 11, 11) +} + +// PrepareELSyncedNodeAndCheck EL-syncs blocks 0..10 from sequencer to verifier, +// inserts block 11 to complete the sync, then asserts safe and finalized labels +// match expectedSafe and expectedFinalized respectively. +// After the check it batch-submits a block and verifies safe advances via derivation. +func PrepareELSyncedNodeAndCheck(t actionsHelpers.Testing, miner *actionsHelpers.L1Miner, sequencer *actionsHelpers.L2Sequencer, seqEng *actionsHelpers.L2Engine, verifier *actionsHelpers.L2Verifier, verEng *actionsHelpers.L2Engine, seqEngCl *sources.EngineClient, batcher *actionsHelpers.L2Batcher, dp *e2eutils.DeployParams, expectedSafe uint64, expectedFinalized uint64) { PerformELSyncAndCheckPayloads(t, miner, seqEng, sequencer, verEng, verifier, seqEngCl, 0, 10) // Despite downloading the blocks, it has not finished finalizing @@ -725,9 +733,9 @@ func PrepareELSyncedNode(t actionsHelpers.Testing, miner *actionsHelpers.L1Miner require.NoError(t, err) verifier.ActL2InsertUnsafePayload(seqHead)(t) - // Check that safe + finalized are there - VerifyBlock(t, verifier.Eng, 11, eth.Safe) - VerifyBlock(t, verifier.Eng, 11, eth.Finalized) + // Check that safe + finalized match expectations + VerifyBlock(t, verifier.Eng, expectedSafe, eth.Safe) + VerifyBlock(t, verifier.Eng, expectedFinalized, eth.Finalized) // Batch submit everything BatchSubmitBlock(t, miner, sequencer, verifier, batcher, dp, 12) @@ -893,6 +901,33 @@ func TestForcedELSyncCLAfterNodeRestart(gt *testing.T) { require.NotNil(t, record, "The verifier should start EL Sync when l2.engineKind is not geth") } +// TestELSyncOffsetELSafe verifies that when OffsetELSafe is configured, the safe and finalized +// heads are retracted from the EL-sync tip by floor(offset / blockTime) blocks. +// With L2BlockTime=1 and offset=5s the verifier should set safe/finalized to block 6 (= 11 - 5) +// instead of the tip (11). +func TestELSyncOffsetELSafe(gt *testing.T) { + t := actionsHelpers.NewDefaultTesting(gt) + dp := e2eutils.MakeDeployParams(t, actionsHelpers.DefaultRollupTestParams()) + sd := e2eutils.Setup(t, dp, actionsHelpers.DefaultAlloc) + logger := testlog.Logger(t, log.LevelInfo) + + miner, seqEng, sequencer := actionsHelpers.SetupSequencerTest(t, sd, logger) + batcher := actionsHelpers.NewL2Batcher(logger, sd.RollupCfg, actionsHelpers.DefaultBatcherCfg(dp), sequencer.RollupClient(), miner.EthClient(), seqEng.EthClient(), seqEng.EngineClient(t, sd.RollupCfg)) + + offset := 5 * time.Second + verEng, verifier := actionsHelpers.SetupVerifier(t, sd, logger, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), + &sync.Config{SyncMode: sync.ELSync, OffsetELSafe: offset}) + + seqEngCl, err := sources.NewEngineClient(seqEng.RPCClient(), logger, nil, sources.EngineClientDefaultConfig(sd.RollupCfg)) + require.NoError(t, err) + + // L2BlockTime defaults to 1s, so DurationToBlocks(5s, 1) = 5. + // EL sync tip will be block 11; safe and finalized should be 11 - 5 = 6. + expectedSafeFinalized := uint64(11) - uint64(offset/time.Second)/sd.RollupCfg.BlockTime + PrepareELSyncedNodeAndCheck(t, miner, sequencer, seqEng, verifier, verEng, seqEngCl, batcher, dp, + expectedSafeFinalized, expectedSafeFinalized) +} + func TestInvalidPayloadInSpanBatch(gt *testing.T) { t := actionsHelpers.NewDefaultTesting(gt) dp := e2eutils.MakeDeployParams(t, actionsHelpers.DefaultRollupTestParams()) diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 888c91c8836..960d359c06c 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log/slog" + "maps" "math/big" "os" "path" @@ -22,7 +23,6 @@ import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/exp/maps" "github.com/ethereum-optimism/optimism/op-e2e/config/secrets" "github.com/ethereum/go-ethereum/common/hexutil" diff --git a/op-e2e/e2eutils/challenger/helper.go b/op-e2e/e2eutils/challenger/helper.go index 4d215d72037..2210eae4d00 100644 --- a/op-e2e/e2eutils/challenger/helper.go +++ b/op-e2e/e2eutils/challenger/helper.go @@ -129,7 +129,7 @@ type MinimalT interface { func handleOptError(t *testing.T, opt shared.Option) Option { return func(c *config.Config) { - require.NoError(t, opt(c)) + require.NoError(t, opt(t.Context(), c)) } } func WithCannon(t *testing.T, system System) Option { diff --git a/op-e2e/e2eutils/disputegame/helper.go b/op-e2e/e2eutils/disputegame/helper.go index 4e92a078656..ccf23968317 100644 --- a/op-e2e/e2eutils/disputegame/helper.go +++ b/op-e2e/e2eutils/disputegame/helper.go @@ -210,6 +210,11 @@ func (h *FactoryHelper) startOutputCannonGameOfType(ctx context.Context, l2Node ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) defer cancel() + bond, err := h.Factory.InitBonds(nil, gameType) + h.Require.NoError(err, "get init bond for game type") + h.Opts.Value = bond + defer func() { h.Opts.Value = nil }() + tx, err := transactions.PadGasEstimate(h.Opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) { return h.Factory.Create(opts, gameType, rootClaim, extraData) }) @@ -272,6 +277,11 @@ func (h *FactoryHelper) startSuperCannonGameOfType(ctx context.Context, timestam ctx, cancel := context.WithTimeout(ctx, 1*time.Minute) defer cancel() + bond, err := h.Factory.InitBonds(nil, gameType) + h.Require.NoError(err, "get init bond for game type") + h.Opts.Value = bond + defer func() { h.Opts.Value = nil }() + tx, err := transactions.PadGasEstimate(h.Opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) { return h.Factory.Create(opts, gameType, rootClaim, extraData) }) diff --git a/op-e2e/e2eutils/geth/fakepos.go b/op-e2e/e2eutils/geth/fakepos.go index 75603252a13..b4c23e5804c 100644 --- a/op-e2e/e2eutils/geth/fakepos.go +++ b/op-e2e/e2eutils/geth/fakepos.go @@ -57,8 +57,8 @@ type Backend interface { } type EngineAPI interface { - ForkchoiceUpdatedV3(engine.ForkchoiceStateV1, *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) - ForkchoiceUpdatedV2(engine.ForkchoiceStateV1, *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) + ForkchoiceUpdatedV3(context.Context, engine.ForkchoiceStateV1, *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) + ForkchoiceUpdatedV2(context.Context, engine.ForkchoiceStateV1, *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) GetPayloadV5(engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) GetPayloadV4(engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) @@ -100,6 +100,13 @@ func (f *FakePoS) Start() error { return fmt.Errorf("get genesis header: %w", err) } f.sub = event.NewSubscription(func(quit <-chan struct{}) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + <-quit + cancel() + }() + // poll every half a second: enough to catch up with any block time when ticks are missed t := f.clock.NewTicker(time.Second / 2) for { @@ -173,9 +180,9 @@ func (f *FakePoS) Start() error { } var res engine.ForkChoiceResponse if isCancun { - res, err = f.engineAPI.ForkchoiceUpdatedV3(fcState, attrs) + res, err = f.engineAPI.ForkchoiceUpdatedV3(ctx, fcState, attrs) } else { - res, err = f.engineAPI.ForkchoiceUpdatedV2(fcState, attrs) + res, err = f.engineAPI.ForkchoiceUpdatedV2(ctx, fcState, attrs) } if err != nil { f.log.Error("failed to start building L1 block", "err", err) @@ -191,7 +198,7 @@ func (f *FakePoS) Start() error { select { case <-tim.Ch(): // no-op - case <-quit: + case <-ctx.Done(): tim.Stop() return nil } @@ -248,7 +255,7 @@ func (f *FakePoS) Start() error { continue } } - if _, err := f.engineAPI.ForkchoiceUpdatedV3(engine.ForkchoiceStateV1{ + if _, err := f.engineAPI.ForkchoiceUpdatedV3(ctx, engine.ForkchoiceStateV1{ HeadBlockHash: envelope.ExecutionPayload.BlockHash, SafeBlockHash: safe.Hash(), FinalizedBlockHash: finalized.Hash(), @@ -260,7 +267,7 @@ func (f *FakePoS) Start() error { // The EL doesn't really care about the value, // but it's nice to mock something consistent with the CL specs. f.withdrawalsIndex += uint64(len(withdrawals)) - case <-quit: + case <-ctx.Done(): return nil } } diff --git a/op-e2e/faultproofs/cannon_benchmark_test.go b/op-e2e/faultproofs/cannon_benchmark_test.go index 953fcdf3638..8b648dd5a57 100644 --- a/op-e2e/faultproofs/cannon_benchmark_test.go +++ b/op-e2e/faultproofs/cannon_benchmark_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/ecdsa" "encoding/json" + "fmt" "math/big" "os" "path" @@ -21,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" - "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/cannon/mipsevm" @@ -142,12 +142,12 @@ func createBigContracts(ctx context.Context, t *testing.T, cfg e2esys.SystemConf defer cancel() err := client.SendTransaction(ctx, tx) if err != nil { - results <- result{err: errors.Wrap(err, "Sending L2 tx")} + results <- result{err: fmt.Errorf("Sending L2 tx: %w", err)} return } receipt, err := wait.ForReceiptOK(ctx, client, tx.Hash()) if err != nil { - results <- result{err: errors.Wrap(err, "Waiting for receipt")} + results <- result{err: fmt.Errorf("Waiting for receipt: %w", err)} return } results <- result{addr: receipt.ContractAddress, err: nil} diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index ca5e7783cd5..263dad2fdf5 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -6,10 +6,12 @@ import ( "crypto/rand" "errors" "fmt" + "maps" "math/big" "net" "os" "path" + "slices" "sort" "strings" "sync/atomic" @@ -18,7 +20,6 @@ import ( gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/stretchr/testify/require" - "golang.org/x/exp/maps" ds "github.com/ipfs/go-datastore" dsSync "github.com/ipfs/go-datastore/sync" @@ -871,7 +872,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, // Rollup nodes // Ensure we are looping through the nodes in alphabetical order - ks := maps.Keys(cfg.Nodes) + ks := slices.Collect(maps.Keys(cfg.Nodes)) // Sort strings in ascending alphabetical order sort.Strings(ks) diff --git a/op-interop-filter/filter/backend.go b/op-interop-filter/filter/backend.go index 200b2206838..867c706f2d6 100644 --- a/op-interop-filter/filter/backend.go +++ b/op-interop-filter/filter/backend.go @@ -6,8 +6,10 @@ import ( "fmt" "sync/atomic" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum-optimism/optimism/op-interop-filter/metrics" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -183,3 +185,29 @@ func (b *Backend) CheckAccessList(ctx context.Context, inboxEntries []common.Has b.metrics.RecordCheckAccessList(true) return nil } + +// GetBlockHashByNumber returns the latest block hash or the block hash at a specific height for the given chain. +// Accepts rpc.BlockNumber: "latest" or a numeric block number. Other named tags are not supported. +func (b *Backend) GetBlockHashByNumber(chainID eth.ChainID, blockNum rpc.BlockNumber) (common.Hash, error) { + ingester, ok := b.chains[chainID] + if !ok { + return common.Hash{}, fmt.Errorf("chain %s: %w", chainID, types.ErrUnknownChain) + } + + if blockNum == rpc.LatestBlockNumber { + block, ok := ingester.LatestBlock() + if !ok { + return common.Hash{}, fmt.Errorf("latest block for chain %s: %w", chainID, ethereum.NotFound) + } + return block.Hash, nil + } + if blockNum < 0 { + return common.Hash{}, fmt.Errorf("unsupported block tag %q: only \"latest\" and block numbers are supported", blockNum) + } + + blockHash, ok := ingester.BlockHashByNumber(uint64(blockNum)) + if !ok { + return common.Hash{}, fmt.Errorf("block %d for chain %s: %w", blockNum, chainID, ethereum.NotFound) + } + return blockHash, nil +} diff --git a/op-interop-filter/filter/frontend.go b/op-interop-filter/filter/frontend.go index f3b1f8cdb18..a0ec0b168b7 100644 --- a/op-interop-filter/filter/frontend.go +++ b/op-interop-filter/filter/frontend.go @@ -30,6 +30,11 @@ func (f *QueryFrontend) CheckAccessList(ctx context.Context, inboxEntries []comm return nil } +// GetBlockHashByNumber returns the latest ingested block hash or the block hash at a specific height. +func (f *QueryFrontend) GetBlockHashByNumber(ctx context.Context, chainID eth.ChainID, blockNum rpc.BlockNumber) (common.Hash, error) { + return f.backend.GetBlockHashByNumber(chainID, blockNum) +} + // PublicAdminFrontend exposes read-only admin methods on the public port. type PublicAdminFrontend struct { backend *Backend diff --git a/op-interop-filter/filter/interfaces.go b/op-interop-filter/filter/interfaces.go index 05153cb2f13..e7c3b1b63c0 100644 --- a/op-interop-filter/filter/interfaces.go +++ b/op-interop-filter/filter/interfaces.go @@ -1,6 +1,8 @@ package filter import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) @@ -31,6 +33,9 @@ type ChainIngester interface { // LatestBlock returns the latest ingested block. LatestBlock() (eth.BlockID, bool) + // BlockHashByNumber returns the hash of the ingested block at the given height. + BlockHashByNumber(number uint64) (common.Hash, bool) + // LatestTimestamp returns the timestamp of the latest ingested block. LatestTimestamp() (uint64, bool) diff --git a/op-interop-filter/filter/logsdb_chain_ingester.go b/op-interop-filter/filter/logsdb_chain_ingester.go index f1fc7316883..4bfda419613 100644 --- a/op-interop-filter/filter/logsdb_chain_ingester.go +++ b/op-interop-filter/filter/logsdb_chain_ingester.go @@ -247,6 +247,11 @@ func (c *LogsDBChainIngester) BlockHashAt(blockNum uint64) (common.Hash, bool) { return seal.Hash, true } +// BlockHashByNumber returns the sealed block hash at the given height. +func (c *LogsDBChainIngester) BlockHashByNumber(blockNum uint64) (common.Hash, bool) { + return c.BlockHashAt(blockNum) +} + // LatestTimestamp returns the timestamp of the latest sealed block func (c *LogsDBChainIngester) LatestTimestamp() (uint64, bool) { c.mu.RLock() diff --git a/op-interop-filter/filter/mock_test.go b/op-interop-filter/filter/mock_test.go index cda5ff42ba1..fa0f65e5216 100644 --- a/op-interop-filter/filter/mock_test.go +++ b/op-interop-filter/filter/mock_test.go @@ -24,6 +24,9 @@ type mockChainIngester struct { // Logs stored by their identifying query logs map[logKey]types.BlockSeal + // Blocks keyed by block number + blocks map[uint64]eth.BlockID + // Executing messages with their inclusion context execMsgs []IncludedMessage @@ -47,6 +50,7 @@ type logKey struct { func newMockChainIngester() *mockChainIngester { return &mockChainIngester{ logs: make(map[logKey]types.BlockSeal), + blocks: make(map[uint64]eth.BlockID), execMsgs: make([]IncludedMessage, 0), ready: true, // Default to ready for simple tests } @@ -70,10 +74,11 @@ func (m *mockChainIngester) AddLog(timestamp, blockNum uint64, logIdx uint32, ch Checksum: checksum, } m.logs[key] = seal + m.blocks[blockNum] = eth.BlockID{Hash: seal.Hash, Number: blockNum} // Update latest block/timestamp if needed if blockNum > m.latestBlock.Number { - m.latestBlock = eth.BlockID{Number: blockNum} + m.latestBlock = eth.BlockID{Hash: seal.Hash, Number: blockNum} m.latestTimestamp = timestamp } if m.earliestIngestedBlock == 0 || blockNum < m.earliestIngestedBlock { @@ -98,6 +103,17 @@ func (m *mockChainIngester) AddExecMsg(msg IncludedMessage) { } } +// AddBlock adds a block directly to the ingester. +func (m *mockChainIngester) AddBlock(block eth.BlockID) { + m.mu.Lock() + defer m.mu.Unlock() + + m.blocks[block.Number] = block + if block.Number >= m.latestBlock.Number { + m.latestBlock = block + } +} + // SetReady sets the ready state. func (m *mockChainIngester) SetReady(ready bool) { m.mu.Lock() @@ -135,6 +151,18 @@ func (m *mockChainIngester) LatestBlock() (eth.BlockID, bool) { return m.latestBlock, true } +// BlockHashByNumber implements ChainIngester. +func (m *mockChainIngester) BlockHashByNumber(number uint64) (common.Hash, bool) { + m.mu.RLock() + defer m.mu.RUnlock() + + block, ok := m.blocks[number] + if !ok { + return common.Hash{}, false + } + return block.Hash, true +} + // LatestTimestamp implements ChainIngester. func (m *mockChainIngester) LatestTimestamp() (uint64, bool) { m.mu.RLock() diff --git a/op-interop-filter/filter/rpc_test.go b/op-interop-filter/filter/rpc_test.go new file mode 100644 index 00000000000..3853dea077a --- /dev/null +++ b/op-interop-filter/filter/rpc_test.go @@ -0,0 +1,82 @@ +package filter + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-interop-filter/metrics" + "github.com/ethereum-optimism/optimism/op-service/eth" + oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" + "github.com/ethereum-optimism/optimism/op-service/testlog" +) + +func TestQueryFrontendGetBlockHashByNumberRPC(t *testing.T) { + logger := testlog.Logger(t, log.LevelInfo) + mock := newMockChainIngester() + mock.AddBlock(eth.BlockID{Hash: common.HexToHash("0x01"), Number: 100}) + mock.AddBlock(eth.BlockID{Hash: common.HexToHash("0x02"), Number: 200}) + + backend := NewBackend(context.Background(), BackendParams{ + Logger: logger, + Metrics: metrics.NoopMetrics, + Chains: map[eth.ChainID]ChainIngester{eth.ChainIDFromUInt64(testChainA): mock}, + CrossValidator: &mockCrossValidator{}, + }) + + server := oprpc.NewServer( + "127.0.0.1", + 0, + "test", + oprpc.WithLogger(logger), + ) + server.AddAPI(rpc.API{ + Namespace: "supervisor", + Service: &QueryFrontend{backend: backend}, + }) + + require.NoError(t, server.Start()) + t.Cleanup(func() { + _ = server.Stop() + }) + + client, err := rpc.Dial("http://" + server.Endpoint()) + require.NoError(t, err) + t.Cleanup(client.Close) + + t.Run("latest selector", func(t *testing.T) { + var result common.Hash + err := client.Call(&result, "supervisor_getBlockHashByNumber", eth.ChainIDFromUInt64(testChainA), "latest") + require.NoError(t, err) + require.Equal(t, common.HexToHash("0x02"), result) + }) + + t.Run("numeric selector", func(t *testing.T) { + var result common.Hash + err := client.Call(&result, "supervisor_getBlockHashByNumber", eth.ChainIDFromUInt64(testChainA), rpc.BlockNumber(100)) + require.NoError(t, err) + require.Equal(t, common.HexToHash("0x01"), result) + }) + + t.Run("missing block", func(t *testing.T) { + var result common.Hash + err := client.Call(&result, "supervisor_getBlockHashByNumber", eth.ChainIDFromUInt64(testChainA), rpc.BlockNumber(999)) + require.ErrorContains(t, err, "not found") + }) + + t.Run("unknown chain", func(t *testing.T) { + var result common.Hash + err := client.Call(&result, "supervisor_getBlockHashByNumber", eth.ChainIDFromUInt64(999), rpc.BlockNumber(100)) + require.ErrorContains(t, err, "unknown chain") + }) + + t.Run("unsupported tag", func(t *testing.T) { + var result common.Hash + err := client.Call(&result, "supervisor_getBlockHashByNumber", eth.ChainIDFromUInt64(testChainA), "safe") + require.ErrorContains(t, err, "unsupported block tag") + }) +} diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index 9cf587f92e9..5c2a4d45a18 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -119,10 +119,19 @@ var ( SyncModeReqRespFlag = &cli.BoolFlag{ Name: "syncmode.req-resp", Required: false, - Value: true, + Value: false, EnvVars: prefixEnvVars("SYNCMODE_REQ_RESP"), Category: RollupCategory, } + SyncModeOffsetELSafeFlag = &cli.DurationFlag{ + Name: "syncmode.offset-el-safe", + Usage: "After execution-layer sync completes, set safe and finalized heads to this duration behind the synced tip " + + "(converted to L2 blocks via rollup block time using ceiling division). Default 0 (safe/finalized stay at the tip). " + + "Set to e.g. 168h to force derivation of the most recent 7 days of blocks before they are considered safe.", + EnvVars: prefixEnvVars("SYNCMODE_OFFSET_EL_SAFE"), + Category: RollupCategory, + Value: 0, + } RPCAdminPersistence = &cli.StringFlag{ Name: "rpc.admin-state", Usage: "File path used to persist state changes made via the admin API so they persist across restarts. Disabled if not set.", @@ -477,6 +486,7 @@ var optionalFlags = []cli.Flag{ BeaconFetchAllSidecars, SyncModeFlag, SyncModeReqRespFlag, + SyncModeOffsetELSafeFlag, FetchWithdrawalRootFromState, L1TrustRPC, L1RPCProviderKind, diff --git a/op-node/node/node.go b/op-node/node/node.go index 3cbec3404fc..31b4ab3d8a0 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -10,8 +10,6 @@ import ( "sync/atomic" "time" - "github.com/hashicorp/go-multierror" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -190,7 +188,7 @@ func NewWithOverride(ctx context.Context, cfg *config.Config, log log.Logger, ap log.Error("Error initializing the rollup node", "err", err) // ensure we always close the node resources if we fail to initialize the node. if closeErr := n.Stop(ctx); closeErr != nil { - return nil, multierror.Append(err, closeErr) + return nil, errors.Join(err, closeErr) } return nil, err } @@ -858,11 +856,11 @@ func (n *OpNode) Stop(ctx context.Context) error { return ErrAlreadyClosed } - var result *multierror.Error + var result error if n.server != nil { if err := n.server.Stop(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close RPC server: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close RPC server: %w", err)) } } @@ -876,14 +874,14 @@ func (n *OpNode) Stop(ctx context.Context) error { case err == nil: n.log.Info("stopped sequencer", "latestHead", latestHead) default: - result = multierror.Append(result, fmt.Errorf("error stopping sequencer: %w", err)) + result = errors.Join(result, fmt.Errorf("error stopping sequencer: %w", err)) } } n.p2pMu.Lock() if n.p2pNode != nil { if err := n.p2pNode.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close p2p node: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close p2p node: %w", err)) } // Prevent further use of p2p. n.p2pNode = nil @@ -892,7 +890,7 @@ func (n *OpNode) Stop(ctx context.Context) error { if n.p2pSigner != nil { if err := n.p2pSigner.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close p2p signer: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close p2p signer: %w", err)) } } @@ -916,14 +914,14 @@ func (n *OpNode) Stop(ctx context.Context) error { // close L2 driver if n.l2Driver != nil { if err := n.l2Driver.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close L2 engine driver cleanly: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close L2 engine driver cleanly: %w", err)) } } // close the interop sub system if n.interopSys != nil { if err := n.interopSys.Stop(ctx); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close interop sub-system: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close interop sub-system: %w", err)) } } @@ -933,7 +931,7 @@ func (n *OpNode) Stop(ctx context.Context) error { if n.safeDB != nil { if err := n.safeDB.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close safe head db: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close safe head db: %w", err)) } } @@ -970,16 +968,16 @@ func (n *OpNode) Stop(ctx context.Context) error { // Close metrics and pprof only after we are done idling if n.pprofService != nil { if err := n.pprofService.Stop(ctx); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close pprof server: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close pprof server: %w", err)) } } if n.metricsSrv != nil { if err := n.metricsSrv.Stop(ctx); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close metrics server: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close metrics server: %w", err)) } } - return result.ErrorOrNil() + return result } func (n *OpNode) Stopped() bool { diff --git a/op-node/p2p/node.go b/op-node/p2p/node.go index 4c60f8b46df..6286b8d46b5 100644 --- a/op-node/p2p/node.go +++ b/op-node/p2p/node.go @@ -8,7 +8,6 @@ import ( "strconv" "time" - "github.com/hashicorp/go-multierror" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/connmgr" "github.com/libp2p/go-libp2p/core/host" @@ -276,7 +275,7 @@ func (n *NodeP2P) BanIP(ip net.IP, expiration time.Time) error { } func (n *NodeP2P) Close() error { - var result *multierror.Error + var result error if n.peerMonitor != nil { n.peerMonitor.Stop() } @@ -285,23 +284,23 @@ func (n *NodeP2P) Close() error { } if n.gsOut != nil { if err := n.gsOut.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close gossip cleanly: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close gossip cleanly: %w", err)) } } if n.host != nil { if err := n.host.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close p2p host cleanly: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close p2p host cleanly: %w", err)) } if n.syncCl != nil { if err := n.syncCl.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close p2p sync client cleanly: %w", err)) + result = errors.Join(result, fmt.Errorf("failed to close p2p sync client cleanly: %w", err)) } } } if n.appScorer != nil { n.appScorer.Stop() } - return result.ErrorOrNil() + return result } func FindActiveTCPPort(h host.Host) (uint16, error) { diff --git a/op-node/rollup/derive/batch_mux.go b/op-node/rollup/derive/batch_mux.go index e18fc69d232..197a9347f8a 100644 --- a/op-node/rollup/derive/batch_mux.go +++ b/op-node/rollup/derive/batch_mux.go @@ -3,12 +3,12 @@ package derive import ( "context" "fmt" + "slices" "github.com/ethereum-optimism/optimism/op-core/forks" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/log" - "golang.org/x/exp/slices" ) // BatchMux multiplexes between different batch stages. diff --git a/op-node/rollup/derive/deposits.go b/op-node/rollup/derive/deposits.go index b71cd619678..1f6f263011e 100644 --- a/op-node/rollup/derive/deposits.go +++ b/op-node/rollup/derive/deposits.go @@ -1,10 +1,9 @@ package derive import ( + "errors" "fmt" - "github.com/hashicorp/go-multierror" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" @@ -22,7 +21,7 @@ func UserDeposits(receipts []*types.Receipt, depositContractAddr common.Address) if log.Address == depositContractAddr && len(log.Topics) > 0 && log.Topics[0] == DepositEventABIHash { dep, err := UnmarshalDepositLogEvent(log) if err != nil { - result = multierror.Append(result, fmt.Errorf("malformatted L1 deposit log in receipt %d, log %d: %w", i, j, err)) + result = errors.Join(result, fmt.Errorf("malformatted L1 deposit log in receipt %d, log %d: %w", i, j, err)) } else { out = append(out, dep) } @@ -36,13 +35,13 @@ func DeriveDeposits(receipts []*types.Receipt, depositContractAddr common.Addres var result error userDeposits, err := UserDeposits(receipts, depositContractAddr) if err != nil { - result = multierror.Append(result, err) + result = errors.Join(result, err) } encodedTxs := make([]hexutil.Bytes, 0, len(userDeposits)) for i, tx := range userDeposits { opaqueTx, err := types.NewTx(tx).MarshalBinary() if err != nil { - result = multierror.Append(result, fmt.Errorf("failed to encode user tx %d", i)) + result = errors.Join(result, fmt.Errorf("failed to encode user tx %d", i)) } else { encodedTxs = append(encodedTxs, opaqueTx) } diff --git a/op-node/rollup/derive/karst_nut_bundle.json b/op-node/rollup/derive/karst_nut_bundle.json deleted file mode 100644 index 6e7a043d739..00000000000 --- a/op-node/rollup/derive/karst_nut_bundle.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "metadata": { - "version": "1.0.0" - }, - "transactions": [] -} diff --git a/op-node/rollup/derive/system_config.go b/op-node/rollup/derive/system_config.go index 8e145d144ff..2540623922c 100644 --- a/op-node/rollup/derive/system_config.go +++ b/op-node/rollup/derive/system_config.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/hashicorp/go-multierror" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -41,7 +40,7 @@ var ( // UpdateSystemConfigWithL1Receipts filters all L1 receipts to find config updates and applies the config updates to the given sysCfg // Updates are applied individually, and any malformed or invalid updates are ignored. -// Any errors encountered during the update process are returned as a multierror. +// Any errors encountered during the update process are returned as a joined error. func UpdateSystemConfigWithL1Receipts(sysCfg *eth.SystemConfig, receipts []*types.Receipt, cfg *rollup.Config, l1Time uint64) error { var result error for i, rec := range receipts { @@ -58,7 +57,7 @@ func UpdateSystemConfigWithL1Receipts(sysCfg *eth.SystemConfig, receipts []*type *sysCfg = updated } else { // or append the error to the result - result = multierror.Append(result, fmt.Errorf("malformatted L1 system sysCfg log in receipt %d, log %d: %w", i, j, err)) + result = errors.Join(result, fmt.Errorf("malformatted L1 system sysCfg log in receipt %d, log %d: %w", i, j, err)) } } } diff --git a/op-node/rollup/derive/upgrade_transaction.go b/op-node/rollup/derive/upgrade_transaction.go index 5a8282da247..3e503ca8b3a 100644 --- a/op-node/rollup/derive/upgrade_transaction.go +++ b/op-node/rollup/derive/upgrade_transaction.go @@ -2,24 +2,24 @@ package derive import ( "bytes" - _ "embed" "encoding/json" "fmt" "io" "math/big" "github.com/ethereum-optimism/optimism/op-core/forks" + "github.com/ethereum-optimism/optimism/op-core/nuts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" ) -//go:embed karst_nut_bundle.json -var karstNUTBundleJSON []byte - // Network Upgrade Transactions (NUTs) are read from a JSON file and // converted into deposit transactions. +// nutBundleVersion is the only bundle schema version this reader accepts. +const nutBundleVersion = "1.0.0" + // nutMetadata contains version information for the NUT bundle format. type nutMetadata struct { Version string `json:"version"` @@ -48,6 +48,9 @@ func readNUTBundle(fork forks.Name, r io.Reader) (*nutBundle, error) { if err := json.NewDecoder(r).Decode(&bundle); err != nil { return nil, fmt.Errorf("failed to parse NUT bundle: %w", err) } + if bundle.Metadata.Version != nutBundleVersion { + return nil, fmt.Errorf("unsupported NUT bundle version: got %q, want %q", bundle.Metadata.Version, nutBundleVersion) + } bundle.ForkName = fork return &bundle, nil } @@ -97,7 +100,7 @@ func UpgradeTransactions(fork forks.Name) ([]hexutil.Bytes, uint64, error) { var bundleJSON []byte switch fork { case forks.Karst: - bundleJSON = karstNUTBundleJSON + bundleJSON = nuts.KarstNUTBundleJSON default: return nil, 0, fmt.Errorf("no NUT bundle for fork %s", fork) } diff --git a/op-node/rollup/derive/upgrade_transaction_test.go b/op-node/rollup/derive/upgrade_transaction_test.go index cf3f40d42e1..e9472faa95d 100644 --- a/op-node/rollup/derive/upgrade_transaction_test.go +++ b/op-node/rollup/derive/upgrade_transaction_test.go @@ -80,6 +80,14 @@ func TestReadNUTBundleInvalidJSON(t *testing.T) { require.Contains(t, err.Error(), "failed to parse NUT bundle") } +func TestReadNUTBundleUnsupportedVersion(t *testing.T) { + jsonData := []byte(`{"metadata":{"version":"2.0.0"},"transactions":[]}`) + _, err := readNUTBundle("Test", bytes.NewReader(jsonData)) + require.Error(t, err) + require.Contains(t, err.Error(), "unsupported NUT bundle version") + require.Contains(t, err.Error(), "2.0.0") +} + func TestNUTBundleMissingIntent(t *testing.T) { jsonData := []byte(`{ "metadata": {"version": "1.0.0"}, diff --git a/op-node/rollup/engine/engine_controller.go b/op-node/rollup/engine/engine_controller.go index fcc73b8dd93..5db7a6c7f30 100644 --- a/op-node/rollup/engine/engine_controller.go +++ b/op-node/rollup/engine/engine_controller.go @@ -28,8 +28,6 @@ const ( // We transition between the 4 EL states linearly. We spend the majority of the time in the second & fourth. // We only want to EL sync if there is no finalized block & once we finish EL sync we need to mark the last block // as finalized so we can switch to consolidation - // TODO(protocol-quest#91): We can restart EL sync & still consolidate if there finalized blocks on the execution client if the - // execution client is running in archive mode. In some cases we may want to switch back from CL to EL sync, but that is complicated. syncStatusWillStartEL // First if we are directed to EL sync, check that nothing has been finalized yet syncStatusStartedEL // Perform our EL sync syncStatusFinishedELButNotFinalized // EL sync is done, but we need to mark the final sync block as finalized @@ -617,14 +615,33 @@ func (e *EngineController) insertUnsafePayload(ctx context.Context, envelope *et FinalizedBlockHash: e.FinalizedHead().Hash, } if e.syncStatus == syncStatusFinishedELButNotFinalized { - fc.SafeBlockHash = envelope.ExecutionPayload.BlockHash - fc.FinalizedBlockHash = envelope.ExecutionPayload.BlockHash - e.SetUnsafeHead(ref) // ensure that the unsafe head stays ahead of safe/finalized labels. + offsetRef := ref + if target := sync.OffsetBlockNum(e.syncCfg.OffsetELSafe, e.rollupCfg.BlockTime, ref.Number, e.rollupCfg.Genesis.L2.Number); target < ref.Number { + d, err := e.engine.L2BlockRefByNumber(ctx, target) + if err != nil { + return derive.NewTemporaryError(fmt.Errorf("EL sync offset-derived head at block %d: %w", target, err)) + } + offsetRef = d + } + // With SupportsPostFinalizationELSync, EL sync can start even when + // there is already a finalized head. Never retract finalized or safe + // behind their prior values. + finalizedRef := offsetRef + if finalizedRef.Number < e.FinalizedHead().Number { + finalizedRef = e.FinalizedHead() + } + safeRef := offsetRef + if safeRef.Number < e.SafeL2Head().Number { + safeRef = e.SafeL2Head() + } + fc.SafeBlockHash = safeRef.Hash + fc.FinalizedBlockHash = finalizedRef.Hash + e.SetUnsafeHead(ref) e.emitter.Emit(ctx, UnsafeUpdateEvent{Ref: ref}) - e.SetLocalSafeHead(ref) - e.SetSafeHead(ref) - e.onSafeUpdate(ctx, ref, ref) - e.SetFinalizedHead(ref) + e.SetLocalSafeHead(safeRef) + e.SetSafeHead(safeRef) + e.onSafeUpdate(ctx, safeRef, safeRef) + e.SetFinalizedHead(finalizedRef) } logFn := e.logSyncProgressMaybe() defer logFn() @@ -655,7 +672,8 @@ func (e *EngineController) insertUnsafePayload(ctx context.Context, envelope *et e.emitter.Emit(ctx, UnsafeUpdateEvent{Ref: ref}) if e.syncStatus == syncStatusFinishedELButNotFinalized { - e.log.Info("Finished EL sync", "sync_duration", e.clock.Since(e.elStart), "finalized_block", ref.ID().String()) + e.log.Info("Finished EL sync", "sync_duration", e.clock.Since(e.elStart), + "unsafe_block", ref.ID().String(), "safe_finalized_block", e.SafeL2Head().ID().String()) e.syncStatus = syncStatusFinishedEL } diff --git a/op-node/rollup/engine/engine_el_sync_offset_test.go b/op-node/rollup/engine/engine_el_sync_offset_test.go new file mode 100644 index 00000000000..8b9f92cf914 --- /dev/null +++ b/op-node/rollup/engine/engine_el_sync_offset_test.go @@ -0,0 +1,164 @@ +// Tests: offset_derived at EL-sync completion (insertUnsafePayload). +package engine + +import ( + "context" + "math/big" + mrand "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-node/metrics" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/event" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-service/testutils" + "github.com/ethereum/go-ethereum/common" + gethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +type discardEmitter struct{} + +func (discardEmitter) Emit(context.Context, event.Event) {} + +type noopSyncDeriver struct{} + +func (noopSyncDeriver) OnELSyncStarted() {} + +// buildELSyncTipChain returns genesis L2 b0 .. b3 (tip) with BlockTime=2 and a valid payload for b3. +func buildELSyncTipChain(t *testing.T) (*rollup.Config, eth.L2BlockRef, eth.L2BlockRef, eth.L2BlockRef, eth.L2BlockRef, *eth.ExecutionPayloadEnvelope) { + t.Helper() + rng := mrand.New(mrand.NewSource(1234)) + refA := testutils.RandomBlockRef(rng) + refA0 := eth.L2BlockRef{ + Hash: testutils.RandomHash(rng), + Number: 0, + ParentHash: common.Hash{}, + Time: refA.Time, + L1Origin: refA.ID(), + SequenceNumber: 0, + } + cfg := &rollup.Config{ + Genesis: rollup.Genesis{ + L1: refA.ID(), + L2: refA0.ID(), + L2Time: refA0.Time, + SystemConfig: eth.SystemConfig{ + BatcherAddr: common.Address{42}, + Overhead: [32]byte{123}, + Scalar: [32]byte{42}, + GasLimit: 20_000_000, + }, + }, + BlockTime: 2, + SeqWindowSize: 2, + } + refA1 := eth.L2BlockRef{ + Hash: common.Hash{'1'}, + Number: 1, + ParentHash: refA0.Hash, + Time: refA0.Time + cfg.BlockTime, + L1Origin: refA.ID(), + SequenceNumber: 1, + } + refA2 := eth.L2BlockRef{ + Hash: common.Hash{'2'}, + Number: 2, + ParentHash: refA1.Hash, + Time: refA1.Time + cfg.BlockTime, + L1Origin: refA.ID(), + SequenceNumber: 2, + } + refA3 := eth.L2BlockRef{ + Hash: common.Hash{'3'}, + Number: 3, + ParentHash: refA2.Hash, + Time: refA2.Time + cfg.BlockTime, + L1Origin: refA.ID(), + SequenceNumber: 3, + } + aL1Info := &testutils.MockBlockInfo{ + InfoParentHash: refA.ParentHash, + InfoNum: refA.Number, + InfoTime: refA.Time, + InfoHash: refA.Hash, + InfoBaseFee: big.NewInt(1), + InfoBlobBaseFee: big.NewInt(1), + InfoReceiptRoot: gethtypes.EmptyRootHash, + InfoRoot: testutils.RandomHash(rng), + InfoGasUsed: rng.Uint64(), + } + l1Bytes, err := derive.L1InfoDepositBytes(cfg, params.SepoliaChainConfig, cfg.Genesis.SystemConfig, refA3.SequenceNumber, aL1Info, refA3.Time) + require.NoError(t, err) + payload := ð.ExecutionPayloadEnvelope{ExecutionPayload: ð.ExecutionPayload{ + ParentHash: refA3.ParentHash, + BlockNumber: eth.Uint64Quantity(refA3.Number), + Timestamp: eth.Uint64Quantity(refA3.Time), + BlockHash: refA3.Hash, + Transactions: []eth.Data{l1Bytes}, + }} + return cfg, refA0, refA1, refA2, refA3, payload +} + +func TestInsertUnsafePayload_ELSync_offsetDerived(t *testing.T) { + cfg, refA0, _, _, refA3, payload := buildELSyncTipChain(t) + + tests := []struct { + name string + offset time.Duration + want eth.L2BlockRef // local safe + finalized + stub func(eng *testutils.MockEngine) + }{ + { + name: "zero sets safe and finalized to tip", + offset: 0, + want: refA3, + stub: func(eng *testutils.MockEngine) { + eng.ExpectL2BlockRefByLabel(eth.Finalized, refA0, nil) + eng.ExpectNewPayload(payload.ExecutionPayload, nil, ð.PayloadStatusV1{Status: eth.ExecutionValid}, nil) + eng.ExpectForkchoiceUpdate(ð.ForkchoiceState{ + HeadBlockHash: refA3.Hash, + SafeBlockHash: refA3.Hash, + FinalizedBlockHash: refA3.Hash, + }, nil, ð.ForkchoiceUpdatedResult{PayloadStatus: eth.PayloadStatusV1{Status: eth.ExecutionValid}}, nil) + }, + }, + { + name: "non-zero retracts safe and finalized by ceil(offset/BlockTime)", + offset: 5 * time.Second, + want: refA0, + stub: func(eng *testutils.MockEngine) { + eng.ExpectL2BlockRefByLabel(eth.Finalized, refA0, nil) + eng.ExpectNewPayload(payload.ExecutionPayload, nil, ð.PayloadStatusV1{Status: eth.ExecutionValid}, nil) + eng.ExpectL2BlockRefByNumber(0, refA0, nil) + eng.ExpectForkchoiceUpdate(ð.ForkchoiceState{ + HeadBlockHash: refA3.Hash, + SafeBlockHash: refA0.Hash, + FinalizedBlockHash: refA0.Hash, + }, nil, ð.ForkchoiceUpdatedResult{PayloadStatus: eth.PayloadStatusV1{Status: eth.ExecutionValid}}, nil) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockEngine := &testutils.MockEngine{} + tt.stub(mockEngine) + ec := NewEngineController(context.Background(), mockEngine, testlog.Logger(t, 0), metrics.NoopMetrics, cfg, + &sync.Config{SyncMode: sync.ELSync, OffsetELSafe: tt.offset}, false, &testutils.MockL1Source{}, discardEmitter{}, nil) + ec.SyncDeriver = noopSyncDeriver{} + + err := ec.InsertUnsafePayload(context.Background(), payload, refA3) + require.NoError(t, err) + require.Equal(t, refA3, ec.unsafeHead) + require.Equal(t, tt.want, ec.localSafeHead) + require.Equal(t, tt.want, ec.FinalizedHead()) + }) + } +} diff --git a/op-node/rollup/sync/config.go b/op-node/rollup/sync/config.go index 2822be0420a..9654a053e69 100644 --- a/op-node/rollup/sync/config.go +++ b/op-node/rollup/sync/config.go @@ -1,8 +1,10 @@ package sync import ( + "errors" "fmt" "strings" + "time" ) type Mode int @@ -77,6 +79,23 @@ type Config struct { L2FollowSourceEndpoint string `json:"l2_follow_source_endpoint"` NeedInitialResetEngine bool `json:"need_initial_reset_engine"` + + // OffsetELSafe retracts safe and finalized from the EL-sync tip by floor(OffsetELSafe / L2BlockTime) blocks. + // Zero disables (safe and finalized stay at the synced tip when EL sync completes). + OffsetELSafe time.Duration `json:"offset_el_safe,omitempty"` +} + +func (c *Config) Check() error { + if c == nil { + return nil + } + if c.OffsetELSafe < 0 { + return errors.New("sync.offset-el-safe must be >= 0") + } + if c.OffsetELSafe > 0 && c.SyncMode != ELSync { + return fmt.Errorf("sync.offset-el-safe is only supported with EL sync (syncmode=%s)", ELSyncString) + } + return nil } func (c *Config) FollowSourceEnabled() bool { diff --git a/op-node/rollup/sync/start.go b/op-node/rollup/sync/start.go index 38ca44df1e8..a29b28ed93d 100644 --- a/op-node/rollup/sync/start.go +++ b/op-node/rollup/sync/start.go @@ -27,6 +27,7 @@ import ( "context" "errors" "fmt" + "time" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -46,6 +47,7 @@ type L1Chain interface { type L2Chain interface { L2BlockRefByHash(ctx context.Context, l2Hash common.Hash) (eth.L2BlockRef, error) L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error) + L2BlockRefByNumber(ctx context.Context, number uint64) (eth.L2BlockRef, error) } var ( @@ -100,6 +102,47 @@ func currentHeads(ctx context.Context, cfg *rollup.Config, l2 L2Chain) (*FindHea }, nil } +// DurationToBlocks returns ceil(offset / blockTime) as a block count. +// Ceiling is used so the retraction always covers the full requested duration. +// A zero or negative offset yields 0; zero block time yields 0. +func DurationToBlocks(offset time.Duration, blockTime uint64) uint64 { + if offset <= 0 || blockTime == 0 { + return 0 + } + sec := uint64(offset / time.Second) + return (sec + blockTime - 1) / blockTime +} + +// OffsetBlockNum returns the block number that is `offset` behind `head`, +// clamped so it never goes below `genesis`. Returns head unchanged when +// offset is zero or head is already at genesis. +func OffsetBlockNum(offset time.Duration, blockTime uint64, head uint64, genesis uint64) uint64 { + n := DurationToBlocks(offset, blockTime) + if n == 0 || head <= genesis { + return head + } + maxRetract := head - genesis + if n > maxRetract { + n = maxRetract + } + return head - n +} + +// L2HeadsForELSyncWithOffset returns the heads for an EL-sync tip: unsafe stays at tip, +// safe and finalized retract by ceil(offset / blockTime) blocks (clamped at genesis). +func L2HeadsForELSyncWithOffset(ctx context.Context, cfg *rollup.Config, l2 L2Chain, syncCfg *Config, tip eth.L2BlockRef) (*FindHeadsResult, error) { + target := OffsetBlockNum(syncCfg.OffsetELSafe, cfg.BlockTime, tip.Number, cfg.Genesis.L2.Number) + derived := tip + if target < tip.Number { + ref, err := l2.L2BlockRefByNumber(ctx, target) + if err != nil { + return nil, fmt.Errorf("EL sync offset-derived head at block %d: %w", target, err) + } + derived = ref + } + return &FindHeadsResult{Unsafe: tip, Safe: derived, Finalized: derived}, nil +} + // FindL2Heads walks back from `start` (the previous unsafe L2 block) and finds // the finalized, unsafe and safe L2 blocks. // @@ -130,7 +173,7 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain result.Unsafe.Number > cfg.Genesis.L2.Number && result.Unsafe.L1Origin.Number > cfg.Genesis.L1.Number+(RecoverMinSeqWindows*cfg.SeqWindowSize) { lgr.Warn("Attempting recovery from sync state without finality.", "head", result.Unsafe) - return &FindHeadsResult{Unsafe: result.Unsafe, Safe: result.Unsafe, Finalized: result.Unsafe}, nil + return L2HeadsForELSyncWithOffset(ctx, cfg, l2, syncCfg, result.Unsafe) } // Check if the execution engine corrupted, and forkchoice is ahead of the remaining chain: @@ -138,7 +181,7 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain if result.Unsafe.Number < result.Finalized.Number || result.Unsafe.Number < result.Safe.Number { lgr.Error("Unsafe head is behind known finalized/safe blocks, execution-engine chain must have been rewound without forkchoice update. Attempting recovery now.", "unsafe_head", result.Unsafe, "safe_head", result.Safe, "finalized_head", result.Finalized) - return &FindHeadsResult{Unsafe: result.Unsafe, Safe: result.Unsafe, Finalized: result.Unsafe}, nil + return L2HeadsForELSyncWithOffset(ctx, cfg, l2, syncCfg, result.Unsafe) } // Remember original unsafe block to determine reorg depth diff --git a/op-node/rollup/sync/start_test.go b/op-node/rollup/sync/start_test.go index e2cfee7712c..9df889a8849 100644 --- a/op-node/rollup/sync/start_test.go +++ b/op-node/rollup/sync/start_test.go @@ -3,6 +3,7 @@ package sync import ( "context" "testing" + "time" "github.com/stretchr/testify/require" @@ -339,3 +340,139 @@ func TestFindSyncStart(t *testing.T) { t.Run(testCase.Name, testCase.Run) } } + +func TestDurationToBlocks(t *testing.T) { + tests := []struct { + name string + offset time.Duration + bt uint64 + want uint64 + }{ + {"zero offset", 0, 2, 0}, + {"negative treated as zero", -time.Hour, 2, 0}, + {"zero block time", time.Hour, 0, 0}, + {"ceil BT=2 offset=3s -> 2", 3 * time.Second, 2, 2}, + {"exact BT=2 offset=4s -> 2", 4 * time.Second, 2, 2}, + {"ceil BT=4 offset=15s -> 4", 15 * time.Second, 4, 4}, + {"exact BT=4 offset=16s -> 4", 16 * time.Second, 4, 4}, + {"sub-second offset truncates", 500 * time.Millisecond, 1, 0}, + {"12h with 2s blocks", 12 * time.Hour, 2, 21600}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, DurationToBlocks(tt.offset, tt.bt)) + }) + } +} + +func TestOffsetBlockNum(t *testing.T) { + tests := []struct { + name string + offset time.Duration + bt uint64 + head uint64 + genesis uint64 + want uint64 + }{ + {"zero offset returns head", 0, 2, 100, 0, 100}, + {"head at genesis returns genesis", 10 * time.Second, 2, 0, 0, 0}, + {"head below genesis returns head", 10 * time.Second, 2, 5, 10, 5}, + {"normal retraction", 10 * time.Second, 2, 100, 0, 95}, + {"clamps to genesis", 1000 * time.Hour, 2, 10, 0, 0}, + {"non-zero genesis clamp", 10 * time.Second, 2, 15, 10, 10}, + {"ceil retraction BT=4 offset=15s", 15 * time.Second, 4, 100, 0, 96}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, OffsetBlockNum(tt.offset, tt.bt, tt.head, tt.genesis)) + }) + } +} + +type errTestFetch struct{} + +func (errTestFetch) Error() string { return "test fetch error" } + +var _ L2Chain = (*testutils.MockL2Client)(nil) + +func TestL2HeadsForELSyncWithOffset(t *testing.T) { + ctx := context.Background() + genesis := eth.BlockID{Hash: common.Hash{'g'}, Number: 0} + b0 := eth.L2BlockRef{Hash: genesis.Hash, Number: 0, ParentHash: common.Hash{}} + b1 := eth.L2BlockRef{Hash: common.Hash{'1'}, Number: 1, ParentHash: b0.Hash} + b3 := eth.L2BlockRef{Hash: common.Hash{'3'}, Number: 3, ParentHash: common.Hash{'2'}} + bt := uint64(2) + + cfg := &rollup.Config{ + Genesis: rollup.Genesis{L2: genesis}, + BlockTime: bt, + } + + tests := []struct { + name string + tip eth.L2BlockRef + offset time.Duration + stub func(m *testutils.MockL2Client) + wantSafe eth.L2BlockRef + wantErr bool + }{ + { + name: "zero offset returns tip as safe", + tip: b3, + offset: 0, + stub: func(m *testutils.MockL2Client) {}, + wantSafe: b3, + }, + { + name: "tip at genesis returns tip", + tip: b0, + offset: 100 * time.Hour, + stub: func(m *testutils.MockL2Client) {}, + wantSafe: b0, + }, + { + name: "retracts by ceil(offset/bt)", + tip: b3, + offset: 4 * time.Second, + stub: func(m *testutils.MockL2Client) { + m.ExpectL2BlockRefByNumber(1, b1, nil) + }, + wantSafe: b1, + }, + { + name: "large offset clamps to genesis", + tip: b1, + offset: 1000 * time.Hour, + stub: func(m *testutils.MockL2Client) { + m.ExpectL2BlockRefByNumber(0, b0, nil) + }, + wantSafe: b0, + }, + { + name: "fetch error propagates", + tip: b3, + offset: 3 * time.Second, + stub: func(m *testutils.MockL2Client) { + m.ExpectL2BlockRefByNumber(1, eth.L2BlockRef{}, errTestFetch{}) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &testutils.MockL2Client{} + tt.stub(m) + syncCfg := &Config{OffsetELSafe: tt.offset} + result, err := L2HeadsForELSyncWithOffset(ctx, cfg, m, syncCfg, tt.tip) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.tip, result.Unsafe, "unsafe head should always be the tip") + require.Equal(t, tt.wantSafe, result.Safe, "safe head") + require.Equal(t, tt.wantSafe, result.Finalized, "finalized head") + }) + } +} diff --git a/op-node/service.go b/op-node/service.go index 42bea8478c6..a5ed2cd3781 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -370,9 +370,13 @@ func NewSyncConfig(ctx cliiface.Context, log log.Logger) (*sync.Config, error) { L2FollowSourceEndpoint: l2FollowSourceEndpoint, // Sequencer needs a manual initial reset when follow source NeedInitialResetEngine: ctx.Bool(flags.SequencerEnabledFlag.Name) && l2FollowSourceEndpoint != "", + OffsetELSafe: ctx.Duration(flags.SyncModeOffsetELSafeFlag.Name), } if ctx.Bool(flags.L2EngineSyncEnabled.Name) { cfg.SyncMode = sync.ELSync } + if err := cfg.Check(); err != nil { + return nil, err + } return cfg, nil } diff --git a/op-node/service_test.go b/op-node/service_test.go index 7bcd0171cae..c700e683893 100644 --- a/op-node/service_test.go +++ b/op-node/service_test.go @@ -15,6 +15,7 @@ func syncConfigCliApp() *cli.App { flags.L2EngineSyncEnabled, flags.SyncModeFlag, flags.SyncModeReqRespFlag, + flags.SyncModeOffsetELSafeFlag, flags.L2FollowSource, flags.L2EngineKind, flags.SkipSyncStartCheck, diff --git a/op-program/Dockerfile.vmcompat b/op-program/Dockerfile.vmcompat index cdbd0136af8..7de8f1d32e0 100644 --- a/op-program/Dockerfile.vmcompat +++ b/op-program/Dockerfile.vmcompat @@ -1,9 +1,11 @@ ARG GO_VERSION=1.24.10-alpine3.21 ARG VM_TARGET=current +ARG VM_COMPAT_TASK=analyze-op-program-client FROM golang:${GO_VERSION} AS builder -# Redeclare ARG to make it available in this build stage +# Redeclare ARGs to make them available in this build stage ARG VM_TARGET +ARG VM_COMPAT_TASK # Install dependencies RUN apk add --no-cache make git bash jq llvm just @@ -31,8 +33,9 @@ WORKDIR /app/op-program ENV FINDINGS_OUTPUT_PATH=/app/op-program/vm-compat-findings.json RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \ touch $FINDINGS_OUTPUT_PATH && \ - just "analyze-op-program-client-${VM_TARGET}"; echo $? > /app/op-program/vm-compat-exit-code + just "${VM_COMPAT_TASK}-${VM_TARGET}"; echo $? > /app/op-program/vm-compat-exit-code FROM scratch AS export COPY --from=builder /app/op-program/vm-compat-findings.json . COPY --from=builder /app/op-program/vm-compat-exit-code . +COPY --from=builder /app/op-program/compatibility-test/baseline-cannon-multithreaded-64.json . diff --git a/op-program/README.md b/op-program/README.md index 97e9369f03a..37add4f0ab6 100644 --- a/op-program/README.md +++ b/op-program/README.md @@ -1,5 +1,9 @@ # op-program +> **Deprecated:** op-program is deprecated and being replaced by [kona-client](../../rust/kona/). +> Existing deployments will be supported until the Karst hardfork, at which point migration to kona-client is required. +> See the [end-of-support notice](https://docs.optimism.io/notices/op-geth-deprecation) for full details and migration guidance. + Implements a fault proof program that runs through the rollup state-transition to verify an L2 output from L1 inputs. This verifiable output can then resolve a disputed output on L1. diff --git a/op-program/client/l2/engineapi/block_processor.go b/op-program/client/l2/engineapi/block_processor.go index 71e194f7279..c1d97021929 100644 --- a/op-program/client/l2/engineapi/block_processor.go +++ b/op-program/client/l2/engineapi/block_processor.go @@ -1,6 +1,7 @@ package engineapi import ( + "context" "errors" "fmt" "math/big" @@ -87,7 +88,7 @@ func NewBlockProcessorFromHeader(provider BlockDataProvider, h *types.Header) (* header.Number = new(big.Int).Add(parentHeader.Number, common.Big1) header.BaseFee = eip1559.CalcBaseFee(provider.Config(), parentHeader, header.Time) header.GasUsed = 0 - gasPool := new(core.GasPool).AddGas(header.GasLimit) + gasPool := core.NewGasPool(header.GasLimit) mkEVM := func() *vm.EVM { // Unfortunately this is not part of any Geth environment setup, // we just have to apply it, like how the Geth block-builder worker does. @@ -148,10 +149,11 @@ func (b *BlockProcessor) CheckTxWithinGasLimit(tx *types.Transaction) error { func (b *BlockProcessor) AddTx(tx *types.Transaction) (*types.Receipt, error) { txIndex := len(b.transactions) b.state.SetTxContext(tx.Hash(), txIndex) - receipt, err := core.ApplyTransaction(b.evm, b.gasPool, b.state, b.header, tx, &b.header.GasUsed) + receipt, err := core.ApplyTransaction(b.evm, b.gasPool, b.state, b.header, tx) if err != nil { return nil, fmt.Errorf("failed to apply transaction to L2 block (tx %d): %w", txIndex, err) } + b.header.GasUsed = b.gasPool.Used() b.receipts = append(b.receipts, receipt) b.transactions = append(b.transactions, tx) return receipt, nil @@ -178,7 +180,7 @@ func (b *BlockProcessor) Assemble() (*types.Block, types.Receipts, error) { } } - block, err := b.dataProvider.Engine().FinalizeAndAssemble(b.dataProvider, b.header, b.state, &body, b.receipts) + block, err := b.dataProvider.Engine().FinalizeAndAssemble(context.Background(), b.dataProvider, b.header, b.state, &body, b.receipts) if err != nil { return nil, nil, err } diff --git a/op-program/compatibility-test/baseline-cannon-multithreaded-64.json b/op-program/compatibility-test/baseline-cannon-multithreaded-64.json index d96da21bd44..d39e67b3dbd 100644 --- a/op-program/compatibility-test/baseline-cannon-multithreaded-64.json +++ b/op-program/compatibility-test/baseline-cannon-multithreaded-64.json @@ -161,6 +161,167 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "03f6054d3ceafd359f0d20f26ae776363e816cac1eefdc1444fc2328a0d4bece" }, + { + "callStack": { + "function": "syscall.lstat", + "callStack": { + "function": "syscall.Lstat", + "callStack": { + "function": "os.lstatNolog", + "callStack": { + "function": "os.Lstat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5006", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "06ae1d22c0f1c73614b534e885f5b4db6075d279f6dffe775df78c23cf5e0eb8" + }, + { + "callStack": { + "function": "syscall.lstat", + "callStack": { + "function": "syscall.Lstat", + "callStack": { + "function": "os.lstatNolog", + "callStack": { + "function": "os.Lstat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5006", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "0996b58ff9dbdf45b77daa8f6146209bf63638e958dc8ee72a9b4da5f92763bd" + }, { "callStack": { "function": "syscall.Getdents", @@ -444,6 +605,91 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "0f56d4e8e81f5e0c3269a52f5765470380a9dc5219a365b5557faa140abe826c" }, + { + "callStack": { + "function": "syscall.lstat", + "callStack": { + "function": "syscall.Lstat", + "callStack": { + "function": "os.lstatNolog", + "callStack": { + "function": "os.Lstat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5006", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "0fa8a9246a03516588d4a1760b9cb6120c0fc83f032a8ac57d67beb819c43a54" + }, { "callStack": { "function": "syscall.Getdents", @@ -1063,49 +1309,55 @@ }, { "callStack": { - "function": "syscall.lstat", + "function": "syscall.Renameat", "callStack": { - "function": "syscall.Lstat", + "function": "os.rename", "callStack": { - "function": "os.lstatNolog", + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", "callStack": { - "function": "os.Lstat", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "os.MkdirAll", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } } } } @@ -1128,25 +1380,98 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5006", + "message": "Potential Incompatible Syscall Detected: 5254", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "2c98b4018477201b7ce1133d2bda2e1ca0724645155e2352b6c087db6efba866" + "hash": "2b22c1446e342415958c2a2d3fe3fee1e2a2d79c22512331600e50b0fa16cb5b" }, { "callStack": { - "function": "syscall.Fsync", + "function": "syscall.lstat", "callStack": { - "function": "internal/poll.(*FD).Fsync", + "function": "syscall.Lstat", "callStack": { - "function": "os.(*File).Sync", + "function": "os.lstatNolog", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTableMeta).write", + "function": "os.Lstat", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "function": "os.MkdirAll", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5006", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "2c98b4018477201b7ce1133d2bda2e1ca0724645155e2352b6c087db6efba866" + }, + { + "callStack": { + "function": "syscall.Fsync", + "callStack": { + "function": "internal/poll.(*FD).Fsync", + "callStack": { + "function": "os.(*File).Sync", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTableMeta).write", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { @@ -1457,6 +1782,82 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "38cd7836fbadcf343f4d284aa10b7dc8a1eec575e9418b2693cc06699d24f1db" }, + { + "callStack": { + "function": "syscall.Renameat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5254", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "3b5c34daa2681b5a44c61c53e3712bf443f75a565e3f474807eacc066ee3f5e6" + }, { "callStack": { "function": "syscall.unlinkat", @@ -2427,6 +2828,76 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "5aa4ea1867769c886a8ee4d9e50a4c84af6eb8375ac0fec45cb04006c8689b32" }, + { + "callStack": { + "function": "syscall.Renameat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5254", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "61f3423bb7162c0f7dfcf8406644452ff6f0e95195a4a581dce0891b29c52d00" + }, { "callStack": { "function": "syscall.getsockname", @@ -2958,11 +3429,11 @@ "callStack": { "function": "os.Lstat", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { @@ -2972,27 +3443,21 @@ "callStack": { "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } + "function": "main.main" } } } @@ -3016,157 +3481,23 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "6d5047b0af006821b7cc913288761108f536df9958ba8b5ad350a765cec6c6f3" + "hash": "6d87b8772a23ccd1f6646911bfd16f29f4da1c80302c3d122b82d186d4ba3d32" }, { "callStack": { - "function": "syscall.lstat", + "function": "syscall.Mkdirat", "callStack": { - "function": "syscall.Lstat", + "function": "os.MkdirAll", "callStack": { - "function": "os.lstatNolog", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "os.Lstat", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5006", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "6d87b8772a23ccd1f6646911bfd16f29f4da1c80302c3d122b82d186d4ba3d32" - }, - { - "callStack": { - "function": "syscall.lstat", - "callStack": { - "function": "syscall.Lstat", - "callStack": { - "function": "os.lstatNolog", - "callStack": { - "function": "os.Lstat", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5006", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "6dd853e3a5b17ed9b4b5dd2ee09ae51b202df9f3bd1b878aab86b7b5142345ba" - }, - { - "callStack": { - "function": "syscall.Mkdirat", - "callStack": { - "function": "os.MkdirAll", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { @@ -3210,73 +3541,6 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "6df1d7fe5b04c477158fbb622ffb2e20d12cde4a5d5e575ee66cf4aea5fa5941" }, - { - "callStack": { - "function": "syscall.Renameat", - "callStack": { - "function": "os.rename", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5254", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "7264bfa70c46f9429fae6fcfabbb33969282137e01a60e0e616fe1c705fe7c2e" - }, { "callStack": { "function": "syscall.unlinkat", @@ -3551,6 +3815,79 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "778448042d300f48cbfd803335851bbaf68d15e117bdd66c228c59bd6fb8f5db" }, + { + "callStack": { + "function": "syscall.Renameat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5254", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "78716f755caf94c6c96b11050154bc60dcb4ce95940f4c9fc707b81a134478b7" + }, { "callStack": { "function": "syscall.lstat", @@ -3561,35 +3898,59 @@ "callStack": { "function": "os.Lstat", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", + "function": "os.rename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } } } } @@ -3613,7 +3974,7 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "7b89a7bb52f9d3eed3ae13645aaa749b11ce906c25ed416cfcc53df7bee9a636" + "hash": "7b57d9f36615c7f04a543ef1907e0a7737d9ed8aea1b74744d5c1ec8750e3cad" }, { "callStack": { @@ -3783,11 +4144,90 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5248", + "message": "Potential Incompatible Syscall Detected: 5248", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "82e9dbb73a6d10e905a534b271e67ccf5b62f19143fd7298b83a684dea6d7d15" + }, + { + "callStack": { + "function": "syscall.lstat", + "callStack": { + "function": "syscall.Lstat", + "callStack": { + "function": "os.lstatNolog", + "callStack": { + "function": "os.Lstat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5006", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "82e9dbb73a6d10e905a534b271e67ccf5b62f19143fd7298b83a684dea6d7d15" + "hash": "8412c11a24a875e9d52d1633ecf24a20bca0cbff3fe5bb14fd631f9202706125" }, { "callStack": { @@ -3923,99 +4363,35 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "89aed313797fadc49483285b760c7f4db04a02d4becda9527e90f90c8777d596" }, - { - "callStack": { - "function": "syscall.lstat", - "callStack": { - "function": "syscall.Lstat", - "callStack": { - "function": "os.lstatNolog", - "callStack": { - "function": "os.Lstat", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5006", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "8a7a2d8f73ec3aa3965a28c463f58beee81aca199ade15ae318b01f9cb26d6ca" - }, { "callStack": { "function": "syscall.Renameat", "callStack": { "function": "os.rename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { @@ -4058,7 +4434,7 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "8f8d0e96f43d447cb45e3b02240ad9c72b8550eb34338f645fc7fd5cd72c5d80" + "hash": "8a0fe9cd4a47c59d80a971cbc9364081ef57e2b82cbf395596dbe6275fc6ff6f" }, { "callStack": { @@ -5709,45 +6085,66 @@ }, { "callStack": { - "function": "internal/syscall/unix.Unlinkat", + "function": "syscall.lstat", "callStack": { - "function": "os.removeAllFrom", + "function": "syscall.Lstat", "callStack": { - "function": "os.removeAll", + "function": "os.lstatNolog", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", + "function": "os.Lstat", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "os.rename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } } } } @@ -5768,37 +6165,37 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5253", + "message": "Potential Incompatible Syscall Detected: 5006", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "c8633d83d79a6b6e7a95595faed154b929aad5fb5df4ae8b563997f8b40a81d2" + "hash": "c426099923bba4df822f5f375db183685b2c1b9e64ebb329721153c1b16ba727" }, { "callStack": { - "function": "syscall.Renameat", + "function": "internal/syscall/unix.Unlinkat", "callStack": { - "function": "os.rename", + "function": "os.removeAllFrom", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "function": "os.removeAll", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { @@ -5835,27 +6232,27 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5254", + "message": "Potential Incompatible Syscall Detected: 5253", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "c9b1d2531f91d2624fafd956e704578acfabc1e8dc8397f304cc3c3227d1eaa0" + "hash": "c8633d83d79a6b6e7a95595faed154b929aad5fb5df4ae8b563997f8b40a81d2" }, { "callStack": { - "function": "syscall.Ftruncate", + "function": "syscall.Renameat", "callStack": { - "function": "internal/poll.(*FD).Ftruncate", + "function": "os.rename", "callStack": { - "function": "os.(*File).Truncate", + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.truncateFreezerFile", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repairIndex", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { @@ -5871,21 +6268,27 @@ "callStack": { "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } } } } @@ -5908,53 +6311,59 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5075", + "message": "Potential Incompatible Syscall Detected: 5254", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "cc8b4fb7fc2f9643461d8f404983e85ba0dadace4e9a7f20ac5abb9a9d166eb3" + "hash": "c9d98ec1249362ee6592736b088f256e856fdacf8eb8c0fa286c253713b38153" }, { "callStack": { - "function": "syscall.unlinkat", + "function": "syscall.Ftruncate", "callStack": { - "function": "os.Remove", + "function": "internal/poll.(*FD).Ftruncate", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesAfter", + "function": "os.(*File).Truncate", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.truncateFreezerFile", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repairIndex", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } } } } @@ -5975,11 +6384,11 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5253", + "message": "Potential Incompatible Syscall Detected: 5075", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "cc997c8afa8cc133e5902d41917b914718f94101f2833671821d7f781e6f00c6" + "hash": "cc8b4fb7fc2f9643461d8f404983e85ba0dadace4e9a7f20ac5abb9a9d166eb3" }, { "callStack": { @@ -5987,7 +6396,7 @@ "callStack": { "function": "os.Remove", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesBefore", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesAfter", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", "callStack": { @@ -5997,7 +6406,7 @@ "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { @@ -6046,7 +6455,7 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "cd18f32ac0a8a69bf5e43077e0c0a705179b1bbe54a06d3909559f5f9579e48e" + "hash": "cc997c8afa8cc133e5902d41917b914718f94101f2833671821d7f781e6f00c6" }, { "callStack": { @@ -6054,7 +6463,7 @@ "callStack": { "function": "os.Remove", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesAfter", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesBefore", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", "callStack": { @@ -6113,7 +6522,7 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "d00bd4d597f84c2993473ae7955ef6f975124fd6ee0d68efebe11432d91b397b" + "hash": "cd18f32ac0a8a69bf5e43077e0c0a705179b1bbe54a06d3909559f5f9579e48e" }, { "callStack": { @@ -6123,15 +6532,15 @@ "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesAfter", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateHead", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", "callStack": { "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { @@ -6180,58 +6589,49 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "d09e885be58cf98cdf33889948720f65e679f2fa36f273ce8853cacfe094f559" + "hash": "d00bd4d597f84c2993473ae7955ef6f975124fd6ee0d68efebe11432d91b397b" }, { "callStack": { - "function": "syscall.lstat", + "function": "syscall.unlinkat", "callStack": { - "function": "syscall.Lstat", + "function": "os.Remove", "callStack": { - "function": "os.lstatNolog", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).releaseFilesAfter", "callStack": { - "function": "os.Lstat", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateHead", "callStack": { - "function": "os.rename", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } + "function": "main.main" } } } @@ -6252,11 +6652,11 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5006", + "message": "Potential Incompatible Syscall Detected: 5253", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "d686f160fce1c757640f7bd3cf27a9bcef6be580d9013eb97c5d90e3894e3353" + "hash": "d09e885be58cf98cdf33889948720f65e679f2fa36f273ce8853cacfe094f559" }, { "callStack": { @@ -6328,79 +6728,6 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "d989a5623edef4b247a30334fc72fc256705ebd0248dfbbcc8f3af968e4e0aa2" }, - { - "callStack": { - "function": "syscall.Renameat", - "callStack": { - "function": "os.rename", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5254", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "dc67d1106742a2d594a32323fb1fc0a8a708dc1269d21e8a40d7828efe6a4e11" - }, { "callStack": { "function": "golang.org/x/sys/unix.Flock", @@ -6742,6 +7069,88 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "e2931e3ca1b5c4bf301ab7cfa9c2f36c6b1530ccf650e232deba012586ee63da" }, + { + "callStack": { + "function": "syscall.lstat", + "callStack": { + "function": "syscall.Lstat", + "callStack": { + "function": "os.lstatNolog", + "callStack": { + "function": "os.Lstat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5006", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "e7cfe92b0bf574605afe59d9486f60bef5fb314999f59f750026a7ffd7bfa915" + }, { "callStack": { "function": "syscall.sendto", @@ -6798,11 +7207,81 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5050", + "message": "Potential Incompatible Syscall Detected: 5050", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "eb3121306d79c84b8f563cde55139daae0c0c84d01e15d996d229beb70b06ffd" + }, + { + "callStack": { + "function": "syscall.Renameat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5254", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "eb3121306d79c84b8f563cde55139daae0c0c84d01e15d996d229beb70b06ffd" + "hash": "ebac7f7a781278a9fd169f67e2bf60731c703a46985a763c6165d208acdb30df" }, { "callStack": { @@ -6941,6 +7420,82 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "eded605ed37bcf2d0ce4a8b37be3f344c36d4e4ab9aa80e1040de8bdcd498f68" }, + { + "callStack": { + "function": "syscall.Renameat", + "callStack": { + "function": "os.rename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential Incompatible Syscall Detected: 5254", + "severity": "CRITICAL", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "f0fa68c170ab0df57af3bad8c760d1dba7c80f44fc36e797a6c87a8002876770" + }, { "callStack": { "function": "syscall.Ftruncate", @@ -7090,45 +7645,48 @@ "callStack": { "function": "os.rename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", + "function": "github.com/ethereum/go-ethereum/core/rawdb.atomicRename", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } } } } @@ -7158,7 +7716,7 @@ "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "f3a50db8c83833e7d4c35d7a6478aff93d9cd4c1c3eb1f743f5f1279735f69ae" + "hash": "f4bf045e983c5146fa01ea1b023acb1a2ef3c29012b7cc969c966c5c6df0aaa4" }, { "callStack": { @@ -7213,189 +7771,31 @@ "callStack": { "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5072", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "f65acfc158aa93245e1a287676efcd7cafc14d65a2d2c2b9da5dc114e016c55c" - }, - { - "callStack": { - "function": "syscall.lstat", - "callStack": { - "function": "syscall.Lstat", - "callStack": { - "function": "os.lstatNolog", - "callStack": { - "function": "os.Lstat", - "callStack": { - "function": "os.rename", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential Incompatible Syscall Detected: 5006", - "severity": "CRITICAL", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "fdbf630978d296817cc6c5ef543cefc7b7961bf3fd35b783c96a05d7564e7554" - }, - { - "callStack": { - "function": "syscall.lstat", - "callStack": { - "function": "syscall.Lstat", - "callStack": { - "function": "os.lstatNolog", - "callStack": { - "function": "os.Lstat", - "callStack": { - "function": "os.rename", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.copyFrom", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } + "function": "main.main" } } } @@ -7419,11 +7819,11 @@ } } }, - "message": "Potential Incompatible Syscall Detected: 5006", + "message": "Potential Incompatible Syscall Detected: 5072", "severity": "CRITICAL", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "ff622f26ef2663958e5f8aa8ac981fd832eff1a3dc04fbc9cc0b6030f9d2be67" + "hash": "f65acfc158aa93245e1a287676efcd7cafc14d65a2d2c2b9da5dc114e016c55c" }, { "callStack": { @@ -7974,6 +8374,91 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "09d1aa443d0f4b91a7a8ffad94aca2804b98bead8b6569ef94ad238892760e3f" }, + { + "callStack": { + "function": "runtime.netpollclose", + "callStack": { + "function": "internal/poll.runtime_pollClose", + "callStack": { + "function": "internal/poll.(*FD).destroy", + "callStack": { + "function": "internal/poll.(*FD).decref", + "callStack": { + "function": "internal/poll.(*FD).Close", + "callStack": { + "function": "os.(*file).close", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential NOOP Syscall Detected: 5208", + "severity": "WARNING", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "0aa642315864f7a6cea348d9b0223af648aa8c1e145272f76147904e78476bac" + }, { "callStack": { "function": "runtime.rtsigprocmask", @@ -8223,88 +8708,6 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "0fd0807c39d90eac11f7ba18640a8d84c4ff43e4f0ee5a0a3d241d0c221cdbe6" }, - { - "callStack": { - "function": "runtime.netpollclose", - "callStack": { - "function": "internal/poll.runtime_pollClose", - "callStack": { - "function": "internal/poll.(*FD).destroy", - "callStack": { - "function": "internal/poll.(*FD).decref", - "callStack": { - "function": "internal/poll.(*FD).Close", - "callStack": { - "function": "os.(*file).close", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).Close", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential NOOP Syscall Detected: 5208", - "severity": "WARNING", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "10381145148ea319e3bb3b16d0a8394de1387598cfaa2eafb8540d143859dc9d" - }, { "callStack": { "function": "syscall.Seek", @@ -8913,127 +9316,45 @@ "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).getIndices", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).sizeHidden", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).sizeNolock", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential NOOP Syscall Detected: 5016", - "severity": "WARNING", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "1a17f6a4426d46a98775a92be872adda56c37eae7e8d7500ff962b3742fd03d4" - }, - { - "callStack": { - "function": "runtime.netpollclose", - "callStack": { - "function": "internal/poll.runtime_pollClose", - "callStack": { - "function": "internal/poll.(*FD).destroy", - "callStack": { - "function": "internal/poll.(*FD).decref", - "callStack": { - "function": "internal/poll.(*FD).Close", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).sizeHidden", "callStack": { - "function": "os.(*file).close", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).sizeNolock", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).Close", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } + "function": "main.main" } } } @@ -9058,11 +9379,11 @@ } } }, - "message": "Potential NOOP Syscall Detected: 5208", + "message": "Potential NOOP Syscall Detected: 5016", "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "1c8054618e5968fa625805588ab6e40d870aaacf30b4d508f5013c32bf6c21ec" + "hash": "1a17f6a4426d46a98775a92be872adda56c37eae7e8d7500ff962b3742fd03d4" }, { "callStack": { @@ -9413,68 +9734,6 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "25aa292de24f2ca6b4287b07e22698bbcf7879d563a237abd6100e67c35e74b1" }, - { - "callStack": { - "function": "syscall.openat", - "callStack": { - "function": "os.open", - "callStack": { - "function": "os.openFileNolog", - "callStack": { - "function": "os.OpenFile", - "callStack": { - "function": "github.com/tklauser/numcpus.getConfigured", - "callStack": { - "function": "github.com/tklauser/go-sysconf.getNprocsConf", - "callStack": { - "function": "github.com/tklauser/go-sysconf.sysconf", - "callStack": { - "function": "github.com/shirou/gopsutil/cpu.init.1" - } - } - } - } - } - } - } - }, - "message": "Potential NOOP Syscall Detected: 5247", - "severity": "WARNING", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "25cc5ca17434c74a54e80ce5d474e3147988aaf330ec85bf8917387940beff93" - }, - { - "callStack": { - "function": "syscall.openat", - "callStack": { - "function": "os.open", - "callStack": { - "function": "os.openFileNolog", - "callStack": { - "function": "os.OpenFile", - "callStack": { - "function": "github.com/tklauser/go-sysconf.getNprocsProcStat", - "callStack": { - "function": "github.com/tklauser/go-sysconf.getNprocs", - "callStack": { - "function": "github.com/tklauser/go-sysconf.sysconf", - "callStack": { - "function": "github.com/shirou/gopsutil/cpu.init.1" - } - } - } - } - } - } - } - }, - "message": "Potential NOOP Syscall Detected: 5247", - "severity": "WARNING", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "264a9b6bf20decd921bcba30b021003c7e58808a9e6255df08fb2d477d5d7e41" - }, { "callStack": { "function": "syscall.pread", @@ -10496,19 +10755,19 @@ }, { "callStack": { - "function": "runtime.netpollclose", + "function": "syscall.Seek", "callStack": { - "function": "internal/poll.runtime_pollClose", + "function": "internal/poll.(*FD).Seek", "callStack": { - "function": "internal/poll.(*FD).destroy", + "function": "os.(*File).seek", "callStack": { - "function": "internal/poll.(*FD).decref", + "function": "os.(*File).Seek", "callStack": { - "function": "internal/poll.(*FD).Close", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTableMeta).write", "callStack": { - "function": "os.(*file).close", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repairIndex", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).Close", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", "callStack": { @@ -10516,7 +10775,7 @@ "callStack": { "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", "callStack": { "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { @@ -10564,63 +10823,97 @@ } } }, - "message": "Potential NOOP Syscall Detected: 5208", + "message": "Potential NOOP Syscall Detected: 5008", "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "3db51aeb89a6648e72f98a600abe8652c317376f06e37993409d1cc8123a8f3e" + "hash": "3e403a72c8f935204762e029beeea1b4a22a00024b177e59222ca42e3015fb62" }, { "callStack": { - "function": "syscall.Seek", + "function": "runtime.netpollinit", "callStack": { - "function": "internal/poll.(*FD).Seek", + "function": "runtime.netpollGenericInit", "callStack": { - "function": "os.(*File).seek", + "function": "runtime.(*timers).addHeap", "callStack": { - "function": "os.(*File).Seek", + "function": "runtime.(*timer).maybeAdd", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTableMeta).write", + "function": "runtime.(*timer).modify", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repairIndex", + "function": "time.newTimer", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).repair", + "function": "time.AfterFunc", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", + "function": "crypto/internal/sysrand.Read", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "crypto/internal/entropy.Depleted", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "crypto/internal/fips140/drbg.Read", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "function": "crypto/internal/fips140/drbg.ReadWithReader", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "crypto/internal/fips140/ecdh.GenerateKey[go.shape.*crypto/internal/fips140/nistec.P256Point]", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "crypto/ecdh.init.func1" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential NOOP Syscall Detected: 5285", + "severity": "WARNING", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "3fa4d4cf151ccbb224d20c5639a72db890cce8eb061595c1b12912252752ed2e" + }, + { + "callStack": { + "function": "internal/syscall/unix.Openat", + "callStack": { + "function": "os.openDirAt", + "callStack": { + "function": "os.removeAllFrom", + "callStack": { + "function": "os.removeAll", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } + "function": "main.main" } } } @@ -10640,39 +10933,66 @@ } } }, - "message": "Potential NOOP Syscall Detected: 5008", + "message": "Potential NOOP Syscall Detected: 5247", "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "3e403a72c8f935204762e029beeea1b4a22a00024b177e59222ca42e3015fb62" + "hash": "3ff85d066f3a8e5eeceadcafd8ab19c9f18bae5eb59270a54d4a629fdd6a5cec" }, { "callStack": { - "function": "runtime.netpollinit", + "function": "syscall.Seek", "callStack": { - "function": "runtime.netpollGenericInit", + "function": "internal/poll.(*FD).Seek", "callStack": { - "function": "runtime.(*timers).addHeap", + "function": "os.(*File).seek", "callStack": { - "function": "runtime.(*timer).maybeAdd", + "function": "os.(*File).Seek", "callStack": { - "function": "runtime.(*timer).modify", + "function": "github.com/ethereum/go-ethereum/core/rawdb.decodeV1", "callStack": { - "function": "time.newTimer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newMetadata", "callStack": { - "function": "time.AfterFunc", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", "callStack": { - "function": "crypto/internal/sysrand.Read", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "crypto/internal/entropy.Depleted", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "crypto/internal/fips140/drbg.Read", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "crypto/internal/fips140/drbg.ReadWithReader", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "crypto/internal/fips140/ecdh.GenerateKey[go.shape.*crypto/internal/fips140/nistec.P256Point]", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "crypto/ecdh.init.func1" + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } } } } @@ -10686,51 +11006,72 @@ } } }, - "message": "Potential NOOP Syscall Detected: 5285", + "message": "Potential NOOP Syscall Detected: 5008", "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "3fa4d4cf151ccbb224d20c5639a72db890cce8eb061595c1b12912252752ed2e" + "hash": "4073c2fd1c0a33923b3f4b1133ad274f7e6d9c6fb63934b178deeb43ab91c525" }, { "callStack": { - "function": "internal/syscall/unix.Openat", + "function": "runtime.netpollclose", "callStack": { - "function": "os.openDirAt", + "function": "internal/poll.runtime_pollClose", "callStack": { - "function": "os.removeAllFrom", + "function": "internal/poll.(*FD).destroy", "callStack": { - "function": "os.removeAll", + "function": "internal/poll.(*FD).decref", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.cleanup", + "function": "internal/poll.(*FD).Close", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "os.(*file).close", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } } } } @@ -10750,57 +11091,63 @@ } } }, - "message": "Potential NOOP Syscall Detected: 5247", + "message": "Potential NOOP Syscall Detected: 5208", "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "3ff85d066f3a8e5eeceadcafd8ab19c9f18bae5eb59270a54d4a629fdd6a5cec" + "hash": "411c4d176eaff3c3b1554710268b4a624c1b39229515fd631d78dcdd447f59b5" }, { "callStack": { - "function": "syscall.Seek", + "function": "syscall.openat", "callStack": { - "function": "internal/poll.(*FD).Seek", + "function": "os.open", "callStack": { - "function": "os.(*File).seek", + "function": "os.openFileNolog", "callStack": { - "function": "os.(*File).Seek", + "function": "os.OpenFile", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.decodeV1", + "function": "os.CreateTemp", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newMetadata", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", "callStack": { - "function": "main.main" + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } } } } @@ -10823,11 +11170,11 @@ } } }, - "message": "Potential NOOP Syscall Detected: 5008", + "message": "Potential NOOP Syscall Detected: 5247", "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "4073c2fd1c0a33923b3f4b1133ad274f7e6d9c6fb63934b178deeb43ab91c525" + "hash": "4411d60f4ac9fe15bdc7f0c0a39f507f1c4b7fd0aa136835f3bfcea28542dd1f" }, { "callStack": { @@ -12141,6 +12488,91 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "5d3a689f37f13e8a93edd916d12bd088bc169370b839012a3d70c80605f7ce25" }, + { + "callStack": { + "function": "syscall.openat", + "callStack": { + "function": "os.open", + "callStack": { + "function": "os.openFileNolog", + "callStack": { + "function": "os.OpenFile", + "callStack": { + "function": "os.CreateTemp", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential NOOP Syscall Detected: 5247", + "severity": "WARNING", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "60037b72581e16f39e1686ca7432c99a9d7d77df96c7ddba2a221b0bccbe0bc1" + }, { "callStack": { "function": "syscall.pread", @@ -13249,6 +13681,85 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "76a0cd291d7265fc9d42368b7a805c38ae68d7c749a13ded8197d58edd41fcbb" }, + { + "callStack": { + "function": "runtime.netpollclose", + "callStack": { + "function": "internal/poll.runtime_pollClose", + "callStack": { + "function": "internal/poll.(*FD).destroy", + "callStack": { + "function": "internal/poll.(*FD).decref", + "callStack": { + "function": "internal/poll.(*FD).Close", + "callStack": { + "function": "os.(*file).close", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewTrienodeFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential NOOP Syscall Detected: 5208", + "severity": "WARNING", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "7958ada6f12bf691f2b12e9b1f747a24d155195af86aaad97c8851bdbaf0dc1a" + }, { "callStack": { "function": "syscall.pread", @@ -13842,11 +14353,68 @@ "callStack": { "function": "os.OpenFile", "callStack": { - "function": "github.com/shirou/gopsutil/internal/common.ReadLinesOffsetN", + "function": "os.CreateTemp", "callStack": { - "function": "github.com/shirou/gopsutil/cpu.TimesWithContext", + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", "callStack": { - "function": "github.com/shirou/gopsutil/cpu.init.0" + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).loadTransactions", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).consolidatedBlockByHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } } } } @@ -13858,7 +14426,7 @@ "severity": "WARNING", "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "8d38f688701e814346859fe44684daab1dc85265ed6085e72bed1fdca7229992" + "hash": "8f1b3fc4a8635bc8496e217b9f22ca9d3d9865998f118bbf5b29f73ba7d37e0a" }, { "callStack": { @@ -15793,6 +16361,85 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "b2c474a2b17e727e61438a03388c628428fccb788724919cb730454782a06291" }, + { + "callStack": { + "function": "runtime.netpollclose", + "callStack": { + "function": "internal/poll.runtime_pollClose", + "callStack": { + "function": "internal/poll.(*FD).destroy", + "callStack": { + "function": "internal/poll.(*FD).decref", + "callStack": { + "function": "internal/poll.(*FD).Close", + "callStack": { + "function": "os.(*file).close", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential NOOP Syscall Detected: 5208", + "severity": "WARNING", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "b3e41a10c92847dc56df6b5179354de7e286cbe71c442dc60dbb8c988e9c409d" + }, { "callStack": { "function": "runtime.netpollinit", @@ -16679,82 +17326,6 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "cfb2a6ede71f8e6eb371e21eaf0e5bdaae0f030459703325666debde21f72ab6" }, - { - "callStack": { - "function": "runtime.netpollclose", - "callStack": { - "function": "internal/poll.runtime_pollClose", - "callStack": { - "function": "internal/poll.(*FD).destroy", - "callStack": { - "function": "internal/poll.(*FD).decref", - "callStack": { - "function": "internal/poll.(*FD).Close", - "callStack": { - "function": "os.(*file).close", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).Close", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newTable", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", - "callStack": { - "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", - "callStack": { - "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", - "callStack": { - "function": "main.main" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "message": "Potential NOOP Syscall Detected: 5208", - "severity": "WARNING", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "d251796d0ea009aca0fd38f5a7077750b11a78b763a676cee7dac43fb43615ff" - }, { "callStack": { "function": "runtime.netpollclose", @@ -17643,6 +18214,85 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "e765d842c669be18a8bd4092a0a168e55aaf724ff54d00020da8ca27b872f888" }, + { + "callStack": { + "function": "syscall.openat", + "callStack": { + "function": "os.open", + "callStack": { + "function": "os.openFileNolog", + "callStack": { + "function": "os.OpenFile", + "callStack": { + "function": "os.CreateTemp", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.reset", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).resetTo", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*freezerTable).truncateTail", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.(*Freezer).repair", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.newResettableFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/core/rawdb.NewStateFreezer", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.repairHistory", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb/pathdb.New", + "callStack": { + "function": "github.com/ethereum/go-ethereum/triedb.NewDatabase", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/mpt.ReadTrie", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.(*ConsolidateOracle).ReceiptsByBlockHash", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.singleRoundConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.RunConsolidation", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.stateTransition", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client/interop.runInteropProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.RunProgram", + "callStack": { + "function": "github.com/ethereum-optimism/optimism/op-program/client.Main", + "callStack": { + "function": "main.main" + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + }, + "message": "Potential NOOP Syscall Detected: 5247", + "severity": "WARNING", + "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", + "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", + "hash": "ebe12d2516a9f6cffdfb698c5c027d3b14fc8afcf7d2168dbd7e7da3100124b8" + }, { "callStack": { "function": "syscall.pread", @@ -17786,40 +18436,6 @@ "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", "hash": "eee60140bf86c881f8591fb1a8fc80b0e98758fa1ce3d71d087d18b8f45d143b" }, - { - "callStack": { - "function": "syscall.openat", - "callStack": { - "function": "os.open", - "callStack": { - "function": "os.openFileNolog", - "callStack": { - "function": "os.OpenFile", - "callStack": { - "function": "github.com/tklauser/go-sysconf.getNprocsProcStat", - "callStack": { - "function": "github.com/tklauser/go-sysconf.getNprocs", - "callStack": { - "function": "github.com/tklauser/go-sysconf.getNprocsConf", - "callStack": { - "function": "github.com/tklauser/go-sysconf.sysconf", - "callStack": { - "function": "github.com/shirou/gopsutil/cpu.init.1" - } - } - } - } - } - } - } - } - }, - "message": "Potential NOOP Syscall Detected: 5247", - "severity": "WARNING", - "impact": "This syscall is present in the program, but its execution depends on the actual runtime behavior. \n If the execution path does not reach this syscall, it may not affect execution.", - "reference": "https://github.com/ChainSafe/vm-compat?tab=readme-ov-file#how-it-works", - "hash": "f22bbb65001661cbee435b60d3b67dc0de1d695dfc5a80dab1094098648b204d" - }, { "callStack": { "function": "runtime.netpollinit", diff --git a/op-program/host/common/l2_store.go b/op-program/host/common/l2_store.go index b9333b1ca66..3ce96ab369e 100644 --- a/op-program/host/common/l2_store.go +++ b/op-program/host/common/l2_store.go @@ -127,3 +127,6 @@ func (b *batch) Replay(w ethdb.KeyValueWriter) error { } return nil } + +func (b *batch) Close() { +} diff --git a/op-program/justfile b/op-program/justfile index fcf443e4859..dc2694e2dcb 100644 --- a/op-program/justfile +++ b/op-program/justfile @@ -164,6 +164,22 @@ verify-compat: verify-mainnet-genesis verify-sepolia-delta verify-sepolia-ecoton analyze-op-program-client-current: ./scripts/run-static-analysis.sh ./vm-profiles/cannon-multithreaded-64.yaml ./compatibility-test/baseline-cannon-multithreaded-64.json +# Regenerate the current baseline (full report without baseline, then normalize) +regenerate-baseline-current: + #!/usr/bin/env bash + set -euo pipefail + FULL_REPORT=$(mktemp) + # Run without baseline — will exit 1 since all findings are "new", which is expected + FINDINGS_OUTPUT_PATH="$FULL_REPORT" ./scripts/run-static-analysis.sh ./vm-profiles/cannon-multithreaded-64.yaml || true + # Strip assembly-level fields that cause false positives when they change + jq 'walk( + if type == "object" and has("line") then del(.line) else . end | + if type == "object" and has("absPath") then del(.absPath) else . end | + if type == "object" and has("file") then del(.file) else . end + )' "$FULL_REPORT" > ./compatibility-test/baseline-cannon-multithreaded-64.json + rm -f "$FULL_REPORT" + echo "Baseline regenerated: ./compatibility-test/baseline-cannon-multithreaded-64.json ($(jq length ./compatibility-test/baseline-cannon-multithreaded-64.json) entries)" + # Analyze next op-program client for VM compatibility analyze-op-program-client-next: ./scripts/run-static-analysis.sh ./vm-profiles/cannon-multithreaded-64-next.yaml ./compatibility-test/baseline-cannon-multithreaded-64-next.json @@ -181,3 +197,16 @@ run-vm-compat: # TODO(#18334): Uncomment once vm-compat supports go1.25 #@docker build --build-arg GO_VERSION=1.25.4-alpine3.21 --build-arg VM_TARGET=next --progress plain --output "{{VM_COMPAT_OUTPUT_DIR}}" -f Dockerfile.vmcompat ../ #@exit $(cat "{{VM_COMPAT_OUTPUT_DIR}}/vm-compat-exit-code") + +# Regenerate the vm-compat baseline via Docker, then copy it back into the repo +regenerate-vm-compat-baseline: + @rm -rf "{{VM_COMPAT_OUTPUT_DIR}}" && mkdir -p "{{VM_COMPAT_OUTPUT_DIR}}" + @docker build \ + --build-arg GO_VERSION=1.24.10-alpine3.21 \ + --build-arg VM_TARGET=current \ + --build-arg VM_COMPAT_TASK=regenerate-baseline \ + --progress plain \ + --output "{{VM_COMPAT_OUTPUT_DIR}}" \ + -f Dockerfile.vmcompat ../ + @cp "{{VM_COMPAT_OUTPUT_DIR}}/baseline-cannon-multithreaded-64.json" ./compatibility-test/baseline-cannon-multithreaded-64.json + @echo "Baseline updated: ./compatibility-test/baseline-cannon-multithreaded-64.json ($(jq length ./compatibility-test/baseline-cannon-multithreaded-64.json) entries)" diff --git a/op-program/scripts/run-static-analysis.sh b/op-program/scripts/run-static-analysis.sh index a47e968c913..591bfa9694e 100755 --- a/op-program/scripts/run-static-analysis.sh +++ b/op-program/scripts/run-static-analysis.sh @@ -5,18 +5,19 @@ set -o pipefail # Ensure failures in pipelines are detected # Usage function usage() { - echo "Usage: $0 " + echo "Usage: $0 [baseline-report]" echo "Valid profiles: cannon-singlethreaded-32, cannon-multithreaded-64" + echo "If baseline-report is omitted, outputs the full report (useful for regenerating baselines)." exit 1 } # Validate input -if [[ $# -ne 2 ]]; then +if [[ $# -lt 1 || $# -gt 2 ]]; then usage fi VM_PROFILE_CONFIG=$1 -BASELINE_REPORT=$2 +BASELINE_REPORT=${2:-} ANALYZER_BIN="vm-compat" @@ -45,11 +46,17 @@ fi echo "✅ vm-compat found at $(which vm-compat)" # Run the analyzer -echo "Running analysis with VM profile: $VM_PROFILE_CONFIG using baseline report: $BASELINE_REPORT..." OUTPUT_FILE=$(mktemp) -# Note: to output the full report, remove the `--baseline-report` option below -"$ANALYZER_BIN" analyze --with-trace=true --skip-warnings=false --format=json --vm-profile-config "$VM_PROFILE_CONFIG" --baseline-report "$BASELINE_REPORT" --report-output-path "$OUTPUT_FILE" ./client/cmd/main.go +BASELINE_ARGS=() +if [ -n "$BASELINE_REPORT" ]; then + echo "Running analysis with VM profile: $VM_PROFILE_CONFIG using baseline report: $BASELINE_REPORT..." + BASELINE_ARGS=(--baseline-report "$BASELINE_REPORT") +else + echo "Running analysis with VM profile: $VM_PROFILE_CONFIG (no baseline — full report)..." +fi + +"$ANALYZER_BIN" analyze --with-trace=true --skip-warnings=false --format=json --vm-profile-config "$VM_PROFILE_CONFIG" "${BASELINE_ARGS[@]}" --report-output-path "$OUTPUT_FILE" ./client/cmd/main.go # Check if JSON output contains any issues ISSUE_COUNT=$(jq 'length' "$OUTPUT_FILE") diff --git a/op-service/README.md b/op-service/README.md index 452fa44b98b..af4b73fdbbf 100644 --- a/op-service/README.md +++ b/op-service/README.md @@ -29,12 +29,10 @@ Pull requests: [monorepo](https://github.com/ethereum-optimism/optimism/pulls?q= ├── locks - Lock utils, like read-write wrapped types ├── log - Logging CLI and middleware utils ├── logpipe - Logs streaming from io.Reader to logger -├── logfilter - Logging filters ├── logmods - Log handler wrapping/unwrapping utils ├── metrics - Metrics types, metering abstractions, server utils ├── oppprof - P-Prof CLI types and server setup ├── plan - Utils to create a lazy evaluated value following dependencies -├── predeploys - OP-Stack predeploy definitions ├── queue - Generic queue implementation ├── retry - Function retry utils ├── rpc - RPC server utils diff --git a/op-service/event/tracer_timing.go b/op-service/event/tracer_timing.go index de20481a21b..ce73dd0974a 100644 --- a/op-service/event/tracer_timing.go +++ b/op-service/event/tracer_timing.go @@ -2,11 +2,11 @@ package event import ( "fmt" + "maps" + "slices" "sort" "strings" "time" - - "golang.org/x/exp/maps" ) // TimingTracer generates an HTML output with an SVG that shows, @@ -131,7 +131,7 @@ func (st *TimingTracer) Output() string { } // sort the keys, to get deterministic diagram order - actors := maps.Keys(byActor) + actors := slices.Collect(maps.Keys(byActor)) sort.Strings(actors) offsetY := float64(0) @@ -142,7 +142,7 @@ func (st *TimingTracer) Output() string { for _, actorName := range actors { m := byActor[actorName] - derived := maps.Keys(m) + derived := slices.Collect(maps.Keys(m)) sort.Strings(derived) for _, d := range derived { @@ -189,7 +189,7 @@ func (st *TimingTracer) Output() string { // draw lines between event-emissions and event-execution for _, actorName := range actors { m := byActor[actorName] - derived := maps.Keys(m) + derived := slices.Collect(maps.Keys(m)) sort.Strings(derived) for _, d := range derived { entries := m[d] diff --git a/op-service/safemath/safemath.go b/op-service/safemath/safemath.go index b4ca6c2536a..9d1bb91f81d 100644 --- a/op-service/safemath/safemath.go +++ b/op-service/safemath/safemath.go @@ -1,10 +1,14 @@ package safemath -import "golang.org/x/exp/constraints" +// Unsigned is a constraint for unsigned integer types, including named types +// whose underlying type is unsigned. Replaces golang.org/x/exp/Unsigned. +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr +} // SaturatingAdd adds two unsigned integer values (of the same type), // and caps the result at the max value of the type. -func SaturatingAdd[V constraints.Unsigned](a, b V) V { +func SaturatingAdd[V Unsigned](a, b V) V { out, overflow := SafeAdd(a, b) if overflow { return ^V(0) // max value @@ -14,7 +18,7 @@ func SaturatingAdd[V constraints.Unsigned](a, b V) V { // SafeAdd adds two unsigned integer values (of the same type), // and allows integer overflows, and returns if it overflowed. -func SafeAdd[V constraints.Unsigned](a, b V) (out V, overflow bool) { +func SafeAdd[V Unsigned](a, b V) (out V, overflow bool) { out = a + b overflow = out < a return @@ -22,7 +26,7 @@ func SafeAdd[V constraints.Unsigned](a, b V) (out V, overflow bool) { // SaturatingSub subtracts two unsigned integer values (of the same type), // and floors the result at zero. -func SaturatingSub[V constraints.Unsigned](a, b V) V { +func SaturatingSub[V Unsigned](a, b V) V { out, underflow := SafeSub(a, b) if underflow { return V(0) // min value @@ -32,7 +36,7 @@ func SaturatingSub[V constraints.Unsigned](a, b V) V { // SafeSub subtracts two unsigned integer values (of the same type), // and allows integer underflows, and returns if it underflowed. -func SafeSub[V constraints.Unsigned](a, b V) (out V, underflow bool) { +func SafeSub[V Unsigned](a, b V) (out V, underflow bool) { out = a - b underflow = out > a return diff --git a/op-service/safemath/safemath_test.go b/op-service/safemath/safemath_test.go index 5856abda966..606a50daf4a 100644 --- a/op-service/safemath/safemath_test.go +++ b/op-service/safemath/safemath_test.go @@ -5,8 +5,6 @@ import ( "math/big" "testing" - "golang.org/x/exp/constraints" - "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/op-service/bigs" @@ -23,7 +21,7 @@ func TestAdd(t *testing.T) { t.Run("uint", testAdd[uint]) } -func testAdd[V constraints.Unsigned](t *testing.T) { +func testAdd[V Unsigned](t *testing.T) { m := ^V(0) require.Less(t, m+1, m, "sanity check max value does overflow") vals := []V{ @@ -66,7 +64,7 @@ func TestSub(t *testing.T) { t.Run("uint", testSub[uint]) } -func testSub[V constraints.Unsigned](t *testing.T) { +func testSub[V Unsigned](t *testing.T) { m := ^V(0) require.Less(t, m+1, m, "sanity check min value does underflow") vals := []V{ diff --git a/op-service/sources/batching/batching.go b/op-service/sources/batching/batching.go index 50fd76da945..0ed493d76c0 100644 --- a/op-service/sources/batching/batching.go +++ b/op-service/sources/batching/batching.go @@ -8,8 +8,6 @@ import ( "sync" "sync/atomic" - "github.com/hashicorp/go-multierror" - "github.com/ethereum/go-ethereum/rpc" ) @@ -149,7 +147,7 @@ func (ibc *IterativeBatchCall[K, V]) Fetch(ctx context.Context) error { var result error for _, elem := range batch { if elem.Error != nil { - result = multierror.Append(result, elem.Error) + result = errors.Join(result, elem.Error) elem.Error = nil // reset, we'll try this element again ibc.scheduled <- elem continue diff --git a/op-service/sources/batching/batching_test.go b/op-service/sources/batching/batching_test.go index c2880dd0c92..3891e95ce01 100644 --- a/op-service/sources/batching/batching_test.go +++ b/op-service/sources/batching/batching_test.go @@ -259,7 +259,7 @@ func TestFetchBatched(t *testing.T) { {id: 0, err: false}, {id: 1, err: true}, }, - err: "1 error occurred:", + err: "mockErr", }, { elems: []elemCall{ @@ -294,14 +294,14 @@ func TestFetchBatched(t *testing.T) { {id: 0, err: true}, {id: 1, err: true}, }, - err: "2 errors occurred:", + err: "mockErr", }, { elems: []elemCall{ {id: 0, err: false}, {id: 1, err: true}, }, - err: "1 error occurred:", + err: "mockErr", }, { elems: []elemCall{ diff --git a/op-service/sources/flashblock_client.go b/op-service/sources/flashblock_client.go index 3235766bd77..7b0ff86a034 100644 --- a/op-service/sources/flashblock_client.go +++ b/op-service/sources/flashblock_client.go @@ -80,7 +80,7 @@ func (c *FlashblockClient) Start(ctx context.Context) error { for { _, msg, err := c.ws.Read(ctx) if err != nil { - if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { + if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) || ctx.Err() != nil { c.logger.Info("FlashblockClient: read loop finished") return nil } diff --git a/op-service/testutils/devnet/anvil.go b/op-service/testutils/devnet/anvil.go index 9be23f10764..d254fe6e0b5 100644 --- a/op-service/testutils/devnet/anvil.go +++ b/op-service/testutils/devnet/anvil.go @@ -22,14 +22,16 @@ import ( const DefaultChainID = 77799777 type Anvil struct { - args map[string]string - proc *exec.Cmd - stdout io.ReadCloser - stderr io.ReadCloser - logger log.Logger - startedCh chan struct{} - wg sync.WaitGroup - port int32 + args map[string]string + proc *exec.Cmd + stdout io.ReadCloser + stderr io.ReadCloser + logger log.Logger + startedCh chan struct{} + wg sync.WaitGroup + port int32 + foundryHome string + ownsFoundryHome bool } type AnvilOption func(*Anvil) @@ -67,6 +69,12 @@ func WithForkBlockNumber(block uint64) AnvilOption { } } +func WithFoundryHome(dir string) AnvilOption { + return func(a *Anvil) { + a.foundryHome = dir + } +} + func NewAnvil(logger log.Logger, opts ...AnvilOption) (*Anvil, error) { if _, err := exec.LookPath("anvil"); err != nil { return nil, fmt.Errorf("anvil not found in PATH: %w", err) @@ -93,6 +101,15 @@ func (r *Anvil) Start() error { args = append(args, k, v) } proc := exec.Command("anvil", args...) + if r.foundryHome == "" { + tmpDir, err := os.MkdirTemp("", "anvil-foundry-home-*") + if err != nil { + return fmt.Errorf("failed to create temp foundry home: %w", err) + } + r.foundryHome = tmpDir + r.ownsFoundryHome = true + } + proc.Env = append(os.Environ(), "FOUNDRY_HOME="+r.foundryHome) stdout, err := proc.StdoutPipe() if err != nil { return err @@ -137,7 +154,17 @@ func (r *Anvil) Stop() error { // make sure the output streams close defer r.wg.Wait() - return r.proc.Wait() + waitErr := r.proc.Wait() + + if r.ownsFoundryHome { + // Clean up the temporary foundry home directory to prevent + // accumulation of anvil state in ~/.foundry/anvil/tmp. + if err := os.RemoveAll(r.foundryHome); err != nil { + r.logger.Warn("failed to clean up foundry home", "path", r.foundryHome, "err", err) + } + } + + return waitErr } func (r *Anvil) outputStream(stream io.ReadCloser) { diff --git a/op-service/testutils/devnet/anvil_test.go b/op-service/testutils/devnet/anvil_test.go new file mode 100644 index 00000000000..f3efb29daef --- /dev/null +++ b/op-service/testutils/devnet/anvil_test.go @@ -0,0 +1,62 @@ +package devnet + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAnvilFoundryHomeIsolation(t *testing.T) { + // Verify that Start() creates an isolated FOUNDRY_HOME and Stop() cleans it up. + // This test does not require anvil to be installed; it only tests the directory lifecycle. + + t.Run("auto-created dir is cleaned up on stop", func(t *testing.T) { + a := &Anvil{ + args: map[string]string{"--port": "0"}, + startedCh: make(chan struct{}, 1), + } + + // Simulate the temp dir creation that Start() does (without actually starting anvil). + tmpDir, err := os.MkdirTemp("", "anvil-foundry-home-test-*") + require.NoError(t, err) + a.foundryHome = tmpDir + a.ownsFoundryHome = true + + // Verify the directory exists. + _, err = os.Stat(tmpDir) + require.NoError(t, err) + + // Stop should clean up the directory even without a running process. + // proc is nil so Stop returns early before process cleanup, but we + // can test the cleanup logic directly. + require.DirExists(t, tmpDir) + require.NoError(t, os.RemoveAll(tmpDir)) + require.NoDirExists(t, tmpDir) + }) + + t.Run("caller-provided dir is not cleaned up on stop", func(t *testing.T) { + callerDir := t.TempDir() + a := &Anvil{ + args: map[string]string{"--port": "0"}, + startedCh: make(chan struct{}, 1), + } + + // Simulate WithFoundryHome — ownsFoundryHome stays false. + a.foundryHome = callerDir + a.ownsFoundryHome = false + + // The directory should not be removed by our cleanup logic. + require.DirExists(t, callerDir) + }) + + t.Run("WithFoundryHome sets dir without ownership", func(t *testing.T) { + a := &Anvil{ + args: map[string]string{"--port": "0"}, + startedCh: make(chan struct{}, 1), + } + WithFoundryHome("/tmp/custom-foundry")(a) + require.Equal(t, "/tmp/custom-foundry", a.foundryHome) + require.False(t, a.ownsFoundryHome) + }) +} diff --git a/op-service/testutils/fake_chain.go b/op-service/testutils/fake_chain.go index eca4de1160b..003c731cfbf 100644 --- a/op-service/testutils/fake_chain.go +++ b/op-service/testutils/fake_chain.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "math/big" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -172,15 +171,12 @@ func (m *FakeChainSource) L2BlockRefByLabel(ctx context.Context, label eth.Block } } -func (m *FakeChainSource) L2BlockRefByNumber(ctx context.Context, l2Num *big.Int) (eth.L2BlockRef, error) { +func (m *FakeChainSource) L2BlockRefByNumber(ctx context.Context, l2Num uint64) (eth.L2BlockRef, error) { m.log.Trace("L2BlockRefByNumber", "l2Num", l2Num, "l2Head", m.l2head, "reorg", m.l2reorg) if len(m.l2s[m.l2reorg]) == 0 { panic("bad test, no l2 chain") } - if l2Num == nil { - return m.l2s[m.l2reorg][m.l2head], nil - } - i := int(l2Num.Int64()) + i := int(l2Num) if i > m.l2head { return eth.L2BlockRef{}, ethereum.NotFound } @@ -191,7 +187,7 @@ func (m *FakeChainSource) L2BlockRefByHash(ctx context.Context, l2Hash common.Ha m.log.Trace("L2BlockRefByHash", "l2Hash", l2Hash, "l2Head", m.l2head, "reorg", m.l2reorg) for i, bl := range m.l2s[m.l2reorg] { if bl.Hash == l2Hash { - return m.L2BlockRefByNumber(ctx, big.NewInt(int64(i))) + return m.L2BlockRefByNumber(ctx, uint64(i)) } } return eth.L2BlockRef{}, ethereum.NotFound diff --git a/op-service/txinclude/resubmitter.go b/op-service/txinclude/resubmitter.go index f043f67bd71..07f151c1a37 100644 --- a/op-service/txinclude/resubmitter.go +++ b/op-service/txinclude/resubmitter.go @@ -59,7 +59,6 @@ var fatalErrs = []error{ // Transaction limits. txpool.ErrOversizedData, - core.ErrMaxInitCodeSizeExceeded, legacypool.ErrAuthorityReserved, legacypool.ErrFutureReplacePending, diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index b6d2aa32b65..d0a99606dac 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -19,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/holiman/uint256" @@ -489,6 +490,7 @@ func (m *SimpleTxManager) estimateOrValidateCandidateTxGas(ctx context.Context, } // If the gas limit is set, we can use that as the gas if candidate.GasLimit == 0 { + callMsg.Gas = params.MaxTxGas gas, err := m.backend.EstimateGas(ctx, callMsg) if err != nil { return 0, fmt.Errorf("failed to estimate gas: %w", errutil.TryAddRevertReason(err)) diff --git a/op-service/txplan/txplan.go b/op-service/txplan/txplan.go index 96258ccb1ec..0230c9e8f86 100644 --- a/op-service/txplan/txplan.go +++ b/op-service/txplan/txplan.go @@ -225,7 +225,7 @@ func WithEstimator(cl Estimator, invalidateOnNewBlock bool) Option { msg := ethereum.CallMsg{ From: tx.Sender.Value(), To: tx.To.Value(), - Gas: 0, // infinite gas, will be estimated + Gas: params.MaxTxGas, // max gas, will be estimated GasPrice: nil, GasFeeCap: tx.GasFeeCap.Value(), GasTipCap: tx.GasTipCap.Value(), diff --git a/op-supernode/cmd/main.go b/op-supernode/cmd/main.go index 4e1bf780863..c27155686ac 100644 --- a/op-supernode/cmd/main.go +++ b/op-supernode/cmd/main.go @@ -79,13 +79,12 @@ func main() { return nil, fmt.Errorf("failed to create virtual node configs: %w", err) } - // Populate config with interop activation timestamp from CLI context if set - // Only set the pointer if the flag is explicitly provided by the user - // If not set, leave as nil to disable interop + // Populate config with an explicit CLI or env override if one is set. + // Otherwise the supernode will derive interop activation from the loaded rollup configs. if cliCtx != nil && cliCtx.IsSet(interop.InteropActivationTimestampFlag.Name) { ts := cliCtx.Uint64(interop.InteropActivationTimestampFlag.Name) cfg.InteropActivationTimestamp = &ts - l.Info("interop activation timestamp set from CLI", "timestamp", ts) + l.Info("interop activation timestamp override set", "timestamp", ts) } // Create the supernode, supplying the logger, version, and close function diff --git a/op-supernode/supernode/activity/interop/algo.go b/op-supernode/supernode/activity/interop/algo.go index 00295024f5c..f724c021ef5 100644 --- a/op-supernode/supernode/activity/interop/algo.go +++ b/op-supernode/supernode/activity/interop/algo.go @@ -8,10 +8,10 @@ import ( "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" ) -// ExpiryTime is the maximum age of an initiating message that can be executed. -// Messages older than this are considered expired and invalid. -// 7 days = 7 * 24 * 60 * 60 = 604800 seconds -const ExpiryTime = 604800 +// defaultMessageExpiryWindow is the default maximum age of an initiating message +// that can be executed. 7 days = 7 * 24 * 60 * 60 = 604800 seconds. +// The actual value used is read from the dependency set at construction time. +const defaultMessageExpiryWindow = 604800 var ( // ErrUnknownChain is returned when an executing message references @@ -23,7 +23,7 @@ var ( ErrTimestampViolation = errors.New("initiating message timestamp must not be greater than executing message timestamp") // ErrMessageExpired is returned when an executing message references - // an initiating message that has expired (older than ExpiryTime). + // an initiating message that has expired (older than the message expiry window). ErrMessageExpired = errors.New("initiating message has expired") ) @@ -58,7 +58,7 @@ func (i *Interop) l1Inclusion(ts uint64, blocksAtTimestamp blockPerChain) (eth.B // 2. For each executing message in the block: // - Verify the initiating message exists in the source chain's logsDB // - Verify the initiating message timestamp <= executing message timestamp -// - Verify the initiating message hasn't expired (within ExpiryTime) +// - Verify the initiating message hasn't expired (within message expiry window) func (i *Interop) verifyInteropMessages(ts uint64, blocksAtTimestamp blockPerChain) (Result, error) { result := Result{ Timestamp: ts, @@ -171,7 +171,7 @@ func (i *Interop) verifyInteropMessages(ts uint64, blocksAtTimestamp blockPerCha // verifyExecutingMessage verifies a single executing message by checking: // 1. The initiating message exists in the source chain's database // 2. The initiating message's timestamp is not greater than the executing block's timestamp -// 3. The initiating message hasn't expired (timestamp + ExpiryTime >= executing timestamp) +// 3. The initiating message hasn't expired (timestamp + messageExpiryWindow >= executing timestamp) func (i *Interop) verifyExecutingMessage(executingChain eth.ChainID, executingTimestamp uint64, logIdx uint32, execMsg *types.ExecutingMessage) error { // Get the source chain's logsDB sourceDB, ok := i.logsDBs[execMsg.ChainID] @@ -185,10 +185,10 @@ func (i *Interop) verifyExecutingMessage(executingChain eth.ChainID, executingTi execMsg.Timestamp, executingTimestamp, ErrTimestampViolation) } - // Verify the message hasn't expired: initiating timestamp + ExpiryTime must be >= executing timestamp - if execMsg.Timestamp+ExpiryTime < executingTimestamp { + // Verify the message hasn't expired: initiating timestamp + messageExpiryWindow must be >= executing timestamp + if execMsg.Timestamp+i.messageExpiryWindow < executingTimestamp { return fmt.Errorf("initiating timestamp %d + expiry %d < executing timestamp %d: %w", - execMsg.Timestamp, ExpiryTime, executingTimestamp, ErrMessageExpired) + execMsg.Timestamp, i.messageExpiryWindow, executingTimestamp, ErrMessageExpired) } // Build the query for the initiating message diff --git a/op-supernode/supernode/activity/interop/algo_test.go b/op-supernode/supernode/activity/interop/algo_test.go index 0842c2f2a36..c4d5f09c256 100644 --- a/op-supernode/supernode/activity/interop/algo_test.go +++ b/op-supernode/supernode/activity/interop/algo_test.go @@ -84,9 +84,10 @@ func TestL1Inclusion(t *testing.T) { l1Block := eth.BlockID{Number: 50, Hash: common.HexToHash("0xL1")} interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{}, - chains: map[eth.ChainID]cc.ChainContainer{chainID: &algoMockChain{id: chainID, optimisticL1: l1Block}}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{}, + chains: map[eth.ChainID]cc.ChainContainer{chainID: &algoMockChain{id: chainID, optimisticL1: l1Block}}, } return interop, 1000, map[eth.ChainID]eth.BlockID{chainID: expectedBlock} }, @@ -105,8 +106,9 @@ func TestL1Inclusion(t *testing.T) { // Chain 2 has L1 at 45 (earliest) // Chain 3 has L1 at 50 (middle) interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{}, chains: map[eth.ChainID]cc.ChainContainer{ chain1ID: &algoMockChain{id: chain1ID, optimisticL1: eth.BlockID{Number: 60, Hash: common.HexToHash("0xL1_1")}}, chain2ID: &algoMockChain{id: chain2ID, optimisticL1: eth.BlockID{Number: 45, Hash: common.HexToHash("0xL1_2")}}, @@ -132,8 +134,9 @@ func TestL1Inclusion(t *testing.T) { l1Block1 := eth.BlockID{Number: 50, Hash: common.HexToHash("0xL1_1")} interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{}, chains: map[eth.ChainID]cc.ChainContainer{ chain1ID: &algoMockChain{id: chain1ID, optimisticL1: l1Block1}, // chain2ID NOT in chains map @@ -154,8 +157,9 @@ func TestL1Inclusion(t *testing.T) { chainID := eth.ChainIDFromUInt64(10) interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{}, chains: map[eth.ChainID]cc.ChainContainer{ chainID: &algoMockChain{id: chainID, optimisticAtErr: errors.New("optimistic at error")}, }, @@ -171,9 +175,10 @@ func TestL1Inclusion(t *testing.T) { name: "NoChains_ReturnsEmpty", setup: func() (*Interop, uint64, map[eth.ChainID]eth.BlockID) { interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{}, - chains: map[eth.ChainID]cc.ChainContainer{}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{}, + chains: map[eth.ChainID]cc.ChainContainer{}, } return interop, 1000, map[eth.ChainID]eth.BlockID{} }, @@ -189,9 +194,10 @@ func TestL1Inclusion(t *testing.T) { l1Block := eth.BlockID{Number: 0, Hash: common.HexToHash("0xGenesisL1")} interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{}, - chains: map[eth.ChainID]cc.ChainContainer{chainID: &algoMockChain{id: chainID, optimisticL1: l1Block}}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{}, + chains: map[eth.ChainID]cc.ChainContainer{chainID: &algoMockChain{id: chainID, optimisticL1: l1Block}}, } return interop, 0, map[eth.ChainID]eth.BlockID{ chainID: {Number: 0, Hash: common.HexToHash("0x123")}, @@ -245,9 +251,10 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{chainID: mockDB}, - chains: map[eth.ChainID]cc.ChainContainer{chainID: newMockChainWithL1(chainID, l1Block)}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{chainID: mockDB}, + chains: map[eth.ChainID]cc.ChainContainer{chainID: newMockChainWithL1(chainID, l1Block)}, } return interop, 1000, map[eth.ChainID]eth.BlockID{chainID: expectedBlock} @@ -294,7 +301,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, destChainID: destDB, @@ -326,7 +334,7 @@ func TestVerifyInteropMessages(t *testing.T) { // Message is exactly at the expiry boundary (should pass) execTimestamp := uint64(1000000) - initTimestamp := execTimestamp - ExpiryTime // Exactly at boundary + initTimestamp := execTimestamp - defaultMessageExpiryWindow // Exactly at boundary sourceBlock := eth.BlockID{Number: 50, Hash: sourceBlockHash} destBlock := eth.BlockID{Number: 100, Hash: destBlockHash} @@ -353,7 +361,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, destChainID: destDB, @@ -414,7 +423,8 @@ func TestVerifyInteropMessages(t *testing.T) { l1Block := eth.BlockID{Number: 40, Hash: common.HexToHash("0xL1")} interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, destChainID: destDB, @@ -451,8 +461,9 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{registeredChain: mockDB}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{registeredChain: mockDB}, chains: map[eth.ChainID]cc.ChainContainer{ registeredChain: newMockChainWithL1(registeredChain, eth.BlockID{Number: 40, Hash: common.HexToHash("0xL1")}), }, @@ -488,9 +499,10 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{chainID: mockDB}, - chains: map[eth.ChainID]cc.ChainContainer{chainID: newMockChainWithL1(chainID, l1Block, expectedBlock)}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{chainID: mockDB}, + chains: map[eth.ChainID]cc.ChainContainer{chainID: newMockChainWithL1(chainID, l1Block, expectedBlock)}, } return interop, 1000, map[eth.ChainID]eth.BlockID{chainID: expectedBlock} @@ -532,7 +544,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, destChainID: destDB, @@ -583,7 +596,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, destChainID: destDB, @@ -627,7 +641,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ destChainID: destDB, // Note: unknownSourceChain NOT in logsDBs @@ -655,8 +670,8 @@ func TestVerifyInteropMessages(t *testing.T) { destBlockHash := common.HexToHash("0xDest") // Executing block is at timestamp 1000000 (well after expiry) execTimestamp := uint64(1000000) - // Initiating message timestamp is more than ExpiryTime (604800) before executing timestamp - initTimestamp := execTimestamp - ExpiryTime - 1 // 1 second past expiry + // Initiating message timestamp is more than defaultMessageExpiryWindow (604800) before executing timestamp + initTimestamp := execTimestamp - defaultMessageExpiryWindow - 1 // 1 second past expiry destBlock := eth.BlockID{Number: 100, Hash: destBlockHash} @@ -680,7 +695,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, destChainID: destDB, @@ -737,7 +753,8 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), logsDBs: map[eth.ChainID]LogsDB{ sourceChainID: sourceDB, validChainID: validDB, @@ -780,9 +797,10 @@ func TestVerifyInteropMessages(t *testing.T) { } interop := &Interop{ - log: gethlog.New(), - logsDBs: map[eth.ChainID]LogsDB{chainID: mockDB}, - chains: map[eth.ChainID]cc.ChainContainer{chainID: newMockChainWithL1(chainID, l1Block)}, + messageExpiryWindow: defaultMessageExpiryWindow, + log: gethlog.New(), + logsDBs: map[eth.ChainID]LogsDB{chainID: mockDB}, + chains: map[eth.ChainID]cc.ChainContainer{chainID: newMockChainWithL1(chainID, l1Block)}, } return interop, 1000, map[eth.ChainID]eth.BlockID{chainID: block} diff --git a/op-supernode/supernode/activity/interop/interop.go b/op-supernode/supernode/activity/interop/interop.go index 4f4be1ced9d..28d702210ef 100644 --- a/op-supernode/supernode/activity/interop/interop.go +++ b/op-supernode/supernode/activity/interop/interop.go @@ -9,6 +9,7 @@ import ( "sync/atomic" "time" + opservice "github.com/ethereum-optimism/optimism/op-service" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-supernode/flags" "github.com/ethereum-optimism/optimism/op-supernode/supernode/activity" @@ -28,9 +29,10 @@ var ( // InteropActivationTimestampFlag is the CLI flag for the interop activation timestamp. var InteropActivationTimestampFlag = &cli.Uint64Flag{ - Name: "interop.activation-timestamp", - Usage: "The timestamp at which interop should start", - Value: 0, + Name: "interop.activation-timestamp", + Usage: "Override the interop activation timestamp derived from rollup configs", + EnvVars: opservice.PrefixEnvVar(flags.EnvVarPrefix, "INTEROP_ACTIVATION_TIMESTAMP"), + Value: 0, } func init() { @@ -79,6 +81,8 @@ type Interop struct { activationTimestamp uint64 dataDir string + messageExpiryWindow uint64 + verifiedDB *VerifiedDB logsDBs map[eth.ChainID]LogsDB @@ -113,6 +117,7 @@ func (i *Interop) Name() string { func New( log log.Logger, activationTimestamp uint64, + messageExpiryWindow uint64, chains map[eth.ChainID]cc.ChainContainer, dataDir string, l1Source l1ByNumberSource, @@ -139,6 +144,9 @@ func New( logsDBs[chainID] = logsDB } + if messageExpiryWindow == 0 { + messageExpiryWindow = defaultMessageExpiryWindow + } i := &Interop{ log: log, chains: chains, @@ -146,6 +154,7 @@ func New( logsDBs: logsDBs, dataDir: dataDir, activationTimestamp: activationTimestamp, + messageExpiryWindow: messageExpiryWindow, } // default to using the verifyInteropMessages function // (can be overridden by tests) diff --git a/op-supernode/supernode/activity/interop/interop_test.go b/op-supernode/supernode/activity/interop/interop_test.go index 16f723e8b40..ee009851e43 100644 --- a/op-supernode/supernode/activity/interop/interop_test.go +++ b/op-supernode/supernode/activity/interop/interop_test.go @@ -89,7 +89,7 @@ func (h *interopTestHarness) Build() *interopTestHarness { for id, mock := range h.mocks { chains[id] = mock } - h.interop = New(testLogger(), h.activationTime, chains, h.dataDir, nil) + h.interop = New(testLogger(), h.activationTime, 0, chains, h.dataDir, nil) if h.interop != nil { h.interop.ctx = context.Background() h.t.Cleanup(func() { _ = h.interop.Stop(context.Background()) }) @@ -129,7 +129,7 @@ func TestNew(t *testing.T) { return h.WithChain(10, nil).WithChain(8453, nil).SkipBuild() }, run: func(t *testing.T, h *interopTestHarness) { - interop := New(testLogger(), h.activationTime, h.Chains(), h.dataDir, nil) + interop := New(testLogger(), h.activationTime, 0, h.Chains(), h.dataDir, nil) require.NotNil(t, interop) t.Cleanup(func() { _ = interop.Stop(context.Background()) }) @@ -152,7 +152,7 @@ func TestNew(t *testing.T) { return h.WithDataDir("/nonexistent/path").SkipBuild() }, run: func(t *testing.T, h *interopTestHarness) { - interop := New(testLogger(), h.activationTime, h.Chains(), h.dataDir, nil) + interop := New(testLogger(), h.activationTime, 0, h.Chains(), h.dataDir, nil) require.Nil(t, interop) }, }, @@ -167,6 +167,12 @@ func TestNew(t *testing.T) { } } +func TestInteropActivationTimestampFlagEnvVar(t *testing.T) { + t.Parallel() + + require.Contains(t, InteropActivationTimestampFlag.GetEnvVars(), "OP_SUPERNODE_INTEROP_ACTIVATION_TIMESTAMP") +} + // ============================================================================= // TestStartStop // ============================================================================= @@ -1139,7 +1145,7 @@ func TestInterop_FullCycle(t *testing.T) { mock.blockAtTimestamp = eth.L2BlockRef{Number: 500, Hash: common.HexToHash("0xL2")} chains := map[eth.ChainID]cc.ChainContainer{mock.id: mock} - interop := New(testLogger(), 100, chains, dataDir, nil) + interop := New(testLogger(), 100, 0, chains, dataDir, nil) require.NotNil(t, interop) interop.ctx = context.Background() diff --git a/op-supernode/supernode/chain_container/chain_container.go b/op-supernode/supernode/chain_container/chain_container.go index bde95224044..fe547748888 100644 --- a/op-supernode/supernode/chain_container/chain_container.go +++ b/op-supernode/supernode/chain_container/chain_container.go @@ -336,10 +336,7 @@ func (c *simpleChainContainer) TimestampToBlockNumber(ctx context.Context, ts ui } func (c *simpleChainContainer) BlockNumberToTimestamp(ctx context.Context, blocknum uint64) (uint64, error) { - if c.vncfg == nil { - return 0, fmt.Errorf("rollup config not available") - } - return c.vncfg.Rollup.Genesis.L2Time + (blocknum * c.vncfg.Rollup.BlockTime), nil + return c.blockNumberToTimestamp(blocknum) } // LocalSafeBlockAtTimestamp returns the highest L2 block with timestamp <= ts using the L2 client, @@ -627,9 +624,12 @@ func (c *simpleChainContainer) SetResetCallback(cb ResetCallback) { } // blockNumberToTimestamp converts a block number to its timestamp using rollup config. -func (c *simpleChainContainer) blockNumberToTimestamp(blockNum uint64) uint64 { +func (c *simpleChainContainer) blockNumberToTimestamp(blockNum uint64) (uint64, error) { if c.vncfg == nil { - return 0 + return 0, fmt.Errorf("rollup config not available") + } + if blockNum < c.vncfg.Rollup.Genesis.L2.Number { + return 0, fmt.Errorf("block number %d before genesis %d", blockNum, c.vncfg.Rollup.Genesis.L2.Number) } - return c.vncfg.Rollup.Genesis.L2Time + (blockNum * c.vncfg.Rollup.BlockTime) + return c.vncfg.Rollup.TimestampForBlock(blockNum), nil } diff --git a/op-supernode/supernode/chain_container/chain_container_test.go b/op-supernode/supernode/chain_container/chain_container_test.go index 1b51047c2e0..cfb5ae27a6f 100644 --- a/op-supernode/supernode/chain_container/chain_container_test.go +++ b/op-supernode/supernode/chain_container/chain_container_test.go @@ -1286,3 +1286,29 @@ func TestChainContainer_SyncStatus_UninitializedVirtualNode(t *testing.T) { require.Nil(t, status) require.ErrorIs(t, err, virtual_node.ErrVirtualNodeNotRunning) } + +func TestChainContainer_BlockNumberToTimestamp_RespectsGenesisBlockNumber(t *testing.T) { + t.Parallel() + + chainID := eth.ChainIDFromUInt64(420) + log := createTestLogger(t) + cfg := createTestCLIConfig(t.TempDir()) + initOverload := &rollupNode.InitializationOverrides{} + + vncfg := createTestVNConfig() + vncfg.Rollup.Genesis.L2Time = 1000 + vncfg.Rollup.Genesis.L2.Number = 100 + vncfg.Rollup.BlockTime = 2 + + container := NewChainContainer(chainID, vncfg, log, cfg, initOverload, nil, nil, nil) + impl, ok := container.(*simpleChainContainer) + require.True(t, ok) + + timestamp, err := impl.BlockNumberToTimestamp(context.Background(), 104) + require.NoError(t, err) + require.Equal(t, uint64(1008), timestamp) + + _, err = impl.BlockNumberToTimestamp(context.Background(), 99) + require.Error(t, err) + require.Contains(t, err.Error(), "before genesis 100") +} diff --git a/op-supernode/supernode/chain_container/invalidation.go b/op-supernode/supernode/chain_container/invalidation.go index a515b4c1ee8..175cfd0ed6f 100644 --- a/op-supernode/supernode/chain_container/invalidation.go +++ b/op-supernode/supernode/chain_container/invalidation.go @@ -389,7 +389,10 @@ func (c *simpleChainContainer) InvalidateBlock(ctx context.Context, height uint6 invalidatedBlock := currentBlock.BlockRef() // Rewind to the prior block's timestamp - priorTimestamp := c.blockNumberToTimestamp(height - 1) + priorTimestamp, err := c.blockNumberToTimestamp(height - 1) + if err != nil { + return false, fmt.Errorf("failed to compute rewind timestamp: %w", err) + } if err := c.RewindEngine(ctx, priorTimestamp, invalidatedBlock); err != nil { return false, fmt.Errorf("failed to rewind engine: %w", err) } diff --git a/op-supernode/supernode/chain_container/invalidation_test.go b/op-supernode/supernode/chain_container/invalidation_test.go index f967de8322e..15471b03c49 100644 --- a/op-supernode/supernode/chain_container/invalidation_test.go +++ b/op-supernode/supernode/chain_container/invalidation_test.go @@ -350,6 +350,7 @@ func TestInvalidateBlock(t *testing.T) { tests := []struct { name string + genesisBlock uint64 height uint64 payloadHash common.Hash currentBlockHash common.Hash @@ -359,6 +360,7 @@ func TestInvalidateBlock(t *testing.T) { }{ { name: "current block matches triggers rewind", + genesisBlock: 0, height: 5, payloadHash: common.HexToHash("0xdead"), currentBlockHash: common.HexToHash("0xdead"), // Same hash @@ -368,6 +370,7 @@ func TestInvalidateBlock(t *testing.T) { }, { name: "current block differs no rewind", + genesisBlock: 0, height: 5, payloadHash: common.HexToHash("0xdead"), currentBlockHash: common.HexToHash("0xbeef"), // Different hash @@ -376,6 +379,7 @@ func TestInvalidateBlock(t *testing.T) { }, { name: "engine unavailable adds to denylist only", + genesisBlock: 0, height: 5, payloadHash: common.HexToHash("0xdead"), engineAvailable: false, @@ -383,6 +387,7 @@ func TestInvalidateBlock(t *testing.T) { }, { name: "rewind to height-1 timestamp calculated correctly", + genesisBlock: 0, height: 10, payloadHash: common.HexToHash("0xabcd"), currentBlockHash: common.HexToHash("0xabcd"), @@ -390,6 +395,16 @@ func TestInvalidateBlock(t *testing.T) { expectRewind: true, expectRewindTs: genesisTime + (9 * blockTime), // height 9 }, + { + name: "rewind timestamp respects nonzero genesis block number", + genesisBlock: 100, + height: 105, + payloadHash: common.HexToHash("0xcafe"), + currentBlockHash: common.HexToHash("0xcafe"), + engineAvailable: true, + expectRewind: true, + expectRewindTs: genesisTime + (4 * blockTime), // block 104 relative to genesis block 100 + }, } // Separate test for genesis block (height=0) which should error @@ -419,6 +434,34 @@ func TestInvalidateBlock(t *testing.T) { require.False(t, found, "genesis block should not be added to denylist") }) + t.Run("missing rollup config returns error before rewind", func(t *testing.T) { + t.Parallel() + dir := t.TempDir() + + dl, err := OpenDenyList(filepath.Join(dir, "denylist")) + require.NoError(t, err) + defer dl.Close() + + mockEng := &mockEngineForInvalidation{ + blockRef: eth.L2BlockRef{Hash: common.HexToHash("0xdead")}, + } + + c := &simpleChainContainer{ + denyList: dl, + log: testLogger(), + engine: mockEng, + vn: &mockVNForInvalidation{}, + } + + rewound, err := c.InvalidateBlock(context.Background(), 5, common.HexToHash("0xdead"), 0, eth.Bytes32{}, eth.Bytes32{}) + + require.Error(t, err) + require.Contains(t, err.Error(), "failed to compute rewind timestamp") + require.Contains(t, err.Error(), "rollup config not available") + require.False(t, rewound) + require.False(t, mockEng.rewindCalled, "rewind should not be attempted without rollup config") + }) + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() @@ -442,6 +485,7 @@ func TestInvalidateBlock(t *testing.T) { vn: &mockVNForInvalidation{}, } c.vncfg.Rollup.Genesis.L2Time = genesisTime + c.vncfg.Rollup.Genesis.L2.Number = tt.genesisBlock c.vncfg.Rollup.BlockTime = blockTime if tt.engineAvailable { diff --git a/op-supernode/supernode/interop_config_test.go b/op-supernode/supernode/interop_config_test.go new file mode 100644 index 00000000000..794d2ad046b --- /dev/null +++ b/op-supernode/supernode/interop_config_test.go @@ -0,0 +1,82 @@ +package supernode + +import ( + "testing" + + opnodecfg "github.com/ethereum-optimism/optimism/op-node/config" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/stretchr/testify/require" +) + +func TestResolveInteropActivationTimestamp(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + override *uint64 + vnCfgs map[eth.ChainID]*opnodecfg.Config + want *uint64 + wantErr string + }{ + { + name: "override wins over rollup configs", + override: uint64Ptr(42), + vnCfgs: map[eth.ChainID]*opnodecfg.Config{ + eth.ChainIDFromUInt64(10): {Rollup: rollup.Config{InteropTime: uint64Ptr(100)}}, + eth.ChainIDFromUInt64(8453): {Rollup: rollup.Config{InteropTime: uint64Ptr(200)}}, + }, + want: uint64Ptr(42), + }, + { + name: "derive from consistent rollup configs", + vnCfgs: map[eth.ChainID]*opnodecfg.Config{ + eth.ChainIDFromUInt64(10): {Rollup: rollup.Config{InteropTime: uint64Ptr(1234)}}, + eth.ChainIDFromUInt64(8453): {Rollup: rollup.Config{InteropTime: uint64Ptr(1234)}}, + }, + want: uint64Ptr(1234), + }, + { + name: "leave interop disabled when no rollup config enables it", + vnCfgs: map[eth.ChainID]*opnodecfg.Config{ + eth.ChainIDFromUInt64(10): {Rollup: rollup.Config{}}, + eth.ChainIDFromUInt64(8453): {Rollup: rollup.Config{}}, + }, + }, + { + name: "error on mixed nil and configured rollup timestamps", + vnCfgs: map[eth.ChainID]*opnodecfg.Config{ + eth.ChainIDFromUInt64(10): {Rollup: rollup.Config{}}, + eth.ChainIDFromUInt64(8453): {Rollup: rollup.Config{InteropTime: uint64Ptr(1234)}}, + }, + wantErr: "has no interop activation timestamp", + }, + { + name: "error on mismatched rollup timestamps", + vnCfgs: map[eth.ChainID]*opnodecfg.Config{ + eth.ChainIDFromUInt64(10): {Rollup: rollup.Config{InteropTime: uint64Ptr(100)}}, + eth.ChainIDFromUInt64(8453): {Rollup: rollup.Config{InteropTime: uint64Ptr(200)}}, + }, + wantErr: "mismatched interop activation timestamps", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := resolveInteropActivationTimestamp(tt.override, tt.vnCfgs) + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func uint64Ptr(v uint64) *uint64 { + return &v +} diff --git a/op-supernode/supernode/supernode.go b/op-supernode/supernode/supernode.go index 81fe96f2804..126a08f84b7 100644 --- a/op-supernode/supernode/supernode.go +++ b/op-supernode/supernode/supernode.go @@ -95,11 +95,24 @@ func New(ctx context.Context, log gethlog.Logger, version string, requestStop co superroot.New(log.New("activity", "superroot"), s.chains), } - log.Info("initializing interop activity? %v", cfg.InteropActivationTimestamp != nil) - // Initialize interop activity if the activation timestamp is set (non-nil) + interopActivationTimestamp, err := resolveInteropActivationTimestamp(cfg.InteropActivationTimestamp, vnCfgs) + if err != nil { + return nil, fmt.Errorf("resolve interop activation timestamp: %w", err) + } + + log.Info("initializing interop activity", "enabled", interopActivationTimestamp != nil) + // Initialize interop activity if the activation timestamp is known (non-nil). // If it's nil, don't start interop. If it's non-nil (including 0), do start it. - if cfg.InteropActivationTimestamp != nil { - interopActivity := interop.New(log.New("activity", "interop"), *cfg.InteropActivationTimestamp, s.chains, cfg.DataDir, s.l1Client) + if interopActivationTimestamp != nil { + // Extract the message expiry window from the first virtual node's dependency set. + var msgExpiryWindow uint64 + for _, vnCfg := range vnCfgs { + if vnCfg.DependencySet != nil { + msgExpiryWindow = vnCfg.DependencySet.MessageExpiryWindow() + break + } + } + interopActivity := interop.New(log.New("activity", "interop"), *interopActivationTimestamp, msgExpiryWindow, s.chains, cfg.DataDir, s.l1Client) s.activities = append(s.activities, interopActivity) for _, chain := range s.chains { chain.RegisterVerifier(interopActivity) @@ -123,6 +136,50 @@ func New(ctx context.Context, log gethlog.Logger, version string, requestStop co return s, nil } +func resolveInteropActivationTimestamp(override *uint64, vnCfgs map[eth.ChainID]*opnodecfg.Config) (*uint64, error) { + if override != nil { + return override, nil + } + + var resolved *uint64 + var resolvedChain eth.ChainID + var missingChain *eth.ChainID + + for chainID, vnCfg := range vnCfgs { + if vnCfg == nil { + continue + } + + if vnCfg.Rollup.InteropTime == nil { + if resolved != nil { + return nil, fmt.Errorf("chain %s has no interop activation timestamp, but chain %s is configured for timestamp %d", chainID, resolvedChain, *resolved) + } + if missingChain == nil { + missingChain = new(eth.ChainID) + *missingChain = chainID + } + continue + } + + if missingChain != nil { + return nil, fmt.Errorf("chain %s is configured for interop activation timestamp %d, but chain %s has no interop activation timestamp", chainID, *vnCfg.Rollup.InteropTime, *missingChain) + } + + if resolved == nil { + ts := *vnCfg.Rollup.InteropTime + resolved = &ts + resolvedChain = chainID + continue + } + + if *resolved != *vnCfg.Rollup.InteropTime { + return nil, fmt.Errorf("mismatched interop activation timestamps: chain %s=%d, chain %s=%d", resolvedChain, *resolved, chainID, *vnCfg.Rollup.InteropTime) + } + } + + return resolved, nil +} + func (s *Supernode) Start(ctx context.Context) error { s.log.Info("supernode starting", "version", s.version) diff --git a/op-test-sequencer/sequencer/backend/work/builders/fakepos/job.go b/op-test-sequencer/sequencer/backend/work/builders/fakepos/job.go index 0436963b4b0..8d850e0aa92 100644 --- a/op-test-sequencer/sequencer/backend/work/builders/fakepos/job.go +++ b/op-test-sequencer/sequencer/backend/work/builders/fakepos/job.go @@ -125,7 +125,7 @@ func (j *Job) Open(ctx context.Context) error { } j.logger.Info("ForkchoiceUpdatedV3", "fcState", fcState) - res, err := j.b.engine.ForkchoiceUpdatedV3(fcState, attrs) + res, err := j.b.engine.ForkchoiceUpdatedV3(ctx, fcState, attrs) if err != nil { j.logger.Error("failed to start building L1 block", "err", err) return err @@ -216,7 +216,7 @@ func (j *Job) Seal(ctx context.Context) (work.Block, error) { j.logger.Info("about to forkchoice update", "safe", j.safe.Hash(), "finalized", j.finalized.Hash(), "head", envelope.ExecutionPayload.BlockHash) - if _, err := j.b.engine.ForkchoiceUpdatedV3(engine.ForkchoiceStateV1{ + if _, err := j.b.engine.ForkchoiceUpdatedV3(ctx, engine.ForkchoiceStateV1{ HeadBlockHash: envelope.ExecutionPayload.BlockHash, SafeBlockHash: j.safe.Hash(), FinalizedBlockHash: j.finalized.Hash(), diff --git a/ops/scripts/check-nut-locks/main.go b/ops/scripts/check-nut-locks/main.go index 76540a2d1d4..4563c643ac7 100644 --- a/ops/scripts/check-nut-locks/main.go +++ b/ops/scripts/check-nut-locks/main.go @@ -5,19 +5,18 @@ import ( "encoding/hex" "fmt" "os" + "os/exec" "path/filepath" "strings" - "github.com/BurntSushi/toml" - + "github.com/ethereum-optimism/optimism/op-core/nuts" opservice "github.com/ethereum-optimism/optimism/op-service" ) // nutBundleGlobs are the locations where NUT bundle JSON files may live. // Update this list when adding new bundle locations. var nutBundleGlobs = []string{ - "op-node/rollup/derive/*_nut_bundle.json", - "op-core/nuts/*_nut_bundle.json", + "op-core/nuts/bundles/*_nut_bundle.json", } // checkAllBundlesLocked searches known paths for *_nut_bundle.json files and @@ -44,9 +43,36 @@ func checkAllBundlesLocked(root string, lockedBundles map[string]bool) error { return nil } -type forkLockEntry struct { - Bundle string `toml:"bundle"` - Hash string `toml:"hash"` +// validateEntry checks a single fork lock entry against its bundle file content. +func validateEntry(fork string, entry nuts.ForkLockEntry, bundleContent []byte) error { + hash := sha256.Sum256(bundleContent) + actual := "sha256:" + hex.EncodeToString(hash[:]) + + expectedHash := strings.TrimSpace(entry.Hash) + if actual != expectedHash { + return fmt.Errorf( + "bundle hash mismatch for fork %s: expected=%s actual=%s. "+ + "If this change is intentional, update the hash in op-core/nuts/fork_lock.toml", + fork, expectedHash, actual, + ) + } + + if entry.Commit == "" { + return fmt.Errorf("fork %s has no commit recorded; "+ + "run 'just nut-snapshot-for %s' to populate the commit field", fork, fork) + } + + return nil +} + +// checkCommitAncestry verifies that a commit is an ancestor of origin/develop. +func checkCommitAncestry(root, fork string, commit string) error { + cmd := exec.Command("git", "merge-base", "--is-ancestor", commit, "origin/develop") + cmd.Dir = root + if err := cmd.Run(); err != nil { + return fmt.Errorf("fork %s: commit %s is not an ancestor of origin/develop", fork, commit[:12]) + } + return nil } func main() { @@ -62,10 +88,9 @@ func run(dir string) error { return fmt.Errorf("finding monorepo root: %w", err) } - lockPath := filepath.Join(root, "op-core", "nuts", "fork_lock.toml") - var locks map[string]forkLockEntry - if _, err := toml.DecodeFile(lockPath, &locks); err != nil { - return fmt.Errorf("reading fork lock file: %w", err) + locks, _, err := nuts.ReadLockFile(dir) + if err != nil { + return err } lockedBundles := make(map[string]bool) @@ -78,16 +103,12 @@ func run(dir string) error { return fmt.Errorf("fork %s: reading bundle %s: %w", fork, entry.Bundle, err) } - hash := sha256.Sum256(content) - actual := "sha256:" + hex.EncodeToString(hash[:]) + if err := validateEntry(fork, entry, content); err != nil { + return err + } - locked := strings.TrimSpace(entry.Hash) - if actual != locked { - return fmt.Errorf( - "bundle hash mismatch for fork %s: locked=%s actual=%s. "+ - "If this change is intentional, update the hash in op-core/nuts/fork_lock.toml", - fork, locked, actual, - ) + if err := checkCommitAncestry(root, fork, entry.Commit); err != nil { + return err } fmt.Printf("fork %s: bundle hash OK\n", fork) diff --git a/ops/scripts/check-nut-locks/main_test.go b/ops/scripts/check-nut-locks/main_test.go new file mode 100644 index 00000000000..c5c85199eab --- /dev/null +++ b/ops/scripts/check-nut-locks/main_test.go @@ -0,0 +1,60 @@ +package main + +import ( + "crypto/sha256" + "encoding/hex" + "testing" + + "github.com/ethereum-optimism/optimism/op-core/nuts" + "github.com/stretchr/testify/require" +) + +func hashOf(data []byte) string { + h := sha256.Sum256(data) + return "sha256:" + hex.EncodeToString(h[:]) +} + +func TestValidateEntry_MatchingHash(t *testing.T) { + content := []byte(`{"transactions":[]}`) + entry := nuts.ForkLockEntry{ + Bundle: "op-core/nuts/bundles/test_nut_bundle.json", + Hash: hashOf(content), + Commit: "abc123", + } + err := validateEntry("test", entry, content) + require.NoError(t, err) +} + +func TestValidateEntry_HashMismatch(t *testing.T) { + content := []byte(`{"transactions":[]}`) + entry := nuts.ForkLockEntry{ + Bundle: "op-core/nuts/bundles/test_nut_bundle.json", + Hash: "sha256:0000000000000000000000000000000000000000000000000000000000000000", + Commit: "abc123", + } + err := validateEntry("test", entry, content) + require.ErrorContains(t, err, "bundle hash mismatch") +} + +func TestValidateEntry_EmptyCommit(t *testing.T) { + content := []byte(`{"transactions":[]}`) + entry := nuts.ForkLockEntry{ + Bundle: "op-core/nuts/bundles/test_nut_bundle.json", + Hash: hashOf(content), + Commit: "", + } + err := validateEntry("test", entry, content) + require.ErrorContains(t, err, "no commit recorded") +} + +func TestValidateEntry_ModifiedBundle(t *testing.T) { + original := []byte(`{"transactions":[{"intent":"deploy"}]}`) + modified := []byte(`{"transactions":[{"intent":"modified"}]}`) + entry := nuts.ForkLockEntry{ + Bundle: "op-core/nuts/bundles/test_nut_bundle.json", + Hash: hashOf(original), + Commit: "abc123", + } + err := validateEntry("test", entry, modified) + require.ErrorContains(t, err, "bundle hash mismatch") +} diff --git a/ops/scripts/nut-provenance-verify-changed.sh b/ops/scripts/nut-provenance-verify-changed.sh new file mode 100755 index 00000000000..725fa1efaef --- /dev/null +++ b/ops/scripts/nut-provenance-verify-changed.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Verifies provenance for all forks whose bundle hash changed vs develop. +# For each fork whose hash changed, checks out the recorded commit, +# regenerates the bundle, and verifies it matches byte-for-byte. +# Unchanged forks are skipped to avoid expensive forge rebuilds. + +git show origin/develop:op-core/nuts/fork_lock.toml > /tmp/base_lock.toml 2>/dev/null || true +for fork in $(yq -p toml -o json op-core/nuts/fork_lock.toml | jq -r 'keys[]'); do + base_hash=$(yq -p toml ".${fork}.hash" /tmp/base_lock.toml 2>/dev/null || echo "") + curr_hash=$(yq -p toml ".${fork}.hash" op-core/nuts/fork_lock.toml) + if [ "$base_hash" != "$curr_hash" ]; then + echo "Verifying $fork (hash changed)..." + go run ./ops/scripts/nut-provenance-verify "$fork" + else + echo "Skipping $fork (unchanged)" + fi +done diff --git a/ops/scripts/nut-provenance-verify/main.go b/ops/scripts/nut-provenance-verify/main.go new file mode 100644 index 00000000000..2b13aa00cc8 --- /dev/null +++ b/ops/scripts/nut-provenance-verify/main.go @@ -0,0 +1,138 @@ +package main + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/ethereum-optimism/optimism/op-core/forks" + "github.com/ethereum-optimism/optimism/op-core/nuts" + opservice "github.com/ethereum-optimism/optimism/op-service" +) + +// bundleGenerator generates a NUT bundle in the given contracts directory. +type bundleGenerator func(contractsDir string) error + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "usage: nut-provenance-verify \n") + os.Exit(1) + } + + fork := forks.Name(os.Args[1]) + if err := run(fork); err != nil { + fmt.Fprintf(os.Stderr, "FAIL: %v\n", err) + os.Exit(1) + } +} + +func run(fork forks.Name) error { + if !forks.IsValid(fork) { + return fmt.Errorf("unknown fork %q; valid forks: %v", fork, forks.All) + } + + root, err := opservice.FindMonorepoRoot(".") + if err != nil { + return fmt.Errorf("finding monorepo root: %w", err) + } + + locks, _, err := nuts.ReadLockFile(".") + if err != nil { + return err + } + + entry, ok := locks[string(fork)] + if !ok { + return fmt.Errorf("no entry for fork %q in fork_lock.toml", fork) + } + + // Step 1: Verify bundle file exists and hash matches. + bundlePath := filepath.Join(root, entry.Bundle) + bundleContent, err := os.ReadFile(bundlePath) + if err != nil { + return fmt.Errorf("reading bundle %s: %w", entry.Bundle, err) + } + + hash := sha256.Sum256(bundleContent) + actual := "sha256:" + hex.EncodeToString(hash[:]) + expectedHash := strings.TrimSpace(entry.Hash) + + if actual != expectedHash { + return fmt.Errorf("hash mismatch: expected=%s actual=%s", expectedHash, actual) + } + fmt.Printf("PASS: bundle hash matches lock (%s)\n", actual) + + // Step 2: Verify the bundle was correctly built from the recorded commit. + if entry.Commit == "" { + return fmt.Errorf("fork %q has no commit recorded; cannot verify provenance", fork) + } + + fmt.Printf("Verifying bundle provenance from commit %s...\n", entry.Commit[:12]) + if err := verifyFromCommit(root, entry, func(contractsDir string) error { + cmd := exec.Command("just", "generate-nut-bundle") + cmd.Dir = contractsDir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() + }); err != nil { + return fmt.Errorf("provenance verification: %w", err) + } + + fmt.Println("PASS: regenerated bundle matches committed bundle") + return nil +} + +// verifyFromCommit creates a temporary worktree at the recorded commit, +// regenerates the NUT bundle, and compares it against the locked bundle. +func verifyFromCommit(root string, entry nuts.ForkLockEntry, generate bundleGenerator) error { + worktreeDir, err := os.MkdirTemp("", "verify-nuts-*") + if err != nil { + return fmt.Errorf("creating temp dir: %w", err) + } + defer os.RemoveAll(worktreeDir) + + // Create worktree at the recorded commit. + addCmd := exec.Command("git", "worktree", "add", "--detach", worktreeDir, entry.Commit) + addCmd.Dir = root + addCmd.Stderr = os.Stderr + if err := addCmd.Run(); err != nil { + return fmt.Errorf("creating worktree at %s: %w", entry.Commit[:12], err) + } + defer func() { + removeCmd := exec.Command("git", "worktree", "remove", "--force", worktreeDir) + removeCmd.Dir = root + _ = removeCmd.Run() + }() + + // Generate NUT bundle in the worktree. + contractsDir := filepath.Join(worktreeDir, "packages", "contracts-bedrock") + if err := generate(contractsDir); err != nil { + return fmt.Errorf("generating NUT bundle at commit %s: %w", entry.Commit[:12], err) + } + + // Read the regenerated bundle. + regenPath := filepath.Join(contractsDir, "snapshots", "upgrades", "current-upgrade-bundle.json") + regenContent, err := os.ReadFile(regenPath) + if err != nil { + return fmt.Errorf("reading regenerated bundle: %w", err) + } + + // Read the committed bundle. + bundlePath := filepath.Join(root, entry.Bundle) + committedContent, err := os.ReadFile(bundlePath) + if err != nil { + return fmt.Errorf("reading committed bundle: %w", err) + } + + if !bytes.Equal(regenContent, committedContent) { + return fmt.Errorf("regenerated bundle at commit %s differs from committed bundle %s", + entry.Commit[:12], entry.Bundle) + } + + return nil +} diff --git a/ops/scripts/nut-provenance-verify/main_test.go b/ops/scripts/nut-provenance-verify/main_test.go new file mode 100644 index 00000000000..5d6f5ca7c78 --- /dev/null +++ b/ops/scripts/nut-provenance-verify/main_test.go @@ -0,0 +1,141 @@ +package main + +import ( + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/ethereum-optimism/optimism/op-core/nuts" + "github.com/stretchr/testify/require" +) + +// initGitRepo creates a bare-minimum git repo with an initial commit +// and returns the repo root and the commit SHA. +func initGitRepo(t *testing.T) (string, string) { + t.Helper() + dir := t.TempDir() + + cmds := [][]string{ + {"git", "init"}, + {"git", "config", "user.email", "test@test.com"}, + {"git", "config", "user.name", "Test"}, + {"git", "config", "commit.gpgsign", "false"}, + {"git", "commit", "--allow-empty", "-m", "init"}, + } + for _, args := range cmds { + cmd := exec.Command(args[0], args[1:]...) + cmd.Dir = dir + out, err := cmd.CombinedOutput() + require.NoError(t, err, "cmd %v failed: %s", args, out) + } + + cmd := exec.Command("git", "rev-parse", "HEAD") + cmd.Dir = dir + out, err := cmd.Output() + require.NoError(t, err) + + return dir, string(out[:len(out)-1]) // trim newline +} + +// writeFileInRepo creates a file at a relative path within the repo and commits it. +func writeFileInRepo(t *testing.T, root, relPath string, content []byte) string { + t.Helper() + absPath := filepath.Join(root, relPath) + require.NoError(t, os.MkdirAll(filepath.Dir(absPath), 0755)) + require.NoError(t, os.WriteFile(absPath, content, 0644)) + + cmd := exec.Command("git", "add", relPath) + cmd.Dir = root + require.NoError(t, cmd.Run()) + + cmd = exec.Command("git", "commit", "-m", "add "+relPath) + cmd.Dir = root + out, err := cmd.CombinedOutput() + require.NoError(t, err, string(out)) + + cmd = exec.Command("git", "rev-parse", "HEAD") + cmd.Dir = root + sha, err := cmd.Output() + require.NoError(t, err) + return string(sha[:len(sha)-1]) +} + +func TestVerifyFromCommit_MatchingBundle(t *testing.T) { + root, _ := initGitRepo(t) + + bundleContent := []byte(`{"metadata":{"version":"1.0.0"},"transactions":[]}`) + bundlePath := "packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json" + commit := writeFileInRepo(t, root, bundlePath, bundleContent) + + // Write the "committed" bundle that verifyFromCommit compares against. + committedBundleRel := "op-core/nuts/bundles/test_nut_bundle.json" + committedBundlePath := filepath.Join(root, committedBundleRel) + require.NoError(t, os.MkdirAll(filepath.Dir(committedBundlePath), 0755)) + require.NoError(t, os.WriteFile(committedBundlePath, bundleContent, 0644)) + + entry := nuts.ForkLockEntry{ + Bundle: committedBundleRel, + Commit: commit, + } + + // Generator is a no-op: the bundle file already exists at the commit. + noopGenerator := func(contractsDir string) error { return nil } + + err := verifyFromCommit(root, entry, noopGenerator) + require.NoError(t, err) +} + +func TestVerifyFromCommit_MismatchedBundle(t *testing.T) { + root, _ := initGitRepo(t) + + bundleContent := []byte(`{"metadata":{"version":"1.0.0"},"transactions":[]}`) + bundlePath := "packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json" + commit := writeFileInRepo(t, root, bundlePath, bundleContent) + + // Write a different bundle as the "committed" version. + committedBundleRel := "op-core/nuts/bundles/test_nut_bundle.json" + committedBundlePath := filepath.Join(root, committedBundleRel) + require.NoError(t, os.MkdirAll(filepath.Dir(committedBundlePath), 0755)) + require.NoError(t, os.WriteFile(committedBundlePath, []byte(`{"modified":true}`), 0644)) + + entry := nuts.ForkLockEntry{ + Bundle: committedBundleRel, + Commit: commit, + } + + noopGenerator := func(contractsDir string) error { return nil } + + err := verifyFromCommit(root, entry, noopGenerator) + require.ErrorContains(t, err, "regenerated bundle at commit") +} + +func TestVerifyFromCommit_GeneratorModifiesBundle(t *testing.T) { + root, _ := initGitRepo(t) + + // Commit an initial bundle. + originalContent := []byte(`{"metadata":{"version":"1.0.0"},"transactions":[]}`) + bundlePath := "packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json" + commit := writeFileInRepo(t, root, bundlePath, originalContent) + + // The committed bundle matches what the generator will produce. + regeneratedContent := []byte(`{"metadata":{"version":"2.0.0"},"transactions":[{"new":true}]}`) + committedBundleRel := "op-core/nuts/bundles/test_nut_bundle.json" + committedBundlePath := filepath.Join(root, committedBundleRel) + require.NoError(t, os.MkdirAll(filepath.Dir(committedBundlePath), 0755)) + require.NoError(t, os.WriteFile(committedBundlePath, regeneratedContent, 0644)) + + entry := nuts.ForkLockEntry{ + Bundle: committedBundleRel, + Commit: commit, + } + + // Generator overwrites the bundle with new content (simulating forge regeneration). + modifyingGenerator := func(contractsDir string) error { + outPath := filepath.Join(contractsDir, "snapshots", "upgrades", "current-upgrade-bundle.json") + return os.WriteFile(outPath, regeneratedContent, 0644) + } + + err := verifyFromCommit(root, entry, modifyingGenerator) + require.NoError(t, err) +} diff --git a/ops/scripts/nut-snapshot-for/main.go b/ops/scripts/nut-snapshot-for/main.go new file mode 100644 index 00000000000..26c22dc68d7 --- /dev/null +++ b/ops/scripts/nut-snapshot-for/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/ethereum-optimism/optimism/op-core/forks" + "github.com/ethereum-optimism/optimism/op-core/nuts" + opservice "github.com/ethereum-optimism/optimism/op-service" +) + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "usage: nut-snapshot-for \n") + os.Exit(1) + } + + fork := forks.Name(os.Args[1]) + if err := run(fork); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} + +func run(fork forks.Name) error { + if !forks.IsValid(fork) { + return fmt.Errorf("unknown fork %q; valid forks: %v", fork, forks.All) + } + + root, err := opservice.FindMonorepoRoot(".") + if err != nil { + return fmt.Errorf("finding monorepo root: %w", err) + } + + // Copy current-upgrade-bundle.json → _nut_bundle.json. + // The caller is responsible for running `just generate-nut-bundle` first if needed. + srcPath := filepath.Join(root, "packages", "contracts-bedrock", "snapshots", "upgrades", "current-upgrade-bundle.json") + content, err := os.ReadFile(srcPath) + if err != nil { + return fmt.Errorf("reading bundle (run 'just generate-nut-bundle' in packages/contracts-bedrock/ first): %w", err) + } + + bundleRel := filepath.Join("op-core", "nuts", "bundles", string(fork)+"_nut_bundle.json") + dstPath := filepath.Join(root, bundleRel) + if err := os.WriteFile(dstPath, content, 0600); err != nil { + return fmt.Errorf("writing bundle to %s: %w", bundleRel, err) + } + fmt.Printf("Copied bundle to %s\n", bundleRel) + + // Compute sha256 of the bundle. + hash := sha256.Sum256(content) + hashStr := "sha256:" + hex.EncodeToString(hash[:]) + + // Record the merge-base with develop (not HEAD) so the commit survives + // squash-merge. Contracts must be merged to develop before snapshotting. + commitCmd := exec.Command("git", "merge-base", "HEAD", "origin/develop") + commitCmd.Dir = root + commitOut, err := commitCmd.Output() + if err != nil { + return fmt.Errorf("finding merge-base with origin/develop (fetch first?): %w", err) + } + commit := strings.TrimSpace(string(commitOut)) + + // Read existing fork_lock.toml, update the entry, write back. + locks, lockPath, err := nuts.ReadLockFile(".") + if err != nil { + return err + } + + locks[string(fork)] = nuts.ForkLockEntry{ + Bundle: bundleRel, + Hash: hashStr, + Commit: commit, + } + + if err := nuts.WriteLockFile(lockPath, locks); err != nil { + return err + } + + fmt.Printf("Updated fork_lock.toml: fork=%s hash=%s commit=%s\n", fork, hashStr, commit[:12]) + return nil +} diff --git a/packages/contracts-bedrock/book/src/contributing/opcm.md b/packages/contracts-bedrock/book/src/contributing/opcm.md index 9bd43d623aa..fe41fbcd372 100644 --- a/packages/contracts-bedrock/book/src/contributing/opcm.md +++ b/packages/contracts-bedrock/book/src/contributing/opcm.md @@ -6,7 +6,7 @@ for the following: 1. Keeping track of the canonical implementation contracts for each [contracts release][versioning]. 2. Deploying new L1 contracts for each OP Chain. 3. Upgrading from one contract release to another. -4. Maintaining the fault proof system by adding game types or updating prestates. +4. Migrating chains to superproofs. All contract upgrades that touch live chains **must** be performed via the OPCM. This guide will walk you through the OPCM's architecture, and how to hook your contracts into it. @@ -17,32 +17,27 @@ the OPCM's architecture, and how to hook your contracts into it. The OPCM consists of multiple contracts: -1. `OPContractsManager`, which serves as the entry point. -2. `OPContractsManagerGameTypeAdder`, which is used to add new game types and update prestates. -3. `OPContractsManagerDeployer`, which is used to deploy new OP Chains. -4. `OPContractsManagerUpgrader`, which is used to upgrade existing OP Chains. -5. `OPContractsManagerContractsContainer`, which is a repository for contract implementations and blueprints. +1. `OPContractsManagerV2`, which serves as the entry point for `deploy()`, `upgrade()`, `upgradeSuperchain()`, and `migrate()`. +2. `OPContractsManagerUtils`, which contains shared helper logic used by deploy and upgrade operations. +3. `OPContractsManagerContainer`, which is a repository for contract implementations and blueprints. +4. `OPContractsManagerMigrator`, which handles migrating chains to superproofs (called via delegatecall from `migrate()`). They fit together like the diagram below: ```mermaid stateDiagram-v2 - state OpContractsManager { + state OPContractsManagerV2 { direction LR - deploy() --> OpContractsManagerDeployer: staticcall - upgrade() --> OpContractsManagerUpgrader: delegatecall - addGameType() --> OpContractsManagerGameTypeAdder: delegatecall - updatePrestate() --> OpContractsManagerGameTypeAdder: delegatecall + deploy() + upgrade() + upgradeSuperchain() + migrate() --> OPContractsManagerMigrator: delegatecall } - state Logic { - OpContractsManagerDeployer --> OpContractsManagerContractsContainer: getImplementations()/getBlueprints() - OpContractsManagerUpgrader --> OpContractsManagerContractsContainer: getImplementations()/getBlueprints() - OpContractsManagerGameTypeAdder --> OpContractsManagerContractsContainer: getImplementations()/getBlueprints() - } - - state Implementations { - OpContractsManagerContractsContainer + state Shared { + OPContractsManagerV2 --> OPContractsManagerUtils: shared helpers + OPContractsManagerV2 --> OPContractsManagerContainer: implementations & blueprints + OPContractsManagerMigrator --> OPContractsManagerContainer: implementations & blueprints } ``` @@ -68,18 +63,17 @@ to each method. This changes between releases, and will not be covered directly Whenever you make updates to in-protocol contracts, you'll need to make corresponding changes inside the OPCM. While the details of each change will vary, we've included some general guidelines below. -### Updating Logic Contracts +### Updating OPContractsManagerV2 -As their name implies, the logic contracts contain the actual logic used to deploy or upgrade contracts. When -modifying these contracts keep the following tips in mind: +All deploy, upgrade, and migration logic lives directly in `OPContractsManagerV2` (with migration delegating to +`OPContractsManagerMigrator`). When modifying these keep the following tips in mind: - The `deploy` method can typically be modified in-place since the deployment process doesn't change much from release to release. For example, most changes to the `deploy` method will involve either adding a new contract or modifying the constructor/initializer for existing contracts. You can use the existing implementation as a guide. - The `upgrade` method changes much more frequently. That said, you can still use the existing implementation as a - guide. Just make sure to delete any old upgrade code that is no longer needed. The `OPContractsManagerUpgrader` - logic contract also contains helpers for things like deploying new dispute games and upgrading proxies to new - implementations. See the `upgradeTo` method for an example. + guide. Just make sure to delete any old upgrade code that is no longer needed. `OPContractsManagerUtils` contains + helpers for things like deploying new dispute games and upgrading proxies to new implementations. - The `upgrade` method will _always_ set the RC on the OPCM to false when called by the upgrade controller. It will only sometimes (depending on your specific upgrade) upgrade Superchain contracts. @@ -94,7 +88,7 @@ When multiple upgrades are in flight at the same time, the fork tests stack upgr tip of `develop` must contain the implementation for the latest upgrade only, fork tests that run upgrades prior to the latest one must use deployed instances of the OPCM. For example, as of this writing upgrades 13, 14, and 15 are all in flight. Therefore, the fork tests will use deployed versions of the OPCM for upgrades 13 and 14 and whatever -is on `develop` for upgrade 15. See `OPContractsManager.t.sol` for the implementation of the fork tests. +is on `develop` for upgrade 15. See `OPContractsManagerV2.t.sol` for the implementation of the fork tests. ## Modifying Contracts for Upgrade diff --git a/packages/contracts-bedrock/book/src/policies/versioning.md b/packages/contracts-bedrock/book/src/policies/versioning.md index 546d164a7a0..dd7e429c654 100644 --- a/packages/contracts-bedrock/book/src/policies/versioning.md +++ b/packages/contracts-bedrock/book/src/policies/versioning.md @@ -79,9 +79,9 @@ Versioning for monorepo releases works as follows: ## Optimism Contracts Manager (OPCM) Versioning -The [OPCM](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OPContractsManager.sol) is the contract that manages the deployment of all contracts on L1. +The [OPCM](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol) is the contract that manages the deployment of all contracts on L1. -The `OPCM` is the source of truth for the contracts that belong in a release, available as on-chain addresses by querying [the `getImplementations` function](https://github.com/ethereum-optimism/optimism/blob/4c8764f0453e141555846d8c9dd2af9edbc1d014/packages/contracts-bedrock/src/L1/OPContractsManager.sol#L1061). +The `OPCM` is the source of truth for the contracts that belong in a release, available as on-chain addresses by querying the [`implementations()`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol) function. ### OPCM Semver Rules diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol deleted file mode 100644 index 8b137891791..00000000000 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerStandardValidator.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerStandardValidator.sol index c4b303b0ce9..decd32d33e3 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerStandardValidator.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerStandardValidator.sol @@ -9,7 +9,6 @@ interface IOPContractsManagerStandardValidator { struct Implementations { address l1ERC721BridgeImpl; address optimismPortalImpl; - address optimismPortalInteropImpl; address ethLockboxImpl; address systemConfigImpl; address optimismMintableERC20FactoryImpl; @@ -64,7 +63,6 @@ interface IOPContractsManagerStandardValidator { function superPermissionedDisputeGameImpl() external view returns (address); function optimismMintableERC20FactoryImpl() external view returns (address); function optimismPortalImpl() external view returns (address); - function optimismPortalInteropImpl() external view returns (address); function ethLockboxImpl() external view returns (address); function preimageOracleVersion() external pure returns (string memory); function superchainConfig() external view returns (ISuperchainConfig); diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol index 41d19d9f56e..fde972fcd46 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol @@ -30,15 +30,25 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { error OptimismPortal_InvalidOutputRootProof(); error OptimismPortal_InvalidProofTimestamp(); error OptimismPortal_InvalidRootClaim(); + error OptimismPortal_MigratingToSameRegistry(); error OptimismPortal_NoReentrancy(); + error OptimismPortal_NotUsingInterop(); error OptimismPortal_ProofNotOldEnough(); error OptimismPortal_Unproven(); + error OptimismPortal_InvalidInteropState(); error OptimismPortal_InvalidLockboxState(); error OutOfGas(); error UnexpectedList(); error UnexpectedString(); event Initialized(uint8 version); + event ETHMigrated(address indexed lockbox, uint256 balance); + event PortalMigrated( + IETHLockbox oldLockbox, + IETHLockbox newLockbox, + IAnchorStateRegistry oldAnchorStateRegistry, + IAnchorStateRegistry newAnchorStateRegistry + ); event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); @@ -71,9 +81,11 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { external; function finalizedWithdrawals(bytes32) external view returns (bool); function guardian() external view returns (address); - function initialize(ISystemConfig _systemConfig, IAnchorStateRegistry _anchorStateRegistry) external; + function initialize(ISystemConfig _systemConfig, IAnchorStateRegistry _anchorStateRegistry, IETHLockbox _ethLockbox) external; function initVersion() external view returns (uint8); function l2Sender() external view returns (address); + function migrateLiquidity() external; + function migrateToSharedDisputeGame(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external; function minimumGasLimit(uint64 _byteCount) external pure returns (uint64); function numProofSubmitters(bytes32 _withdrawalHash) external view returns (uint256); function params() external view returns (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum); // nosemgrep diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol deleted file mode 100644 index 4f3917a70d2..00000000000 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { Types } from "src/libraries/Types.sol"; -import { GameType } from "src/dispute/lib/LibUDT.sol"; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; -import { IProxyAdminOwnedBase } from "interfaces/universal/IProxyAdminOwnedBase.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; - -interface IOptimismPortalInterop is IProxyAdminOwnedBase { - error ContentLengthMismatch(); - error EmptyItem(); - error InvalidDataRemainder(); - error InvalidHeader(); - error ReinitializableBase_ZeroInitVersion(); - error OptimismPortal_AlreadyFinalized(); - error OptimismPortal_BadTarget(); - error OptimismPortal_CallPaused(); - error OptimismPortal_CalldataTooLarge(); - error OptimismPortal_GasEstimation(); - error OptimismPortal_GasLimitTooLow(); - error OptimismPortal_ImproperDisputeGame(); - error OptimismPortal_InvalidDisputeGame(); - error OptimismPortal_InvalidMerkleProof(); - error OptimismPortal_InvalidOutputRootProof(); - error OptimismPortal_InvalidProofTimestamp(); - error OptimismPortal_InvalidRootClaim(); - error OptimismPortal_NoReentrancy(); - error OptimismPortal_ProofNotOldEnough(); - error OptimismPortal_Unproven(); - error OptimismPortal_MigratingToSameRegistry(); - error OutOfGas(); - error UnexpectedList(); - error UnexpectedString(); - - event Initialized(uint8 version); - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); - event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); - event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); - event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); - event ETHMigrated(address indexed lockbox, uint256 ethBalance); - event PortalMigrated(IETHLockbox oldLockbox, IETHLockbox newLockbox, IAnchorStateRegistry oldAnchorStateRegistry, IAnchorStateRegistry newAnchorStateRegistry); - - receive() external payable; - - function anchorStateRegistry() external view returns (IAnchorStateRegistry); - function ethLockbox() external view returns (IETHLockbox); - function checkWithdrawal(bytes32 _withdrawalHash, address _proofSubmitter) external view; - function depositTransaction( - address _to, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) - external - payable; - function disputeGameBlacklist(IDisputeGame _disputeGame) external view returns (bool); - function disputeGameFactory() external view returns (IDisputeGameFactory); - function disputeGameFinalityDelaySeconds() external view returns (uint256); - function donateETH() external payable; - function superchainConfig() external view returns (ISuperchainConfig); - function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external; - function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external; - function finalizeWithdrawalTransactionExternalProof( - Types.WithdrawalTransaction memory _tx, - address _proofSubmitter - ) - external; - function finalizedWithdrawals(bytes32) external view returns (bool); - function guardian() external view returns (address); - function initialize( - ISystemConfig _systemConfig, - IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox - ) - external; - function initVersion() external view returns (uint8); - function l2Sender() external view returns (address); - function minimumGasLimit(uint64 _byteCount) external pure returns (uint64); - function numProofSubmitters(bytes32 _withdrawalHash) external view returns (uint256); - function params() external view returns (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum); // nosemgrep - function paused() external view returns (bool); - function proofMaturityDelaySeconds() external view returns (uint256); - function proofSubmitters(bytes32, uint256) external view returns (address); - function proveWithdrawalTransaction( - Types.WithdrawalTransaction memory _tx, - uint256 _disputeGameIndex, - Types.OutputRootProof memory _outputRootProof, - bytes[] memory _withdrawalProof - ) - external; - function provenWithdrawals( - bytes32, - address - ) - external - view - returns (IDisputeGame disputeGameProxy, uint64 timestamp); - function respectedGameType() external view returns (GameType); - function respectedGameTypeUpdatedAt() external view returns (uint64); - function superRootsActive() external view returns (bool); - function systemConfig() external view returns (ISystemConfig); - function upgrade(IAnchorStateRegistry _anchorStateRegistry, IETHLockbox _ethLockbox) external; - function version() external pure returns (string memory); - function migrateLiquidity() external; - - function __constructor__(uint256 _proofMaturityDelaySeconds) external; -} diff --git a/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerContainer.sol b/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerContainer.sol index 03ba33e9fce..aeb5c17f95b 100644 --- a/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerContainer.sol +++ b/packages/contracts-bedrock/interfaces/L1/opcm/IOPContractsManagerContainer.sol @@ -15,7 +15,6 @@ interface IOPContractsManagerContainer { address protocolVersionsImpl; address l1ERC721BridgeImpl; address optimismPortalImpl; - address optimismPortalInteropImpl; address ethLockboxImpl; address systemConfigImpl; address optimismMintableERC20FactoryImpl; diff --git a/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol b/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol index 727a62bb99f..de996f9193f 100644 --- a/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol +++ b/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol @@ -33,6 +33,10 @@ interface IL2ContractsManager is ISemver { /// @notice Thrown when a v5 slot is passed with a non-zero offset. error L2ContractsManager_InvalidV5Offset(); + /// @notice Thrown when an address has no runtime code. + /// @param _target The address that has no code. + error L2ContractsManager_EmptyImplementation(address _target); + /// @notice Executes the upgrade for all predeploys. /// @dev This function MUST be called via DELEGATECALL from the L2ProxyAdmin. function upgrade() external; diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 89e7c93d158..4133663766e 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -236,6 +236,8 @@ genesis: forge script scripts/L2Genesis.s.sol:L2Genesis --sig 'runWithStateDump()' # Generates the Network Upgrade Transaction (NUT) bundle. +# NOTE: be very careful about renaming this command, it's used in the CI check-nut-locks job, +# which expects this command to be available in historical commits. generate-nut-bundle: build-no-tests @forge script scripts/upgrade/GenerateNUTBundle.s.sol:GenerateNUTBundle --sig 'run()' > /dev/null diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index f85e2a91f8c..73081fedcfd 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -135,6 +135,11 @@ contract L2Genesis is Script { /// @notice Alias for `runWithStateDump` so that no `--sig` needs to be specified. function run(Input memory _input) public { + require( + _input.useInterop + == DevFeatures.isDevFeatureEnabled(_input.devFeatureBitmap, DevFeatures.OPTIMISM_PORTAL_INTEROP), + "L2Genesis: useInterop and OPTIMISM_PORTAL_INTEROP devFeature bit must agree" + ); address deployer = makeAddr("deployer"); vm.startPrank(deployer); vm.chainId(_input.l2ChainID); diff --git a/packages/contracts-bedrock/scripts/checks/valid-semver-check/main.go b/packages/contracts-bedrock/scripts/checks/valid-semver-check/main.go index 119fa6c3c8a..45906edb121 100644 --- a/packages/contracts-bedrock/scripts/checks/valid-semver-check/main.go +++ b/packages/contracts-bedrock/scripts/checks/valid-semver-check/main.go @@ -13,7 +13,7 @@ import ( func main() { if _, err := common.ProcessFilesGlob( []string{"forge-artifacts/**/*.json"}, - []string{"forge-artifacts/L2StandardBridgeInterop.sol/**.json", "forge-artifacts/OptimismPortalInterop.sol/**.json", "forge-artifacts/EAS.sol/**.json", "forge-artifacts/SchemaRegistry.sol/**.json", "forge-artifacts/L1BlockCGT.sol/**.json", "forge-artifacts/L2ToL1MessagePasserCGT.sol/**.json"}, + []string{"forge-artifacts/L2StandardBridgeInterop.sol/**.json", "forge-artifacts/EAS.sol/**.json", "forge-artifacts/SchemaRegistry.sol/**.json", "forge-artifacts/L1BlockCGT.sol/**.json", "forge-artifacts/L2ToL1MessagePasserCGT.sol/**.json"}, processFile, ); err != nil { fmt.Printf("Error: %v/n", err) diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index fa53fd43050..c0b3f00a208 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -53,6 +53,9 @@ import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMin contract Deploy is Deployer { using stdJson for string; + /// @notice The default initial bond. Should match DeployOPChain.DEFAULT_INIT_BOND for consistency. + uint256 private constant DEFAULT_INIT_BOND = 0.08 ether; + //////////////////////////////////////////////////////////////// // Modifiers // //////////////////////////////////////////////////////////////// @@ -486,7 +489,7 @@ contract Deploy is Deployer { }); disputeGameConfigs[1] = IOPContractsManagerUtils.DisputeGameConfig({ enabled: true, - initBond: 0, + initBond: DEFAULT_INIT_BOND, gameType: GameTypes.PERMISSIONED_CANNON, gameArgs: abi.encode( IOPContractsManagerUtils.PermissionedDisputeGameConfig({ diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index b2c4a44fedb..2b06b8b1ba8 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -25,7 +25,6 @@ import { IOPContractsManagerContainer } from "interfaces/L1/opcm/IOPContractsMan import { IOPContractsManagerUtils } from "interfaces/L1/opcm/IOPContractsManagerUtils.sol"; import { IOPContractsManagerMigrator } from "interfaces/L1/opcm/IOPContractsManagerMigrator.sol"; import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; @@ -78,7 +77,6 @@ contract DeployImplementations is Script { IOPContractsManagerContainer opcmContainer; IDelayedWETH delayedWETHImpl; IOptimismPortal optimismPortalImpl; - IOptimismPortalInterop optimismPortalInteropImpl; IETHLockbox ethLockboxImpl; IPreimageOracle preimageOracleSingleton; IMIPS64 mipsSingleton; @@ -120,7 +118,6 @@ contract DeployImplementations is Script { deployL1StandardBridgeImpl(output_); deployOptimismMintableERC20FactoryImpl(output_); deployOptimismPortalImpl(_input, output_); - deployOptimismPortalInteropImpl(_input, output_); deployETHLockboxImpl(output_); deployDelayedWETHImpl(_input, output_); deployPreimageOracleSingleton(_input, output_); @@ -168,7 +165,6 @@ contract DeployImplementations is Script { protocolVersionsImpl: address(_output.protocolVersionsImpl), l1ERC721BridgeImpl: address(_output.l1ERC721BridgeImpl), optimismPortalImpl: address(_output.optimismPortalImpl), - optimismPortalInteropImpl: address(_output.optimismPortalInteropImpl), ethLockboxImpl: address(_output.ethLockboxImpl), systemConfigImpl: address(_output.systemConfigImpl), optimismMintableERC20FactoryImpl: address(_output.optimismMintableERC20FactoryImpl), @@ -370,21 +366,6 @@ contract DeployImplementations is Script { _output.optimismPortalImpl = impl; } - function deployOptimismPortalInteropImpl(Input memory _input, Output memory _output) private { - uint256 proofMaturityDelaySeconds = _input.proofMaturityDelaySeconds; - IOptimismPortalInterop impl = IOptimismPortalInterop( - DeployUtils.createDeterministic({ - _name: "OptimismPortalInterop", - _args: DeployUtils.encodeConstructor( - abi.encodeCall(IOptimismPortalInterop.__constructor__, (proofMaturityDelaySeconds)) - ), - _salt: _salt - }) - ); - vm.label(address(impl), "OptimismPortalInteropImpl"); - _output.optimismPortalInteropImpl = impl; - } - function deployDelayedWETHImpl(Input memory _input, Output memory _output) private { uint256 withdrawalDelaySeconds = _input.withdrawalDelaySeconds; IDelayedWETH impl = IDelayedWETH( @@ -600,7 +581,6 @@ contract DeployImplementations is Script { IOPContractsManagerStandardValidator.Implementations memory opcmImplementations; opcmImplementations.l1ERC721BridgeImpl = _implementations.l1ERC721BridgeImpl; opcmImplementations.optimismPortalImpl = _implementations.optimismPortalImpl; - opcmImplementations.optimismPortalInteropImpl = _implementations.optimismPortalInteropImpl; opcmImplementations.ethLockboxImpl = _implementations.ethLockboxImpl; opcmImplementations.systemConfigImpl = _implementations.systemConfigImpl; opcmImplementations.optimismMintableERC20FactoryImpl = _implementations.optimismMintableERC20FactoryImpl; diff --git a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol index 9aa029b92d0..4b1865f1ca9 100644 --- a/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/ReadImplementationAddresses.s.sol @@ -24,7 +24,6 @@ contract ReadImplementationAddresses is Script { struct Output { address delayedWETH; address optimismPortal; - address optimismPortalInterop; address ethLockbox; address systemConfig; address anchorStateRegistry; @@ -73,7 +72,6 @@ contract ReadImplementationAddresses is Script { output_.delayedWETH = impls.delayedWETHImpl; output_.ethLockbox = impls.ethLockboxImpl; output_.anchorStateRegistry = impls.anchorStateRegistryImpl; - output_.optimismPortalInterop = impls.optimismPortalInteropImpl; output_.faultDisputeGame = impls.faultDisputeGameImpl; output_.permissionedDisputeGame = impls.permissionedDisputeGameImpl; output_.superFaultDisputeGame = impls.superFaultDisputeGameImpl; diff --git a/packages/contracts-bedrock/scripts/deploy/ReadSuperchainDeployment.s.sol b/packages/contracts-bedrock/scripts/deploy/ReadSuperchainDeployment.s.sol index d73b7f48217..fa4fc2eba4e 100644 --- a/packages/contracts-bedrock/scripts/deploy/ReadSuperchainDeployment.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/ReadSuperchainDeployment.s.sol @@ -10,20 +10,10 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; contract ReadSuperchainDeployment is Script { struct Input { - // Deprecated v1 field — kept for Go ABI compatibility, ignored. - // Remove when Go op-deployer drops v1 struct fields. - address opcmAddress; ISuperchainConfig superchainConfigProxy; } struct Output { - // Deprecated v1 ProtocolVersions fields — kept for Go ABI compatibility, always zero. - // Remove when Go op-deployer drops v1 struct fields. - address protocolVersionsImpl; - address protocolVersionsProxy; - address protocolVersionsOwner; - bytes32 recommendedProtocolVersion; - bytes32 requiredProtocolVersion; ISuperchainConfig superchainConfigImpl; ISuperchainConfig superchainConfigProxy; IProxyAdmin superchainProxyAdmin; diff --git a/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol b/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol index 27230d23e29..21a3879304b 100644 --- a/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol @@ -169,7 +169,6 @@ contract VerifyOPCM is Script { function setUp() public { // Overrides for situations where field names do not cleanly map to contract names. fieldNameOverrides["optimismPortalImpl"] = "OptimismPortal2"; - fieldNameOverrides["optimismPortalInteropImpl"] = "OptimismPortalInterop"; fieldNameOverrides["mipsImpl"] = "MIPS64"; fieldNameOverrides["ethLockboxImpl"] = "ETHLockbox"; fieldNameOverrides["faultDisputeGameImpl"] = "FaultDisputeGame"; @@ -235,7 +234,6 @@ contract VerifyOPCM is Script { // Implementation addresses - verify against Container validatorGetterChecks["l1ERC721BridgeImpl"] = "CONTAINER_IMPL"; validatorGetterChecks["optimismPortalImpl"] = "CONTAINER_IMPL"; - validatorGetterChecks["optimismPortalInteropImpl"] = "CONTAINER_IMPL"; validatorGetterChecks["ethLockboxImpl"] = "CONTAINER_IMPL"; validatorGetterChecks["systemConfigImpl"] = "CONTAINER_IMPL"; validatorGetterChecks["optimismMintableERC20FactoryImpl"] = "CONTAINER_IMPL"; @@ -1268,7 +1266,7 @@ contract VerifyOPCM is Script { } // OptimismPortal2: Verify PROOF_MATURITY_DELAY_SECONDS - if (LibString.eq(_target.name, "OptimismPortal2") || LibString.eq(_target.name, "OptimismPortalInterop")) { + if (LibString.eq(_target.name, "OptimismPortal2")) { success = _verifyPortalDelays(IOptimismPortal2(payable(_target.addr))) && success; } diff --git a/packages/contracts-bedrock/snapshots/abi/L2ContractsManager.json b/packages/contracts-bedrock/snapshots/abi/L2ContractsManager.json index 6155c4f6c79..e11bd272d2a 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ContractsManager.json @@ -328,6 +328,17 @@ "name": "L2ContractsManager_DowngradeNotAllowed", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "_target", + "type": "address" + } + ], + "name": "L2ContractsManager_EmptyImplementation", + "type": "error" + }, { "inputs": [], "name": "L2ContractsManager_FeatureFlagMismatch", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerContainer.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerContainer.json index 7907cfda080..67171bfb9ce 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerContainer.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerContainer.json @@ -55,11 +55,6 @@ "name": "optimismPortalImpl", "type": "address" }, - { - "internalType": "address", - "name": "optimismPortalInteropImpl", - "type": "address" - }, { "internalType": "address", "name": "ethLockboxImpl", @@ -223,11 +218,6 @@ "name": "optimismPortalImpl", "type": "address" }, - { - "internalType": "address", - "name": "optimismPortalInteropImpl", - "type": "address" - }, { "internalType": "address", "name": "ethLockboxImpl", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerStandardValidator.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerStandardValidator.json index 50514fcab4c..cc6fb61677b 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerStandardValidator.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerStandardValidator.json @@ -13,11 +13,6 @@ "name": "optimismPortalImpl", "type": "address" }, - { - "internalType": "address", - "name": "optimismPortalInteropImpl", - "type": "address" - }, { "internalType": "address", "name": "ethLockboxImpl", @@ -299,19 +294,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "optimismPortalInteropImpl", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "permissionedDisputeGameImpl", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUtils.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUtils.json index ec7ef5c22dd..cd231961bf0 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUtils.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUtils.json @@ -245,11 +245,6 @@ "name": "optimismPortalImpl", "type": "address" }, - { - "internalType": "address", - "name": "optimismPortalInteropImpl", - "type": "address" - }, { "internalType": "address", "name": "ethLockboxImpl", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerV2.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerV2.json index af59d2e866c..bed5faf833d 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerV2.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerV2.json @@ -336,11 +336,6 @@ "name": "optimismPortalImpl", "type": "address" }, - { - "internalType": "address", - "name": "optimismPortalInteropImpl", - "type": "address" - }, { "internalType": "address", "name": "ethLockboxImpl", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index 9d19d18dbe5..602251f6288 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -294,6 +294,11 @@ "internalType": "contract IAnchorStateRegistry", "name": "_anchorStateRegistry", "type": "address" + }, + { + "internalType": "contract IETHLockbox", + "name": "_ethLockbox", + "type": "address" } ], "name": "initialize", @@ -314,6 +319,31 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "migrateLiquidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IETHLockbox", + "name": "_newLockbox", + "type": "address" + }, + { + "internalType": "contract IAnchorStateRegistry", + "name": "_newAnchorStateRegistry", + "type": "address" + } + ], + "name": "migrateToSharedDisputeGame", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -627,6 +657,25 @@ "stateMutability": "pure", "type": "function" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "lockbox", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "name": "ETHMigrated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -640,6 +689,37 @@ "name": "Initialized", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IETHLockbox", + "name": "oldLockbox", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IETHLockbox", + "name": "newLockbox", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "oldAnchorStateRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "newAnchorStateRegistry", + "type": "address" + } + ], + "name": "PortalMigrated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -794,6 +874,11 @@ "name": "OptimismPortal_InvalidDisputeGame", "type": "error" }, + { + "inputs": [], + "name": "OptimismPortal_InvalidInteropState", + "type": "error" + }, { "inputs": [], "name": "OptimismPortal_InvalidLockboxState", @@ -819,6 +904,11 @@ "name": "OptimismPortal_InvalidRootClaim", "type": "error" }, + { + "inputs": [], + "name": "OptimismPortal_MigratingToSameRegistry", + "type": "error" + }, { "inputs": [], "name": "OptimismPortal_NoReentrancy", @@ -829,6 +919,11 @@ "name": "OptimismPortal_NotAllowedOnCGTMode", "type": "error" }, + { + "inputs": [], + "name": "OptimismPortal_NotUsingInterop", + "type": "error" + }, { "inputs": [], "name": "OptimismPortal_ProofNotOldEnough", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json deleted file mode 100644 index 88c5cce91cf..00000000000 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json +++ /dev/null @@ -1,998 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "_proofMaturityDelaySeconds", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "stateMutability": "payable", - "type": "receive" - }, - { - "inputs": [], - "name": "anchorStateRegistry", - "outputs": [ - { - "internalType": "contract IAnchorStateRegistry", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_withdrawalHash", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "_proofSubmitter", - "type": "address" - } - ], - "name": "checkWithdrawal", - "outputs": [], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_value", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "_gasLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "_isCreation", - "type": "bool" - }, - { - "internalType": "bytes", - "name": "_data", - "type": "bytes" - } - ], - "name": "depositTransaction", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IDisputeGame", - "name": "_disputeGame", - "type": "address" - } - ], - "name": "disputeGameBlacklist", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "disputeGameFactory", - "outputs": [ - { - "internalType": "contract IDisputeGameFactory", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "disputeGameFinalityDelaySeconds", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "donateETH", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "ethLockbox", - "outputs": [ - { - "internalType": "contract IETHLockbox", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasLimit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct Types.WithdrawalTransaction", - "name": "_tx", - "type": "tuple" - } - ], - "name": "finalizeWithdrawalTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasLimit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct Types.WithdrawalTransaction", - "name": "_tx", - "type": "tuple" - }, - { - "internalType": "address", - "name": "_proofSubmitter", - "type": "address" - } - ], - "name": "finalizeWithdrawalTransactionExternalProof", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "finalizedWithdrawals", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "guardian", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "initVersion", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract ISystemConfig", - "name": "_systemConfig", - "type": "address" - }, - { - "internalType": "contract IAnchorStateRegistry", - "name": "_anchorStateRegistry", - "type": "address" - }, - { - "internalType": "contract IETHLockbox", - "name": "_ethLockbox", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "l2Sender", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "migrateLiquidity", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "_newLockbox", - "type": "address" - }, - { - "internalType": "contract IAnchorStateRegistry", - "name": "_newAnchorStateRegistry", - "type": "address" - } - ], - "name": "migrateToSuperRoots", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "_byteCount", - "type": "uint64" - } - ], - "name": "minimumGasLimit", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_withdrawalHash", - "type": "bytes32" - } - ], - "name": "numProofSubmitters", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "params", - "outputs": [ - { - "internalType": "uint128", - "name": "prevBaseFee", - "type": "uint128" - }, - { - "internalType": "uint64", - "name": "prevBoughtGas", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "prevBlockNum", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proofMaturityDelaySeconds", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "proofSubmitters", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "nonce", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "target", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "gasLimit", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "internalType": "struct Types.WithdrawalTransaction", - "name": "_tx", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "_disputeGameIndex", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "version", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "stateRoot", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "messagePasserStorageRoot", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "latestBlockhash", - "type": "bytes32" - } - ], - "internalType": "struct Types.OutputRootProof", - "name": "_outputRootProof", - "type": "tuple" - }, - { - "internalType": "bytes[]", - "name": "_withdrawalProof", - "type": "bytes[]" - } - ], - "name": "proveWithdrawalTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "provenWithdrawals", - "outputs": [ - { - "internalType": "contract IDisputeGame", - "name": "disputeGameProxy", - "type": "address" - }, - { - "internalType": "uint64", - "name": "timestamp", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxyAdmin", - "outputs": [ - { - "internalType": "contract IProxyAdmin", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxyAdminOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "respectedGameType", - "outputs": [ - { - "internalType": "GameType", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "respectedGameTypeUpdatedAt", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "superRootsActive", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "superchainConfig", - "outputs": [ - { - "internalType": "contract ISuperchainConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "systemConfig", - "outputs": [ - { - "internalType": "contract ISystemConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IAnchorStateRegistry", - "name": "_anchorStateRegistry", - "type": "address" - }, - { - "internalType": "contract IETHLockbox", - "name": "_ethLockbox", - "type": "address" - } - ], - "name": "upgrade", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "lockbox", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "ethBalance", - "type": "uint256" - } - ], - "name": "ETHMigrated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "contract IETHLockbox", - "name": "oldLockbox", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IETHLockbox", - "name": "newLockbox", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IAnchorStateRegistry", - "name": "oldAnchorStateRegistry", - "type": "address" - }, - { - "indexed": false, - "internalType": "contract IAnchorStateRegistry", - "name": "newAnchorStateRegistry", - "type": "address" - } - ], - "name": "PortalMigrated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "version", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "opaqueData", - "type": "bytes" - } - ], - "name": "TransactionDeposited", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "withdrawalHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bool", - "name": "success", - "type": "bool" - } - ], - "name": "WithdrawalFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "withdrawalHash", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - } - ], - "name": "WithdrawalProven", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "withdrawalHash", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "proofSubmitter", - "type": "address" - } - ], - "name": "WithdrawalProvenExtension1", - "type": "event" - }, - { - "inputs": [], - "name": "ContentLengthMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "EmptyItem", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidDataRemainder", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidHeader", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_AlreadyFinalized", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_BadTarget", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_CallPaused", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_CalldataTooLarge", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_GasEstimation", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_GasLimitTooLow", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_ImproperDisputeGame", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_InvalidDisputeGame", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_InvalidMerkleProof", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_InvalidOutputRootProof", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_InvalidProofTimestamp", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_InvalidRootClaim", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_MigratingToSameRegistry", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_NoReentrancy", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_ProofNotOldEnough", - "type": "error" - }, - { - "inputs": [], - "name": "OptimismPortal_Unproven", - "type": "error" - }, - { - "inputs": [], - "name": "OutOfGas", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotProxyAdmin", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotProxyAdminOwner", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotResolvedDelegateProxy", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotSharedProxyAdminOwner", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_ProxyAdminNotFound", - "type": "error" - }, - { - "inputs": [], - "name": "ReinitializableBase_ZeroInitVersion", - "type": "error" - }, - { - "inputs": [], - "name": "UnexpectedList", - "type": "error" - }, - { - "inputs": [], - "name": "UnexpectedString", - "type": "error" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index e0490008eb9..88837f494e3 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -4,8 +4,8 @@ "sourceCodeHash": "0xe772f7db8033e4a738850cb28ac4849d3a454c93732135a8a10d4f7cb498088e" }, "src/L1/ETHLockbox.sol:ETHLockbox": { - "initCodeHash": "0x781079a80d379658eb4553622a9da86f7532ffa424f1e8957a82680ee9435f66", - "sourceCodeHash": "0xf4d9f6adc3d99d65b70df3255976980d36d37f8a4514ecc24d786dd03efdb7be" + "initCodeHash": "0xb2ff8426ab2eb36352f790748963c8e1f7a91caf16bee6a035c2c41bac532836", + "sourceCodeHash": "0x870004d5acc24a704277680f09ccca51a020a512e6c6d48cca583a23a0de43a2" }, "src/L1/FeesDepositor.sol:FeesDepositor": { "initCodeHash": "0xe2ca240d728f711df438b7aeb3589c95ad11a97d742539a692ddafaf1365eb54", @@ -24,16 +24,12 @@ "sourceCodeHash": "0x9d16e900a764cd7f19db3656cf7a9e555b23b9c7e018641ed21566657847a314" }, "src/L1/OPContractsManagerStandardValidator.sol:OPContractsManagerStandardValidator": { - "initCodeHash": "0x233f5f4b424bc2aabe170cf758c9ff80841ceab4c78e37edfe3a7bc660c5577d", - "sourceCodeHash": "0x7c0cb663f82b07da8dec8a7497cf2fa56a335fb5bdc57b612c86462f8527d4d5" + "initCodeHash": "0x7f91525e45ea13bd680259e061f15d1bdfbdef7d9fd6c72bc62827b7fa732efe", + "sourceCodeHash": "0x1a0e19b0b6ec15affdb6888267046ddcf3419b3053c77fb8fb8810470f2486a7" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x8c296124bc1b1468cf301a434eebf3f0d9a194cde06876b993a8672577f08187", - "sourceCodeHash": "0xb14d8bceab135616e55fd560a077a4cc66fc3b535f09931d3b9167ee940fa62f" - }, - "src/L1/OptimismPortalInterop.sol:OptimismPortalInterop": { - "initCodeHash": "0xbafd0b80deb0a834335052e32a4199a96121148d9bda05acb62535ac18bd9909", - "sourceCodeHash": "0x24373f3fd28c5c6ae93cc32e2a213bb47458bc0f36e81b2a7b20a7b6b0a97119" + "initCodeHash": "0x1101a3124e60908ec3e7690b33389d835d0405416d107114edd1684528f39bee", + "sourceCodeHash": "0xe9baa6201cba314f5db2f30a6d8c6c23fcdc2e2407246c9e0624d7e781f511e3" }, "src/L1/ProtocolVersions.sol:ProtocolVersions": { "initCodeHash": "0xcb59ad9a5ec2a0831b7f4daa74bdacba82ffa03035dafb499a732c641e017f4e", @@ -48,8 +44,8 @@ "sourceCodeHash": "0xb09cb2f7cbde8585fad5c5beb6811fa9044b156b4203da8005d3f6a7a68c30b2" }, "src/L1/opcm/OPContractsManagerV2.sol:OPContractsManagerV2": { - "initCodeHash": "0x6c8af9dac0ff4dc0c783fcf8af06bde4d444ebab065c907785a24fd4f65f2414", - "sourceCodeHash": "0x937e16a99db4a376c8855b3df8eb529d19614c0fa3d5d7dbe334006bad1452a3" + "initCodeHash": "0xd8b7a5e075e0fd404d8e0aed10b345bb6064c21ac110caf4cf9ee26a66cc9b86", + "sourceCodeHash": "0xbddd81f0a8091778052008a67735b46b9f788872b40562d1906b4a7cbe823649" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0xf1fb169c6dd4eceb5cec6ed6dfa3affc45970e5a01e00827d06af1f9e8df026d", @@ -92,8 +88,8 @@ "sourceCodeHash": "0x7e438cbbe9a8248887b8c21f68c811f90a5cae4902cbbf7b0a1f6cd644dc42d9" }, "src/L2/L2ContractsManager.sol:L2ContractsManager": { - "initCodeHash": "0xc10e87311ed3cc3ffa1bfe65d9612a668b6fb3c1236d2cc6d792b8b628c61d6b", - "sourceCodeHash": "0xcfb64a58770d22187781a271f078fe1080ad43d4229ff4dd132f0db3fe9b86b3" + "initCodeHash": "0xf0e3dbf5a849fae137eec83487d781496edb59f8aa5ae8420f8cb5c805743196", + "sourceCodeHash": "0xc27daa6b6b4617d8c10aaed76fb26e623774639770242212b6174362351d2643" }, "src/L2/L2CrossDomainMessenger.sol:L2CrossDomainMessenger": { "initCodeHash": "0x76784e1bc7abe615094033f3eb16d3a6bd5caf28c2717377a3dd25e6825228f3", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerContainer.json b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerContainer.json index 891f048bf30..7609df72ae4 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerContainer.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerContainer.json @@ -7,7 +7,7 @@ "type": "struct OPContractsManagerContainer.Blueprints" }, { - "bytes": "608", + "bytes": "576", "label": "impls", "offset": 0, "slot": "5", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerStandardValidator.json b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerStandardValidator.json index 16ce2e3bd4b..0346a8ca900 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerStandardValidator.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/OPContractsManagerStandardValidator.json @@ -41,109 +41,102 @@ "slot": "5", "type": "address" }, - { - "bytes": "20", - "label": "optimismPortalInteropImpl", - "offset": 0, - "slot": "6", - "type": "address" - }, { "bytes": "20", "label": "ethLockboxImpl", "offset": 0, - "slot": "7", + "slot": "6", "type": "address" }, { "bytes": "20", "label": "systemConfigImpl", "offset": 0, - "slot": "8", + "slot": "7", "type": "address" }, { "bytes": "20", "label": "optimismMintableERC20FactoryImpl", "offset": 0, - "slot": "9", + "slot": "8", "type": "address" }, { "bytes": "20", "label": "l1CrossDomainMessengerImpl", "offset": 0, - "slot": "10", + "slot": "9", "type": "address" }, { "bytes": "20", "label": "l1StandardBridgeImpl", "offset": 0, - "slot": "11", + "slot": "10", "type": "address" }, { "bytes": "20", "label": "disputeGameFactoryImpl", "offset": 0, - "slot": "12", + "slot": "11", "type": "address" }, { "bytes": "20", "label": "anchorStateRegistryImpl", "offset": 0, - "slot": "13", + "slot": "12", "type": "address" }, { "bytes": "20", "label": "delayedWETHImpl", "offset": 0, - "slot": "14", + "slot": "13", "type": "address" }, { "bytes": "20", "label": "mipsImpl", "offset": 0, - "slot": "15", + "slot": "14", "type": "address" }, { "bytes": "20", "label": "faultDisputeGameImpl", "offset": 0, - "slot": "16", + "slot": "15", "type": "address" }, { "bytes": "20", "label": "permissionedDisputeGameImpl", "offset": 0, - "slot": "17", + "slot": "16", "type": "address" }, { "bytes": "20", "label": "superFaultDisputeGameImpl", "offset": 0, - "slot": "18", + "slot": "17", "type": "address" }, { "bytes": "20", "label": "superPermissionedDisputeGameImpl", "offset": 0, - "slot": "19", + "slot": "18", "type": "address" }, { "bytes": "32", "label": "devFeatureBitmap", "offset": 0, - "slot": "20", + "slot": "19", "type": "bytes32" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortalInterop.json b/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortalInterop.json deleted file mode 100644 index c0bcf34cc42..00000000000 --- a/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortalInterop.json +++ /dev/null @@ -1,149 +0,0 @@ -[ - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "32", - "label": "params", - "offset": 0, - "slot": "1", - "type": "struct ResourceMetering.ResourceParams" - }, - { - "bytes": "1536", - "label": "__gap", - "offset": 0, - "slot": "2", - "type": "uint256[48]" - }, - { - "bytes": "20", - "label": "l2Sender", - "offset": 0, - "slot": "50", - "type": "address" - }, - { - "bytes": "32", - "label": "finalizedWithdrawals", - "offset": 0, - "slot": "51", - "type": "mapping(bytes32 => bool)" - }, - { - "bytes": "32", - "label": "spacer_52_0_32", - "offset": 0, - "slot": "52", - "type": "bytes32" - }, - { - "bytes": "1", - "label": "spacer_53_0_1", - "offset": 0, - "slot": "53", - "type": "bool" - }, - { - "bytes": "20", - "label": "spacer_53_1_20", - "offset": 1, - "slot": "53", - "type": "address" - }, - { - "bytes": "20", - "label": "spacer_54_0_20", - "offset": 0, - "slot": "54", - "type": "address" - }, - { - "bytes": "20", - "label": "systemConfig", - "offset": 0, - "slot": "55", - "type": "contract ISystemConfig" - }, - { - "bytes": "20", - "label": "spacer_56_0_20", - "offset": 0, - "slot": "56", - "type": "address" - }, - { - "bytes": "32", - "label": "provenWithdrawals", - "offset": 0, - "slot": "57", - "type": "mapping(bytes32 => mapping(address => struct OptimismPortalInterop.ProvenWithdrawal))" - }, - { - "bytes": "32", - "label": "spacer_58_0_32", - "offset": 0, - "slot": "58", - "type": "bytes32" - }, - { - "bytes": "4", - "label": "spacer_59_0_4", - "offset": 0, - "slot": "59", - "type": "GameType" - }, - { - "bytes": "8", - "label": "spacer_59_4_8", - "offset": 4, - "slot": "59", - "type": "uint64" - }, - { - "bytes": "32", - "label": "proofSubmitters", - "offset": 0, - "slot": "60", - "type": "mapping(bytes32 => address[])" - }, - { - "bytes": "32", - "label": "spacer_61_0_32", - "offset": 0, - "slot": "61", - "type": "uint256" - }, - { - "bytes": "20", - "label": "anchorStateRegistry", - "offset": 0, - "slot": "62", - "type": "contract IAnchorStateRegistry" - }, - { - "bytes": "20", - "label": "ethLockbox", - "offset": 0, - "slot": "63", - "type": "contract IETHLockbox" - }, - { - "bytes": "1", - "label": "superRootsActive", - "offset": 20, - "slot": "63", - "type": "bool" - } -] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json b/packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json index 9a75d366669..579a6ecbcfe 100644 --- a/packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json +++ b/packages/contracts-bedrock/snapshots/upgrades/current-upgrade-bundle.json @@ -214,14 +214,14 @@ "to": "0x4200000000000000000000000000000000000018" }, { - "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca400000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000003f6c6104006040523480156200001257600080fd5b5060405162003c0c38038062003c0c8339810160408190526200003591620001d4565b30608090815281516001600160a01b0390811660a09081526020840151821660e09081526040850151831660c0908152606086015184166101009081529486015184166101209081529286015184166101409081529086015184166101609081529186015184166101809081529486015184166101a09081529286015184166101c09081529086015184166101e09081529186015184166102009081529486015184166102209081529286015184166102409081529086015184166102609081529186015184166102809081529486015184166102a09081529286015184166102c09081529086015184166102e090815291860151841661030090815294860151841661032090815292860151841661034090815290860151841661036052908501518316610380529284015182166103a05283015181166103c052910151166103e05262000407565b60405161036081016001600160401b0381118282101715620001b157634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b0381168114620001cf57600080fd5b919050565b60006103608284031215620001e857600080fd5b620001f26200017f565b620001fd83620001b7565b81526200020d60208401620001b7565b60208201526200022060408401620001b7565b60408201526200023360608401620001b7565b60608201526200024660808401620001b7565b60808201526200025960a08401620001b7565b60a08201526200026c60c08401620001b7565b60c08201526200027f60e08401620001b7565b60e082015261010062000294818501620001b7565b90820152610120620002a8848201620001b7565b90820152610140620002bc848201620001b7565b90820152610160620002d0848201620001b7565b90820152610180620002e4848201620001b7565b908201526101a0620002f8848201620001b7565b908201526101c06200030c848201620001b7565b908201526101e062000320848201620001b7565b9082015261020062000334848201620001b7565b9082015261022062000348848201620001b7565b908201526102406200035c848201620001b7565b9082015261026062000370848201620001b7565b9082015261028062000384848201620001b7565b908201526102a062000398848201620001b7565b908201526102c0620003ac848201620001b7565b908201526102e0620003c0848201620001b7565b90820152610300620003d4848201620001b7565b90820152610320620003e8848201620001b7565b90820152610340620003fc848201620001b7565b908201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161036051610380516103a0516103c0516103e0516135c26200064a600039600081816104d801526119530152600081816104af0152611b10015260008181610486015261145301526000818161045d015261130501526000818161043401526113ec01526000818161040b0152611a560152600081816103e20152611a180152600081816103b901526119da015260008181610390015261199c0152600081816103670152611ad201526000818161033e0152611a9401526000818161031501526116e90152600081816102ec01526116690152600081816102c301526115ea01526000818161029a0152611915015260008181610271015261120501526000818161024801526118d701526000818161021f01526118b10152600081816101f601526117c00152600081816101ce015261179a0152600081816101a601526110e101526000818161017e015261116d01526000818161015601526114cd01526000818161012e0152610fdb01526000818160df0152610ed5015260008181610107015261175201526000818160ba01528181610ef701528181610ffd015281816111030152818161118f015281816112270152818161132701528181611475015281816114ef0152818161160c0152818161168b015261170b0152600061052b01526135c26000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d5014610046578063615f64fe14610098578063d55ec6971461050a575b600080fd5b6100826040518060400160405280600581526020017f312e352e3000000000000000000000000000000000000000000000000000000081525081565b60405161008f9190612fd5565b60405180910390f35b604080516103608101825273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116828401527f0000000000000000000000000000000000000000000000000000000000000000811660608301527f0000000000000000000000000000000000000000000000000000000000000000811660808301527f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f0000000000000000000000000000000000000000000000000000000000000000811660e08301527f000000000000000000000000000000000000000000000000000000000000000081166101008301527f000000000000000000000000000000000000000000000000000000000000000081166101208301527f000000000000000000000000000000000000000000000000000000000000000081166101408301527f000000000000000000000000000000000000000000000000000000000000000081166101608301527f000000000000000000000000000000000000000000000000000000000000000081166101808301527f000000000000000000000000000000000000000000000000000000000000000081166101a08301527f000000000000000000000000000000000000000000000000000000000000000081166101c08301527f000000000000000000000000000000000000000000000000000000000000000081166101e08301527f000000000000000000000000000000000000000000000000000000000000000081166102008301527f000000000000000000000000000000000000000000000000000000000000000081166102208301527f000000000000000000000000000000000000000000000000000000000000000081166102408301527f000000000000000000000000000000000000000000000000000000000000000081166102608301527f000000000000000000000000000000000000000000000000000000000000000081166102808301527f000000000000000000000000000000000000000000000000000000000000000081166102a08301527f000000000000000000000000000000000000000000000000000000000000000081166102c08301527f000000000000000000000000000000000000000000000000000000000000000081166102e08301527f000000000000000000000000000000000000000000000000000000000000000081166103008301527f000000000000000000000000000000000000000000000000000000000000000081166103208301527f000000000000000000000000000000000000000000000000000000000000000016610340820152905161008f9190612fe8565b610512610514565b005b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610583576040517fada337cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061058d61059b565b905061059881610e96565b50565b6105a3612e1f565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663213268496040518163ffffffff1660e01b8152600401602060405180830381865afa158015610602573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106269190613236565b15156101608201526040517f47af267b0000000000000000000000000000000000000000000000000000000081527f494e5445524f50000000000000000000000000000000000000000000000000006004820152734200000000000000000000000000000000000015906347af267b90602401602060405180830381865afa9250505080156106f0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526106ed91810190613236565b60015b61070157600061018082015261070a565b15156101808201525b806101800151801561072357506107216001611b34565b155b1561075a576040517fa27dcc8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051806020016040528073420000000000000000000000000000000000000773ffffffffffffffffffffffffffffffffffffffff16639fce812c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e8919061327a565b73ffffffffffffffffffffffffffffffffffffffff16905281526040805160208082018084527f7f46ddb200000000000000000000000000000000000000000000000000000000905291519091829173420000000000000000000000000000000000001091637f46ddb29160248086019291908187030181865afa158015610874573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610898919061327a565b73ffffffffffffffffffffffffffffffffffffffff168152508160200181905250604051806020016040528073420000000000000000000000000000000000001473ffffffffffffffffffffffffffffffffffffffff16637f46ddb26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610947919061327a565b73ffffffffffffffffffffffffffffffffffffffff168152508160400181905250604051806020016040528073420000000000000000000000000000000000001273ffffffffffffffffffffffffffffffffffffffff1663ee9a31a26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f6919061327a565b73ffffffffffffffffffffffffffffffffffffffff1690526060820152604080518082018083527fee9a31a2000000000000000000000000000000000000000000000000000000009052905181907342000000000000000000000000000000000000179063ee9a31a2906044808501916020918187030181865afa158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa6919061327a565b73ffffffffffffffffffffffffffffffffffffffff16815260200173420000000000000000000000000000000000001773ffffffffffffffffffffffffffffffffffffffff16637d1d0c5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b449190613297565b90526080820152610b68734200000000000000000000000000000000000011611c97565b60a0820152610b8a734200000000000000000000000000000000000019611c97565b60c0820152610bac73420000000000000000000000000000000000001a611c97565b60e0820152610bce73420000000000000000000000000000000000001b611c97565b61010082015261016081015115610dbe57600073420000000000000000000000000000000000002a905060405180606001604052808273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c72919061327a565b73ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1663d84447156040518163ffffffff1660e01b8152600401600060405180830381865afa158015610cd8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610d1e9190810190613371565b81526020018273ffffffffffffffffffffffffffffffffffffffff1663550fcdc96040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610db49190810190613371565b9052610120830152505b600073420000000000000000000000000000000000002b73ffffffffffffffffffffffffffffffffffffffff1663d61a398b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610e59575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610e569181019061327a565b60015b610e6557506000610e68565b90505b604080516020810190915273ffffffffffffffffffffffffffffffffffffffff909116815261014082015290565b80515160405173ffffffffffffffffffffffffffffffffffffffff9091166024820152610f9990734200000000000000000000000000000000000007907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de80000000000000000000000000000000000000000000000000000000017905260006014611e78565b60208101515160405173ffffffffffffffffffffffffffffffffffffffff909116602482015261109f90734200000000000000000000000000000000000010907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de800000000000000000000000000000000000000000000000000000000179052600080611e78565b60408082015151905173ffffffffffffffffffffffffffffffffffffffff909116602482015261112b90734200000000000000000000000000000000000014907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b60608101515160405173ffffffffffffffffffffffffffffffffffffffff90911660248201526111b790734200000000000000000000000000000000000012907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b6080810151805160209091015160405173ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526112c990734200000000000000000000000000000000000017907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fcd6dc6870000000000000000000000000000000000000000000000000000000017905260016000611e78565b80610160015115611410576101208101518051602082015160409283015192516113d29373420000000000000000000000000000000000002a937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361135493906024016133c2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9065714700000000000000000000000000000000000000000000000000000000179052600080611e78565b6114107342000000000000000000000000000000000000297f00000000000000000000000000000000000000000000000000000000000000006125d4565b6101408101515160405173ffffffffffffffffffffffffffffffffffffffff909116602482015261149d9073420000000000000000000000000000000000002b907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b60a08101518051602082015160409283015192516115ba93734200000000000000000000000000000000000011937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb49dc741000000000000000000000000000000000000000000000000000000001790527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006000611e78565b60c081015180516020820151604092830151925161163993734200000000000000000000000000000000000019937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b60e08101518051602082015160409283015192516116b89373420000000000000000000000000000000000001a937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b6101008101518051602082015160409283015192516117389373420000000000000000000000000000000000001b937f0000000000000000000000000000000000000000000000000000000000000000937f00000000000000000000000000000000000000000000000000000000000000009361151c939060240161343c565b61177673420000000000000000000000000000000000000f7f00000000000000000000000000000000000000000000000000000000000000006125d4565b6117e47342000000000000000000000000000000000000158261016001516117be577f00000000000000000000000000000000000000000000000000000000000000006125d4565b7f00000000000000000000000000000000000000000000000000000000000000006125d4565b8061016001511561188d576040517f46a4d7800000000000000000000000000000000000000000000000000000000081527f435553544f4d5f4741535f544f4b454e000000000000000000000000000000006004820152734200000000000000000000000000000000000015906346a4d78090602401600060405180830381600087803b15801561187457600080fd5b505af1158015611888573d6000803e3d6000fd5b505050505b6118fb7342000000000000000000000000000000000000168261016001516118d5577f00000000000000000000000000000000000000000000000000000000000000006125d4565b7f00000000000000000000000000000000000000000000000000000000000000006125d4565b6119397342000000000000000000000000000000000000187f00000000000000000000000000000000000000000000000000000000000000006125d4565b61197773420000000000000000000000000000000000002d7f00000000000000000000000000000000000000000000000000000000000000006125d4565b80610180015115611a7a576119c07342000000000000000000000000000000000000227f00000000000000000000000000000000000000000000000000000000000000006125d4565b6119fe7342000000000000000000000000000000000000237f00000000000000000000000000000000000000000000000000000000000000006125d4565b611a3c7342000000000000000000000000000000000000247f00000000000000000000000000000000000000000000000000000000000000006125d4565b611a7a7342000000000000000000000000000000000000257f00000000000000000000000000000000000000000000000000000000000000006125d4565b611ab87342000000000000000000000000000000000000207f00000000000000000000000000000000000000000000000000000000000000006125d4565b611af67342000000000000000000000000000000000000217f00000000000000000000000000000000000000000000000000000000000000006125d4565b61059873420000000000000000000000000000000000002c7f00000000000000000000000000000000000000000000000000000000000000006125d4565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000002d600482015260009081907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa158015611bb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bd9919061327a565b90508073ffffffffffffffffffffffffffffffffffffffff163b600003611c035750600092915050565b6040517f78ecabce0000000000000000000000000000000000000000000000000000000081526004810184905273420000000000000000000000000000000000002d906378ecabce90602401602060405180830381865afa158015611c6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c909190613236565b9392505050565b611cb860408051606081018252600080825260208201819052909182015290565b60008273ffffffffffffffffffffffffffffffffffffffff1663d0e12f906040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611d3f575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252611d3c918101906134a5565b60015b611d4b57506000611d4e565b90505b600083905060405180606001604052808273ffffffffffffffffffffffffffffffffffffffff16630d9019e16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611da9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dcd919061327a565b73ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1663d3e5792b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e579190613297565b8152602001836001811115611e6e57611e6e61340d565b9052949350505050565b611e81866128a7565b611ed4576040517fc3fe4a6600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024015b60405180910390fd5b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526000907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa158015611f55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f79919061327a565b905073ffffffffffffffffffffffffffffffffffffffff81163b158015906120c657506120c68773ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015611fea573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526120309190810190613371565b8773ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa15801561207b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526120c19190810190613371565b6128f0565b15612115576040517ff8ce5d1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88166004820152602401611ecb565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152881690633659cfe690602401600060405180830381600087803b15801561217e57600080fd5b505af1158015612192573d6000803e3d6000fd5b507ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009250505083811480156121c9575060ff831615155b15612200576040517f10415a2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808414612394576040517fa6ed563e0000000000000000000000000000000000000000000000000000000081526004810185905260009073ffffffffffffffffffffffffffffffffffffffff8a169063a6ed563e90602401602060405180830381865afa158015612275573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122999190613297565b90506122a68460016134f5565b6122b49060ff16600861351a565b81901c60ff16156122f1576040517fc996d78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061230160ff8616600861351a565b6040517f4e91db080000000000000000000000000000000000000000000000000000000081526004810188905260ff90911b198381166024830152915073ffffffffffffffffffffffffffffffffffffffff8b1690634e91db0890604401600060405180830381600087803b15801561237957600080fd5b505af115801561238d573d6000803e3d6000fd5b5050505050505b6040517fa6ed563e0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff8a169063a6ed563e90602401602060405180830381865afa158015612402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124269190613297565b905060ff604082901c1615612467576040517fc996d78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f4e91db08000000000000000000000000000000000000000000000000000000008152600481018390527fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008216602482015273ffffffffffffffffffffffffffffffffffffffff8a1690634e91db0890604401600060405180830381600087803b1580156124f757600080fd5b505af115801561250b573d6000803e3d6000fd5b50506040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c169250634f1ef2869150612563908b908a90600401613557565b6000604051808303816000875af1158015612582573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526125c89190810190613371565b50505050505050505050565b6125dd826128a7565b61262b576040517fc3fe4a6600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401611ecb565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526000907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa1580156126ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d0919061327a565b905073ffffffffffffffffffffffffffffffffffffffff81163b158015906127d257506127d28373ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015612741573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526127879190810190613371565b8373ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa15801561207b573d6000803e3d6000fd5b15612821576040517ff8ce5d1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401611ecb565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152841690633659cfe690602401600060405180830381600087803b15801561288a57600080fd5b505af115801561289e573d6000803e3d6000fd5b50505050505050565b60007208400000000000000000000000000000000000600b83901c721fffffffffffffffffffffffffffffffffffff161480156128ea57506128e882612916565b155b92915050565b60006128fc838361297e565b158015611c90575061290e83836129ce565b159392505050565b600073ffffffffffffffffffffffffffffffffffffffff821673420000000000000000000000000000000000004214806128ea575073ffffffffffffffffffffffffffffffffffffffff82167342000000000000000000000000000000000000061492915050565b60008061298a84612a43565b9050600061299784612a43565b805183519192501480156129b2575080602001518260200151145b80156129c5575080604001518260400151145b95945050505050565b6000806129da84612a43565b905060006129e784612a43565b805183519192501180612a0d575080518251148015612a0d575080602001518260200151105b806129c5575080518251148015612a2b575080602001518260200151145b80156129c55750604090810151910151109392505050565b612a6760405180606001604052806000815260200160008152602001600081525090565b6000612aa8836040518060400160405280600181526020017f2e00000000000000000000000000000000000000000000000000000000000000815250612c0f565b9050600381511015612ae6576040517f9eda858c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612b4182600281518110612afe57612afe613586565b60200260200101516040518060400160405280600181526020017f2d00000000000000000000000000000000000000000000000000000000000000815250612c0f565b90506000612b9e82600081518110612b5b57612b5b613586565b60200260200101516040518060400160405280600181526020017f2b00000000000000000000000000000000000000000000000000000000000000815250612c0f565b90506040518060600160405280612bce85600081518110612bc157612bc1613586565b6020026020010151612cba565b8152602001612be985600181518110612bc157612bc1613586565b8152602001612c0483600081518110612bc157612bc1613586565b905295945050505050565b60606000612c1d8484612d32565b9050601f1960208201600183510160051b81018651838201526001845101845260005b825160608452818114612c855760405182820380825286601f8201165b8b850181015183820152870180612c5d5750600082820160200152603f018616810160405284525b875160209490940193019050818310612c4057505050508091508251612cb357602081019150600281510382525b5092915050565b80516000907f1999999999999999999999999999999999999999999999999999999999999999825b600181019050603060ff82870151160382851185600a028281019650600983118188108317171586029550505050828110612ce257505080612d2c5763101827966000526004601cfd5b50919050565b606082518251818111612e17576020850194506020840193506020604051019250846001828488010301600060208410612d6b57508286205b601f841660200360031b87515b8951818118831c612dcd578315612dab5783878c2014612dab5760018b019a50848b10612da55750612ddc565b50612d78565b858b038952998601996020909801978615612dcd57848b10612da55750612ddc565b5060018a019950838a10612d78575b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08189030160051c8152602090970190525050505b505092915050565b604080516101c08101825260006101a08201818152825282516020808201855282825280840191909152835180820185528281528385015283518082018552828152606084015283518085019094528184528301529060808201908152602001612ea060408051606081018252600080825260208201819052909182015290565b8152602001612ec660408051606081018252600080825260208201819052909182015290565b8152602001612eec60408051606081018252600080825260208201819052909182015290565b8152602001612f1260408051606081018252600080825260208201819052909182015290565b8152604080516060808201835260008252602082810182905292820152910190815260408051602081810190925260008152910190815260006020820181905260409091015290565b60005b83811015612f76578181015183820152602001612f5e565b83811115612f85576000848401525b50505050565b60008151808452612fa3816020860160208601612f5b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611c906020830184612f8b565b815173ffffffffffffffffffffffffffffffffffffffff1681526103608101602083015161302e602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151613056604084018273ffffffffffffffffffffffffffffffffffffffff169052565b50606083015161307e606084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060808301516130a6608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a08301516130ce60a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c08301516130f660c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161311e60e084018273ffffffffffffffffffffffffffffffffffffffff169052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff90811691840191909152610120808501518216908401526101408085015182169084015261016080850151821690840152610180808501518216908401526101a0808501518216908401526101c0808501518216908401526101e08085015182169084015261020080850151821690840152610220808501518216908401526102408085015182169084015261026080850151821690840152610280808501518216908401526102a0808501518216908401526102c0808501518216908401526102e0808501518216908401526103008085015182169084015261032080850151821690840152610340808501519182168185015290612e17565b60006020828403121561324857600080fd5b81518015158114611c9057600080fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461059857600080fd5b60006020828403121561328c57600080fd5b8151611c9081613258565b6000602082840312156132a957600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff808411156132fa576132fa6132b0565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715613340576133406132b0565b8160405280935085815286868601111561335957600080fd5b613367866020830187612f5b565b5050509392505050565b60006020828403121561338357600080fd5b815167ffffffffffffffff81111561339a57600080fd5b8201601f810184136133ab57600080fd5b6133ba848251602084016132df565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff841681526060602082015260006133f16060830185612f8b565b82810360408401526134038185612f8b565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff84168152602081018390526060810160028310613497577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b826040830152949350505050565b6000602082840312156134b757600080fd5b815160028110611c9057600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600060ff821660ff84168060ff03821115613512576135126134c6565b019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613552576135526134c6565b500290565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260006133ba6040830184612f8b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a0000000000000000000000002a5a3eabb9fd571a3af0299eebdf8eaafe29a914000000000000000000000000250af3f400cf8aac8d410c90f1ba3968dd87df96000000000000000000000000547d0fba434877d7237d511cf87fabe2ee26b152000000000000000000000000ac8538a2e6a1f5dfbb1c4b8bd97cefb2997824a8000000000000000000000000b178cdaa8336f25624a63c049edb5af7ca36c2da000000000000000000000000c053fc0155bf8bda5b568af53276e538f0ea4d58000000000000000000000000716ead0cf3e7ff86a02d4f8cb41a6d14922fa8330000000000000000000000006a97c5d55a21265326150efe12fc30fb21cbff56000000000000000000000000a0734858ba5085ff6db493021a0f8c54605c2cda00000000000000000000000027e51b2254433a3284d9ba73ea551c397db2a124000000000000000000000000a0f4ffff79a0a3e039fcbef738751efba8e84f96000000000000000000000000f43862b9d814bb4504158ceccb0b74b31265e4ee000000000000000000000000893c2ceeb71d38514daf67728d3ff1b213fc4b5f000000000000000000000000f7bed7215eef1003fac426682cf2edeb958569f7000000000000000000000000f7bed7215eef1003fac426682cf2edeb958569f7000000000000000000000000eddf416c7159387cc6df3015700f79cfb891137300000000000000000000000070de55bc0bfbc52c5d0cca1da5816c2428886a34000000000000000000000000bec660b456b84a081e90af29be43385bda5bf7b600000000000000000000000093a8a7a9c98cb998d88dba3373a6c7f8ee2e8a4600000000000000000000000037dc2fe754052a9fac35f17282599fafbeb9f423000000000000000000000000784f1fae11f1c3a9c413423fe1b370a3636b8d560000000000000000000000002f76618143d9d2731c56778192d3893864b423d7000000000000000000000000dda87ef358082ab3f4ba8982290c671efdc4d1590000000000000000000000008256398a687e740006098445b05d5ca46b7be21e0000000000000000000000008684ccc5bf484ec242dbc7119004a83533934a79000000000000000000000000906835344844979ffd3a752eaa23728d513db00b000000000000000000000000e35b194efc4907f383b7e3b87f4c2c339ce239f60000000000000000000000000000000000000000", + "data": "0xcdcb760a9b217f1b15f9c04316d04d42f550c340c5b2ee8e5ae05cab4f8cd9cb21970ca4000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000040b26104006040523480156200001257600080fd5b5060405162003d5238038062003d528339810160408190526200003591620001d4565b30608090815281516001600160a01b0390811660a09081526020840151821660e09081526040850151831660c0908152606086015184166101009081529486015184166101209081529286015184166101409081529086015184166101609081529186015184166101809081529486015184166101a09081529286015184166101c09081529086015184166101e09081529186015184166102009081529486015184166102209081529286015184166102409081529086015184166102609081529186015184166102809081529486015184166102a09081529286015184166102c09081529086015184166102e090815291860151841661030090815294860151841661032090815292860151841661034090815290860151841661036052908501518316610380529284015182166103a05283015181166103c052910151166103e05262000407565b60405161036081016001600160401b0381118282101715620001b157634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b0381168114620001cf57600080fd5b919050565b60006103608284031215620001e857600080fd5b620001f26200017f565b620001fd83620001b7565b81526200020d60208401620001b7565b60208201526200022060408401620001b7565b60408201526200023360608401620001b7565b60608201526200024660808401620001b7565b60808201526200025960a08401620001b7565b60a08201526200026c60c08401620001b7565b60c08201526200027f60e08401620001b7565b60e082015261010062000294818501620001b7565b90820152610120620002a8848201620001b7565b90820152610140620002bc848201620001b7565b90820152610160620002d0848201620001b7565b90820152610180620002e4848201620001b7565b908201526101a0620002f8848201620001b7565b908201526101c06200030c848201620001b7565b908201526101e062000320848201620001b7565b9082015261020062000334848201620001b7565b9082015261022062000348848201620001b7565b908201526102406200035c848201620001b7565b9082015261026062000370848201620001b7565b9082015261028062000384848201620001b7565b908201526102a062000398848201620001b7565b908201526102c0620003ac848201620001b7565b908201526102e0620003c0848201620001b7565b90820152610300620003d4848201620001b7565b90820152610320620003e8848201620001b7565b90820152610340620003fc848201620001b7565b908201529392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161036051610380516103a0516103c0516103e0516137086200064a600039600081816104d801526119150152600081816104af0152611b1b015260008181610486015261141501526000818161045d0152611305015260008181610434015261195e01526000818161040b0152611a610152600081816103e20152611a230152600081816103b901526119e501526000818161039001526119a70152600081816103670152611add01526000818161033e0152611a9f01526000818161031501526116ab0152600081816102ec015261162b0152600081816102c301526115ac01526000818161029a01526118d70152600081816102710152611205015260008181610248015261189901526000818161021f01526118730152600081816101f601526117820152600081816101ce015261175c0152600081816101a601526110e101526000818161017e015261116d015260008181610156015261148f01526000818161012e0152610fdb01526000818160df0152610ed5015260008181610107015261171401526000818160ba01528181610ef701528181610ffd015281816111030152818161118f015281816112270152818161132701528181611437015281816114b1015281816115ce0152818161164d01526116cd0152600061052b01526137086000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d5014610046578063615f64fe14610098578063d55ec6971461050a575b600080fd5b6100826040518060400160405280600581526020017f312e362e3000000000000000000000000000000000000000000000000000000081525081565b60405161008f919061311b565b60405180910390f35b604080516103608101825273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116828401527f0000000000000000000000000000000000000000000000000000000000000000811660608301527f0000000000000000000000000000000000000000000000000000000000000000811660808301527f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f0000000000000000000000000000000000000000000000000000000000000000811660e08301527f000000000000000000000000000000000000000000000000000000000000000081166101008301527f000000000000000000000000000000000000000000000000000000000000000081166101208301527f000000000000000000000000000000000000000000000000000000000000000081166101408301527f000000000000000000000000000000000000000000000000000000000000000081166101608301527f000000000000000000000000000000000000000000000000000000000000000081166101808301527f000000000000000000000000000000000000000000000000000000000000000081166101a08301527f000000000000000000000000000000000000000000000000000000000000000081166101c08301527f000000000000000000000000000000000000000000000000000000000000000081166101e08301527f000000000000000000000000000000000000000000000000000000000000000081166102008301527f000000000000000000000000000000000000000000000000000000000000000081166102208301527f000000000000000000000000000000000000000000000000000000000000000081166102408301527f000000000000000000000000000000000000000000000000000000000000000081166102608301527f000000000000000000000000000000000000000000000000000000000000000081166102808301527f000000000000000000000000000000000000000000000000000000000000000081166102a08301527f000000000000000000000000000000000000000000000000000000000000000081166102c08301527f000000000000000000000000000000000000000000000000000000000000000081166102e08301527f000000000000000000000000000000000000000000000000000000000000000081166103008301527f000000000000000000000000000000000000000000000000000000000000000081166103208301527f000000000000000000000000000000000000000000000000000000000000000016610340820152905161008f919061312e565b610512610514565b005b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163003610583576040517fada337cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061058d61059b565b905061059881610e96565b50565b6105a3612f65565b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff1663213268496040518163ffffffff1660e01b8152600401602060405180830381865afa158015610602573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610626919061337c565b15156101608201526040517f47af267b0000000000000000000000000000000000000000000000000000000081527f494e5445524f50000000000000000000000000000000000000000000000000006004820152734200000000000000000000000000000000000015906347af267b90602401602060405180830381865afa9250505080156106f0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526106ed9181019061337c565b60015b61070157600061018082015261070a565b15156101808201525b806101800151801561072357506107216001611b3f565b155b1561075a576040517fa27dcc8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051806020016040528073420000000000000000000000000000000000000773ffffffffffffffffffffffffffffffffffffffff16639fce812c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e891906133c0565b73ffffffffffffffffffffffffffffffffffffffff16905281526040805160208082018084527f7f46ddb200000000000000000000000000000000000000000000000000000000905291519091829173420000000000000000000000000000000000001091637f46ddb29160248086019291908187030181865afa158015610874573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061089891906133c0565b73ffffffffffffffffffffffffffffffffffffffff168152508160200181905250604051806020016040528073420000000000000000000000000000000000001473ffffffffffffffffffffffffffffffffffffffff16637f46ddb26040518163ffffffff1660e01b8152600401602060405180830381865afa158015610923573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094791906133c0565b73ffffffffffffffffffffffffffffffffffffffff168152508160400181905250604051806020016040528073420000000000000000000000000000000000001273ffffffffffffffffffffffffffffffffffffffff1663ee9a31a26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109f691906133c0565b73ffffffffffffffffffffffffffffffffffffffff1690526060820152604080518082018083527fee9a31a2000000000000000000000000000000000000000000000000000000009052905181907342000000000000000000000000000000000000179063ee9a31a2906044808501916020918187030181865afa158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa691906133c0565b73ffffffffffffffffffffffffffffffffffffffff16815260200173420000000000000000000000000000000000001773ffffffffffffffffffffffffffffffffffffffff16637d1d0c5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b4491906133dd565b90526080820152610b68734200000000000000000000000000000000000011611ca2565b60a0820152610b8a734200000000000000000000000000000000000019611ca2565b60c0820152610bac73420000000000000000000000000000000000001a611ca2565b60e0820152610bce73420000000000000000000000000000000000001b611ca2565b61010082015261016081015115610dbe57600073420000000000000000000000000000000000002a905060405180606001604052808273ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7291906133c0565b73ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1663d84447156040518163ffffffff1660e01b8152600401600060405180830381865afa158015610cd8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610d1e91908101906134b7565b81526020018273ffffffffffffffffffffffffffffffffffffffff1663550fcdc96040518163ffffffff1660e01b8152600401600060405180830381865afa158015610d6e573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610db491908101906134b7565b9052610120830152505b600073420000000000000000000000000000000000002b73ffffffffffffffffffffffffffffffffffffffff1663d61a398b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610e59575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610e56918101906133c0565b60015b610e6557506000610e68565b90505b604080516020810190915273ffffffffffffffffffffffffffffffffffffffff909116815261014082015290565b80515160405173ffffffffffffffffffffffffffffffffffffffff9091166024820152610f9990734200000000000000000000000000000000000007907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de80000000000000000000000000000000000000000000000000000000017905260006014611e83565b60208101515160405173ffffffffffffffffffffffffffffffffffffffff909116602482015261109f90734200000000000000000000000000000000000010907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906044015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fc4d66de800000000000000000000000000000000000000000000000000000000179052600080611e83565b60408082015151905173ffffffffffffffffffffffffffffffffffffffff909116602482015261112b90734200000000000000000000000000000000000014907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b60608101515160405173ffffffffffffffffffffffffffffffffffffffff90911660248201526111b790734200000000000000000000000000000000000012907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b6080810151805160209091015160405173ffffffffffffffffffffffffffffffffffffffff909216602483015260448201526112c990734200000000000000000000000000000000000017907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fcd6dc6870000000000000000000000000000000000000000000000000000000017905260016000611e83565b806101600151156113d2576101208101518051602082015160409283015192516113d29373420000000000000000000000000000000000002a937f0000000000000000000000000000000000000000000000000000000000000000937f0000000000000000000000000000000000000000000000000000000000000000936113549390602401613508565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9065714700000000000000000000000000000000000000000000000000000000179052600080611e83565b6101408101515160405173ffffffffffffffffffffffffffffffffffffffff909116602482015261145f9073420000000000000000000000000000000000002b907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090604401611021565b60a081015180516020820151604092830151925161157c93734200000000000000000000000000000000000011937f0000000000000000000000000000000000000000000000000000000000000000937f0000000000000000000000000000000000000000000000000000000000000000936114de9390602401613582565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb49dc741000000000000000000000000000000000000000000000000000000001790527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006000611e83565b60c08101518051602082015160409283015192516115fb93734200000000000000000000000000000000000019937f0000000000000000000000000000000000000000000000000000000000000000937f0000000000000000000000000000000000000000000000000000000000000000936114de9390602401613582565b60e081015180516020820151604092830151925161167a9373420000000000000000000000000000000000001a937f0000000000000000000000000000000000000000000000000000000000000000937f0000000000000000000000000000000000000000000000000000000000000000936114de9390602401613582565b6101008101518051602082015160409283015192516116fa9373420000000000000000000000000000000000001b937f0000000000000000000000000000000000000000000000000000000000000000937f0000000000000000000000000000000000000000000000000000000000000000936114de9390602401613582565b61173873420000000000000000000000000000000000000f7f00000000000000000000000000000000000000000000000000000000000000006126b1565b6117a6734200000000000000000000000000000000000015826101600151611780577f00000000000000000000000000000000000000000000000000000000000000006126b1565b7f00000000000000000000000000000000000000000000000000000000000000006126b1565b8061016001511561184f576040517f46a4d7800000000000000000000000000000000000000000000000000000000081527f435553544f4d5f4741535f544f4b454e000000000000000000000000000000006004820152734200000000000000000000000000000000000015906346a4d78090602401600060405180830381600087803b15801561183657600080fd5b505af115801561184a573d6000803e3d6000fd5b505050505b6118bd734200000000000000000000000000000000000016826101600151611897577f00000000000000000000000000000000000000000000000000000000000000006126b1565b7f00000000000000000000000000000000000000000000000000000000000000006126b1565b6118fb7342000000000000000000000000000000000000187f00000000000000000000000000000000000000000000000000000000000000006126b1565b61193973420000000000000000000000000000000000002d7f00000000000000000000000000000000000000000000000000000000000000006126b1565b80610160015115611982576119827342000000000000000000000000000000000000297f00000000000000000000000000000000000000000000000000000000000000006126b1565b80610180015115611a85576119cb7342000000000000000000000000000000000000227f00000000000000000000000000000000000000000000000000000000000000006126b1565b611a097342000000000000000000000000000000000000237f00000000000000000000000000000000000000000000000000000000000000006126b1565b611a477342000000000000000000000000000000000000247f00000000000000000000000000000000000000000000000000000000000000006126b1565b611a857342000000000000000000000000000000000000257f00000000000000000000000000000000000000000000000000000000000000006126b1565b611ac37342000000000000000000000000000000000000207f00000000000000000000000000000000000000000000000000000000000000006126b1565b611b017342000000000000000000000000000000000000217f00000000000000000000000000000000000000000000000000000000000000006126b1565b61059873420000000000000000000000000000000000002c7f00000000000000000000000000000000000000000000000000000000000000006126b1565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000002d600482015260009081907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa158015611bc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be491906133c0565b90508073ffffffffffffffffffffffffffffffffffffffff163b600003611c0e5750600092915050565b6040517f78ecabce0000000000000000000000000000000000000000000000000000000081526004810184905273420000000000000000000000000000000000002d906378ecabce90602401602060405180830381865afa158015611c77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9b919061337c565b9392505050565b611cc360408051606081018252600080825260208201819052909182015290565b60008273ffffffffffffffffffffffffffffffffffffffff1663d0e12f906040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611d4a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252611d47918101906135eb565b60015b611d5657506000611d59565b90505b600083905060405180606001604052808273ffffffffffffffffffffffffffffffffffffffff16630d9019e16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd891906133c0565b73ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1663d3e5792b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6291906133dd565b8152602001836001811115611e7957611e79613553565b9052949350505050565b611e8c866129ed565b611edf576040517fc3fe4a6600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526024015b60405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff163b600003611f48576040517f1d38e98500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401611ed6565b8473ffffffffffffffffffffffffffffffffffffffff163b600003611fb1576040517f1d38e98500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86166004820152602401611ed6565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff871660048201526000907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa158015612032573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205691906133c0565b905073ffffffffffffffffffffffffffffffffffffffff81163b158015906121a357506121a38773ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa1580156120c7573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261210d91908101906134b7565b8773ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015612158573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261219e91908101906134b7565b612a36565b156121f2576040517ff8ce5d1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88166004820152602401611ed6565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152881690633659cfe690602401600060405180830381600087803b15801561225b57600080fd5b505af115801561226f573d6000803e3d6000fd5b507ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009250505083811480156122a6575060ff831615155b156122dd576040517f10415a2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808414612471576040517fa6ed563e0000000000000000000000000000000000000000000000000000000081526004810185905260009073ffffffffffffffffffffffffffffffffffffffff8a169063a6ed563e90602401602060405180830381865afa158015612352573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237691906133dd565b905061238384600161363b565b6123919060ff166008613660565b81901c60ff16156123ce576040517fc996d78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006123de60ff86166008613660565b6040517f4e91db080000000000000000000000000000000000000000000000000000000081526004810188905260ff90911b198381166024830152915073ffffffffffffffffffffffffffffffffffffffff8b1690634e91db0890604401600060405180830381600087803b15801561245657600080fd5b505af115801561246a573d6000803e3d6000fd5b5050505050505b6040517fa6ed563e0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff8a169063a6ed563e90602401602060405180830381865afa1580156124df573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061250391906133dd565b905060ff604082901c1615612544576040517fc996d78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f4e91db08000000000000000000000000000000000000000000000000000000008152600481018390527fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008216602482015273ffffffffffffffffffffffffffffffffffffffff8a1690634e91db0890604401600060405180830381600087803b1580156125d457600080fd5b505af11580156125e8573d6000803e3d6000fd5b50506040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c169250634f1ef2869150612640908b908a9060040161369d565b6000604051808303816000875af115801561265f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526126a591908101906134b7565b50505050505050505050565b8073ffffffffffffffffffffffffffffffffffffffff163b60000361271a576040517f1d38e98500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401611ed6565b612723826129ed565b612771576040517fc3fe4a6600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401611ed6565b6040517f204e1c7a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526000907342000000000000000000000000000000000000189063204e1c7a90602401602060405180830381865afa1580156127f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061281691906133c0565b905073ffffffffffffffffffffffffffffffffffffffff81163b1580159061291857506129188373ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015612887573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526128cd91908101906134b7565b8373ffffffffffffffffffffffffffffffffffffffff166354fd4d506040518163ffffffff1660e01b8152600401600060405180830381865afa158015612158573d6000803e3d6000fd5b15612967576040517ff8ce5d1600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401611ed6565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152841690633659cfe690602401600060405180830381600087803b1580156129d057600080fd5b505af11580156129e4573d6000803e3d6000fd5b50505050505050565b60007208400000000000000000000000000000000000600b83901c721fffffffffffffffffffffffffffffffffffff16148015612a305750612a2e82612a5c565b155b92915050565b6000612a428383612ac4565b158015611c9b5750612a548383612b14565b159392505050565b600073ffffffffffffffffffffffffffffffffffffffff82167342000000000000000000000000000000000000421480612a30575073ffffffffffffffffffffffffffffffffffffffff82167342000000000000000000000000000000000000061492915050565b600080612ad084612b89565b90506000612add84612b89565b80518351919250148015612af8575080602001518260200151145b8015612b0b575080604001518260400151145b95945050505050565b600080612b2084612b89565b90506000612b2d84612b89565b805183519192501180612b53575080518251148015612b53575080602001518260200151105b80612b0b575080518251148015612b71575080602001518260200151145b8015612b0b5750604090810151910151109392505050565b612bad60405180606001604052806000815260200160008152602001600081525090565b6000612bee836040518060400160405280600181526020017f2e00000000000000000000000000000000000000000000000000000000000000815250612d55565b9050600381511015612c2c576040517f9eda858c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612c8782600281518110612c4457612c446136cc565b60200260200101516040518060400160405280600181526020017f2d00000000000000000000000000000000000000000000000000000000000000815250612d55565b90506000612ce482600081518110612ca157612ca16136cc565b60200260200101516040518060400160405280600181526020017f2b00000000000000000000000000000000000000000000000000000000000000815250612d55565b90506040518060600160405280612d1485600081518110612d0757612d076136cc565b6020026020010151612e00565b8152602001612d2f85600181518110612d0757612d076136cc565b8152602001612d4a83600081518110612d0757612d076136cc565b905295945050505050565b60606000612d638484612e78565b9050601f1960208201600183510160051b81018651838201526001845101845260005b825160608452818114612dcb5760405182820380825286601f8201165b8b850181015183820152870180612da35750600082820160200152603f018616810160405284525b875160209490940193019050818310612d8657505050508091508251612df957602081019150600281510382525b5092915050565b80516000907f1999999999999999999999999999999999999999999999999999999999999999825b600181019050603060ff82870151160382851185600a028281019650600983118188108317171586029550505050828110612e2857505080612e725763101827966000526004601cfd5b50919050565b606082518251818111612f5d576020850194506020840193506020604051019250846001828488010301600060208410612eb157508286205b601f841660200360031b87515b8951818118831c612f13578315612ef15783878c2014612ef15760018b019a50848b10612eeb5750612f22565b50612ebe565b858b038952998601996020909801978615612f1357848b10612eeb5750612f22565b5060018a019950838a10612ebe575b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08189030160051c8152602090970190525050505b505092915050565b604080516101c08101825260006101a08201818152825282516020808201855282825280840191909152835180820185528281528385015283518082018552828152606084015283518085019094528184528301529060808201908152602001612fe660408051606081018252600080825260208201819052909182015290565b815260200161300c60408051606081018252600080825260208201819052909182015290565b815260200161303260408051606081018252600080825260208201819052909182015290565b815260200161305860408051606081018252600080825260208201819052909182015290565b8152604080516060808201835260008252602082810182905292820152910190815260408051602081810190925260008152910190815260006020820181905260409091015290565b60005b838110156130bc5781810151838201526020016130a4565b838111156130cb576000848401525b50505050565b600081518084526130e98160208601602086016130a1565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000611c9b60208301846130d1565b815173ffffffffffffffffffffffffffffffffffffffff16815261036081016020830151613174602084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015161319c604084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060608301516131c4606084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060808301516131ec608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a083015161321460a084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c083015161323c60c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161326460e084018273ffffffffffffffffffffffffffffffffffffffff169052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff90811691840191909152610120808501518216908401526101408085015182169084015261016080850151821690840152610180808501518216908401526101a0808501518216908401526101c0808501518216908401526101e08085015182169084015261020080850151821690840152610220808501518216908401526102408085015182169084015261026080850151821690840152610280808501518216908401526102a0808501518216908401526102c0808501518216908401526102e0808501518216908401526103008085015182169084015261032080850151821690840152610340808501519182168185015290612f5d565b60006020828403121561338e57600080fd5b81518015158114611c9b57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461059857600080fd5b6000602082840312156133d257600080fd5b8151611c9b8161339e565b6000602082840312156133ef57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600067ffffffffffffffff80841115613440576134406133f6565b604051601f85017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715613486576134866133f6565b8160405280935085815286868601111561349f57600080fd5b6134ad8660208301876130a1565b5050509392505050565b6000602082840312156134c957600080fd5b815167ffffffffffffffff8111156134e057600080fd5b8201601f810184136134f157600080fd5b61350084825160208401613425565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8416815260606020820152600061353760608301856130d1565b828103604084015261354981856130d1565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff841681526020810183905260608101600283106135dd577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b826040830152949350505050565b6000602082840312156135fd57600080fd5b815160028110611c9b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600060ff821660ff84168060ff038211156136585761365861360c565b019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156136985761369861360c565b500290565b73ffffffffffffffffffffffffffffffffffffffff8316815260406020820152600061350060408301846130d1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a0000000000000000000000002a5a3eabb9fd571a3af0299eebdf8eaafe29a914000000000000000000000000250af3f400cf8aac8d410c90f1ba3968dd87df96000000000000000000000000547d0fba434877d7237d511cf87fabe2ee26b152000000000000000000000000ac8538a2e6a1f5dfbb1c4b8bd97cefb2997824a8000000000000000000000000b178cdaa8336f25624a63c049edb5af7ca36c2da000000000000000000000000c053fc0155bf8bda5b568af53276e538f0ea4d58000000000000000000000000716ead0cf3e7ff86a02d4f8cb41a6d14922fa8330000000000000000000000006a97c5d55a21265326150efe12fc30fb21cbff56000000000000000000000000a0734858ba5085ff6db493021a0f8c54605c2cda00000000000000000000000027e51b2254433a3284d9ba73ea551c397db2a124000000000000000000000000a0f4ffff79a0a3e039fcbef738751efba8e84f96000000000000000000000000f43862b9d814bb4504158ceccb0b74b31265e4ee000000000000000000000000893c2ceeb71d38514daf67728d3ff1b213fc4b5f000000000000000000000000f7bed7215eef1003fac426682cf2edeb958569f7000000000000000000000000f7bed7215eef1003fac426682cf2edeb958569f7000000000000000000000000eddf416c7159387cc6df3015700f79cfb891137300000000000000000000000070de55bc0bfbc52c5d0cca1da5816c2428886a34000000000000000000000000bec660b456b84a081e90af29be43385bda5bf7b600000000000000000000000093a8a7a9c98cb998d88dba3373a6c7f8ee2e8a4600000000000000000000000037dc2fe754052a9fac35f17282599fafbeb9f423000000000000000000000000784f1fae11f1c3a9c413423fe1b370a3636b8d560000000000000000000000002f76618143d9d2731c56778192d3893864b423d7000000000000000000000000dda87ef358082ab3f4ba8982290c671efdc4d1590000000000000000000000008256398a687e740006098445b05d5ca46b7be21e0000000000000000000000008684ccc5bf484ec242dbc7119004a83533934a79000000000000000000000000906835344844979ffd3a752eaa23728d513db00b000000000000000000000000e35b194efc4907f383b7e3b87f4c2c339ce239f60000000000000000000000000000", "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", "gasLimit": 4500000, "intent": "Deploy L2ContractsManager Implementation", "to": "0x420000000000000000000000000000000000002C" }, { - "data": "0x7c36f37e000000000000000000000000a723d436b320015ebead5e589c49e03131b80ee3", + "data": "0x7c36f37e00000000000000000000000039ea556668555b5987bee6610232ad03ce83c508", "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", "gasLimit": 3000000, "intent": "L2ProxyAdmin Upgrade Predeploys", diff --git a/packages/contracts-bedrock/specs/l2-upgrades-2-contracts.md b/packages/contracts-bedrock/specs/l2-upgrades-2-contracts.md index a14c8ae335c..a21b78c9d5e 100644 --- a/packages/contracts-bedrock/specs/l2-upgrades-2-contracts.md +++ b/packages/contracts-bedrock/specs/l2-upgrades-2-contracts.md @@ -86,7 +86,7 @@ client implementations. The upgrade system maintains the existing pattern of injecting Network Upgrade Transactions at specific fork block heights while improving the development and testing process. Upgrade transactions are defined in JSON bundles (see -[Bundle Format](./l2-upgrades-1-execution.md#bundle-format)) that are tracked in git, generated from Solidity scripts, +Bundle Format) that are tracked in git, generated from Solidity scripts, and executed deterministically at fork activation. ## ConditionalDeployer diff --git a/packages/contracts-bedrock/src/L1/ETHLockbox.sol b/packages/contracts-bedrock/src/L1/ETHLockbox.sol index 5cb777150b9..63c470a2ca6 100644 --- a/packages/contracts-bedrock/src/L1/ETHLockbox.sol +++ b/packages/contracts-bedrock/src/L1/ETHLockbox.sol @@ -73,9 +73,9 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ReinitializableBase, mapping(IETHLockbox => bool) public authorizedLockboxes; /// @notice Semantic version. - /// @custom:semver 1.2.1 + /// @custom:semver 1.3.1 function version() public view virtual returns (string memory) { - return "1.2.1"; + return "1.3.1"; } /// @notice Constructs the ETHLockbox contract. @@ -195,7 +195,7 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ReinitializableBase, } /// @notice Migrates liquidity from the current ETH lockbox to another. - /// @dev Must be called atomically with `OptimismPortal.migrateToSuperRoots()` in the same + /// @dev Must be called atomically with `OptimismPortal.migrateToSharedDisputeGame()` in the same /// transaction batch, or otherwise the OptimismPortal may not be able to unlock ETH /// from the ETHLockbox on finalized withdrawals. /// @param _lockbox The address of the ETH lockbox to migrate liquidity to. diff --git a/packages/contracts-bedrock/src/L1/OPContractsManagerStandardValidator.sol b/packages/contracts-bedrock/src/L1/OPContractsManagerStandardValidator.sol index 7310b8d0ed3..1789ebb5aba 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManagerStandardValidator.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManagerStandardValidator.sol @@ -40,8 +40,8 @@ import { IBigStepper } from "interfaces/dispute/IBigStepper.sol"; /// before and after an upgrade. contract OPContractsManagerStandardValidator is ISemver { /// @notice The semantic version of the OPContractsManagerStandardValidator contract. - /// @custom:semver 2.5.0 - string public constant version = "2.5.0"; + /// @custom:semver 2.7.0 + string public constant version = "2.7.0"; /// @notice The SuperchainConfig contract. ISuperchainConfig public superchainConfig; @@ -63,9 +63,6 @@ contract OPContractsManagerStandardValidator is ISemver { /// @notice The OptimismPortal implementation address. address public optimismPortalImpl; - /// @notice The OptimismPortalInterop implementation address. - address public optimismPortalInteropImpl; - /// @notice The ETHLockbox implementation address. address public ethLockboxImpl; @@ -112,7 +109,6 @@ contract OPContractsManagerStandardValidator is ISemver { struct Implementations { address l1ERC721BridgeImpl; address optimismPortalImpl; - address optimismPortalInteropImpl; address ethLockboxImpl; address systemConfigImpl; address optimismMintableERC20FactoryImpl; @@ -189,7 +185,6 @@ contract OPContractsManagerStandardValidator is ISemver { // Set implementation addresses from struct l1ERC721BridgeImpl = _implementations.l1ERC721BridgeImpl; optimismPortalImpl = _implementations.optimismPortalImpl; - optimismPortalInteropImpl = _implementations.optimismPortalInteropImpl; ethLockboxImpl = _implementations.ethLockboxImpl; systemConfigImpl = _implementations.systemConfigImpl; optimismMintableERC20FactoryImpl = _implementations.optimismMintableERC20FactoryImpl; @@ -437,23 +432,12 @@ contract OPContractsManagerStandardValidator is ISemver { { IOptimismPortal2 _portal = IOptimismPortal2(payable(_sysCfg.optimismPortal())); - if (DevFeatures.isDevFeatureEnabled(devFeatureBitmap, DevFeatures.OPTIMISM_PORTAL_INTEROP)) { - _errors = internalRequire( - LibString.eq(getVersion(address(_portal)), string.concat(getVersion(optimismPortalInteropImpl))), - "PORTAL-10", - _errors - ); - _errors = internalRequire( - getProxyImplementation(_admin, address(_portal)) == optimismPortalInteropImpl, "PORTAL-20", _errors - ); - } else { - _errors = internalRequire( - LibString.eq(getVersion(address(_portal)), getVersion(optimismPortalImpl)), "PORTAL-10", _errors - ); - _errors = internalRequire( - getProxyImplementation(_admin, address(_portal)) == optimismPortalImpl, "PORTAL-20", _errors - ); - } + _errors = internalRequire( + LibString.eq(getVersion(address(_portal)), getVersion(optimismPortalImpl)), "PORTAL-10", _errors + ); + _errors = internalRequire( + getProxyImplementation(_admin, address(_portal)) == optimismPortalImpl, "PORTAL-20", _errors + ); IDisputeGameFactory _dgf = IDisputeGameFactory(_sysCfg.disputeGameFactory()); _errors = internalRequire(address(_portal.disputeGameFactory()) == address(_dgf), "PORTAL-30", _errors); diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 9117301b9d7..42fb472cbec 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -130,6 +130,18 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @custom:spacer superRootsActive bool private spacer_63_20_1; + /// @notice Emitted when the Portal is migrated. + /// @param oldLockbox The lockbox before the migration + /// @param newLockbox The shared lockbox + /// @param oldAnchorStateRegistry The anchorStateRegistry used before the migration + /// @param newAnchorStateRegistry The anchorStateRegistry used after the migration + event PortalMigrated( + IETHLockbox oldLockbox, + IETHLockbox newLockbox, + IAnchorStateRegistry oldAnchorStateRegistry, + IAnchorStateRegistry newAnchorStateRegistry + ); + /// @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event /// are read by the rollup node and used to derive deposit transactions on L2. /// @param from Address that triggered the deposit transaction. @@ -168,6 +180,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when the portal is paused. error OptimismPortal_CallPaused(); + /// @notice Migrates the total ETH balance to the ETHLockbox. + event ETHMigrated(address indexed lockbox, uint256 balance); + /// @notice Thrown when a CGT withdrawal is not allowed. error OptimismPortal_NotAllowedOnCGTMode(); @@ -183,6 +198,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when a withdrawal has not been proven against a valid dispute game. error OptimismPortal_InvalidDisputeGame(); + /// @notice Thrown when Interop is set without the lockbox feature flag + error OptimismPortal_InvalidInteropState(); + /// @notice Thrown when a withdrawal has not been proven against a valid merkle proof. error OptimismPortal_InvalidMerkleProof(); @@ -195,9 +213,17 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when the root claim of a dispute game is invalid. error OptimismPortal_InvalidRootClaim(); + /// @notice Thrown when migrating to the registry that was previously + /// set on the OptimismPortal prior to the migration + error OptimismPortal_MigratingToSameRegistry(); + /// @notice Thrown when a withdrawal is being finalized by a reentrant call. error OptimismPortal_NoReentrancy(); + /// @notice Thrown when calling a function that is only available when INTEROP + /// is enabled + error OptimismPortal_NotUsingInterop(); + /// @notice Thrown when a withdrawal has not been proven for long enough. error OptimismPortal_ProofNotOldEnough(); @@ -208,9 +234,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase error OptimismPortal_InvalidLockboxState(); /// @notice Semantic version. - /// @custom:semver 5.3.0 + /// @custom:semver 5.6.0 function version() public pure virtual returns (string memory) { - return "5.3.0"; + return "5.6.0"; } /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. @@ -224,7 +250,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @param _anchorStateRegistry Address of the AnchorStateRegistry. function initialize( ISystemConfig _systemConfig, - IAnchorStateRegistry _anchorStateRegistry + IAnchorStateRegistry _anchorStateRegistry, + IETHLockbox _ethLockbox ) external reinitializer(initVersion()) @@ -235,7 +262,11 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Now perform initialization logic. systemConfig = _systemConfig; anchorStateRegistry = _anchorStateRegistry; + if (address(_ethLockbox) != address(0)) { + ethLockbox = _ethLockbox; + } + _assertValidInteropState(); // Assert that the lockbox state is valid. _assertValidLockboxState(); @@ -444,6 +475,67 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase finalizeWithdrawalTransactionExternalProof(_tx, msg.sender); } + /// @notice Migrates the total ETH balance of this contract to the ETHLockbox. + function migrateLiquidity() public { + if (!_isUsingInterop()) revert OptimismPortal_NotUsingInterop(); + // Liquidity migration can only be triggered by the ProxyAdmin owner. + _assertOnlyProxyAdminOwner(); + + // Migrate the liquidity. + uint256 ethBalance = address(this).balance; + ethLockbox.lockETH{ value: ethBalance }(); + emit ETHMigrated(address(ethLockbox), ethBalance); + } + + /// @notice Allows the owner of the ProxyAdmin to migrate the OptimismPortal to use a new + /// lockbox, point at a new AnchorStateRegistry, and start to use the Super Roots proof + /// method. Primarily used for OptimismPortal instances to join the interop set, but + /// can also be used to swap the proof method from Output Roots to Super Roots if the + /// provided lockbox is the same as the current one. + /// @dev It is possible to change lockboxes without migrating liquidity. This can cause one + /// of the OptimismPortal instances connected to the new lockbox to not be able to + /// unlock sufficient ETH to finalize withdrawals which would trigger reverts. To avoid + /// this issue, guarantee that this function is called atomically alongside the + /// ETHLockbox.migrateLiquidity() function within the same transaction. + /// @param _newLockbox The address of the new ETHLockbox contract. + /// @param _newAnchorStateRegistry The address of the new AnchorStateRegistry contract. + + function migrateToSharedDisputeGame( + IETHLockbox _newLockbox, + IAnchorStateRegistry _newAnchorStateRegistry + ) + external + { + if (!_isUsingInterop()) revert OptimismPortal_NotUsingInterop(); + // Migration can only be triggered when the system is not paused because the migration can + // potentially unpause the system as a result of the modified ETHLockbox address. + _assertNotPaused(); + + // Migration can only be triggered by the ProxyAdmin owner. + _assertOnlyProxyAdminOwner(); + + // Chains can use this method to swap the proof method from Output Roots to Super Roots + // without joining the interop set. In this case, the old and new lockboxes will be the + // same. However, whether or not a chain is joining the interop set, all chains will need a + // new AnchorStateRegistry when migrating to Super Roots. We therefore check that the new + // AnchorStateRegistry is different than the old one to prevent this function from being + // accidentally misused. + if (anchorStateRegistry == _newAnchorStateRegistry) { + revert OptimismPortal_MigratingToSameRegistry(); + } + + // Update the ETHLockbox. + IETHLockbox oldLockbox = ethLockbox; + ethLockbox = _newLockbox; + + // Update the AnchorStateRegistry. + IAnchorStateRegistry oldAnchorStateRegistry = anchorStateRegistry; + anchorStateRegistry = _newAnchorStateRegistry; + + // Emit a PortalMigrated event. + emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); + } + /// @notice Finalizes a withdrawal transaction, using an external proof submitter. /// @param _tx Withdrawal transaction to finalize. /// @param _proofSubmitter Address of the proof submitter. @@ -641,6 +733,12 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase return systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX) && address(ethLockbox) != address(0); } + /// @notice Checks if the Interop feature is enabled. + /// @return bool True if the Interop feature is enabled. + function _isUsingInterop() internal view returns (bool) { + return systemConfig.isFeatureEnabled(Features.INTEROP) && systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX); + } + /// @notice Checks if the Custom Gas Token feature is enabled. /// @return bool True if the Custom Gas Token feature is enabled. function _isUsingCustomGasToken() internal view returns (bool) { @@ -656,6 +754,13 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase } } + /// @notice Asserts the ETHLockbox feature flag must be set if INTEROP is set + function _assertValidInteropState() internal view { + if (systemConfig.isFeatureEnabled(Features.INTEROP) && !systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { + revert OptimismPortal_InvalidInteropState(); + } + } + /// @notice Asserts that the ETHLockbox is set/unset correctly depending on the feature flag. function _assertValidLockboxState() internal view { if ( diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol deleted file mode 100644 index fda0ae38660..00000000000 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ /dev/null @@ -1,744 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Contracts -import { ProxyAdminOwnedBase } from "src/universal/ProxyAdminOwnedBase.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import { ResourceMetering } from "src/L1/ResourceMetering.sol"; -import { ReinitializableBase } from "src/universal/ReinitializableBase.sol"; - -// Libraries -import { EOA } from "src/libraries/EOA.sol"; -import { SafeCall } from "src/libraries/SafeCall.sol"; -import { Constants } from "src/libraries/Constants.sol"; -import { Types } from "src/libraries/Types.sol"; -import { Hashing } from "src/libraries/Hashing.sol"; -import { SecureMerkleTrie } from "src/libraries/trie/SecureMerkleTrie.sol"; -import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; -import { GameStatus, GameType, Claim } from "src/dispute/lib/Types.sol"; - -// Interfaces -import { ISemver } from "interfaces/universal/ISemver.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; -import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; - -/// @custom:proxied true -/// @title OptimismPortalInterop -/// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1 -/// and L2. Messages sent directly to the OptimismPortal have no form of replayability. -/// Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. -contract OptimismPortalInterop is Initializable, ResourceMetering, ReinitializableBase, ProxyAdminOwnedBase, ISemver { - /// @notice Represents a proven withdrawal. - /// @custom:field disputeGameProxy Game that the withdrawal was proven against. - /// @custom:field timestamp Timestamp at which the withdrawal was proven. - struct ProvenWithdrawal { - IDisputeGame disputeGameProxy; - uint64 timestamp; - } - - /// @notice The delay between when a withdrawal is proven and when it may be finalized. - uint256 internal immutable PROOF_MATURITY_DELAY_SECONDS; - - /// @notice Version of the deposit event. - uint256 internal constant DEPOSIT_VERSION = 0; - - /// @notice The L2 gas limit set when eth is deposited using the receive() function. - uint64 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000; - - /// @notice Address of the L2 account which initiated a withdrawal in this transaction. - /// If the value of this variable is the default L2 sender address, then we are NOT - /// inside of a call to finalizeWithdrawalTransaction. - address public l2Sender; - - /// @notice A list of withdrawal hashes which have been successfully finalized. - mapping(bytes32 => bool) public finalizedWithdrawals; - - /// @custom:legacy - /// @custom:spacer provenWithdrawals - /// @notice Spacer taking up the legacy `provenWithdrawals` mapping slot. - bytes32 private spacer_52_0_32; - - /// @custom:legacy - /// @custom:spacer paused - /// @notice Spacer for backwards compatibility. - bool private spacer_53_0_1; - - /// @custom:legacy - /// @custom:spacer superchainConfig - /// @notice Spacer for backwards compatibility. - address private spacer_53_1_20; - - /// @custom:legacy - /// @custom:spacer l2Oracle - /// @notice Spacer taking up the legacy `l2Oracle` address slot. - address private spacer_54_0_20; - - /// @notice Address of the SystemConfig contract. - /// @custom:network-specific - ISystemConfig public systemConfig; - - /// @custom:network-specific - /// @custom:legacy - /// @custom:spacer disputeGameFactory - /// @notice Spacer taking up the legacy `disputeGameFactory` address slot. - address private spacer_56_0_20; - - /// @notice A mapping of withdrawal hashes to proof submitters to ProvenWithdrawal data. - mapping(bytes32 => mapping(address => ProvenWithdrawal)) public provenWithdrawals; - - /// @custom:legacy - /// @custom:spacer disputeGameBlacklist - bytes32 private spacer_58_0_32; - - /// @custom:legacy - /// @custom:spacer respectedGameType - GameType private spacer_59_0_4; - - /// @custom:legacy - /// @custom:spacer respectedGameTypeUpdatedAt - uint64 private spacer_59_4_8; - - /// @notice Mapping of withdrawal hashes to addresses that have submitted a proof for the - /// withdrawal. Original OptimismPortal contract only allowed one proof to be submitted - /// for any given withdrawal hash. Fault Proofs version of this contract must allow - /// multiple proofs for the same withdrawal hash to prevent a malicious user from - /// blocking other withdrawals by proving them against invalid proposals. Submitters - /// are tracked in an array to simplify the off-chain process of determining which - /// proof submission should be used when finalizing a withdrawal. - mapping(bytes32 => address[]) public proofSubmitters; - - /// @custom:legacy - /// @custom:spacer _balance - uint256 private spacer_61_0_32; - - /// @notice Address of the AnchorStateRegistry contract. - IAnchorStateRegistry public anchorStateRegistry; - - /// @notice Address of the ETHLockbox contract. - IETHLockbox public ethLockbox; - - /// @notice Whether the OptimismPortal is using Super Roots or Output Roots. - bool public superRootsActive; - - /// @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event - /// are read by the rollup node and used to derive deposit transactions on L2. - /// @param from Address that triggered the deposit transaction. - /// @param to Address that the deposit transaction is directed to. - /// @param version Version of this deposit transaction event. - /// @param opaqueData ABI encoded deposit data to be parsed off-chain. - event TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData); - - /// @notice Emitted when a withdrawal transaction is proven. - /// @param withdrawalHash Hash of the withdrawal transaction. - /// @param from Address that triggered the withdrawal transaction. - /// @param to Address that the withdrawal transaction is directed to. - event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); - - /// @notice Emitted when a withdrawal transaction is proven. Exists as a separate event to - /// allow for backwards compatibility for tooling that observes the WithdrawalProven - /// event. - /// @param withdrawalHash Hash of the withdrawal transaction. - /// @param proofSubmitter Address of the proof submitter. - event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); - - /// @notice Emitted when a withdrawal transaction is finalized. - /// @param withdrawalHash Hash of the withdrawal transaction. - /// @param success Whether the withdrawal transaction was successful. - event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); - - /// @notice Emitted when the total ETH balance is migrated to the ETHLockbox. - /// @param lockbox The address of the ETHLockbox contract. - /// @param ethBalance Amount of ETH migrated. - event ETHMigrated(address indexed lockbox, uint256 ethBalance); - - /// @notice Emitted when the ETHLockbox contract is updated. - /// @param oldLockbox The address of the old ETHLockbox contract. - /// @param newLockbox The address of the new ETHLockbox contract. - /// @param oldAnchorStateRegistry The address of the old AnchorStateRegistry contract. - /// @param newAnchorStateRegistry The address of the new AnchorStateRegistry contract. - event PortalMigrated( - IETHLockbox oldLockbox, - IETHLockbox newLockbox, - IAnchorStateRegistry oldAnchorStateRegistry, - IAnchorStateRegistry newAnchorStateRegistry - ); - - /// @notice Thrown when a withdrawal has already been finalized. - error OptimismPortal_AlreadyFinalized(); - - /// @notice Thrown when the target of a withdrawal is unsafe. - error OptimismPortal_BadTarget(); - - /// @notice Thrown when the calldata for a deposit is too large. - error OptimismPortal_CalldataTooLarge(); - - /// @notice Thrown when the portal is paused. - error OptimismPortal_CallPaused(); - - /// @notice Thrown when a gas estimation transaction is being executed. - error OptimismPortal_GasEstimation(); - - /// @notice Thrown when the gas limit for a deposit is too low. - error OptimismPortal_GasLimitTooLow(); - - /// @notice Thrown when the target of a withdrawal is not a proper dispute game. - error OptimismPortal_ImproperDisputeGame(); - - /// @notice Thrown when a withdrawal has not been proven against a valid dispute game. - error OptimismPortal_InvalidDisputeGame(); - - /// @notice Thrown when a withdrawal has not been proven against a valid merkle proof. - error OptimismPortal_InvalidMerkleProof(); - - /// @notice Thrown when a withdrawal has not been proven against a valid output root proof. - error OptimismPortal_InvalidOutputRootProof(); - - /// @notice Thrown when a withdrawal's timestamp is not greater than the dispute game's creation timestamp. - error OptimismPortal_InvalidProofTimestamp(); - - /// @notice Thrown when the root claim of a dispute game is invalid. - error OptimismPortal_InvalidRootClaim(); - - /// @notice Thrown when a withdrawal is being finalized by a reentrant call. - error OptimismPortal_NoReentrancy(); - - /// @notice Thrown when a withdrawal has not been proven for long enough. - error OptimismPortal_ProofNotOldEnough(); - - /// @notice Thrown when a withdrawal has not been proven. - error OptimismPortal_Unproven(); - - /// @notice Thrown when trying to migrate to the same AnchorStateRegistry. - error OptimismPortal_MigratingToSameRegistry(); - - /// @notice Semantic version. - /// @custom:semver 5.3.1+interop - function version() public pure virtual returns (string memory) { - return "5.3.1+interop"; - } - - /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. - constructor(uint256 _proofMaturityDelaySeconds) ReinitializableBase(4) { - PROOF_MATURITY_DELAY_SECONDS = _proofMaturityDelaySeconds; - _disableInitializers(); - } - - /// @notice Initializer. - /// @param _systemConfig Address of the SystemConfig. - /// @param _anchorStateRegistry Address of the AnchorStateRegistry. - /// @param _ethLockbox Contract of the ETHLockbox. - function initialize( - ISystemConfig _systemConfig, - IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox - ) - external - reinitializer(initVersion()) - { - // Initialization transactions must come from the ProxyAdmin or its owner. - _assertOnlyProxyAdminOrProxyAdminOwner(); - - // Now perform initialization logic. - systemConfig = _systemConfig; - anchorStateRegistry = _anchorStateRegistry; - ethLockbox = _ethLockbox; - - // Set the l2Sender slot, only if it is currently empty. This signals the first - // initialization of the contract. - if (l2Sender == address(0)) { - l2Sender = Constants.DEFAULT_L2_SENDER; - } - - __ResourceMetering_init(); - } - - /// @notice Upgrades the OptimismPortal contract to have a reference to the AnchorStateRegistry and SystemConfig - /// @param _anchorStateRegistry AnchorStateRegistry contract. - /// @param _ethLockbox ETHLockbox contract. - function upgrade( - IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox - ) - external - reinitializer(initVersion()) - { - // Upgrade transactions must come from the ProxyAdmin or its owner. - _assertOnlyProxyAdminOrProxyAdminOwner(); - - // Now perform upgrade logic. - anchorStateRegistry = _anchorStateRegistry; - ethLockbox = _ethLockbox; - } - - /// @notice Getter for the current paused status. - function paused() public view returns (bool) { - return systemConfig.paused(); - } - - /// @notice Getter for the proof maturity delay. - function proofMaturityDelaySeconds() public view returns (uint256) { - return PROOF_MATURITY_DELAY_SECONDS; - } - - /// @notice Getter for the address of the DisputeGameFactory contract. - function disputeGameFactory() public view returns (IDisputeGameFactory) { - return anchorStateRegistry.disputeGameFactory(); - } - - /// @notice Returns the SuperchainConfig contract. - /// @return ISuperchainConfig The SuperchainConfig contract. - function superchainConfig() external view returns (ISuperchainConfig) { - return systemConfig.superchainConfig(); - } - - /// @custom:legacy - /// @notice Getter function for the address of the guardian. - function guardian() external view returns (address) { - return systemConfig.guardian(); - } - - /// @custom:legacy - /// @notice Getter for the dispute game finality delay. - function disputeGameFinalityDelaySeconds() external view returns (uint256) { - return anchorStateRegistry.disputeGameFinalityDelaySeconds(); - } - - /// @custom:legacy - /// @notice Getter for the respected game type. - function respectedGameType() external view returns (GameType) { - return anchorStateRegistry.respectedGameType(); - } - - /// @custom:legacy - /// @notice Getter for the retirement timestamp. Note that this value NO LONGER reflects the - /// timestamp at which the respected game type was updated. Game retirement and - /// respected game type value have been decoupled, this function now only returns the - /// retirement timestamp. - function respectedGameTypeUpdatedAt() external view returns (uint64) { - return anchorStateRegistry.retirementTimestamp(); - } - - /// @custom:legacy - /// @notice Getter for the dispute game blacklist. - /// @param _disputeGame The dispute game to check. - /// @return Whether the dispute game is blacklisted. - function disputeGameBlacklist(IDisputeGame _disputeGame) public view returns (bool) { - return anchorStateRegistry.disputeGameBlacklist(_disputeGame); - } - - /// @notice Computes the minimum gas limit for a deposit. - /// The minimum gas limit linearly increases based on the size of the calldata. - /// This is to prevent users from creating L2 resource usage without paying for it. - /// This function can be used when interacting with the portal to ensure forwards - /// compatibility. - /// @param _byteCount Number of bytes in the calldata. - /// @return The minimum gas limit for a deposit. - function minimumGasLimit(uint64 _byteCount) public pure returns (uint64) { - return _byteCount * 40 + 21000; - } - - /// @notice Accepts value so that users can send ETH directly to this contract and have the - /// funds be deposited to their address on L2. This is intended as a convenience - /// function for EOAs. Contracts should call the depositTransaction() function directly - /// otherwise any deposited funds will be lost due to address aliasing. - receive() external payable { - depositTransaction(msg.sender, msg.value, RECEIVE_DEFAULT_GAS_LIMIT, false, bytes("")); - } - - /// @notice Accepts ETH value without triggering a deposit to L2. - function donateETH() external payable { - // Intentionally empty. - } - - /// @notice Migrates the total ETH balance to the ETHLockbox. - function migrateLiquidity() public { - // Liquidity migration can only be triggered by the ProxyAdmin owner. - _assertOnlyProxyAdminOwner(); - - // Migrate the liquidity. - uint256 ethBalance = address(this).balance; - ethLockbox.lockETH{ value: ethBalance }(); - emit ETHMigrated(address(ethLockbox), ethBalance); - } - - /// @notice Allows the owner of the ProxyAdmin to migrate the OptimismPortal to use a new - /// lockbox, point at a new AnchorStateRegistry, and start to use the Super Roots proof - /// method. Primarily used for OptimismPortal instances to join the interop set, but - /// can also be used to swap the proof method from Output Roots to Super Roots if the - /// provided lockbox is the same as the current one. - /// @dev It is possible to change lockboxes without migrating liquidity. This can cause one - /// of the OptimismPortal instances connected to the new lockbox to not be able to - /// unlock sufficient ETH to finalize withdrawals which would trigger reverts. To avoid - /// this issue, guarantee that this function is called atomically alongside the - /// ETHLockbox.migrateLiquidity() function within the same transaction. - /// @param _newLockbox The address of the new ETHLockbox contract. - /// @param _newAnchorStateRegistry The address of the new AnchorStateRegistry contract. - function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external { - // Migration can only be triggered when the system is not paused because the migration can - // potentially unpause the system as a result of the modified ETHLockbox address. - _assertNotPaused(); - - // Migration can only be triggered by the ProxyAdmin owner. - _assertOnlyProxyAdminOwner(); - - // Chains can use this method to swap the proof method from Output Roots to Super Roots - // without joining the interop set. In this case, the old and new lockboxes will be the - // same. However, whether or not a chain is joining the interop set, all chains will need a - // new AnchorStateRegistry when migrating to Super Roots. We therefore check that the new - // AnchorStateRegistry is different than the old one to prevent this function from being - // accidentally misused. - if (anchorStateRegistry == _newAnchorStateRegistry) { - revert OptimismPortal_MigratingToSameRegistry(); - } - - // Update the ETHLockbox. - IETHLockbox oldLockbox = ethLockbox; - ethLockbox = _newLockbox; - - // Update the AnchorStateRegistry. - IAnchorStateRegistry oldAnchorStateRegistry = anchorStateRegistry; - anchorStateRegistry = _newAnchorStateRegistry; - - // Set the proof method to Super Roots. We expect that migration will happen more than once - // for some chains (switching to single-chain Super Roots and then later joining the - // interop set) so we don't need to check that this is false. - superRootsActive = true; - - // Emit a PortalMigrated event. - emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); - } - - /// @notice Proves a withdrawal transaction. When super roots are active, uses the game's - /// rootClaimByChainId to get the output root for this chain. When super roots are - /// inactive, validates directly against the game's root claim. - /// @param _tx Withdrawal transaction to finalize. - /// @param _disputeGameIndex Index of the dispute game to prove the withdrawal against. - /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser storage root. - /// @param _withdrawalProof Inclusion proof of the withdrawal within the L2ToL1MessagePasser. - function proveWithdrawalTransaction( - Types.WithdrawalTransaction memory _tx, - uint256 _disputeGameIndex, - Types.OutputRootProof calldata _outputRootProof, - bytes[] calldata _withdrawalProof - ) - external - { - _assertNotPaused(); - - // Fetch the dispute game proxy from the DisputeGameFactory contract. - (,, IDisputeGame disputeGameProxy) = disputeGameFactory().gameAtIndex(_disputeGameIndex); - - _proveWithdrawalTransaction(_tx, disputeGameProxy, _outputRootProof, _withdrawalProof); - } - - /// @notice Internal function for proving a withdrawal transaction. - /// @param _tx Withdrawal transaction to prove. - /// @param _disputeGameProxy Address of the dispute game to prove the withdrawal against. - /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser storage root. - /// @param _withdrawalProof Inclusion proof of the withdrawal within the L2ToL1MessagePasser. - function _proveWithdrawalTransaction( - Types.WithdrawalTransaction memory _tx, - IDisputeGame _disputeGameProxy, - Types.OutputRootProof memory _outputRootProof, - bytes[] memory _withdrawalProof - ) - internal - { - // Make sure that the target address is safe. - if (_isUnsafeTarget(_tx.target)) { - revert OptimismPortal_BadTarget(); - } - - // Game must be a Proper Game. - if (!anchorStateRegistry.isGameProper(_disputeGameProxy)) { - revert OptimismPortal_ImproperDisputeGame(); - } - - // Game must have been respected game type when created. - if (!anchorStateRegistry.isGameRespected(_disputeGameProxy)) { - revert OptimismPortal_InvalidDisputeGame(); - } - - // Game must not have resolved in favor of the Challenger (invalid root claim). - if (_disputeGameProxy.status() == GameStatus.CHALLENGER_WINS) { - revert OptimismPortal_InvalidDisputeGame(); - } - - // As a sanity check, we make sure that the current timestamp is not less than or equal to - // the dispute game's creation timestamp. Not strictly necessary but extra layer of - // safety against weird bugs. Note that this blocks withdrawals from being proven in the - // same block that a dispute game is created. - if (block.timestamp <= _disputeGameProxy.createdAt().raw()) { - revert OptimismPortal_InvalidProofTimestamp(); - } - - // Validate the output root proof depending on proof method. - if (superRootsActive) { - // Get output root for this chain from the game's extraData (super root proof). - // Reverts with UnknownChainId if chainId not found in the super root. - uint256 chainId = systemConfig.l2ChainId(); - Claim outputRootClaim = _disputeGameProxy.rootClaimByChainId(chainId); - - // Verify that the output root can be generated with the elements in the proof. - if (outputRootClaim.raw() != Hashing.hashOutputRootProof(_outputRootProof)) { - revert OptimismPortal_InvalidOutputRootProof(); - } - } else { - // Verify that the output root can be generated with the elements in the proof. - if (_disputeGameProxy.rootClaim().raw() != Hashing.hashOutputRootProof(_outputRootProof)) { - revert OptimismPortal_InvalidOutputRootProof(); - } - } - - // Load the ProvenWithdrawal into memory, using the withdrawal hash as a unique identifier. - bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx); - - // Compute the storage slot of the withdrawal hash in the L2ToL1MessagePasser contract. - // Refer to the Solidity documentation for more information on how storage layouts are - // computed for mappings. - bytes32 storageKey = keccak256( - abi.encode( - withdrawalHash, - uint256(0) // The withdrawals mapping is at the first slot in the layout. - ) - ); - - // Verify that the hash of this withdrawal was stored in the L2toL1MessagePasser contract - // on L2. If this is true, under the assumption that the SecureMerkleTrie does not have - // bugs, then we know that this withdrawal was actually triggered on L2 and can therefore - // be relayed on L1. - if ( - SecureMerkleTrie.verifyInclusionProof({ - _key: abi.encode(storageKey), - _value: hex"01", - _proof: _withdrawalProof, - _root: _outputRootProof.messagePasserStorageRoot - }) == false - ) { - revert OptimismPortal_InvalidMerkleProof(); - } - - // Designate the withdrawalHash as proven by storing the disputeGameProxy and timestamp in - // the provenWithdrawals mapping. A given user may re-prove a withdrawalHash multiple - // times, but each proof will reset the proof timer. - provenWithdrawals[withdrawalHash][msg.sender] = - ProvenWithdrawal({ disputeGameProxy: _disputeGameProxy, timestamp: uint64(block.timestamp) }); - - // Add the proof submitter to the list of proof submitters for this withdrawal hash. - proofSubmitters[withdrawalHash].push(msg.sender); - - // Emit a WithdrawalProven events. - emit WithdrawalProven(withdrawalHash, _tx.sender, _tx.target); - emit WithdrawalProvenExtension1(withdrawalHash, msg.sender); - } - - /// @notice Finalizes a withdrawal transaction. - /// @param _tx Withdrawal transaction to finalize. - function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external { - finalizeWithdrawalTransactionExternalProof(_tx, msg.sender); - } - - /// @notice Finalizes a withdrawal transaction, using an external proof submitter. - /// @param _tx Withdrawal transaction to finalize. - /// @param _proofSubmitter Address of the proof submitter. - function finalizeWithdrawalTransactionExternalProof( - Types.WithdrawalTransaction memory _tx, - address _proofSubmitter - ) - public - { - // Cannot finalize withdrawal transactions while the system is paused. - _assertNotPaused(); - - // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other - // than the default value when a withdrawal transaction is being finalized. This check is - // a defacto reentrancy guard. - if (l2Sender != Constants.DEFAULT_L2_SENDER) { - revert OptimismPortal_NoReentrancy(); - } - - // Make sure that the target address is safe. - if (_isUnsafeTarget(_tx.target)) { - revert OptimismPortal_BadTarget(); - } - - // Grab the withdrawal. - bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx); - - // Check that the withdrawal can be finalized. - checkWithdrawal(withdrawalHash, _proofSubmitter); - - // Mark the withdrawal as finalized so it can't be replayed. - finalizedWithdrawals[withdrawalHash] = true; - - // Unlock the ETH from the ETHLockbox. - if (_tx.value > 0) ethLockbox.unlockETH(_tx.value); - - // Set the l2Sender so contracts know who triggered this withdrawal on L2. - l2Sender = _tx.sender; - - // Trigger the call to the target contract. We use a custom low level method - // SafeCall.callWithMinGas to ensure two key properties - // 1. Target contracts cannot force this call to run out of gas by returning a very large - // amount of data (and this is OK because we don't care about the returndata here). - // 2. The amount of gas provided to the execution context of the target is at least the - // gas limit specified by the user. If there is not enough gas in the current context - // to accomplish this, `callWithMinGas` will revert. - bool success = SafeCall.callWithMinGas(_tx.target, _tx.gasLimit, _tx.value, _tx.data); - - // Reset the l2Sender back to the default value. - l2Sender = Constants.DEFAULT_L2_SENDER; - - // All withdrawals are immediately finalized. Replayability can - // be achieved through contracts built on top of this contract - emit WithdrawalFinalized(withdrawalHash, success); - - // Send ETH back to the Lockbox in the case of a failed transaction or it'll get stuck here - // and would need to be moved back via the migrateLiquidity function. - if (!success && _tx.value > 0) { - ethLockbox.lockETH{ value: _tx.value }(); - } - - // Reverting here is useful for determining the exact gas cost to successfully execute the - // sub call to the target contract if the minimum gas limit specified by the user would not - // be sufficient to execute the sub call. - if (!success && tx.origin == Constants.ESTIMATION_ADDRESS) { - revert OptimismPortal_GasEstimation(); - } - } - - /// @notice Checks that a withdrawal has been proven and is ready to be finalized. - /// @param _withdrawalHash Hash of the withdrawal. - /// @param _proofSubmitter Address of the proof submitter. - function checkWithdrawal(bytes32 _withdrawalHash, address _proofSubmitter) public view { - // Grab the withdrawal and dispute game proxy. - ProvenWithdrawal memory provenWithdrawal = provenWithdrawals[_withdrawalHash][_proofSubmitter]; - IDisputeGame disputeGameProxy = provenWithdrawal.disputeGameProxy; - - // Check that this withdrawal has not already been finalized, this is replay protection. - if (finalizedWithdrawals[_withdrawalHash]) { - revert OptimismPortal_AlreadyFinalized(); - } - - // A withdrawal can only be finalized if it has been proven. We know that a withdrawal has - // been proven at least once when its timestamp is non-zero. Unproven withdrawals will have - // a timestamp of zero. - if (provenWithdrawal.timestamp == 0) { - revert OptimismPortal_Unproven(); - } - - // As a sanity check, we make sure that the proven withdrawal's timestamp is greater than - // starting timestamp inside the Dispute Game. Not strictly necessary but extra layer of - // safety against weird bugs in the proving step. Note that this blocks withdrawals that - // are proven in the same block that a dispute game is created. - if (provenWithdrawal.timestamp <= disputeGameProxy.createdAt().raw()) { - revert OptimismPortal_InvalidProofTimestamp(); - } - - // A proven withdrawal must wait at least `PROOF_MATURITY_DELAY_SECONDS` before finalizing. - if (block.timestamp - provenWithdrawal.timestamp <= PROOF_MATURITY_DELAY_SECONDS) { - revert OptimismPortal_ProofNotOldEnough(); - } - - // Check that the root claim is valid. - if (!anchorStateRegistry.isGameClaimValid(disputeGameProxy)) { - revert OptimismPortal_InvalidRootClaim(); - } - } - - /// @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in - /// deriving deposit transactions. Note that if a deposit is made by a contract, its - /// address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider - /// using the CrossDomainMessenger contracts for a simpler developer experience. - /// @dev The `msg.value` is locked on the ETHLockbox and minted as ETH when the deposit - /// arrives on L2, while `_value` specifies how much ETH to send to the target. - /// @param _to Target address on L2. - /// @param _value ETH value to send to the recipient. - /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. - /// @param _isCreation Whether or not the transaction is a contract creation. - /// @param _data Data to trigger the recipient with. - function depositTransaction( - address _to, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) - public - payable - metered(_gasLimit) - { - // Lock the ETH in the ETHLockbox. - if (msg.value > 0) ethLockbox.lockETH{ value: msg.value }(); - - // Just to be safe, make sure that people specify address(0) as the target when doing - // contract creations. - if (_isCreation && _to != address(0)) { - revert OptimismPortal_BadTarget(); - } - - // Prevent depositing transactions that have too small of a gas limit. Users should pay - // more for more resource usage. - if (_gasLimit < minimumGasLimit(uint64(_data.length))) { - revert OptimismPortal_GasLimitTooLow(); - } - - // Prevent the creation of deposit transactions that have too much calldata. This gives an - // upper limit on the size of unsafe blocks over the p2p network. 120kb is chosen to ensure - // that the transaction can fit into the p2p network policy of 128kb even though deposit - // transactions are not gossipped over the p2p network. - if (_data.length > 120_000) { - revert OptimismPortal_CalldataTooLarge(); - } - - // Transform the from-address to its alias if the caller is a contract. - address from = msg.sender; - if (!EOA.isSenderEOA()) { - from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); - } - - // Compute the opaque data that will be emitted as part of the TransactionDeposited event. - // We use opaque data so that we can update the TransactionDeposited event in the future - // without breaking the current interface. - bytes memory opaqueData = abi.encodePacked(msg.value, _value, _gasLimit, _isCreation, _data); - - // Emit a TransactionDeposited event so that the rollup node can derive a deposit - // transaction for this deposit. - emit TransactionDeposited(from, _to, DEPOSIT_VERSION, opaqueData); - } - - /// @notice External getter for the number of proof submitters for a withdrawal hash. - /// @param _withdrawalHash Hash of the withdrawal. - /// @return The number of proof submitters for the withdrawal hash. - function numProofSubmitters(bytes32 _withdrawalHash) external view returns (uint256) { - return proofSubmitters[_withdrawalHash].length; - } - - /// @notice Asserts that the contract is not paused. - function _assertNotPaused() internal view { - if (paused()) { - revert OptimismPortal_CallPaused(); - } - } - - /// @notice Checks if a target address is unsafe. - function _isUnsafeTarget(address _target) internal view virtual returns (bool) { - // Prevent users from targeting an unsafe target address on a withdrawal transaction. - return _target == address(this) || _target == address(ethLockbox); - } - - /// @notice Getter for the resource config. Used internally by the ResourceMetering contract. - /// The SystemConfig is the source of truth for the resource config. - /// @return config_ ResourceMetering ResourceConfig - function _resourceConfig() internal view override returns (ResourceMetering.ResourceConfig memory config_) { - IResourceMetering.ResourceConfig memory config = systemConfig.resourceConfig(); - assembly ("memory-safe") { - config_ := config - } - } -} diff --git a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerContainer.sol b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerContainer.sol index 5315096a0ee..6c127ec8de6 100644 --- a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerContainer.sol +++ b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerContainer.sol @@ -26,7 +26,6 @@ contract OPContractsManagerContainer { address protocolVersionsImpl; address l1ERC721BridgeImpl; address optimismPortalImpl; - address optimismPortalInteropImpl; address ethLockboxImpl; address systemConfigImpl; address optimismMintableERC20FactoryImpl; diff --git a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerMigrator.sol b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerMigrator.sol index d0e9e70312a..0f0ef8e3c79 100644 --- a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerMigrator.sol +++ b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerMigrator.sol @@ -17,7 +17,6 @@ import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IOPContractsManagerContainer } from "interfaces/L1/opcm/IOPContractsManagerContainer.sol"; import { IOPContractsManagerUtils } from "interfaces/L1/opcm/IOPContractsManagerUtils.sol"; @@ -240,7 +239,7 @@ contract OPContractsManagerMigrator is OPContractsManagerUtilsCaller { internal { // Convert portal to interop portal interface, and grab existing ETHLockbox and DGF. - IOptimismPortalInterop portal = IOptimismPortalInterop(payable(_systemConfig.optimismPortal())); + IOptimismPortal portal = IOptimismPortal(payable(_systemConfig.optimismPortal())); IETHLockbox existingLockbox = IETHLockbox(payable(address(portal.ethLockbox()))); IDisputeGameFactory existingDGF = IDisputeGameFactory(payable(address(portal.disputeGameFactory()))); @@ -272,11 +271,11 @@ contract OPContractsManagerMigrator is OPContractsManagerUtilsCaller { } // Migrate the portal to the new ETHLockbox and AnchorStateRegistry. - // This also sets superRootsActive = true. // NOTE: This requires the portal to already be upgraded to the interop version - // (OptimismPortalInterop). If the portal is not on the interop version, this call will + // (OptimismPortal2). And it requires the feature flag for INTEROP to be enabled + // If the portal is not on the interop version, this call will // fail. - portal.migrateToSuperRoots(_newLockbox, _newASR); + portal.migrateToSharedDisputeGame(_newLockbox, _newASR); } /// @notice Returns the contracts container. diff --git a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol index 440f104211e..79aff092eb1 100644 --- a/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol +++ b/packages/contracts-bedrock/src/L1/opcm/OPContractsManagerV2.sol @@ -24,7 +24,6 @@ import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol"; @@ -153,9 +152,9 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller { /// - Major bump: New required sequential upgrade /// - Minor bump: Replacement OPCM for same upgrade /// - Patch bump: Development changes (expected for normal dev work) - /// @custom:semver 7.0.14 + /// @custom:semver 7.1.15 function version() public pure returns (string memory) { - return "7.0.14"; + return "7.1.15"; } /// @param _standardValidator The standard validator for this OPCM release. @@ -783,13 +782,20 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller { ); // Update the OptimismPortal. + // When interop is enabled, the ETH_LOCKBOX feature must be set on SystemConfig before + // upgrading the portal. OptimismPortal2.initialize() calls _assertValidLockboxState() + // which requires the ETH_LOCKBOX feature flag and ethLockbox address to be consistent. + // Otherwise we end up in a state where we have a lockbox and the feature flag is off. if (isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + if (!_cts.systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { + _cts.systemConfig.setFeature(Features.ETH_LOCKBOX, true); + } _upgrade( _cts.proxyAdmin, address(_cts.optimismPortal), - impls.optimismPortalInteropImpl, + impls.optimismPortalImpl, abi.encodeCall( - IOptimismPortalInterop.initialize, (_cts.systemConfig, _cts.anchorStateRegistry, _cts.ethLockbox) + IOptimismPortal.initialize, (_cts.systemConfig, _cts.anchorStateRegistry, _cts.ethLockbox) ) ); } else { @@ -797,7 +803,9 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller { _cts.proxyAdmin, address(_cts.optimismPortal), impls.optimismPortalImpl, - abi.encodeCall(IOptimismPortal.initialize, (_cts.systemConfig, _cts.anchorStateRegistry)) + abi.encodeCall( + IOptimismPortal.initialize, (_cts.systemConfig, _cts.anchorStateRegistry, IETHLockbox(address(0))) + ) ); } @@ -828,9 +836,12 @@ contract OPContractsManagerV2 is ISemver, OPContractsManagerUtilsCaller { if (!_cts.systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { _cts.systemConfig.setFeature(Features.ETH_LOCKBOX, true); } + if (!_cts.systemConfig.isFeatureEnabled(Features.INTEROP)) { + _cts.systemConfig.setFeature(Features.INTEROP, true); + } // Migrate any ETH into the ETHLockbox. - IOptimismPortalInterop(payable(_cts.optimismPortal)).migrateLiquidity(); + IOptimismPortal(payable(_cts.optimismPortal)).migrateLiquidity(); } // Update the L1CrossDomainMessenger. diff --git a/packages/contracts-bedrock/src/L2/L2ContractsManager.sol b/packages/contracts-bedrock/src/L2/L2ContractsManager.sol index d0699ab861d..8298353494f 100644 --- a/packages/contracts-bedrock/src/L2/L2ContractsManager.sol +++ b/packages/contracts-bedrock/src/L2/L2ContractsManager.sol @@ -35,8 +35,8 @@ contract L2ContractsManager is ISemver { error L2ContractsManager_FeatureFlagMismatch(); /// @notice The semantic version of the L2ContractsManager contract. - /// @custom:semver 1.5.0 - string public constant version = "1.5.0"; + /// @custom:semver 1.6.0 + string public constant version = "1.6.0"; /// @notice The address of this contract. Used to enforce that the upgrade function is only /// called via DELEGATECALL. @@ -333,9 +333,6 @@ contract L2ContractsManager is ISemver { INITIALIZABLE_SLOT_OZ_V4, 0 ); - - // NativeAssetLiquidity - L2ContractsManagerUtils.upgradeTo(Predeploys.NATIVE_ASSET_LIQUIDITY, NATIVE_ASSET_LIQUIDITY_IMPL); } // TODO(#19600): Remove FeeSplitter upgrade as part of revenue sharing deprecation. @@ -439,6 +436,9 @@ contract L2ContractsManager is ISemver { ); L2ContractsManagerUtils.upgradeTo(Predeploys.PROXY_ADMIN, PROXY_ADMIN_IMPL); L2ContractsManagerUtils.upgradeTo(Predeploys.L2_DEV_FEATURE_FLAGS, L2_DEV_FEATURE_FLAGS_IMPL); + if (_config.isCustomGasToken) { + L2ContractsManagerUtils.upgradeTo(Predeploys.NATIVE_ASSET_LIQUIDITY, NATIVE_ASSET_LIQUIDITY_IMPL); + } // Interop predeploys are gated behind the OPTIMISM_PORTAL_INTEROP dev feature flag. if (_config.isInterop) { diff --git a/packages/contracts-bedrock/src/libraries/DevFeatures.sol b/packages/contracts-bedrock/src/libraries/DevFeatures.sol index b91b29f7bda..d677dbdac73 100644 --- a/packages/contracts-bedrock/src/libraries/DevFeatures.sol +++ b/packages/contracts-bedrock/src/libraries/DevFeatures.sol @@ -10,7 +10,7 @@ pragma solidity ^0.8.0; /// etc. /// We'll expand to using all available bits if we need more than 64 concurrent features. library DevFeatures { - /// @notice The feature that enables the OptimismPortalInterop contract. + /// @notice The feature that enables the Interop migration functions on the OptimismPortal2 contract. bytes32 public constant OPTIMISM_PORTAL_INTEROP = bytes32(0x0000000000000000000000000000000000000000000000000000000000000001); diff --git a/packages/contracts-bedrock/src/libraries/L2ContractsManagerUtils.sol b/packages/contracts-bedrock/src/libraries/L2ContractsManagerUtils.sol index 6d49c785186..29672482a4e 100644 --- a/packages/contracts-bedrock/src/libraries/L2ContractsManagerUtils.sol +++ b/packages/contracts-bedrock/src/libraries/L2ContractsManagerUtils.sol @@ -35,11 +35,16 @@ library L2ContractsManagerUtils { /// @notice Thrown when a v5 slot is passed with a non-zero offset. error L2ContractsManager_InvalidV5Offset(); + /// @notice Thrown when an address has no runtime code. + /// @param _target The address that has no code. + error L2ContractsManager_EmptyImplementation(address _target); + /// @notice Upgrades a predeploy to a new implementation without calling an initializer. /// Reverts if the predeploy is not upgradeable. /// @param _proxy The proxy address of the predeploy. /// @param _implementation The new implementation address. function upgradeTo(address _proxy, address _implementation) internal { + if (_implementation.code.length == 0) revert L2ContractsManager_EmptyImplementation(_implementation); if (!Predeploys.isUpgradeable(_proxy)) revert L2ContractsManager_NotUpgradeable(_proxy); // We skip checking the version for those predeploys that have no code. This would be the case for newly added @@ -111,6 +116,9 @@ library L2ContractsManagerUtils { { if (!Predeploys.isUpgradeable(_proxy)) revert L2ContractsManager_NotUpgradeable(_proxy); + if (_storageSetterImpl.code.length == 0) revert L2ContractsManager_EmptyImplementation(_storageSetterImpl); + if (_implementation.code.length == 0) revert L2ContractsManager_EmptyImplementation(_implementation); + // We skip checking the version for those predeploys that have no code. This would be the case for newly added // predeploys that are being introduced on this particular upgrade. address implementation = L2ProxyAdmin(Predeploys.PROXY_ADMIN).getProxyImplementation(_proxy); diff --git a/packages/contracts-bedrock/src/libraries/NetworkUpgradeTxns.sol b/packages/contracts-bedrock/src/libraries/NetworkUpgradeTxns.sol index e78700c666e..cf115aad659 100644 --- a/packages/contracts-bedrock/src/libraries/NetworkUpgradeTxns.sol +++ b/packages/contracts-bedrock/src/libraries/NetworkUpgradeTxns.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.15; // Utilities import { Vm } from "forge-std/Vm.sol"; import { stdJson } from "forge-std/StdJson.sol"; +import { LibString } from "@solady/utils/LibString.sol"; /// @title NetworkUpgradeTxns /// @notice Standard library for generating Network Upgrade Transaction (NUT) artifacts. @@ -13,6 +14,9 @@ library NetworkUpgradeTxns { Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + /// @notice The only bundle schema version this library accepts when reading artifacts. + string internal constant BUNDLE_VERSION = "1.0.0"; + /// @notice Metadata for the Network Upgrade Transaction bundle. /// @param version Bundle format version for compatibility tracking. struct BundleMetadata { @@ -98,6 +102,13 @@ library NetworkUpgradeTxns { /// @return txns_ The array of upgrade transactions. function readArtifact(string memory _inputPath) internal view returns (NetworkUpgradeTxn[] memory txns_) { string memory json = vm.readFile(_inputPath); + // Verify the bundle version before decoding to catch schema mismatches early. + bytes memory versionBytes = vm.parseJson(json, ".metadata.version"); + string memory version = abi.decode(versionBytes, (string)); + require( + LibString.eq(version, BUNDLE_VERSION), + string.concat("NetworkUpgradeTxns: unsupported bundle version: ", version) + ); // Parse the transactions array from the bundle structure bytes memory parsedData = vm.parseJson(json, ".transactions"); txns_ = abi.decode(parsedData, (NetworkUpgradeTxns.NetworkUpgradeTxn[])); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index 2d30b345920..f30f5bd0563 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -28,7 +28,6 @@ import { UnknownChainId } from "src/dispute/lib/Errors.sol"; // Interfaces import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IProxy } from "interfaces/universal/IProxy.sol"; @@ -162,24 +161,6 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { assertFalse(optimismPortal2.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx))); } - /// @notice Sets the supeRootsActive variable to the provided value. - /// @param _superRootsActive The value to set the superRootsActive variable to. - function setSuperRootsActive(bool _superRootsActive) public { - // Get the slot for superRootsActive. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortalInterop", "superRootsActive"); - - // Load the existing storage slot value. - bytes32 existingValue = vm.load(address(optimismPortal2), bytes32(slot.slot)); - - // Inject the bool into the existing storage slot value with a bitwise OR. - // Shift the bool left by the offset of the storage slot and OR with existing value. - bytes32 newValue = - bytes32(uint256(uint8(_superRootsActive ? 1 : 0)) << slot.offset * 8 | uint256(existingValue)); - - // Store the new value at the correct slot/offset. - vm.store(address(optimismPortal2), bytes32(slot.slot), newValue); - } - /// @notice Checks if the ETHLockbox feature is enabled. /// @return bool True if the ETHLockbox feature is enabled. function isUsingLockbox() public view returns (bool) { @@ -316,7 +297,7 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { // Call the `initialize` function with the sender vm.prank(_sender); - IOptimismPortalInterop(payable(optimismPortal2)).initialize(systemConfig, anchorStateRegistry, ethLockbox); + optimismPortal2.initialize(systemConfig, anchorStateRegistry, ethLockbox); } /// @notice Tests that the initialize function reverts when lockbox state is invalid. @@ -344,7 +325,7 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { // Call the `initialize` function vm.prank(address(proxyAdmin)); - optimismPortal2.initialize(systemConfig, anchorStateRegistry); + optimismPortal2.initialize(systemConfig, anchorStateRegistry, IETHLockbox(address(0))); } /// @notice Tests that the initialize function reverts if called by a non-proxy admin or owner. @@ -366,131 +347,7 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { // Call the `initialize` function with the sender vm.prank(_sender); - optimismPortal2.initialize(systemConfig, anchorStateRegistry); - } -} - -/// @title OptimismPortal2_UpgradeInterop_Test -/// @notice Reusable test for the current upgrade() function in the OptimismPortal2 contract. If -/// the upgrade() function is changed, tests inside of this contract should be updated to -/// reflect the new function. If the upgrade() function is removed, remove the -/// corresponding tests but leave this contract in place so it's easy to add tests back -/// in the future. -contract OptimismPortal2_UpgradeInterop_Test is CommonTest { - function setUp() public virtual override { - super.setUp(); - skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); - } - - /// @notice Tests that the upgrade() function succeeds. - function testFuzz_upgrade_interop_succeeds(address _newAnchorStateRegistry, uint256 _balance) external { - // Prevent overflow on an upgrade context - _balance = bound(_balance, 0, type(uint256).max - address(ethLockbox).balance); - - // Get the slot for _initialized. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); - - // Set the initialized slot to 0. - vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); - - // Set the balance of the portal and get the lockbox balance before the upgrade. - deal(address(optimismPortal2), _balance); - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - - // Expect the ETH to be migrated to the lockbox. - vm.expectCall(address(ethLockbox), _balance, abi.encodeCall(ethLockbox.lockETH, ())); - - // Call the upgrade function. - vm.prank(address(optimismPortal2.proxyAdmin())); - IOptimismPortalInterop(payable(optimismPortal2)).upgrade( - IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox) - ); - - // Verify that the initialized slot was updated. - bytes32 initializedSlotAfter = vm.load(address(optimismPortal2), bytes32(slot.slot)); - assertEq(initializedSlotAfter, bytes32(uint256(optimismPortal2.initVersion()))); - - // Assert the portal is properly upgraded. - assertEq(address(optimismPortal2.ethLockbox()), address(ethLockbox)); - assertEq(address(optimismPortal2.anchorStateRegistry()), _newAnchorStateRegistry); - - // Balance has not updated. - assertEq(address(optimismPortal2).balance, _balance); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore); - - // Now we migrate liquidity. - vm.prank(proxyAdminOwner); - IOptimismPortalInterop(payable(optimismPortal2)).migrateLiquidity(); - - // Balance has been updated. - assertEq(address(optimismPortal2).balance, 0); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _balance); - } - - /// @notice Tests that the upgrade() function reverts if called a second time. - function test_upgrade_upgradeTwice_reverts() external { - // Get the slot for _initialized. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); - - // Set the initialized slot to 0. - vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); - - // Trigger first upgrade. - vm.prank(address(optimismPortal2.proxyAdmin())); - IOptimismPortalInterop(payable(optimismPortal2)).upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox) - ); - - // Try to trigger second upgrade. - vm.prank(address(optimismPortal2.proxyAdmin())); - vm.expectRevert("Initializable: contract is already initialized"); - IOptimismPortalInterop(payable(optimismPortal2)).upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox) - ); - } - - /// @notice Tests that the upgrade() function reverts if called after initialization. - function test_upgrade_afterInitialization_reverts() external { - // Get the slot for _initialized. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); - - // Slot value should be set to already initialized. - bytes32 initializedSlotBefore = vm.load(address(optimismPortal2), bytes32(slot.slot)); - assertEq(initializedSlotBefore, bytes32(uint256(optimismPortal2.initVersion()))); - - // AnchorStateRegistry address should be non-zero. - assertNotEq(address(optimismPortal2.anchorStateRegistry()), address(0)); - - // SystemConfig address should be non-zero. - assertNotEq(address(optimismPortal2.systemConfig()), address(0)); - - // Try to trigger upgrade(). - vm.expectRevert("Initializable: contract is already initialized"); - IOptimismPortalInterop(payable(optimismPortal2)).upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox) - ); - } - - /// @notice Tests that the upgrade() function reverts if called by a non-proxy admin or owner. - /// @param _sender The address of the sender to test. - function testFuzz_upgrade_notProxyAdminOrProxyAdminOwner_reverts(address _sender) public { - // Prank as the not ProxyAdmin or ProxyAdmin owner. - vm.assume(_sender != address(proxyAdmin) && _sender != proxyAdminOwner); - - // Get the slot for _initialized. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); - - // Set the initialized slot to 0. - vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); - - // Expect the revert with `ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner` selector. - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner.selector); - - // Call the `upgrade` function with the sender - vm.prank(_sender); - IOptimismPortalInterop(payable(optimismPortal2)).upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox) - ); + optimismPortal2.initialize(systemConfig, anchorStateRegistry, IETHLockbox(address(0))); } } @@ -827,7 +684,7 @@ contract OptimismPortal2_MigrateLiquidity_Test is CommonTest { vm.assume(_caller != optimismPortal2.proxyAdminOwner()); vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOwner.selector); vm.prank(_caller); - IOptimismPortalInterop(payable(optimismPortal2)).migrateLiquidity(); + optimismPortal2.migrateLiquidity(); } /// @notice Tests that the liquidity migration from the portal to the lockbox succeeds. @@ -844,37 +701,35 @@ contract OptimismPortal2_MigrateLiquidity_Test is CommonTest { emit ETHMigrated(address(ethLockbox), _portalBalance); vm.prank(proxyAdminOwner); - IOptimismPortalInterop(payable(optimismPortal2)).migrateLiquidity(); + optimismPortal2.migrateLiquidity(); assertEq(address(optimismPortal2).balance, 0); assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _portalBalance); } } -/// @title OptimismPortal2_MigrateToSuperRoots_Test -/// @notice Test contract for OptimismPortal2 `migrateToSuperRoots` function. -contract OptimismPortal2_MigrateToSuperRoots_Test is OptimismPortal2_TestInit { +/// @title OptimismPortal2_migrateToSharedDisputeGame_Test +/// @notice Test contract for OptimismPortal2 `migrateToSharedDisputeGame` function. +contract OptimismPortal2_migrateToSharedDisputeGame_Test is OptimismPortal2_TestInit { function setUp() public override { super.setUp(); skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); } - /// @notice Tests that `migrateToSuperRoots` reverts if the caller is not the proxy admin + /// @notice Tests that `migrateToSharedDisputeGame` reverts if the caller is not the proxy admin /// owner. - function testFuzz_migrateToSuperRoots_notProxyAdminOwner_reverts(address _caller) external { + function testFuzz_migrateToSharedDisputeGame_notProxyAdminOwner_reverts(address _caller) external { vm.assume(_caller != optimismPortal2.proxyAdminOwner()); vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOwner.selector); vm.prank(_caller); - IOptimismPortalInterop(payable(optimismPortal2)).migrateToSuperRoots( - IETHLockbox(address(1)), IAnchorStateRegistry(address(1)) - ); + optimismPortal2.migrateToSharedDisputeGame(IETHLockbox(address(1)), IAnchorStateRegistry(address(1))); } - /// @notice Tests that `migrateToSuperRoots` reverts if the new registry is the same as the + /// @notice Tests that `migrateToSharedDisputeGame` reverts if the new registry is the same as the /// current one. /// @param _newLockbox The new ETHLockbox to migrate to. - function testFuzz_migrateToSuperRoots_usingSameRegistry_reverts(address _newLockbox) external { + function testFuzz_migrateToSharedDisputeGame_usingSameRegistry_reverts(address _newLockbox) external { vm.assume(_newLockbox != address(optimismPortal2.ethLockbox())); // Use the same registry as the current one. @@ -884,18 +739,21 @@ contract OptimismPortal2_MigrateToSuperRoots_Test is OptimismPortal2_TestInit { address caller = optimismPortal2.proxyAdminOwner(); // Expect the migration to revert. - vm.expectRevert(IOptimismPortalInterop.OptimismPortal_MigratingToSameRegistry.selector); + vm.expectRevert(IOptimismPortal.OptimismPortal_MigratingToSameRegistry.selector); vm.prank(caller); - IOptimismPortalInterop(payable(optimismPortal2)).migrateToSuperRoots( - IETHLockbox(_newLockbox), newAnchorStateRegistry - ); + optimismPortal2.migrateToSharedDisputeGame(IETHLockbox(_newLockbox), newAnchorStateRegistry); } - /// @notice Tests that `migrateToSuperRoots` updates the ETHLockbox contract, updates the + /// @notice Tests that `migrateToSharedDisputeGame` updates the ETHLockbox contract, updates the /// AnchorStateRegistry, and sets the superRootsActive flag to true. /// @param _newLockbox The new ETHLockbox to migrate to. /// @param _newAnchorStateRegistry The new AnchorStateRegistry to migrate to. - function testFuzz_migrateToSuperRoots_succeeds(address _newLockbox, address _newAnchorStateRegistry) external { + function testFuzz_migrateToSharedDisputeGame_succeeds( + address _newLockbox, + address _newAnchorStateRegistry + ) + external + { address oldLockbox = address(optimismPortal2.ethLockbox()); address oldAnchorStateRegistry = address(optimismPortal2.anchorStateRegistry()); vm.assume(_newLockbox != oldLockbox); @@ -905,17 +763,17 @@ contract OptimismPortal2_MigrateToSuperRoots_Test is OptimismPortal2_TestInit { emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); vm.prank(optimismPortal2.proxyAdminOwner()); - IOptimismPortalInterop(payable(optimismPortal2)).migrateToSuperRoots( + optimismPortal2.migrateToSharedDisputeGame( IETHLockbox(_newLockbox), IAnchorStateRegistry(_newAnchorStateRegistry) ); assertEq(address(optimismPortal2.ethLockbox()), _newLockbox); assertEq(address(optimismPortal2.anchorStateRegistry()), _newAnchorStateRegistry); - assertTrue(IOptimismPortalInterop(payable(optimismPortal2)).superRootsActive()); + assertTrue(systemConfig.isFeatureEnabled(Features.INTEROP)); } - /// @notice Tests that `migrateToSuperRoots` reverts when the system is paused. - function test_migrateToSuperRoots_paused_reverts() external { + /// @notice Tests that `migrateToSharedDisputeGame` reverts when the system is paused. + function test_migrateToSharedDisputeGame_paused_reverts() external { vm.startPrank(optimismPortal2.guardian()); systemConfig.superchainConfig().pause(address(0)); vm.stopPrank(); @@ -923,15 +781,19 @@ contract OptimismPortal2_MigrateToSuperRoots_Test is OptimismPortal2_TestInit { address caller = optimismPortal2.proxyAdminOwner(); vm.expectRevert(IOptimismPortal.OptimismPortal_CallPaused.selector); vm.prank(caller); - IOptimismPortalInterop(payable(optimismPortal2)).migrateToSuperRoots( - IETHLockbox(address(1)), IAnchorStateRegistry(address(1)) - ); + optimismPortal2.migrateToSharedDisputeGame(IETHLockbox(address(1)), IAnchorStateRegistry(address(1))); } } /// @title OptimismPortal2_ProveWithdrawalTransaction_Test /// @notice Test contract for OptimismPortal2 `proveWithdrawalTransaction` function. contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_TestInit { + /// @notice Enables super root behavior on the portal for testing. + function _enableSuperRoots() internal { + // OptimismPortal2 uses stateless GameTypes.isSuperGame() check. + vm.mockCall(address(game), abi.encodeCall(game.gameType, ()), abi.encode(GameTypes.SUPER_CANNON_KONA)); + } + /// @notice Tests that `proveWithdrawalTransaction` reverts when paused. function test_proveWithdrawalTransaction_paused_reverts() external { vm.startPrank(optimismPortal2.guardian()); @@ -1195,13 +1057,12 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test }); } - /// @notice Tests that `proveWithdrawalTransaction` reverts when superRootsActive is true + /// @notice Tests that `proveWithdrawalTransaction` reverts when super roots are active /// and the game's rootClaimByChainId reverts with UnknownChainId. function test_proveWithdrawalTransaction_superRootsVersionBadChainId_reverts() external { skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); - // Enable super roots. - setSuperRootsActive(true); + _enableSuperRoots(); // Mock rootClaimByChainId to revert with UnknownChainId. vm.mockCallRevert( @@ -1212,7 +1073,7 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test // Should revert because chainId not found in super root. vm.expectRevert(UnknownChainId.selector); - IOptimismPortalInterop(payable(optimismPortal2)).proveWithdrawalTransaction({ + optimismPortal2.proveWithdrawalTransaction({ _tx: _defaultTx, _disputeGameIndex: _proposedGameIndex, _outputRootProof: _outputRootProof, @@ -1220,13 +1081,12 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test }); } - /// @notice Tests that `proveWithdrawalTransaction` reverts when superRootsActive is true + /// @notice Tests that `proveWithdrawalTransaction` reverts when super roots are active /// and the output root proof doesn't match the game's rootClaimByChainId. function test_proveWithdrawalTransaction_superRootsVersionBadOutputRootProof_reverts() external { skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); - // Enable super roots. - setSuperRootsActive(true); + _enableSuperRoots(); // Mock rootClaimByChainId to return a different output root (wrong one). bytes32 wrongOutputRoot = keccak256(abi.encode(_outputRoot)); @@ -1237,8 +1097,8 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test ); // Should revert because the output root proof doesn't match. - vm.expectRevert(IOptimismPortalInterop.OptimismPortal_InvalidOutputRootProof.selector); - IOptimismPortalInterop(payable(optimismPortal2)).proveWithdrawalTransaction({ + vm.expectRevert(IOptimismPortal.OptimismPortal_InvalidOutputRootProof.selector); + optimismPortal2.proveWithdrawalTransaction({ _tx: _defaultTx, _disputeGameIndex: _proposedGameIndex, _outputRootProof: _outputRootProof, @@ -1246,13 +1106,12 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test }); } - /// @notice Tests that `proveWithdrawalTransaction` succeeds when superRootsActive is true + /// @notice Tests that `proveWithdrawalTransaction` succeeds when super roots are active /// and all parameters are valid. function test_proveWithdrawalTransaction_superRootsVersion_succeeds() external { skipIfDevFeatureDisabled(DevFeatures.OPTIMISM_PORTAL_INTEROP); - // Enable super roots. - setSuperRootsActive(true); + _enableSuperRoots(); // Mock rootClaimByChainId to return the correct output root. vm.mockCall( @@ -1262,7 +1121,7 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test ); // Should succeed. - IOptimismPortalInterop(payable(optimismPortal2)).proveWithdrawalTransaction({ + optimismPortal2.proveWithdrawalTransaction({ _tx: _defaultTx, _disputeGameIndex: _proposedGameIndex, _outputRootProof: _outputRootProof, diff --git a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol index e4635d1b6c9..ed84b847e72 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfig.t.sol @@ -10,6 +10,7 @@ import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.so // Libraries import { Constants } from "src/libraries/Constants.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; +import { DevFeatures } from "src/libraries/DevFeatures.sol"; import { Features } from "src/libraries/Features.sol"; // Interfaces @@ -100,6 +101,16 @@ contract SystemConfig_Initialize_Test is SystemConfig_TestInit { skipIfForkTest("SystemConfig_Initialize_Test: cannot test initialization on forked network"); } + function test_initialize_interopFlag_succeeds() external view { + if (isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + /// if devfeature flag is on, check in system config is on + assertTrue(systemConfig.isFeatureEnabled(Features.INTEROP)); + } else { + /// if dev feature flag is off, check system config is off + assertFalse(systemConfig.isFeatureEnabled(Features.INTEROP)); + } + } + /// @notice Tests that initialization sets the correct values. function test_initialize_succeeds() external view { assertEq(systemConfig.owner(), owner); @@ -883,6 +894,12 @@ contract SystemConfig_IsFeatureEnabled_Test is SystemConfig_TestInit { systemConfig.setFeature(Features.CUSTOM_GAS_TOKEN, false); } + // Normalize INTEROP to avoid environment-dependent state + if (systemConfig.isFeatureEnabled(Features.INTEROP)) { + vm.prank(address(systemConfig.proxyAdmin())); + systemConfig.setFeature(Features.INTEROP, false); + } + assertFalse(systemConfig.isFeatureEnabled(_feature)); } diff --git a/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerContainer.t.sol b/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerContainer.t.sol index 6ce881b6171..2cfe2e9f407 100644 --- a/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerContainer.t.sol +++ b/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerContainer.t.sol @@ -30,7 +30,6 @@ contract OPContractsManagerContainer_TestInit is Test { protocolVersionsImpl: makeAddr("protocolVersionsImpl"), l1ERC721BridgeImpl: makeAddr("l1ERC721BridgeImpl"), optimismPortalImpl: makeAddr("optimismPortalImpl"), - optimismPortalInteropImpl: makeAddr("optimismPortalInteropImpl"), ethLockboxImpl: makeAddr("ethLockboxImpl"), systemConfigImpl: makeAddr("systemConfigImpl"), optimismMintableERC20FactoryImpl: makeAddr("optimismMintableERC20FactoryImpl"), diff --git a/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerUtils.t.sol b/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerUtils.t.sol index c725605e978..c8bc625f148 100644 --- a/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerUtils.t.sol +++ b/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerUtils.t.sol @@ -105,7 +105,6 @@ contract OPContractsManagerUtils_TestInit is Test { protocolVersionsImpl: makeAddr("protocolVersionsImpl"), l1ERC721BridgeImpl: makeAddr("l1ERC721BridgeImpl"), optimismPortalImpl: makeAddr("optimismPortalImpl"), - optimismPortalInteropImpl: makeAddr("optimismPortalInteropImpl"), ethLockboxImpl: makeAddr("ethLockboxImpl"), systemConfigImpl: makeAddr("systemConfigImpl"), optimismMintableERC20FactoryImpl: makeAddr("optimismMintableERC20FactoryImpl"), diff --git a/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerV2.t.sol b/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerV2.t.sol index d3eb2c62a91..e805a06652a 100644 --- a/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerV2.t.sol +++ b/packages/contracts-bedrock/test/L1/opcm/OPContractsManagerV2.t.sol @@ -29,7 +29,6 @@ import { IOPContractsManagerUtils } from "interfaces/L1/opcm/IOPContractsManager import { IOPContractsManagerContainer } from "interfaces/L1/opcm/IOPContractsManagerContainer.sol"; import { IOPContractsManagerMigrator } from "interfaces/L1/opcm/IOPContractsManagerMigrator.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; @@ -2041,16 +2040,13 @@ contract OPContractsManagerV2_Migrate_Test is OPContractsManagerV2_TestInit { assertTrue(newLockbox.authorizedPortals(portal1), "ETHLockbox does not have portal 1 authorized"); assertTrue(newLockbox.authorizedPortals(portal2), "ETHLockbox does not have portal 2 authorized"); - // Check that superRootsActive is true on both portals. + // Check that the INTEROP feature is enabled on both SystemConfigs. assertTrue( - IOptimismPortalInterop(payable(address(portal1))).superRootsActive(), - "Portal 1 superRootsActive should be true" + chainContracts1.systemConfig.isFeatureEnabled(Features.INTEROP), "Chain 1 INTEROP feature should be enabled" ); assertTrue( - IOptimismPortalInterop(payable(address(portal2))).superRootsActive(), - "Portal 2 superRootsActive should be true" + chainContracts2.systemConfig.isFeatureEnabled(Features.INTEROP), "Chain 2 INTEROP feature should be enabled" ); - // Check that the ETH_LOCKBOX feature is enabled on both SystemConfigs. assertTrue( chainContracts1.systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX), diff --git a/packages/contracts-bedrock/test/L2/L1Block.t.sol b/packages/contracts-bedrock/test/L2/L1Block.t.sol index 97bb570b88d..834eda27ae8 100644 --- a/packages/contracts-bedrock/test/L2/L1Block.t.sol +++ b/packages/contracts-bedrock/test/L2/L1Block.t.sol @@ -11,6 +11,7 @@ import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import "src/libraries/L1BlockErrors.sol"; import { Features } from "src/libraries/Features.sol"; +import { DevFeatures } from "src/libraries/DevFeatures.sol"; // Interfaces import { IL1Block } from "interfaces/L2/IL1Block.sol"; @@ -520,6 +521,9 @@ contract L1Block_SetFeature_Test is L1Block_TestInit { /// @notice Tests that setFeature succeeds when called by the depositor. function test_setFeature_succeeds() external { + if (isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + vm.skip(true); + } vm.expectEmit(Predeploys.L1_BLOCK_ATTRIBUTES); emit FeatureSet(Features.INTEROP, true); @@ -542,8 +546,11 @@ contract L1Block_SetFeature_Test is L1Block_TestInit { /// @notice Tests that setFeature reverts when the feature is already enabled. function test_setFeature_alreadyEnabled_reverts() external { - vm.prank(depositor); - l1Block.setFeature(Features.INTEROP); + // If the interop dev feature is not enabled, set the feature + if (!isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + vm.prank(depositor); + l1Block.setFeature(Features.INTEROP); + } vm.prank(depositor); vm.expectRevert(L1Block_FeatureAlreadyEnabled.selector); @@ -551,14 +558,20 @@ contract L1Block_SetFeature_Test is L1Block_TestInit { } /// @notice Tests that isFeatureEnabled returns false by default. - function test_isFeatureEnabled_defaultFalse_succeeds() external view { + function test_isFeatureEnabled_defaultFalse_succeeds() external { + // If the interop dev feature is enabled, skip this test + if (isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + vm.skip(true); + } assertFalse(l1Block.isFeatureEnabled(Features.INTEROP)); } /// @notice Tests that multiple features can be enabled independently. function test_setFeature_multipleFeatures_succeeds() external { vm.startPrank(depositor); - l1Block.setFeature(Features.INTEROP); + if (!isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + l1Block.setFeature(Features.INTEROP); + } // The custom gas token feature may already be enabled in the setUp if // SYSTEM_FEATURE__CUSTOM_GAS_TOKEN is enabled. diff --git a/packages/contracts-bedrock/test/L2/L2DevFeatureFlags.t.sol b/packages/contracts-bedrock/test/L2/L2DevFeatureFlags.t.sol index 0e277961dc8..88a3248148c 100644 --- a/packages/contracts-bedrock/test/L2/L2DevFeatureFlags.t.sol +++ b/packages/contracts-bedrock/test/L2/L2DevFeatureFlags.t.sol @@ -85,7 +85,7 @@ contract L2DevFeatureFlags_IsDevFeatureEnabled_Test is L2DevFeatureFlags_TestIni } /// @notice Tests that `isDevFeatureEnabled` works correctly with the known OPTIMISM_PORTAL_INTEROP feature. - function test_isDevFeatureEnabled_optimismPortalInterop_succeeds() public { + function test_isDevFeatureEnabled_interop_succeeds() public { vm.prank(Constants.DEPOSITOR_ACCOUNT); l2DevFeatureFlags.setDevFeatureBitmap(DevFeatures.OPTIMISM_PORTAL_INTEROP); diff --git a/packages/contracts-bedrock/test/L2/fork/L2ForkUpgrade.t.sol b/packages/contracts-bedrock/test/L2/fork/L2ForkUpgrade.t.sol index 85812e2c76e..d31520c534f 100644 --- a/packages/contracts-bedrock/test/L2/fork/L2ForkUpgrade.t.sol +++ b/packages/contracts-bedrock/test/L2/fork/L2ForkUpgrade.t.sol @@ -31,6 +31,9 @@ import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { ILiquidityController } from "interfaces/L2/ILiquidityController.sol"; import { IFeeSplitter } from "interfaces/L2/IFeeSplitter.sol"; import { ISharesCalculator } from "interfaces/L2/ISharesCalculator.sol"; +import { IL1Block } from "interfaces/L2/IL1Block.sol"; +import { IL1BlockCGT } from "interfaces/L2/IL1BlockCGT.sol"; +import { Features } from "src/libraries/Features.sol"; /// @title L2ForkUpgrade_TestInit /// @notice Reusable test initialization for L2 fork upgrade tests. @@ -218,6 +221,9 @@ contract L2ForkUpgrade_Initialization_Test is L2ForkUpgrade_TestInit { address liquidityControllerOwner; string liquidityControllerGasPayingTokenName; string liquidityControllerGasPayingTokenSymbol; + // L1Block feature state + string l1BlockGasPayingTokenName; + string l1BlockGasPayingTokenSymbol; // FeeSplitter configuration address feeSplitterSharesCalculator; // Fee vault configuration @@ -274,6 +280,9 @@ contract L2ForkUpgrade_Initialization_Test is L2ForkUpgrade_TestInit { state_.liquidityControllerOwner = liquidityController.owner(); state_.liquidityControllerGasPayingTokenName = liquidityController.gasPayingTokenName(); state_.liquidityControllerGasPayingTokenSymbol = liquidityController.gasPayingTokenSymbol(); + // Capture L1Block gas paying token metadata for post-upgrade verification. + state_.l1BlockGasPayingTokenName = IL1BlockCGT(Predeploys.L1_BLOCK_ATTRIBUTES).gasPayingTokenName(); + state_.l1BlockGasPayingTokenSymbol = IL1BlockCGT(Predeploys.L1_BLOCK_ATTRIBUTES).gasPayingTokenSymbol(); } // Capture FeeSplitter configuration @@ -346,6 +355,7 @@ contract L2ForkUpgrade_Initialization_Test is L2ForkUpgrade_TestInit { _verifyLiquidityControllerConfiguration(_preState); _verifyFeeSplitterConfiguration(_preState); _verifyProxyAdminOwnership(_preState); + _verifyL1BlockFeatureState(_preState); // OpenZeppelin v4 Initializable contracts - slot varies by contract _verifyOZv4Initialization(Predeploys.L2_CROSS_DOMAIN_MESSENGER, bytes32(0), 20, "L2CrossDomainMessenger"); @@ -526,6 +536,33 @@ contract L2ForkUpgrade_Initialization_Test is L2ForkUpgrade_TestInit { ); } + /// @notice Verifies that L1Block feature flags match the expected post-upgrade state. + /// Catches regressions where the setFeature migration step is missing or reordered. + function _verifyL1BlockFeatureState(PreUpgradeInitializationState memory _preState) internal view { + if (commonState.isCustomGasToken) { + assertTrue( + IL1BlockCGT(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken(), + "L1Block.isCustomGasToken() must be true after upgrade on CGT chains" + ); + assertEq( + IL1BlockCGT(Predeploys.L1_BLOCK_ATTRIBUTES).gasPayingTokenName(), + _preState.l1BlockGasPayingTokenName, + "L1Block.gasPayingTokenName() not preserved after upgrade" + ); + assertEq( + IL1BlockCGT(Predeploys.L1_BLOCK_ATTRIBUTES).gasPayingTokenSymbol(), + _preState.l1BlockGasPayingTokenSymbol, + "L1Block.gasPayingTokenSymbol() not preserved after upgrade" + ); + } + if (commonState.isInteropEnabled) { + assertTrue( + IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isFeatureEnabled(Features.INTEROP), + "L1Block.isFeatureEnabled(INTEROP) must be true after upgrade on interop chains" + ); + } + } + /// @notice Helper to verify OpenZeppelin v4 initialization state. /// @param _proxy The proxy address of the predeploy. /// @param _slot The storage slot where the initialized value is located. diff --git a/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol b/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol index 611fe194c1c..476666bb036 100644 --- a/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol +++ b/packages/contracts-bedrock/test/dispute/AnchorStateRegistry.t.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.15; import { BaseFaultDisputeGame_TestInit, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol"; // Libraries -import { GameType, GameTypes, GameStatus, Hash, Claim, VMStatuses, Proposal } from "src/dispute/lib/Types.sol"; +import { GameType, GameTypes, GameStatus, Hash, Claim, Duration, VMStatuses, Proposal } from "src/dispute/lib/Types.sol"; import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.sol"; // Interfaces @@ -14,6 +14,7 @@ import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IProxyAdminOwnedBase } from "interfaces/universal/IProxyAdminOwnedBase.sol"; +import { DevFeatures } from "src/libraries/DevFeatures.sol"; /// @title AnchorStateRegistry_TestInit /// @notice Reusable test initialization for `AnchorStateRegistry` tests. @@ -814,80 +815,78 @@ contract AnchorStateRegistry_IsGameResolved_Test is AnchorStateRegistry_TestInit } } -/// @title AnchorStateRegistry_IsGameProper_Test -/// @notice Tests the `isGameProper` function of the `AnchorStateRegistry` contract. -contract AnchorStateRegistry_IsGameProper_Test is AnchorStateRegistry_TestInit { - /// @notice Tests that isGameProper will return true if the game meets all conditions. +/// @title AnchorStateRegistry_IsGameProper_TestInit +/// @notice Abstract base providing shared `isGameProper` tests for any dispute game type. +/// Concrete subcontracts supply a `_game()` getter and any game-type-specific tests. +abstract contract AnchorStateRegistry_IsGameProper_TestInit is AnchorStateRegistry_TestInit { + /// @notice Returns the game under test. + function _game() internal view virtual returns (IDisputeGame); + + /// @notice Tests that isGameProper returns true when the game meets all conditions. function test_isGameProper_meetsAllConditions_succeeds() public view { - // Game will meet all conditions by default. - assertTrue(anchorStateRegistry.isGameProper(gameProxy)); + assertTrue(anchorStateRegistry.isGameProper(_game())); } - /// @notice Tests that isGameProper will return false if the game is not registered. - function test_isGameProper_isNotFactoryRegistered_succeeds() public { - // Mock the DisputeGameFactory to make it seem that the game was not registered. + /// @notice Tests that isGameProper returns false when the game is not registered. + function test_isGameProper_notRegistered_succeeds() public { vm.mockCall( address(disputeGameFactory), - abi.encodeCall( - disputeGameFactory.games, (gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData()) - ), + abi.encodeCall(disputeGameFactory.games, (_game().gameType(), _game().rootClaim(), _game().extraData())), abi.encode(address(0), 0) ); + assertFalse(anchorStateRegistry.isGameProper(_game())); + } - assertFalse(anchorStateRegistry.isGameProper(gameProxy)); + /// @notice Tests that isGameProper returns false when the game is blacklisted. + function test_isGameProper_blacklisted_succeeds() public { + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.blacklistDisputeGame(_game()); + assertFalse(anchorStateRegistry.isGameProper(_game())); } - /// @notice Tests that isGameProper will return false if the game is not the respected game - /// type. + /// @notice Tests that isGameProper returns false when the superchain is paused. + function test_isGameProper_superchainPaused_succeeds() public { + vm.prank(superchainConfig.guardian()); + superchainConfig.pause(address(0)); + assertFalse(anchorStateRegistry.isGameProper(_game())); + } +} + +/// @title AnchorStateRegistry_IsGameProper_Test +/// @notice Tests the `isGameProper` function of the `AnchorStateRegistry` contract. +contract AnchorStateRegistry_IsGameProper_Test is AnchorStateRegistry_IsGameProper_TestInit { + /// @notice Returns the fault dispute game proxy. + function _game() internal view override returns (IDisputeGame) { + return gameProxy; + } + + /// @notice Tests that isGameProper returns true even when the game was not the respected type + /// at creation, since `wasRespectedGameTypeWhenCreated` is not a condition for + /// isGameProper. /// @param _gameType The game type to use for the test. function testFuzz_isGameProper_anyGameType_succeeds(GameType _gameType) public { if (_gameType.raw() == gameProxy.gameType().raw()) { _gameType = GameType.wrap(_gameType.raw() + 1); } - // Mock that the game was not respected. vm.mockCall( address(gameProxy), abi.encodeCall(gameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(false) ); - // Still a proper game. assertTrue(anchorStateRegistry.isGameProper(gameProxy)); } - /// @notice Tests that isGameProper will return false if the game is blacklisted. - function test_isGameProper_isBlacklisted_succeeds() public { - // Blacklist the game. - vm.prank(superchainConfig.guardian()); - anchorStateRegistry.blacklistDisputeGame(gameProxy); - - // Should return false. - assertFalse(anchorStateRegistry.isGameProper(gameProxy)); - } - - /// @notice Tests that isGameProper will return false if the superchain is paused. - function test_isGameProper_superchainPaused_succeeds() public { - // Pause the superchain. - vm.prank(superchainConfig.guardian()); - superchainConfig.pause(address(0)); - - // Game should not be proper. - assertFalse(anchorStateRegistry.isGameProper(gameProxy)); - } - - /// @notice Tests that isGameProper will return false if the game is retired. + /// @notice Tests that isGameProper returns false when the FaultDisputeGame is retired. + /// Retirement is determined via the game's `createdAt` timestamp. /// @param _createdAtTimestamp The createdAt timestamp to use for the test. function testFuzz_isGameProper_isRetired_succeeds(uint64 _createdAtTimestamp) public { - // Set the retirement timestamp to now. vm.prank(superchainConfig.guardian()); anchorStateRegistry.updateRetirementTimestamp(); - // Make sure createdAt timestamp is less than or equal to the retirementTimestamp. _createdAtTimestamp = uint64(bound(_createdAtTimestamp, 0, anchorStateRegistry.retirementTimestamp())); - // Mock the call to createdAt. vm.mockCall(address(gameProxy), abi.encodeCall(gameProxy.createdAt, ()), abi.encode(_createdAtTimestamp)); - // Game should not be proper. assertFalse(anchorStateRegistry.isGameProper(gameProxy)); } } @@ -1374,3 +1373,338 @@ contract AnchorStateRegistry_SetAnchorState_Test is AnchorStateRegistry_TestInit anchorStateRegistry.setAnchorState(gameProxy); } } + +/// @title AnchorStateRegistry_ZkDisputeGame_TestInit +/// @notice Reusable test initialization for ZKDisputeGame AnchorStateRegistry tests. +abstract contract AnchorStateRegistry_ZkDisputeGame_TestInit is AnchorStateRegistry_TestInit { + IDisputeGame zkGameProxy; + uint256 zkL2SequenceNumber; + + function setUp() public virtual override { + super.setUp(); + + skipIfDevFeatureDisabled(DevFeatures.ZK_DISPUTE_GAME); + + // Register ZK game implementation and set it as the respected game type. + setupZKDisputeGame( + ZKDisputeGameParams({ + maxChallengeDuration: Duration.wrap(3.5 days), + maxProveDuration: Duration.wrap(12 hours), + absolutePrestate: keccak256("absolutePrestate"), + challengerBond: 1 ether + }) + ); + + // Get anchor state to pick a valid l2SequenceNumber. + (, uint256 anchorL2SeqNum) = anchorStateRegistry.getAnchorRoot(); + zkL2SequenceNumber = anchorL2SeqNum + 2000; + + // Create a ZK game via the factory. + Claim rootClaim_ = changeClaimStatus(Claim.wrap(keccak256("zkRootClaim")), VMStatuses.INVALID); + bytes memory extraData_ = abi.encodePacked(zkL2SequenceNumber, type(uint32).max); + + address proposer = makeAddr("zkProposer"); + vm.deal(proposer, 1 ether); + vm.warp(block.timestamp + 1000); + + vm.prank(proposer); + zkGameProxy = disputeGameFactory.create{ value: 1 ether }(GameTypes.ZK_DISPUTE_GAME, rootClaim_, extraData_); + } + + /// @notice Mocks the ZK game as a valid, resolved, finalized game. + function _mockZkGameAsValid() internal { + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(true) + ); + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.resolvedAt, ()), abi.encode(block.timestamp)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.l2SequenceNumber, ()), abi.encode(zkL2SequenceNumber) + ); + vm.warp(block.timestamp + optimismPortal2.disputeGameFinalityDelaySeconds() + 1); + } +} + +/// @title AnchorStateRegistry_SetAnchorState_ZkDisputeGame_Test +/// @notice Tests the `setAnchorState` function with ZKDisputeGame. +contract AnchorStateRegistry_SetAnchorState_ZkDisputeGame_Test is AnchorStateRegistry_ZkDisputeGame_TestInit { + /// @notice Tests that a valid ZK game can update the anchor state. + function testFuzz_setAnchorState_validNewerState_succeeds(uint256 _l2SequenceNumber) public { + (, uint256 anchorL2SeqNum) = anchorStateRegistry.getAnchorRoot(); + _l2SequenceNumber = bound(_l2SequenceNumber, anchorL2SeqNum + 1, type(uint256).max); + + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(true) + ); + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.resolvedAt, ()), abi.encode(block.timestamp)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.l2SequenceNumber, ()), abi.encode(_l2SequenceNumber) + ); + vm.warp(block.timestamp + optimismPortal2.disputeGameFinalityDelaySeconds() + 1); + + vm.prank(address(zkGameProxy)); + vm.expectEmit(address(anchorStateRegistry)); + emit AnchorUpdated(IFaultDisputeGame(address(zkGameProxy))); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm anchor state updated to ZK game's claim. + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(l2BlockNumber, _l2SequenceNumber); + assertEq(root.raw(), zkGameProxy.rootClaim().raw()); + + // Confirm anchor game is the ZK game. + IDisputeGame anchorGame = anchorStateRegistry.anchorGame(); + assertEq(address(anchorGame), address(zkGameProxy)); + } + + /// @notice Tests that a valid ZK game with an older l2SequenceNumber cannot update the anchor. + function testFuzz_setAnchorState_olderValidGameClaim_fails(uint256 _l2SequenceNumber) public { + // First, set the anchor to the ZK game so we have a known anchor block. + _mockZkGameAsValid(); + vm.prank(address(zkGameProxy)); + anchorStateRegistry.setAnchorState(zkGameProxy); + + (, uint256 anchorBlockNumber) = anchorStateRegistry.getAnchorRoot(); + + // Bound to at or below the anchor. + _l2SequenceNumber = bound(_l2SequenceNumber, 0, anchorBlockNumber); + + // Mock the ZK game's sequence number to be at or below the anchor. + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.l2SequenceNumber, ()), abi.encode(_l2SequenceNumber) + ); + + // Capture state before the rejected call. + (Hash rootBefore, uint256 l2BlockNumberBefore) = anchorStateRegistry.getAnchorRoot(); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumberBefore); + assertEq(updatedRoot.raw(), rootBefore.raw()); + } + + /// @notice Tests that a blacklisted ZK game cannot update the anchor state. + function test_setAnchorState_blacklistedGame_fails() public { + _mockZkGameAsValid(); + + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + + // Blacklist the ZK game. + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.blacklistDisputeGame(zkGameProxy); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumber); + assertEq(updatedRoot.raw(), root.raw()); + } + + /// @notice Tests that a retired ZK game cannot update the anchor state. + function test_setAnchorState_retiredGame_fails() public { + _mockZkGameAsValid(); + + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + + // Retire all games by setting retirement timestamp before game creation. + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.updateRetirementTimestamp(); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumber); + assertEq(updatedRoot.raw(), root.raw()); + } + + /// @notice Tests that a ZK game resolved as CHALLENGER_WINS cannot update the anchor state. + function test_setAnchorState_challengerWins_fails() public { + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.status, ()), abi.encode(GameStatus.CHALLENGER_WINS) + ); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(true) + ); + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.resolvedAt, ()), abi.encode(block.timestamp)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.l2SequenceNumber, ()), abi.encode(zkL2SequenceNumber) + ); + vm.warp(block.timestamp + optimismPortal2.disputeGameFinalityDelaySeconds() + 1); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumber); + assertEq(updatedRoot.raw(), root.raw()); + } + + /// @notice Tests that a ZK game still in progress (resolvedAt == 0) cannot update the anchor state. + function test_setAnchorState_inProgress_fails() public { + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.status, ()), abi.encode(GameStatus.IN_PROGRESS)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(true) + ); + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.resolvedAt, ()), abi.encode(uint256(0))); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.l2SequenceNumber, ()), abi.encode(zkL2SequenceNumber) + ); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumber); + assertEq(updatedRoot.raw(), root.raw()); + } + + /// @notice Tests that an unfinalized ZK game cannot update the anchor state. + function testFuzz_setAnchorState_notFinalized_fails(uint256 _resolvedAtTimestamp) public { + uint256 finalityDelay = optimismPortal2.disputeGameFinalityDelaySeconds(); + // Bound to avoid overflow when adding finalityDelay. + _resolvedAtTimestamp = bound(_resolvedAtTimestamp, block.timestamp, type(uint64).max); + + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.status, ()), abi.encode(GameStatus.DEFENDER_WINS)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(true) + ); + vm.mockCall(address(zkGameProxy), abi.encodeCall(zkGameProxy.resolvedAt, ()), abi.encode(_resolvedAtTimestamp)); + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.l2SequenceNumber, ()), abi.encode(zkL2SequenceNumber) + ); + // Warp to before the finality delay has elapsed. + vm.warp(_resolvedAtTimestamp + finalityDelay); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumber); + assertEq(updatedRoot.raw(), root.raw()); + } + + /// @notice Tests that a ZK game cannot update the anchor state when the superchain is paused. + function test_setAnchorState_superchainPaused_fails() public { + // Mock the game as valid first so the only failing condition is the pause. + _mockZkGameAsValid(); + + (Hash root, uint256 l2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + + vm.prank(superchainConfig.guardian()); + superchainConfig.pause(address(0)); + + vm.prank(address(zkGameProxy)); + vm.expectRevert(IAnchorStateRegistry.AnchorStateRegistry_InvalidAnchorGame.selector); + anchorStateRegistry.setAnchorState(zkGameProxy); + + // Confirm that the anchor state has not updated. + (Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.getAnchorRoot(); + assertEq(updatedL2BlockNumber, l2BlockNumber); + assertEq(updatedRoot.raw(), root.raw()); + } +} + +/// @title AnchorStateRegistry_IsGameClaimValid_ZkDisputeGame_Test +/// @notice Tests the `isGameClaimValid` function with ZKDisputeGame. +contract AnchorStateRegistry_IsGameClaimValid_ZkDisputeGame_Test is AnchorStateRegistry_ZkDisputeGame_TestInit { + /// @notice Tests that a valid ZK game claim is recognized. + function test_isGameClaimValid_validClaim_succeeds() public { + _mockZkGameAsValid(); + + assertTrue(anchorStateRegistry.isGameClaimValid(zkGameProxy)); + } + + /// @notice Tests that a blacklisted ZK game claim is not valid. + function test_isGameClaimValid_blacklisted_succeeds() public { + _mockZkGameAsValid(); + + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.blacklistDisputeGame(zkGameProxy); + + assertFalse(anchorStateRegistry.isGameClaimValid(zkGameProxy)); + } + + /// @notice Tests that a retired ZK game claim is not valid. + function test_isGameClaimValid_retired_succeeds() public { + _mockZkGameAsValid(); + + // Retire all games by setting retirement timestamp before game creation. + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.updateRetirementTimestamp(); + + assertFalse(anchorStateRegistry.isGameClaimValid(zkGameProxy)); + } + + /// @notice Tests that a ZK game claim is not valid when it was not the respected type at creation. + function test_isGameClaimValid_notRespected_succeeds() public { + _mockZkGameAsValid(); + + // Mock that the game was not respected when created. + vm.mockCall( + address(zkGameProxy), abi.encodeCall(zkGameProxy.wasRespectedGameTypeWhenCreated, ()), abi.encode(false) + ); + + assertFalse(anchorStateRegistry.isGameClaimValid(zkGameProxy)); + } +} + +/// @title AnchorStateRegistry_IsGameProper_ZkDisputeGame_Test +/// @notice Tests the `isGameProper` function with ZKDisputeGame. +/// Shared cases are inherited from AnchorStateRegistry_IsGameProper_TestInit. +contract AnchorStateRegistry_IsGameProper_ZkDisputeGame_Test is + AnchorStateRegistry_IsGameProper_TestInit, + AnchorStateRegistry_ZkDisputeGame_TestInit +{ + function setUp() public override(AnchorStateRegistry_TestInit, AnchorStateRegistry_ZkDisputeGame_TestInit) { + AnchorStateRegistry_ZkDisputeGame_TestInit.setUp(); + } + + /// @notice Returns the ZK dispute game proxy. + function _game() internal view override returns (IDisputeGame) { + return zkGameProxy; + } + + /// @notice Tests that a retired ZK game is not proper. + /// ZKDisputeGame retirement does not rely on `createdAt`, unlike FaultDisputeGame. + function test_isGameProper_retired_succeeds() public { + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.updateRetirementTimestamp(); + + assertFalse(anchorStateRegistry.isGameProper(zkGameProxy)); + } +} + +/// @title AnchorStateRegistry_BlacklistDisputeGame_ZkDisputeGame_Test +/// @notice Tests the `blacklistDisputeGame` function with ZKDisputeGame. +contract AnchorStateRegistry_BlacklistDisputeGame_ZkDisputeGame_Test is AnchorStateRegistry_ZkDisputeGame_TestInit { + /// @notice Tests that a ZK game can be blacklisted. + function test_blacklistDisputeGame_succeeds() public { + vm.prank(superchainConfig.guardian()); + anchorStateRegistry.blacklistDisputeGame(zkGameProxy); + + assertTrue(anchorStateRegistry.disputeGameBlacklist(zkGameProxy)); + } +} diff --git a/packages/contracts-bedrock/test/dispute/DisputeGameFactory.t.sol b/packages/contracts-bedrock/test/dispute/DisputeGameFactory.t.sol index 5d70a4fedc8..f0c7cd7f302 100644 --- a/packages/contracts-bedrock/test/dispute/DisputeGameFactory.t.sol +++ b/packages/contracts-bedrock/test/dispute/DisputeGameFactory.t.sol @@ -9,6 +9,7 @@ import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.so import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; // Libraries +import { DevFeatures } from "src/libraries/DevFeatures.sol"; import "src/dispute/lib/Types.sol"; import "src/dispute/lib/Errors.sol"; @@ -407,12 +408,12 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { { // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible // values. - uint32 maxGameType = 8; + uint32 maxGameType = 10; GameType gt = GameType.wrap(uint8(bound(gameType, 0, maxGameType))); // Ensure the rootClaim has a VMStatus that disagrees with the validity. rootClaim = changeClaimStatus(rootClaim, VMStatuses.INVALID); - // Set all three implementations to the same `FakeClone` contract. + // Set all implementations to the same `FakeClone` contract. for (uint8 i; i < maxGameType + 1; i++) { GameType lgt = GameType.wrap(i); disputeGameFactory.setImplementation(lgt, IDisputeGame(address(fakeClone))); @@ -472,9 +473,9 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { /// the given `GameType`. function testFuzz_create_noImpl_reverts(uint32 gameType, Claim rootClaim, bytes calldata extraData) public { // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible - // values. We skip over game type = 0, since the deploy script set the implementation for - // that game type. - uint32 maxGameType = 8; + // values. We skip over game types 0-10, since those are known types and may have + // implementations set by the deploy script or test setup. + uint32 maxGameType = 10; GameType gt = GameType.wrap(uint32(bound(gameType, maxGameType + 1, type(uint32).max))); // Ensure the rootClaim has a VMStatus that disagrees with the validity. rootClaim = changeClaimStatus(rootClaim, VMStatuses.INVALID); @@ -488,12 +489,12 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { function testFuzz_create_sameUUID_reverts(uint32 gameType, Claim rootClaim, bytes calldata extraData) public { // Ensure that the `gameType` is within the bounds of the `GameType` enum's possible // values. - uint32 maxGameType = 8; + uint32 maxGameType = 10; GameType gt = GameType.wrap(uint8(bound(gameType, 0, maxGameType))); // Ensure the rootClaim has a VMStatus that disagrees with the validity. rootClaim = changeClaimStatus(rootClaim, VMStatuses.INVALID); - // Set all three implementations to the same `FakeClone` contract. + // Set all implementations to the same `FakeClone` contract. for (uint8 i; i < maxGameType + 1; i++) { disputeGameFactory.setImplementation(GameType.wrap(i), IDisputeGame(address(fakeClone))); } @@ -518,47 +519,6 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { disputeGameFactory.create{ value: bondAmount }(gt, rootClaim, extraData); } - function test_create_implArgs_succeeds() public { - Claim absolutePrestate = Claim.wrap(bytes32(hex"dead")); - (, AlphabetVM vm_,) = setupFaultDisputeGame(absolutePrestate); - - Claim rootClaim = changeClaimStatus(Claim.wrap(bytes32(hex"beef")), VMStatuses.INVALID); - // extraData should contain the l2BlockNumber as first 32 bytes - bytes memory extraData = bytes.concat(bytes32(uint256(type(uint32).max))); - - uint256 bondAmount = disputeGameFactory.initBonds(GameTypes.CANNON); - vm.deal(address(this), bondAmount); - - // Create the game - IDisputeGame proxy = disputeGameFactory.create{ value: bondAmount }(GameTypes.CANNON, rootClaim, extraData); - - // Verify the game was created and stored - (IDisputeGame game, Timestamp timestamp) = disputeGameFactory.games(GameTypes.CANNON, rootClaim, extraData); - - assertEq(address(game), address(proxy)); - assertEq(Timestamp.unwrap(timestamp), block.timestamp); - - // Verify the game has the correct parameters via CWIA - IFaultDisputeGame game_ = IFaultDisputeGame(address(proxy)); - - // Test CWIA getters - assertEq(Claim.unwrap(game_.absolutePrestate()), Claim.unwrap(absolutePrestate)); - assertEq(Claim.unwrap(game_.rootClaim()), Claim.unwrap(rootClaim)); - assertEq(game_.extraData(), extraData); - assertEq(game_.l2ChainId(), l2ChainId); - assertEq(address(game_.gameCreator()), address(this)); - assertEq(game_.l2BlockNumber(), uint256(type(uint32).max)); - assertEq(address(game_.vm()), address(vm_)); - assertEq(address(game_.weth()), address(delayedWeth)); - assertEq(address(game_.anchorStateRegistry()), address(anchorStateRegistry)); - // Test Constructor args - assertEq(GameType.unwrap(game_.gameType()), GameType.unwrap(GameTypes.CANNON)); - assertEq(game_.maxGameDepth(), 2 ** 3); - assertEq(game_.splitDepth(), 2 ** 2); - assertEq(Duration.unwrap(game_.clockExtension()), Duration.unwrap(Duration.wrap(3 hours))); - assertEq(Duration.unwrap(game_.maxClockDuration()), Duration.unwrap(Duration.wrap(3.5 days))); - } - /// @notice Tests that games get unique addresses based on their inputs (gameType, rootClaim, extraData) /// even when the factory's nonce is unchanged. This test would fail if CREATE was used instead /// of CREATE2, since CREATE only depends on deployer address and nonce. @@ -867,6 +827,344 @@ contract DisputeGameFactory_FindLatestGames_Test is DisputeGameFactory_TestInit } } +/// @title DisputeGameFactory_Create_FaultDisputeGame_Test +/// @notice Tests that the factory creates FaultDisputeGame clones with correct CWIA args. +contract DisputeGameFactory_Create_FaultDisputeGame_Test is DisputeGameFactory_TestInit { + function test_create_implArgs_succeeds() public { + Claim absolutePrestate = Claim.wrap(bytes32(hex"dead")); + (, AlphabetVM vm_,) = setupFaultDisputeGame(absolutePrestate); + + Claim rootClaim = changeClaimStatus(Claim.wrap(bytes32(hex"beef")), VMStatuses.INVALID); + // extraData should contain the l2BlockNumber as first 32 bytes + bytes memory extraData = bytes.concat(bytes32(uint256(type(uint32).max))); + + uint256 bondAmount = disputeGameFactory.initBonds(GameTypes.CANNON); + vm.deal(address(this), bondAmount); + + // Create the game + IDisputeGame proxy = disputeGameFactory.create{ value: bondAmount }(GameTypes.CANNON, rootClaim, extraData); + + // Verify the game was created and stored + (IDisputeGame game, Timestamp timestamp) = disputeGameFactory.games(GameTypes.CANNON, rootClaim, extraData); + + assertEq(address(game), address(proxy)); + assertEq(Timestamp.unwrap(timestamp), block.timestamp); + + // Verify the game has the correct parameters via CWIA + IFaultDisputeGame game_ = IFaultDisputeGame(address(proxy)); + + // Test CWIA getters + assertEq(Claim.unwrap(game_.absolutePrestate()), Claim.unwrap(absolutePrestate)); + assertEq(Claim.unwrap(game_.rootClaim()), Claim.unwrap(rootClaim)); + assertEq(game_.extraData(), extraData); + assertEq(game_.l2ChainId(), l2ChainId); + assertEq(address(game_.gameCreator()), address(this)); + assertEq(game_.l2BlockNumber(), uint256(type(uint32).max)); + assertEq(address(game_.vm()), address(vm_)); + assertEq(address(game_.weth()), address(delayedWeth)); + assertEq(address(game_.anchorStateRegistry()), address(anchorStateRegistry)); + // Test Constructor args + assertEq(GameType.unwrap(game_.gameType()), GameType.unwrap(GameTypes.CANNON)); + assertEq(game_.maxGameDepth(), 2 ** 3); + assertEq(game_.splitDepth(), 2 ** 2); + assertEq(Duration.unwrap(game_.clockExtension()), Duration.unwrap(Duration.wrap(3 hours))); + assertEq(Duration.unwrap(game_.maxClockDuration()), Duration.unwrap(Duration.wrap(3.5 days))); + } +} + +/// @title DisputeGameFactory_ZkDisputeGame_TestInit +/// @notice Reusable test initialization for ZKDisputeGame factory tests. +abstract contract DisputeGameFactory_ZkDisputeGame_TestInit is DisputeGameFactory_TestInit { + IZKVerifier zkVerifier; + address proposer = makeAddr("proposer"); + ZKDisputeGameParams defaultZKParams = ZKDisputeGameParams({ + maxChallengeDuration: Duration.wrap(3.5 days), + maxProveDuration: Duration.wrap(12 hours), + absolutePrestate: keccak256("absolutePrestate"), + challengerBond: 1 ether + }); + + function setUp() public virtual override { + super.setUp(); + skipIfDevFeatureDisabled(DevFeatures.ZK_DISPUTE_GAME); + vm.warp(block.timestamp + 1000); + } + + function _createZKGameWithParams(ZKDisputeGameParams memory _params) internal returns (ZKDisputeGame proxy_) { + // Setup ZK game implementation: deploys impl, encodes gameArgs, registers with factory. + (, zkVerifier) = setupZKDisputeGame(_params); + + (Claim rootClaim_, bytes memory extraData_) = _zkCreateParams(); + + vm.deal(proposer, _params.challengerBond); + + vm.expectEmit(false, true, true, false); + emit DisputeGameCreated(address(0), GameTypes.ZK_DISPUTE_GAME, rootClaim_); + vm.prank(proposer); + proxy_ = ZKDisputeGame( + payable( + address( + disputeGameFactory.create{ value: _params.challengerBond }( + GameTypes.ZK_DISPUTE_GAME, rootClaim_, extraData_ + ) + ) + ) + ); + } + + /// @notice Returns valid rootClaim and extraData for creating a ZK game. + function _zkCreateParams() internal view returns (Claim rootClaim_, bytes memory extraData_) { + (, uint256 anchorL2SeqNum) = anchorStateRegistry.getAnchorRoot(); + extraData_ = abi.encodePacked(anchorL2SeqNum + 1000, type(uint32).max); + rootClaim_ = changeClaimStatus(Claim.wrap(keccak256("zkRootClaim")), VMStatuses.INVALID); + } + + function _assertZKGameFactoryStorage(ZKDisputeGame _proxy) internal view { + // extraData is 36 bytes: l2SequenceNumber (32) + parentIndex (4). + bytes memory extraData_ = _proxy.extraData(); + assertEq(extraData_.length, 36); + + // Verify factory mappings and list. + (IDisputeGame storedGame, Timestamp storedTs) = + disputeGameFactory.games(GameTypes.ZK_DISPUTE_GAME, _proxy.rootClaim(), extraData_); + assertEq(address(storedGame), address(_proxy)); + assertEq(storedTs.raw(), block.timestamp); + + uint256 idx = disputeGameFactory.gameCount() - 1; + (, Timestamp indexedTs, IDisputeGame indexedGame) = disputeGameFactory.gameAtIndex(idx); + assertEq(address(indexedGame), address(_proxy)); + assertEq(indexedTs.raw(), block.timestamp); + } + + function _assertZKGameCWIA( + ZKDisputeGame _proxy, + address _proposer, + ZKDisputeGameParams memory _params + ) + internal + view + { + // Verify CWIA getters — confirms gameArgs were forwarded correctly without re-encoding. + assertEq(GameType.unwrap(_proxy.gameType()), GameType.unwrap(GameTypes.ZK_DISPUTE_GAME)); + assertEq(_proxy.gameCreator(), _proposer); + assertEq(_proxy.l1Head().raw(), blockhash(block.number - 1)); + assertEq(_proxy.parentIndex(), type(uint32).max); + assertEq(_proxy.absolutePrestate(), _params.absolutePrestate); + assertEq(address(_proxy.verifier()), address(zkVerifier)); + assertEq(_proxy.maxChallengeDuration().raw(), Duration.unwrap(_params.maxChallengeDuration)); + assertEq(_proxy.maxProveDuration().raw(), Duration.unwrap(_params.maxProveDuration)); + assertEq(_proxy.challengerBond(), _params.challengerBond); + assertEq(address(_proxy.anchorStateRegistry()), address(anchorStateRegistry)); + assertEq(address(_proxy.weth()), address(delayedWeth)); + assertEq(_proxy.l2ChainId(), l2ChainId); + + // Bond is held by DelayedWETH, not the game proxy itself. + assertEq(address(_proxy).balance, 0); + assertEq(_proxy.totalBonds(), _params.challengerBond); + + // Game was created while its game type was the respected one. + assertTrue(_proxy.wasRespectedGameTypeWhenCreated()); + } +} + +/// @title DisputeGameFactory_Create_ZkDisputeGame_Test +/// @notice Tests the `create` function of the `DisputeGameFactory` contract with ZKDisputeGame. +contract DisputeGameFactory_Create_ZkDisputeGame_Test is DisputeGameFactory_ZkDisputeGame_TestInit { + /// @notice Tests that the factory creates a ZKDisputeGame CWIA clone correctly. + function testFuzz_create_succeeds( + bytes32 _absolutePrestate, + uint64 _maxChallengeDuration, + uint64 _maxProveDuration, + uint256 _challengerBond + ) + public + { + vm.assume(_challengerBond > 0); + + ZKDisputeGameParams memory params = ZKDisputeGameParams({ + maxChallengeDuration: Duration.wrap(_maxChallengeDuration), + maxProveDuration: Duration.wrap(_maxProveDuration), + absolutePrestate: _absolutePrestate, + challengerBond: _challengerBond + }); + + ZKDisputeGame proxy = _createZKGameWithParams(params); + _assertZKGameFactoryStorage(proxy); + _assertZKGameCWIA(proxy, proposer, params); + } + + /// @notice Tests that creating a ZKDisputeGame with any incorrect bond reverts. + /// Fuzzes both the required bond and the sent amount, covering underpayment, + /// overpayment, and zero-value scenarios via `_wrongBond != _challengerBond`. + function testFuzz_create_wrongBond_reverts(uint256 _challengerBond, uint256 _wrongBond) public { + vm.assume(_challengerBond > 0); + vm.assume(_wrongBond != _challengerBond); + + setupZKDisputeGame( + ZKDisputeGameParams({ + maxChallengeDuration: Duration.wrap(3.5 days), + maxProveDuration: Duration.wrap(12 hours), + absolutePrestate: keccak256("absolutePrestate"), + challengerBond: _challengerBond + }) + ); + + (Claim rootClaim_, bytes memory extraData_) = _zkCreateParams(); + + vm.deal(proposer, _wrongBond); + vm.prank(proposer); + vm.expectRevert(IncorrectBondAmount.selector); + disputeGameFactory.create{ value: _wrongBond }(GameTypes.ZK_DISPUTE_GAME, rootClaim_, extraData_); + } + + /// @notice Tests that creating a ZKDisputeGame without a registered implementation reverts. + function test_create_noImpl_reverts() public { + (Claim rootClaim_, bytes memory extraData_) = _zkCreateParams(); + + // ZK_DISPUTE_GAME implementation is not registered — factory must revert. + vm.expectRevert(abi.encodeWithSelector(NoImplementation.selector, GameTypes.ZK_DISPUTE_GAME)); + disputeGameFactory.create{ value: 1 ether }(GameTypes.ZK_DISPUTE_GAME, rootClaim_, extraData_); + } + + /// @notice Tests that creating a duplicate ZKDisputeGame (same UUID) reverts. + function test_create_duplicateUUID_reverts() public { + ZKDisputeGame proxy = _createZKGameWithParams(defaultZKParams); + + vm.deal(proposer, 1 ether); + + // Cache args before applying prank — Solidity evaluates arguments before the call, + // so any external call in the argument list would consume the prank prematurely. + Claim rc = proxy.rootClaim(); + bytes memory ed = proxy.extraData(); + Hash uuid = disputeGameFactory.getGameUUID(GameTypes.ZK_DISPUTE_GAME, rc, ed); + + vm.expectRevert(abi.encodeWithSelector(GameAlreadyExists.selector, uuid)); + vm.prank(proposer); + disputeGameFactory.create{ value: 1 ether }(GameTypes.ZK_DISPUTE_GAME, rc, ed); + } +} + +/// @title DisputeGameFactory_SetImplementation_ZkDisputeGame_Test +/// @notice Tests the `setImplementation` function with ZKDisputeGame. +contract DisputeGameFactory_SetImplementation_ZkDisputeGame_Test is DisputeGameFactory_ZkDisputeGame_TestInit { + /// @notice Tests that setImplementation correctly stores the ZK game implementation and + /// its CWIA gameArgs with fuzzed parameters. + function testFuzz_setImplementation_succeeds( + bytes32 _absolutePrestate, + uint64 _maxChallengeDuration, + uint64 _maxProveDuration, + uint256 _challengerBond + ) + public + { + address zkImpl = address(new ZKDisputeGame()); + IZKVerifier _zkVerifier = IZKVerifier(address(new ZKMockVerifier())); + + bytes memory args = abi.encodePacked( + _absolutePrestate, + _zkVerifier, + _maxChallengeDuration, + _maxProveDuration, + _challengerBond, + anchorStateRegistry, + delayedWeth, + uint256(l2ChainId) + ); + + vm.expectEmit(true, true, true, true, address(disputeGameFactory)); + emit ImplementationSet(zkImpl, GameTypes.ZK_DISPUTE_GAME); + vm.expectEmit(true, true, true, true, address(disputeGameFactory)); + emit ImplementationArgsSet(GameTypes.ZK_DISPUTE_GAME, args); + + vm.prank(disputeGameFactory.owner()); + disputeGameFactory.setImplementation(GameTypes.ZK_DISPUTE_GAME, IDisputeGame(zkImpl), args); + + assertEq(address(disputeGameFactory.gameImpls(GameTypes.ZK_DISPUTE_GAME)), zkImpl); + assertEq(disputeGameFactory.gameArgs(GameTypes.ZK_DISPUTE_GAME), args); + } + + /// @notice Tests that setImplementation reverts when called by a non-owner. + function testFuzz_setImplementation_notOwner_reverts(address _caller) public { + vm.assume(_caller != disputeGameFactory.owner()); + + address zkImpl = address(new ZKDisputeGame()); + bytes memory args = abi.encodePacked(keccak256("absolutePrestate")); + + vm.prank(_caller); + vm.expectRevert("Ownable: caller is not the owner"); + disputeGameFactory.setImplementation(GameTypes.ZK_DISPUTE_GAME, IDisputeGame(zkImpl), args); + } +} + +/// @title DisputeGameFactory_SetInitBond_ZkDisputeGame_Test +/// @notice Tests the `setInitBond` function with ZKDisputeGame. +contract DisputeGameFactory_SetInitBond_ZkDisputeGame_Test is DisputeGameFactory_ZkDisputeGame_TestInit { + /// @notice Tests that setInitBond properly sets and updates the bond for ZK_DISPUTE_GAME. + function testFuzz_setInitBond_succeeds(uint256 _bond1, uint256 _bond2) public { + vm.expectEmit(true, true, true, true, address(disputeGameFactory)); + emit InitBondUpdated(GameTypes.ZK_DISPUTE_GAME, _bond1); + + disputeGameFactory.setInitBond(GameTypes.ZK_DISPUTE_GAME, _bond1); + assertEq(disputeGameFactory.initBonds(GameTypes.ZK_DISPUTE_GAME), _bond1); + + vm.expectEmit(true, true, true, true, address(disputeGameFactory)); + emit InitBondUpdated(GameTypes.ZK_DISPUTE_GAME, _bond2); + + disputeGameFactory.setInitBond(GameTypes.ZK_DISPUTE_GAME, _bond2); + assertEq(disputeGameFactory.initBonds(GameTypes.ZK_DISPUTE_GAME), _bond2); + } + + /// @notice Tests that setInitBond reverts when called by a non-owner. + function testFuzz_setInitBond_notOwner_reverts(address _caller, uint256 _bond) public { + vm.assume(_caller != disputeGameFactory.owner()); + + vm.prank(_caller); + vm.expectRevert("Ownable: caller is not the owner"); + disputeGameFactory.setInitBond(GameTypes.ZK_DISPUTE_GAME, _bond); + } +} + +/// @title DisputeGameFactory_FindLatestGames_ZkDisputeGame_Test +/// @notice Tests the `findLatestGames` function with ZKDisputeGame. +contract DisputeGameFactory_FindLatestGames_ZkDisputeGame_Test is DisputeGameFactory_ZkDisputeGame_TestInit { + /// @notice Tests that findLatestGames correctly returns ZKDisputeGame entries. + function test_findLatestGames_succeeds() public { + // Setup ZK game and create two games with different sequence numbers. + setupZKDisputeGame(defaultZKParams); + + (, uint256 anchorL2SeqNum) = anchorStateRegistry.getAnchorRoot(); + vm.deal(proposer, 10 ether); + + Claim rootClaim1 = changeClaimStatus(Claim.wrap(keccak256("zkRoot1")), VMStatuses.INVALID); + Claim rootClaim2 = changeClaimStatus(Claim.wrap(keccak256("zkRoot2")), VMStatuses.INVALID); + + vm.startPrank(proposer); + IDisputeGame game1 = disputeGameFactory.create{ value: 1 ether }( + GameTypes.ZK_DISPUTE_GAME, rootClaim1, abi.encodePacked(anchorL2SeqNum + 1000, type(uint32).max) + ); + IDisputeGame game2 = disputeGameFactory.create{ value: 1 ether }( + GameTypes.ZK_DISPUTE_GAME, rootClaim2, abi.encodePacked(anchorL2SeqNum + 2000, type(uint32).max) + ); + vm.stopPrank(); + + uint256 latestIdx = disputeGameFactory.gameCount() - 1; + + // Find the 2 most recent ZK games. + IDisputeGameFactory.GameSearchResult[] memory results = + disputeGameFactory.findLatestGames(GameTypes.ZK_DISPUTE_GAME, latestIdx, 2); + + assertEq(results.length, 2); + assertEq(results[0].rootClaim.raw(), rootClaim2.raw()); + assertEq(results[1].rootClaim.raw(), rootClaim1.raw()); + + // Verify the game addresses match. + (,, address g2) = results[0].metadata.unpack(); + (,, address g1) = results[1].metadata.unpack(); + assertEq(g2, address(game2)); + assertEq(g1, address(game1)); + } +} + /// @title DisputeGameFactory_Uncategorized_Test /// @notice General tests that are not testing any function directly of the `DisputeGameFactory` /// contract or are testing multiple functions at once. diff --git a/packages/contracts-bedrock/test/dispute/zk/ZKDisputeGame.t.sol b/packages/contracts-bedrock/test/dispute/zk/ZKDisputeGame.t.sol index 0c012373b9d..d238d2fbeaf 100644 --- a/packages/contracts-bedrock/test/dispute/zk/ZKDisputeGame.t.sol +++ b/packages/contracts-bedrock/test/dispute/zk/ZKDisputeGame.t.sol @@ -78,7 +78,6 @@ abstract contract ZKDisputeGame_TestInit is DisputeGameFactory_TestInit { function setUp() public virtual override { super.setUp(); skipIfDevFeatureDisabled(DevFeatures.ZK_DISPUTE_GAME); - skipIfForkTest("Skip not supported yet"); // Get anchor state to calculate valid sequence numbers (, anchorL2SequenceNumber) = anchorStateRegistry.getAnchorRoot(); diff --git a/packages/contracts-bedrock/test/kontrol/README.md b/packages/contracts-bedrock/test/kontrol/README.md index a22e918bba3..619d0205ede 100644 --- a/packages/contracts-bedrock/test/kontrol/README.md +++ b/packages/contracts-bedrock/test/kontrol/README.md @@ -35,14 +35,12 @@ The directory is structured as follows
 ├── pausability-lemmas.md: File containing the necessary lemmas for this project
-├── deployment: Custom deploy sequence for Kontrol proofs and tests for its fast summarization
-│   ├── KontrolDeployment.sol: Deployment sequence for Kontrol proofs
 ├── proofs: Where the proofs (tests) themselves live
 │   ├── *.k.sol: Symbolic property tests for contracts
 │   └── utils: Proof dependencies, including the autogenerated deployment summary contracts
 └── scripts: Where the scripts of the projects live
-    ├── json: Data cleaning scripts for the output of KontrolDeployment.sol
-    ├── make-summary-deployment.sh: Executes KontrolDeployment.sol, curates the result and writes the summary deployment contract
+    ├── json: Data cleaning scripts for the output of KontrolDeployment.sol
+    ├── make-summary-deployment.sh: Executes KontrolDeployment.sol, curates the result and writes the summary deployment contract
     └── run-kontrol.sh: Wrapper around the kontrol CLI to run the proofs
 
@@ -58,7 +56,7 @@ Verifying proofs has two steps: build, and execute. ### Build Deployment Summary -First, generate a deployment summary contract from the deploy script in [`KontrolDeployment.sol`](./deployment/KontrolDeployment.sol) by running the following command: +First, generate a deployment summary contract from the deploy script in `KontrolDeployment.sol` by running the following command: ``` ./test/kontrol/scripts/make-summary-deployment.sh [container|local|dev] @@ -69,7 +67,7 @@ First, generate a deployment summary contract from the deploy script in [`Kontro The [`make-summary-deployment.sh`](./scripts/make-summary-deployment.sh) supports the same execution modes as `run-kontrol.sh` below. -[`KontrolDeployment.sol`](./deployment/KontrolDeployment.sol) contains the deployment sequence required by the proofs. +`KontrolDeployment.sol` contains the deployment sequence required by the proofs. The [`make-summary-deployment.sh`](./scripts/make-summary-deployment.sh) script will generate a JSON state diff. This state diff is used in two ways by Kontrol. First, Kontrol generates a summary contract recreating the state diffs recorded in the JSON. This contract is used to test that the information contained in the generated JSON is correct and aids in the specification of the symbolic property tests. The generation of this contract is also handled by the `make-summary-deployment.sh` script. Second, the state diff JSON is used to load the post-deployment state directly into Kontrol when running the proofs. @@ -77,7 +75,7 @@ Second, the state diff JSON is used to load the post-deployment state directly i This step is optional if an up-to-date summary contract already exists, which will be the case until the `KontrolDeployment` contract changes, or any of the source contracts under test change. See the [Implementation Details](#implementation-details) section for more information, and to learn about the CI check that ensures the committed autogenerated files from this step are up-to-date. -The summary contract can be found in [`DeploymentSummary.sol`](./proofs/utils/DeploymentSummary.sol), which is summarization (state changes) of the [`KontrolDeployment.sol`](./deployment/KontrolDeployment.sol) contract. +The summary contract can be found in [`DeploymentSummary.sol`](./scripts/DeploymentSummary.sol), which is summarization (state changes) of the `KontrolDeployment.sol` contract. ### Execute Proofs @@ -104,11 +102,11 @@ For a similar description of the options run `run-kontrol.sh --help`. ### Add New Proofs -These are the instructions to add a new proof to this project. If all functions involved in the new proof are from a contract already deployed by [`KontrolDeployment`](./deployment/KontrolDeployment.sol) the first two steps can be skipped. +These are the instructions to add a new proof to this project. If all functions involved in the new proof are from a contract already deployed by `KontrolDeployment` the first two steps can be skipped. #### Make Kontrol aware of the new contract being tested -The `runKontrolDeployment` function of [`KontrolDeployment`](./deployment/KontrolDeployment.sol) reproduces the deployment process laid out in the `_run` function of [`Deploy.s.sol`](../../scripts/deploy/Deploy.s.sol). `runKontrolDeployment` has the `stateDiff` modifier to make use of [Foundry's state diff cheatcodes](https://book.getfoundry.sh/cheatcodes/start-state-diff-recording). Kontrol utilizes the JSON resulting from this modifier for two purposes: +The `runKontrolDeployment` function of `KontrolDeployment` reproduces the deployment process laid out in the `_run` function of [`Deploy.s.sol`](../../scripts/deploy/Deploy.s.sol). `runKontrolDeployment` has the `stateDiff` modifier to make use of [Foundry's state diff cheatcodes](https://book.getfoundry.sh/cheatcodes/start-state-diff-recording). Kontrol utilizes the JSON resulting from this modifier for two purposes: 1. Load all the state updates generated by `runKontrolDeployment` as the initial configuration for all proofs, effectively offloading the computation of the deployment process to `forge` and thus improving performance. 2. Produce the [`DeploymentSummary`](./proofs/utils/DeploymentSummary.sol) script contract to test that the produced JSON contains correct updates. @@ -149,7 +147,7 @@ As described in [Execute Proofs](#execute-proofs), there's a `script` mode for s 1. A critical invariant of the `KontrolDeployment.sol` contract is that it stays in sync with the original `Deploy.s.sol` contract. A more rigorous approach may be to leverage the `ChainAssertions` library, but more investigation is required to determine if this is feasible without large changes to the deploy script. -2. Size of `bytes[]` arguments. In [`OptimismPortal.k.sol`](./proofs/OptimismPortal.k.sol), the `prove_proveWithdrawalTransaction_paused` proof is broken down into 11 different proofs, each corresponding to a different size of the `_withdrawalProof` argument, which is of type `bytes[]`. We execute the same logic for lengths of `_withdrawalProof` ranging from 0 to 10, setting the length of each symbolic `bytes` element to 600. +2. Size of `bytes[]` arguments. In [`OptimismPortal2.k.sol`](./proofs/OptimismPortal2.k.sol), the `prove_proveWithdrawalTransaction_paused` proof is broken down into 11 different proofs, each corresponding to a different size of the `_withdrawalProof` argument, which is of type `bytes[]`. We execute the same logic for lengths of `_withdrawalProof` ranging from 0 to 10, setting the length of each symbolic `bytes` element to 600. - The reason for a max length of 10 is that it provides a conservative upper bound based on [historical data](https://dune.com/queries/3433954/5768623) for proof sizes. - The reason for choosing 600 as the length for the elements of `_withdrawalProof` is that each element is `17 * 32 = 544` bytes long, so adding a 10% margin for RLP encoding and rounding up yields 600 bytes. The same historical data confirms this is a valid bound. - All other symbolic `bytes` arguments that are not part of a `bytes` array have a symbolic length bounded by `2^63`. @@ -165,8 +163,8 @@ Therefore we want to minimize the amount of code executed in Kontrol, and the fa This project uses two different [`foundry.toml` profiles](../../foundry.toml), `kdeploy` and `kprove`, to facilitate usage of this fast summarization feature.: -- `kdeploy`: Used by [`make-summary-deployment.sh`](./scripts/make-summary-deployment.sh) to generate the [`DeploymentSummary.sol`](./proofs/utils/DeploymentSummary.sol) contract based on execution of the [`KontrolDeployment.sol`](./deployment/KontrolDeployment.sol) contract using Foundry's state diff recording cheatcodes. - This is where all necessary [`src/L1`](../../src/L1) files are compiled with their bytecode saved into the [`DeploymentSummaryCode.sol`](./proofs/utils/DeploymentSummaryCode.sol) file, which is inherited by `DeploymentSummary`. +- `kdeploy`: Used by [`make-summary-deployment.sh`](./scripts/make-summary-deployment.sh) to generate the [`DeploymentSummary.sol`](./scripts/DeploymentSummary.sol) contract based on execution of the `KontrolDeployment.sol` contract using Foundry's state diff recording cheatcodes. + This is where all necessary [`src/L1`](../../src/L1) files are compiled with their bytecode saved into the [`DeploymentSummaryCode.sol`](./scripts/DeploymentSummaryCode.sol) file, which is inherited by `DeploymentSummary`. - `kprove`: Used by the [`run-kontrol.sh`](./scripts/run-kontrol.sh) script to execute proofs, which can be run once a `DeploymentSummary.sol` contract is present. This profile's `src` and `script` paths point to a test folder because we only want to compile what is in the `test/kontrol/proofs` folder, since that folder contains all bytecode and proofs. @@ -219,7 +217,7 @@ Method 1: GitHub's `gh` CLI tool Method 2: [Github API](https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28) List the artifacts for a run: -- GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts -- See [documentation](httpshttps://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts) for more details +- GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts -- See [documentation](https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts) for more details ```bash curl -L \ -H "Accept: application/vnd.github+json" \ diff --git a/packages/contracts-bedrock/test/libraries/L2ContractsManagerUtils.t.sol b/packages/contracts-bedrock/test/libraries/L2ContractsManagerUtils.t.sol index 3f71a52fd94..bdbf8fe98c1 100644 --- a/packages/contracts-bedrock/test/libraries/L2ContractsManagerUtils.t.sol +++ b/packages/contracts-bedrock/test/libraries/L2ContractsManagerUtils.t.sol @@ -360,4 +360,45 @@ contract L2ContractsManagerUtils_UpgradeToAndCall_Test is CommonTest { 0 ); } + + /// @notice Tests that upgradeTo reverts when the implementation address has no code. + function test_upgradeTo_emptyImplementation_reverts() public { + address empty = makeAddr("empty"); + vm.expectRevert( + abi.encodeWithSelector(L2ContractsManagerUtils.L2ContractsManager_EmptyImplementation.selector, empty) + ); + this._callUpgradeTo(Predeploys.L2_CROSS_DOMAIN_MESSENGER, empty); + } + + /// @notice Tests that upgradeToAndCall reverts when the implementation address has no code. + function test_upgradeToAndCall_emptyImplementation_reverts() public { + address empty = makeAddr("empty"); + vm.expectRevert( + abi.encodeWithSelector(L2ContractsManagerUtils.L2ContractsManager_EmptyImplementation.selector, empty) + ); + this._callUpgradeToAndCall( + Predeploys.L2_CROSS_DOMAIN_MESSENGER, + empty, + _storageSetterImpl, + abi.encodeCall(L2ContractsManagerUtils_ImplV2_Harness.initialize, ()), + INITIALIZABLE_SLOT_OZ_V4, + 0 + ); + } + + /// @notice Tests that upgradeToAndCall reverts when the storage setter address has no code. + function test_upgradeToAndCall_emptyStorageSetter_reverts() public { + address empty = makeAddr("empty"); + vm.expectRevert( + abi.encodeWithSelector(L2ContractsManagerUtils.L2ContractsManager_EmptyImplementation.selector, empty) + ); + this._callUpgradeToAndCall( + Predeploys.L2_CROSS_DOMAIN_MESSENGER, + implV2, + empty, + abi.encodeCall(L2ContractsManagerUtils_ImplV2_Harness.initialize, ()), + INITIALIZABLE_SLOT_OZ_V4, + 0 + ); + } } diff --git a/packages/contracts-bedrock/test/libraries/NetworkUpgradeTxns.t.sol b/packages/contracts-bedrock/test/libraries/NetworkUpgradeTxns.t.sol index 2ead1c2e3f5..b65d628b8ed 100644 --- a/packages/contracts-bedrock/test/libraries/NetworkUpgradeTxns.t.sol +++ b/packages/contracts-bedrock/test/libraries/NetworkUpgradeTxns.t.sol @@ -127,6 +127,11 @@ contract NetworkUpgradeTxns_SerializeTxn_Test is NetworkUpgradeTxns_TestInit { /// @title NetworkUpgradeTxns_WriteArtifact_Test /// @notice Tests the `writeArtifact` function. contract NetworkUpgradeTxns_WriteArtifact_Test is NetworkUpgradeTxns_TestInit { + /// @notice External wrapper so vm.expectRevert can catch the revert from the internal library call. + function _callReadArtifact(string memory _path) external view { + NetworkUpgradeTxns.readArtifact(_path); + } + /// @notice Test writeArtifact with empty array function test_writeArtifact_emptyArray_succeeds() public { NetworkUpgradeTxns.NetworkUpgradeTxn[] memory txns = new NetworkUpgradeTxns.NetworkUpgradeTxn[](0); @@ -135,6 +140,16 @@ contract NetworkUpgradeTxns_WriteArtifact_Test is NetworkUpgradeTxns_TestInit { NetworkUpgradeTxns.writeArtifact(txns, metadata, outputPath); } + /// @notice Test that readArtifact reverts when the bundle version is not BUNDLE_VERSION. + function test_readArtifact_unsupportedVersion_reverts() public { + string memory path = "deployments/nut-test-bad-version.json"; + NetworkUpgradeTxns.NetworkUpgradeTxn[] memory txns = new NetworkUpgradeTxns.NetworkUpgradeTxn[](0); + NetworkUpgradeTxns.writeArtifact(txns, NetworkUpgradeTxns.BundleMetadata({ version: "2.0.0" }), path); + + vm.expectRevert("NetworkUpgradeTxns: unsupported bundle version: 2.0.0"); + this._callReadArtifact(path); + } + /// @notice Test writeArtifact creates valid JSON file function test_writeArtifact_succeeds() public { NetworkUpgradeTxns.NetworkUpgradeTxn[] memory txns = new NetworkUpgradeTxns.NetworkUpgradeTxn[](2); diff --git a/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol b/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol index 35b0ef9ee8e..7962d5f5f16 100644 --- a/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol +++ b/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol @@ -166,44 +166,6 @@ library SafeTestLib { return sortedPKs; } - /// @dev Sign a transaction as a safe owner with a private key. - function signTransaction( - SafeInstance memory instance, - uint256 pk, - address to, - uint256 value, - bytes memory data, - Enum.Operation operation, - uint256 safeTxGas, - uint256 baseGas, - uint256 gasPrice, - address gasToken, - address refundReceiver - ) - internal - view - returns (uint8 v, bytes32 r, bytes32 s) - { - bytes32 txDataHash; - { - uint256 _nonce = instance.safe.nonce(); - txDataHash = instance.safe.getTransactionHash({ - to: to, - value: value, - data: data, - operation: operation, - safeTxGas: safeTxGas, - baseGas: baseGas, - gasPrice: gasPrice, - gasToken: gasToken, - refundReceiver: refundReceiver, - _nonce: _nonce - }); - } - - (v, r, s) = Vm(VM_ADDR).sign(pk, txDataHash); - } - /// @dev Get the previous owner in the linked list of owners. /// This version of getPrevOwner will call to the Safe contract to get the current list of owners. /// Note that this will break vm.expectRevert() tests by making a call which does not revert.. @@ -350,12 +312,6 @@ library SafeTestLib { EIP1271Sign(instance, abi.encodePacked(digest)); } - /// @dev Increments the nonce of the Safe by sending an empty transaction. - function incrementNonce(SafeInstance memory instance) internal returns (uint256 newNonce) { - execTransaction(instance, address(0), 0, "", Enum.Operation.Call, 0, 0, 0, address(0), address(0), ""); - return instance.safe.nonce(); - } - /// @dev Adds a new owner to the safe function changeThreshold(SafeInstance memory instance, uint256 threshold) internal { execTransaction(instance, address(instance.safe), 0, abi.encodeCall(OwnerManager.changeThreshold, (threshold))); @@ -495,13 +451,6 @@ contract SafeTestTools { uint256 internal saltNonce = uint256(keccak256(bytes("SAFE TEST"))); - /// @dev can be called to reinitialize the singleton, proxyFactory and handler. Useful for forking. - function _initializeSafeTools() internal { - singleton = new GnosisSafe(); - proxyFactory = new GnosisSafeProxyFactory(); - handler = new CompatibilityFallbackHandler(); - } - /// @dev Sets up a Safe with the given parameters. /// @param ownerPKs The public keys of the owners. /// @param threshold The threshold for the Safe. diff --git a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol index b01aca6fd63..7c57d643687 100644 --- a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol @@ -486,4 +486,20 @@ contract L2Genesis_Run_Test is L2Genesis_TestInit { testForks(); testFeeSplitter(); } + + /// @notice Tests that run reverts when useInterop is true but the OPTIMISM_PORTAL_INTEROP dev bit is not set. + function test_run_useInteropWithoutDevBit_reverts() external { + input.useInterop = true; + // devFeatureBitmap left at 0 — OPTIMISM_PORTAL_INTEROP bit not set + vm.expectRevert("L2Genesis: useInterop and OPTIMISM_PORTAL_INTEROP devFeature bit must agree"); + genesis.run(input); + } + + /// @notice Tests that run reverts when the OPTIMISM_PORTAL_INTEROP dev bit is set but useInterop is false. + function test_run_devBitWithoutUseInterop_reverts() external { + input.useInterop = false; + input.devFeatureBitmap = bytes32(DevFeatures.OPTIMISM_PORTAL_INTEROP); + vm.expectRevert("L2Genesis: useInterop and OPTIMISM_PORTAL_INTEROP devFeature bit must agree"); + genesis.run(input); + } } diff --git a/packages/contracts-bedrock/test/setup/CommonTest.sol b/packages/contracts-bedrock/test/setup/CommonTest.sol index 8fca5316f5a..612b48b677c 100644 --- a/packages/contracts-bedrock/test/setup/CommonTest.sol +++ b/packages/contracts-bedrock/test/setup/CommonTest.sol @@ -28,8 +28,6 @@ abstract contract CommonTest is Test, Setup, Events { address alice; address bob; - bytes32 constant nonZeroHash = keccak256(abi.encode("NON_ZERO")); - /// @notice The default initial bond value for dispute games. uint256 constant DEFAULT_DISPUTE_GAME_INIT_BOND = 0.08 ether; @@ -55,7 +53,6 @@ abstract contract CommonTest is Test, Setup, Events { IOptimismMintableERC20Full L2Token; ILegacyMintableERC20Full LegacyL2Token; ERC20 NativeL2Token; - IOptimismMintableERC20Full RemoteL1Token; function setUp() public virtual override { // Setup.setup() may switch the tests over to a newly forked network. Therefore @@ -189,14 +186,6 @@ abstract contract CommonTest is Test, Setup, Events { NativeL2Token = new ERC20("Native L2 Token", "L2T"); - RemoteL1Token = IOptimismMintableERC20Full( - l1OptimismMintableERC20Factory.createStandardL2Token( - address(NativeL2Token), - string(abi.encodePacked("L1-", NativeL2Token.name())), - string(abi.encodePacked("L1-", NativeL2Token.symbol())) - ) - ); - BadL1Token = ERC20( l1OptimismMintableERC20Factory.createStandardL2Token( address(1), diff --git a/packages/contracts-bedrock/test/setup/DisputeGames.sol b/packages/contracts-bedrock/test/setup/DisputeGames.sol index 4a7bcf13415..74a11a9f006 100644 --- a/packages/contracts-bedrock/test/setup/DisputeGames.sol +++ b/packages/contracts-bedrock/test/setup/DisputeGames.sol @@ -68,11 +68,6 @@ library DisputeGames { return address(gameProxy); } - function isGamePermissioned(GameType _gameType) internal pure returns (bool) { - return _gameType.raw() == GameTypes.PERMISSIONED_CANNON.raw() - || _gameType.raw() == GameTypes.SUPER_PERMISSIONED_CANNON.raw(); - } - /// @notice Checks if the game type is a super game type function isSuperGame(GameType _gameType) internal pure returns (bool) { return GameTypes.isSuperGame(_gameType); @@ -125,35 +120,6 @@ library DisputeGames { } } - /// @notice Gets the absolute prestate for a game type, handling both v1 and v2 dispute games. - /// V1 games store the prestate on the game implementation, v2 games store it in gameArgs. - /// Returns Claim.wrap(bytes32(0)) if no implementation exists for the game type. - /// @param _dgf The dispute game factory. - /// @param _gameType The game type to get the prestate for. - /// @return prestate_ The absolute prestate claim. - function getGameImplPrestate( - IDisputeGameFactory _dgf, - GameType _gameType - ) - internal - view - returns (Claim prestate_) - { - // Return zero if no implementation exists for this game type - address gameImpl = address(_dgf.gameImpls(_gameType)); - if (gameImpl == address(0)) { - return Claim.wrap(bytes32(0)); - } - - (bool gameArgsExist, bytes memory gameArgsData) = _getGameArgs(_dgf, _gameType); - if (gameArgsExist) { - LibGameArgs.GameArgs memory gameArgs = LibGameArgs.decode(gameArgsData); - prestate_ = Claim.wrap(gameArgs.absolutePrestate); - } else { - prestate_ = IFaultDisputeGame(gameImpl).absolutePrestate(); - } - } - /// @notice Gets the DelayedWETH for a game type, handling both v1 and v2 dispute games. /// V1 games store the prestate on the game implementation, v2 games store it in gameArgs. /// Returns address(0) if no implementation exists for the game type. diff --git a/packages/contracts-bedrock/test/setup/Events.sol b/packages/contracts-bedrock/test/setup/Events.sol index 7a87a29dc27..9c5922fce3c 100644 --- a/packages/contracts-bedrock/test/setup/Events.sol +++ b/packages/contracts-bedrock/test/setup/Events.sol @@ -46,14 +46,6 @@ abstract contract Events { bool isCreation, bytes data ); - event WhatHappened(bool success, bytes returndata); - - event OutputProposed( - bytes32 indexed outputRoot, uint256 indexed l2OutputIndex, uint256 indexed l2BlockNumber, uint256 l1Timestamp - ); - - event OutputsDeleted(uint256 indexed prevNextOutputIndex, uint256 indexed newNextOutputIndex); - event Withdrawal(uint256 value, address to, address from); event Withdrawal(uint256 value, address to, address from, Types.WithdrawalNetwork withdrawalNetwork); @@ -77,10 +69,6 @@ abstract contract Events { address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes data ); - event DepositFailed( - address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes data - ); - event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes data); event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes data); diff --git a/packages/contracts-bedrock/test/setup/FFIInterface.sol b/packages/contracts-bedrock/test/setup/FFIInterface.sol index cb516716d9a..2345757be42 100644 --- a/packages/contracts-bedrock/test/setup/FFIInterface.sol +++ b/packages/contracts-bedrock/test/setup/FFIInterface.sol @@ -293,52 +293,6 @@ contract FFIInterface { return (memRoot, proof); } - function getCannonMemoryProof2( - uint32 pc, - uint32 insn, - uint32 memAddr, - uint32 memVal, - uint32 memAddrForProof - ) - external - returns (bytes32, bytes memory) - { - string[] memory cmds = new string[](8); - cmds[0] = "scripts/go-ffi/go-ffi"; - cmds[1] = "diff"; - cmds[2] = "cannonMemoryProof2"; - cmds[3] = vm.toString(pc); - cmds[4] = vm.toString(insn); - cmds[5] = vm.toString(memAddr); - cmds[6] = vm.toString(memVal); - cmds[7] = vm.toString(memAddrForProof); - bytes memory result = Process.run(cmds); - (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); - return (memRoot, proof); - } - - function getCannonMemoryProofWrongLeaf( - uint32 pc, - uint32 insn, - uint32 memAddr, - uint32 memVal - ) - external - returns (bytes32, bytes memory) - { - string[] memory cmds = new string[](7); - cmds[0] = "scripts/go-ffi/go-ffi"; - cmds[1] = "diff"; - cmds[2] = "cannonMemoryProofWrongLeaf"; - cmds[3] = vm.toString(pc); - cmds[4] = vm.toString(insn); - cmds[5] = vm.toString(memAddr); - cmds[6] = vm.toString(memVal); - bytes memory result = Process.run(cmds); - (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); - return (memRoot, proof); - } - function getCannonMemory64Proof(uint64 addr, uint64 value) external returns (bytes32, bytes memory) { string[] memory cmds = new string[](5); cmds[0] = "scripts/go-ffi/go-ffi"; diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index 256b9d97a4e..ed6754d15cc 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -26,8 +26,9 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; import { Chains } from "scripts/libraries/Chains.sol"; -// Interfaces +import { DevFeatures } from "src/libraries/DevFeatures.sol"; +// Interfaces import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; @@ -117,7 +118,6 @@ abstract contract Setup is FeatureFlags { IFaultDisputeGame faultDisputeGame; IDelayedWETH delayedWeth; IPermissionedDisputeGame permissionedDisputeGame; - IDelayedWETH delayedWETHPermissionedGameProxy; // L1 contracts - core address proxyAdminOwner; @@ -226,6 +226,11 @@ abstract contract Setup is FeatureFlags { resolveFeaturesFromEnv(); deploy.cfg().setDevFeatureBitmap(devFeatureBitmap); + // If the OPTIMISM_PORTAL_INTEROP dev feature is enabled, set the useInterop flag to true + if (isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { + deploy.cfg().setUseInterop(true); + } + console.log("Setup: L1 setup done!"); // Skip L2 genesis for both L1 and L2 fork tests diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index e135a75dc90..c4fee59eed4 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -15,6 +15,7 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { DevFeatures } from "src/libraries/DevFeatures.sol"; // Interfaces +import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; @@ -22,7 +23,6 @@ import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.so import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { ProtocolVersion } from "interfaces/L1/IProtocolVersions.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOptimismPortalInterop } from "interfaces/L1/IOptimismPortalInterop.sol"; /// @title Initializer_Test /// @dev Ensures that the `initialize()` function on contracts cannot be called more than @@ -123,14 +123,12 @@ contract Initializer_Test is CommonTest { ); if (isDevFeatureEnabled(DevFeatures.OPTIMISM_PORTAL_INTEROP)) { - // OptimismPortal2Impl contracts.push( InitializeableContract({ name: "OptimismPortal2Impl", target: EIP1967Helper.getImplementation(address(optimismPortal2)), initCalldata: abi.encodeCall( - IOptimismPortalInterop(payable(optimismPortal2)).initialize, - (systemConfig, anchorStateRegistry, ethLockbox) + optimismPortal2.initialize, (systemConfig, anchorStateRegistry, ethLockbox) ) }) ); @@ -140,18 +138,18 @@ contract Initializer_Test is CommonTest { name: "OptimismPortal2Proxy", target: address(optimismPortal2), initCalldata: abi.encodeCall( - IOptimismPortalInterop(payable(optimismPortal2)).initialize, - (systemConfig, anchorStateRegistry, ethLockbox) + optimismPortal2.initialize, (systemConfig, anchorStateRegistry, ethLockbox) ) }) ); } else { - // OptimismPortal2Impl contracts.push( InitializeableContract({ name: "OptimismPortal2Impl", target: EIP1967Helper.getImplementation(address(optimismPortal2)), - initCalldata: abi.encodeCall(optimismPortal2.initialize, (systemConfig, anchorStateRegistry)) + initCalldata: abi.encodeCall( + optimismPortal2.initialize, (systemConfig, anchorStateRegistry, IETHLockbox(address(0))) + ) }) ); // OptimismPortal2Proxy @@ -159,7 +157,9 @@ contract Initializer_Test is CommonTest { InitializeableContract({ name: "OptimismPortal2Proxy", target: address(optimismPortal2), - initCalldata: abi.encodeCall(optimismPortal2.initialize, (systemConfig, anchorStateRegistry)) + initCalldata: abi.encodeCall( + optimismPortal2.initialize, (systemConfig, anchorStateRegistry, IETHLockbox(address(0))) + ) }) ); } @@ -383,7 +383,7 @@ contract Initializer_Test is CommonTest { function test_cannotReinitialize_succeeds() public { // Collect exclusions. uint256 j; - string[] memory excludes = new string[](10); + string[] memory excludes = new string[](9); // Contract is currently not being deployed as part of the standard deployment script. excludes[j++] = "src/L2/OptimismSuperchainERC20.sol"; // Periphery contracts don't get deployed as part of the standard deployment script. @@ -398,8 +398,6 @@ contract Initializer_Test is CommonTest { excludes[j++] = "src/dispute/SuperFaultDisputeGame.sol"; excludes[j++] = "src/dispute/SuperPermissionedDisputeGame.sol"; excludes[j++] = "src/dispute/zk/ZKDisputeGame.sol"; - // TODO: Eventually remove this exclusion. Same reason as above dispute contracts. - excludes[j++] = "src/L1/OptimismPortalInterop.sol"; // L2 contract initialization is tested in Predeploys.t.sol excludes[j++] = "src/L2/*"; excludes[j++] = "src/L1/FeesDepositor.sol"; diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 863fa704717..73ada2844f6 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -279,7 +279,7 @@ dependencies = [ "alloy-sol-types", "auto_impl", "derive_more", - "revm 36.0.0", + "revm", "thiserror 2.0.18", "tracing", ] @@ -391,8 +391,8 @@ dependencies = [ "alloy-primitives", "auto_impl", "op-alloy", - "op-revm 17.0.0", - "revm 36.0.0", + "op-revm", + "revm", "test-case", "thiserror 2.0.18", ] @@ -3641,7 +3641,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "reth-codecs", "reth-db-api", "reth-engine-primitives", @@ -3661,7 +3661,7 @@ dependencies = [ "reth-rpc-api", "reth-rpc-engine-api", "reth-trie-common", - "revm 36.0.0", + "revm", "revm-primitives", "serde", "thiserror 2.0.18", @@ -5510,8 +5510,8 @@ dependencies = [ "lru 0.16.3", "op-alloy-consensus", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", - "revm 36.0.0", + "op-revm", + "revm", "serde", "serde_json", "sha2", @@ -5654,9 +5654,9 @@ dependencies = [ "kona-registry", "op-alloy-consensus", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "rand 0.9.2", - "revm 36.0.0", + "revm", "rocksdb", "rstest", "serde", @@ -5681,7 +5681,7 @@ dependencies = [ "alloy-sol-types", "arbitrary", "derive_more", - "op-revm 17.0.0", + "op-revm", "rand 0.9.2", "serde", "serde_json", @@ -5738,8 +5738,8 @@ dependencies = [ "alloy-primitives", "kona-protocol", "op-alloy-consensus", - "op-revm 15.0.0", - "revm 34.0.0", + "op-revm", + "revm", ] [[package]] @@ -5780,9 +5780,9 @@ dependencies = [ "kona-std-fpvm", "op-alloy-network", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "proptest", - "revm 36.0.0", + "revm", "rocksdb", "serde", "serde_json", @@ -6025,7 +6025,7 @@ dependencies = [ "lru 0.16.3", "op-alloy-consensus", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "rand 0.9.2", "rayon", "rstest", @@ -6060,9 +6060,9 @@ dependencies = [ "kona-registry", "op-alloy-consensus", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "rand 0.9.2", - "revm 36.0.0", + "revm", "serde", "serde_json", "spin 0.10.0", @@ -7739,25 +7739,18 @@ dependencies = [ "tokio", ] -[[package]] -name = "op-revm" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40" -dependencies = [ - "auto_impl", - "revm 34.0.0", -] - [[package]] name = "op-revm" version = "17.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a98f3a512a7e02a1dcf1242b57302d83657b265a665d50ad98d0b158efaf2c" dependencies = [ + "alloy-primitives", + "alloy-sol-types", "auto_impl", - "revm 36.0.0", + "revm", + "rstest", "serde", + "serde_json", + "sha2", ] [[package]] @@ -9056,8 +9049,8 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "reth-basic-payload-builder" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9083,8 +9076,8 @@ dependencies = [ [[package]] name = "reth-chain-state" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9105,8 +9098,8 @@ dependencies = [ "reth-primitives-traits", "reth-storage-api", "reth-trie", - "revm-database 12.0.0", - "revm-state 10.0.0", + "revm-database", + "revm-state", "serde", "tokio", "tokio-stream", @@ -9115,8 +9108,8 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-chains", "alloy-consensus", @@ -9135,8 +9128,8 @@ dependencies = [ [[package]] name = "reth-cli" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-genesis", "clap", @@ -9148,8 +9141,8 @@ dependencies = [ [[package]] name = "reth-cli-commands" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-chains", "alloy-consensus", @@ -9236,8 +9229,8 @@ dependencies = [ [[package]] name = "reth-cli-runner" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "reth-tasks", "tokio", @@ -9246,8 +9239,8 @@ dependencies = [ [[package]] name = "reth-cli-util" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -9299,8 +9292,8 @@ dependencies = [ [[package]] name = "reth-config" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "eyre", "humantime-serde", @@ -9315,8 +9308,8 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9328,8 +9321,8 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9341,8 +9334,8 @@ dependencies = [ [[package]] name = "reth-consensus-debug-client" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9367,8 +9360,8 @@ dependencies = [ [[package]] name = "reth-db" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "derive_more", @@ -9395,8 +9388,8 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9421,8 +9414,8 @@ dependencies = [ [[package]] name = "reth-db-common" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-genesis", @@ -9451,8 +9444,8 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -9466,8 +9459,8 @@ dependencies = [ [[package]] name = "reth-discv4" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9491,8 +9484,8 @@ dependencies = [ [[package]] name = "reth-discv5" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9515,8 +9508,8 @@ dependencies = [ [[package]] name = "reth-dns-discovery" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "dashmap", @@ -9539,8 +9532,8 @@ dependencies = [ [[package]] name = "reth-downloaders" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9574,8 +9567,8 @@ dependencies = [ [[package]] name = "reth-e2e-test-utils" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9620,7 +9613,7 @@ dependencies = [ "reth-tasks", "reth-tokio-util", "reth-tracing", - "revm 36.0.0", + "revm", "serde_json", "tempfile", "tokio", @@ -9631,8 +9624,8 @@ dependencies = [ [[package]] name = "reth-ecies" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "aes", "alloy-primitives", @@ -9659,8 +9652,8 @@ dependencies = [ [[package]] name = "reth-engine-local" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9682,8 +9675,8 @@ dependencies = [ [[package]] name = "reth-engine-primitives" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9707,8 +9700,8 @@ dependencies = [ [[package]] name = "reth-engine-tree" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eip7928", @@ -9753,7 +9746,7 @@ dependencies = [ "reth-trie-db", "reth-trie-parallel", "reth-trie-sparse", - "revm 36.0.0", + "revm", "revm-primitives", "schnellru", "thiserror 2.0.18", @@ -9763,8 +9756,8 @@ dependencies = [ [[package]] name = "reth-engine-util" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-rpc-types-engine", @@ -9791,8 +9784,8 @@ dependencies = [ [[package]] name = "reth-era" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9806,8 +9799,8 @@ dependencies = [ [[package]] name = "reth-era-downloader" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "bytes", @@ -9822,8 +9815,8 @@ dependencies = [ [[package]] name = "reth-era-utils" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9844,8 +9837,8 @@ dependencies = [ [[package]] name = "reth-errors" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "reth-consensus", "reth-execution-errors", @@ -9855,8 +9848,8 @@ dependencies = [ [[package]] name = "reth-eth-wire" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-chains", "alloy-primitives", @@ -9884,8 +9877,8 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-chains", "alloy-consensus", @@ -9908,8 +9901,8 @@ dependencies = [ [[package]] name = "reth-ethereum" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", @@ -9949,8 +9942,8 @@ dependencies = [ [[package]] name = "reth-ethereum-cli" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "clap", "eyre", @@ -9972,8 +9965,8 @@ dependencies = [ [[package]] name = "reth-ethereum-consensus" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9988,8 +9981,8 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -10004,8 +9997,8 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eip2124", "alloy-hardforks", @@ -10018,8 +10011,8 @@ dependencies = [ [[package]] name = "reth-ethereum-payload-builder" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10042,14 +10035,14 @@ dependencies = [ "reth-revm", "reth-storage-api", "reth-transaction-pool", - "revm 36.0.0", + "revm", "tracing", ] [[package]] name = "reth-ethereum-primitives" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10062,8 +10055,8 @@ dependencies = [ [[package]] name = "reth-etl" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "rayon", "reth-db-api", @@ -10072,8 +10065,8 @@ dependencies = [ [[package]] name = "reth-evm" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10091,13 +10084,13 @@ dependencies = [ "reth-storage-api", "reth-storage-errors", "reth-trie-common", - "revm 36.0.0", + "revm", ] [[package]] name = "reth-evm-ethereum" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10111,13 +10104,13 @@ dependencies = [ "reth-execution-types", "reth-primitives-traits", "reth-storage-errors", - "revm 36.0.0", + "revm", ] [[package]] name = "reth-execution-cache" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "fixed-cache", @@ -10134,8 +10127,8 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-evm", "alloy-primitives", @@ -10147,8 +10140,8 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10159,15 +10152,15 @@ dependencies = [ "reth-ethereum-primitives", "reth-primitives-traits", "reth-trie-common", - "revm 36.0.0", + "revm", "serde", "serde_with", ] [[package]] name = "reth-exex" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10204,8 +10197,8 @@ dependencies = [ [[package]] name = "reth-exex-test-utils" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "eyre", @@ -10236,8 +10229,8 @@ dependencies = [ [[package]] name = "reth-exex-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -10250,8 +10243,8 @@ dependencies = [ [[package]] name = "reth-fs-util" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "serde", "serde_json", @@ -10260,8 +10253,8 @@ dependencies = [ [[package]] name = "reth-invalid-block-hooks" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -10279,17 +10272,17 @@ dependencies = [ "reth-rpc-api", "reth-tracing", "reth-trie", - "revm 36.0.0", - "revm-bytecode 9.0.0", - "revm-database 12.0.0", + "revm", + "revm-bytecode", + "revm-database", "serde", "serde_json", ] [[package]] name = "reth-ipc" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "bytes", "futures", @@ -10308,8 +10301,8 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "bitflags 2.11.0", "byteorder", @@ -10325,8 +10318,8 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "bindgen", "cc", @@ -10334,8 +10327,8 @@ dependencies = [ [[package]] name = "reth-metrics" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "futures", "metrics", @@ -10346,8 +10339,8 @@ dependencies = [ [[package]] name = "reth-net-banlist" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "ipnet", @@ -10355,8 +10348,8 @@ dependencies = [ [[package]] name = "reth-net-nat" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "futures-util", "if-addrs 0.14.0", @@ -10369,8 +10362,8 @@ dependencies = [ [[package]] name = "reth-network" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10426,8 +10419,8 @@ dependencies = [ [[package]] name = "reth-network-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -10451,8 +10444,8 @@ dependencies = [ [[package]] name = "reth-network-p2p" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10474,8 +10467,8 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -10489,8 +10482,8 @@ dependencies = [ [[package]] name = "reth-network-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eip2124", "humantime-serde", @@ -10503,8 +10496,8 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "anyhow", "bincode 1.3.3", @@ -10520,8 +10513,8 @@ dependencies = [ [[package]] name = "reth-node-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-rpc-types-engine", "eyre", @@ -10544,8 +10537,8 @@ dependencies = [ [[package]] name = "reth-node-builder" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10612,8 +10605,8 @@ dependencies = [ [[package]] name = "reth-node-core" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10667,8 +10660,8 @@ dependencies = [ [[package]] name = "reth-node-ethereum" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-network", @@ -10699,14 +10692,14 @@ dependencies = [ "reth-rpc-server-types", "reth-tracing", "reth-transaction-pool", - "revm 36.0.0", + "revm", "tokio", ] [[package]] name = "reth-node-ethstats" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -10729,8 +10722,8 @@ dependencies = [ [[package]] name = "reth-node-events" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10753,8 +10746,8 @@ dependencies = [ [[package]] name = "reth-node-metrics" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "bytes", "eyre", @@ -10782,8 +10775,8 @@ dependencies = [ [[package]] name = "reth-node-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "reth-chainspec", "reth-db-api", @@ -10938,7 +10931,7 @@ dependencies = [ "reth-storage-errors", "reth-trie", "reth-trie-common", - "revm 36.0.0", + "revm", "thiserror 2.0.18", "tracing", ] @@ -10956,7 +10949,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "reth-chainspec", "reth-evm", "reth-execution-errors", @@ -10969,7 +10962,7 @@ dependencies = [ "reth-revm", "reth-rpc-eth-api", "reth-storage-errors", - "revm 36.0.0", + "revm", "thiserror 2.0.18", ] @@ -11018,7 +11011,7 @@ dependencies = [ "metrics", "op-alloy-consensus", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "reth-chain-state", "reth-engine-primitives", "reth-errors", @@ -11075,7 +11068,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "reth-chainspec", "reth-consensus", "reth-db", @@ -11114,7 +11107,7 @@ dependencies = [ "reth-transaction-pool", "reth-trie-common", "reth-trie-db", - "revm 36.0.0", + "revm", "serde", "serde_json", "tokio", @@ -11137,7 +11130,7 @@ dependencies = [ "either", "op-alloy-consensus", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "reth-basic-payload-builder", "reth-chainspec", "reth-evm", @@ -11154,7 +11147,7 @@ dependencies = [ "reth-revm", "reth-storage-api", "reth-transaction-pool", - "revm 36.0.0", + "revm", "serde", "sha2", "thiserror 2.0.18", @@ -11219,7 +11212,7 @@ dependencies = [ "op-alloy-rpc-jsonrpsee", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", - "op-revm 17.0.0", + "op-revm", "reqwest 0.13.2", "reth-basic-payload-builder", "reth-chain-state", @@ -11249,7 +11242,7 @@ dependencies = [ "reth-storage-api", "reth-tasks", "reth-transaction-pool", - "revm 36.0.0", + "revm", "serde", "serde_json", "strum", @@ -11335,7 +11328,7 @@ dependencies = [ "op-alloy-consensus", "op-alloy-flz", "op-alloy-rpc-types", - "op-revm 17.0.0", + "op-revm", "parking_lot", "reth-chain-state", "reth-chainspec", @@ -11359,8 +11352,8 @@ dependencies = [ [[package]] name = "reth-payload-builder" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -11383,8 +11376,8 @@ dependencies = [ [[package]] name = "reth-payload-builder-primitives" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "pin-project", "reth-payload-primitives", @@ -11395,8 +11388,8 @@ dependencies = [ [[package]] name = "reth-payload-primitives" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -11419,8 +11412,8 @@ dependencies = [ [[package]] name = "reth-payload-util" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -11429,8 +11422,8 @@ dependencies = [ [[package]] name = "reth-payload-validator" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-rpc-types-engine", @@ -11462,9 +11455,9 @@ dependencies = [ "quanta", "rayon", "reth-codecs", - "revm-bytecode 9.0.0", + "revm-bytecode", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "secp256k1 0.30.0", "serde", "thiserror 2.0.18", @@ -11472,8 +11465,8 @@ dependencies = [ [[package]] name = "reth-provider" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -11508,8 +11501,8 @@ dependencies = [ "reth-tasks", "reth-trie", "reth-trie-db", - "revm-database 12.0.0", - "revm-state 10.0.0", + "revm-database", + "revm-state", "rocksdb", "strum", "tokio", @@ -11518,8 +11511,8 @@ dependencies = [ [[package]] name = "reth-prune" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -11547,8 +11540,8 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "arbitrary", @@ -11563,21 +11556,23 @@ dependencies = [ [[package]] name = "reth-revm" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-debug", "reth-primitives-traits", "reth-storage-api", "reth-storage-errors", "reth-trie", - "revm 36.0.0", + "revm", ] [[package]] name = "reth-rpc" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -11639,7 +11634,7 @@ dependencies = [ "reth-tracing", "reth-transaction-pool", "reth-trie-common", - "revm 36.0.0", + "revm", "revm-inspectors", "revm-primitives", "serde", @@ -11654,8 +11649,8 @@ dependencies = [ [[package]] name = "reth-rpc-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eip7928", "alloy-eips", @@ -11685,8 +11680,8 @@ dependencies = [ [[package]] name = "reth-rpc-builder" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-network", "alloy-provider", @@ -11728,8 +11723,8 @@ dependencies = [ [[package]] name = "reth-rpc-convert" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-evm", @@ -11748,8 +11743,8 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -11779,11 +11774,12 @@ dependencies = [ [[package]] name = "reth-rpc-eth-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-dyn-abi", + "alloy-eip7928", "alloy-eips", "alloy-evm", "alloy-json-rpc", @@ -11815,16 +11811,17 @@ dependencies = [ "reth-tasks", "reth-transaction-pool", "reth-trie-common", - "revm 36.0.0", + "revm", "revm-inspectors", + "serde_json", "tokio", "tracing", ] [[package]] name = "reth-rpc-eth-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -11858,7 +11855,7 @@ dependencies = [ "reth-tasks", "reth-transaction-pool", "reth-trie", - "revm 36.0.0", + "revm", "revm-inspectors", "schnellru", "serde", @@ -11871,8 +11868,8 @@ dependencies = [ [[package]] name = "reth-rpc-layer" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-rpc-types-engine", "http", @@ -11885,8 +11882,8 @@ dependencies = [ [[package]] name = "reth-rpc-server-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -11916,8 +11913,8 @@ dependencies = [ [[package]] name = "reth-stages" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -11968,8 +11965,8 @@ dependencies = [ [[package]] name = "reth-stages-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -11996,8 +11993,8 @@ dependencies = [ [[package]] name = "reth-stages-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "arbitrary", @@ -12010,8 +12007,8 @@ dependencies = [ [[package]] name = "reth-static-file" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "parking_lot", @@ -12030,8 +12027,8 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "clap", @@ -12045,8 +12042,8 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -12063,14 +12060,14 @@ dependencies = [ "reth-stages-types", "reth-storage-errors", "reth-trie-common", - "revm-database 12.0.0", + "revm-database", "serde_json", ] [[package]] name = "reth-storage-errors" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eips", "alloy-primitives", @@ -12080,15 +12077,15 @@ dependencies = [ "reth-primitives-traits", "reth-prune-types", "reth-static-file-types", - "revm-database-interface 10.0.0", - "revm-state 10.0.0", + "revm-database-interface", + "revm-state", "thiserror 2.0.18", ] [[package]] name = "reth-tasks" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "crossbeam-utils", "dashmap", @@ -12108,8 +12105,8 @@ dependencies = [ [[package]] name = "reth-testing-utils" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -12124,8 +12121,8 @@ dependencies = [ [[package]] name = "reth-tokio-util" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "tokio", "tokio-stream", @@ -12134,8 +12131,8 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "clap", "eyre", @@ -12153,8 +12150,8 @@ dependencies = [ [[package]] name = "reth-tracing-otlp" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "clap", "eyre", @@ -12171,8 +12168,8 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -12201,8 +12198,8 @@ dependencies = [ "reth-primitives-traits", "reth-storage-api", "reth-tasks", - "revm 36.0.0", - "revm-interpreter 34.0.0", + "revm", + "revm-interpreter", "revm-primitives", "rustc-hash", "schnellru", @@ -12217,8 +12214,8 @@ dependencies = [ [[package]] name = "reth-trie" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -12236,15 +12233,15 @@ dependencies = [ "reth-storage-errors", "reth-trie-common", "reth-trie-sparse", - "revm-database 12.0.0", + "revm-database", "tracing", "triehash", ] [[package]] name = "reth-trie-common" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -12263,15 +12260,15 @@ dependencies = [ "rayon", "reth-codecs", "reth-primitives-traits", - "revm-database 12.0.0", + "revm-database", "serde", "serde_with", ] [[package]] name = "reth-trie-db" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "metrics", @@ -12290,8 +12287,8 @@ dependencies = [ [[package]] name = "reth-trie-parallel" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-eip7928", "alloy-evm", @@ -12311,15 +12308,15 @@ dependencies = [ "reth-tasks", "reth-trie", "reth-trie-sparse", - "revm-state 10.0.0", + "revm-state", "thiserror 2.0.18", "tracing", ] [[package]] name = "reth-trie-sparse" -version = "1.11.3" -source = "git+https://github.com/paradigmxyz/reth?rev=082c36e#082c36ebee634baacacc4ca556ae77f7f60df708" +version = "2.0.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v2.0.0#eb4c15e5e36d8776d46629beae4c0a69af7ab04f" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -12347,52 +12344,23 @@ dependencies = [ "zstd", ] -[[package]] -name = "revm" -version = "34.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" -dependencies = [ - "revm-bytecode 8.0.0", - "revm-context 13.0.0", - "revm-context-interface 14.0.0", - "revm-database 10.0.0", - "revm-database-interface 9.0.0", - "revm-handler 15.0.0", - "revm-inspector 15.0.0", - "revm-interpreter 32.0.0", - "revm-precompile", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm" version = "36.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0abc15d09cd211e9e73410ada10134069c794d4bcdb787dfc16a1bf0939849c" dependencies = [ - "revm-bytecode 9.0.0", - "revm-context 15.0.0", - "revm-context-interface 16.0.0", - "revm-database 12.0.0", - "revm-database-interface 10.0.0", - "revm-handler 17.0.0", - "revm-inspector 17.0.0", - "revm-interpreter 34.0.0", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", "revm-precompile", "revm-primitives", - "revm-state 10.0.0", -] - -[[package]] -name = "revm-bytecode" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d1e5c1eaa44d39d537f668bc5c3409dc01e5c8be954da6c83370bbdf006457" -dependencies = [ - "bitvec", - "revm-primitives", + "revm-state", ] [[package]] @@ -12407,22 +12375,6 @@ dependencies = [ "serde", ] -[[package]] -name = "revm-context" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "892ff3e6a566cf8d72ffb627fdced3becebbd9ba64089c25975b9b028af326a5" -dependencies = [ - "bitvec", - "cfg-if", - "derive-where", - "revm-bytecode 8.0.0", - "revm-context-interface 14.0.0", - "revm-database-interface 9.0.0", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm-context" version = "15.0.0" @@ -12432,29 +12384,14 @@ dependencies = [ "bitvec", "cfg-if", "derive-where", - "revm-bytecode 9.0.0", - "revm-context-interface 16.0.0", - "revm-database-interface 10.0.0", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", ] -[[package]] -name = "revm-context-interface" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98" -dependencies = [ - "alloy-eip2930", - "alloy-eip7702", - "auto_impl", - "either", - "revm-database-interface 9.0.0", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm-context-interface" version = "16.0.0" @@ -12465,24 +12402,12 @@ dependencies = [ "alloy-eip7702", "auto_impl", "either", - "revm-database-interface 10.0.0", + "revm-database-interface", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", ] -[[package]] -name = "revm-database" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" -dependencies = [ - "revm-bytecode 8.0.0", - "revm-database-interface 9.0.0", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm-database" version = "12.0.0" @@ -12490,26 +12415,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c0a7d6da41061f2c50f99a2632571026b23684b5449ff319914151f4449b6c8" dependencies = [ "alloy-eips", - "revm-bytecode 9.0.0", - "revm-database-interface 10.0.0", + "revm-bytecode", + "revm-database-interface", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", ] -[[package]] -name = "revm-database-interface" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7bf93ac5b91347c057610c0d96e923db8c62807e03f036762d03e981feddc1d" -dependencies = [ - "auto_impl", - "either", - "revm-primitives", - "revm-state 9.0.0", - "thiserror 2.0.18", -] - [[package]] name = "revm-database-interface" version = "10.0.0" @@ -12519,29 +12431,11 @@ dependencies = [ "auto_impl", "either", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", "thiserror 2.0.18", ] -[[package]] -name = "revm-handler" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2" -dependencies = [ - "auto_impl", - "derive-where", - "revm-bytecode 8.0.0", - "revm-context 13.0.0", - "revm-context-interface 14.0.0", - "revm-database-interface 9.0.0", - "revm-interpreter 32.0.0", - "revm-precompile", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm-handler" version = "17.0.0" @@ -12550,33 +12444,17 @@ checksum = "9f1eed729ca9b228ae98688f352235871e9b8be3d568d488e4070f64c56e9d3d" dependencies = [ "auto_impl", "derive-where", - "revm-bytecode 9.0.0", - "revm-context 15.0.0", - "revm-context-interface 16.0.0", - "revm-database-interface 10.0.0", - "revm-interpreter 34.0.0", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", + "revm-interpreter", "revm-precompile", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", ] -[[package]] -name = "revm-inspector" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" -dependencies = [ - "auto_impl", - "either", - "revm-context 13.0.0", - "revm-database-interface 9.0.0", - "revm-handler 15.0.0", - "revm-interpreter 32.0.0", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm-inspector" version = "17.0.0" @@ -12585,12 +12463,12 @@ checksum = "cbf5102391706513689f91cb3cb3d97b5f13a02e8647e6e9cb7620877ef84847" dependencies = [ "auto_impl", "either", - "revm-context 15.0.0", - "revm-database-interface 10.0.0", - "revm-handler 17.0.0", - "revm-interpreter 34.0.0", + "revm-context", + "revm-database-interface", + "revm-handler", + "revm-interpreter", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", "serde_json", ] @@ -12609,34 +12487,22 @@ dependencies = [ "boa_engine", "boa_gc", "colorchoice", - "revm 36.0.0", + "revm", "serde", "serde_json", "thiserror 2.0.18", ] -[[package]] -name = "revm-interpreter" -version = "32.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" -dependencies = [ - "revm-bytecode 8.0.0", - "revm-context-interface 14.0.0", - "revm-primitives", - "revm-state 9.0.0", -] - [[package]] name = "revm-interpreter" version = "34.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf22f80612bb8f58fd1f578750281f2afadb6c93835b14ae6a4d6b75ca26f445" dependencies = [ - "revm-bytecode 9.0.0", - "revm-context-interface 16.0.0", + "revm-bytecode", + "revm-context-interface", "revm-primitives", - "revm-state 10.0.0", + "revm-state", "serde", ] @@ -12663,6 +12529,7 @@ dependencies = [ "ripemd", "secp256k1 0.31.1", "sha2", + "substrate-bn", ] [[package]] @@ -12677,18 +12544,6 @@ dependencies = [ "serde", ] -[[package]] -name = "revm-state" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" -dependencies = [ - "alloy-eip7928", - "bitflags 2.11.0", - "revm-bytecode 8.0.0", - "revm-primitives", -] - [[package]] name = "revm-state" version = "10.0.0" @@ -12697,7 +12552,7 @@ checksum = "d29404707763da607e5d6e4771cb203998c28159279c2f64cc32de08d2814651" dependencies = [ "alloy-eip7928", "bitflags 2.11.0", - "revm-bytecode 9.0.0", + "revm-bytecode", "revm-primitives", "serde", ] @@ -13820,6 +13675,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6967169cd0f..7353326abc5 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -49,6 +49,9 @@ members = [ # Alloy OP Hardforks "alloy-op-hardforks/", + + # Op-Revm + "op-revm/", ] default-members = [ "kona/bin/host", @@ -298,83 +301,85 @@ alloy-op-evm = { version = "0.30.0", path = "alloy-op-evm/", default-features = alloy-op-hardforks = { version = "0.4.7", path = "alloy-op-hardforks/", default-features = false } # ==================== RETH CRATES (crates.io) ==================== -reth-codecs = { version = "0.1.0", default-features = false, features = ["alloy"] } +reth-codecs = { version = "0.1.0", default-features = false, features = [ + "alloy", +] } reth-codecs-derive = "0.1.0" reth-primitives-traits = { version = "0.1.0", default-features = false } reth-rpc-traits = { version = "0.1.0", default-features = false } reth-zstd-compressors = { version = "0.1.0", default-features = false } -# ==================== RETH CRATES (git @ f0d07c3) ==================== -reth = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-chain-state = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-chainspec = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-cli = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-cli-commands = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-cli-runner = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-cli-util = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-consensus = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-consensus-common = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-db = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-db-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-db-common = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-downloaders = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-engine-local = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-engine-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-errors = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-eth-wire-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-ethereum = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-ethereum-cli = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-ethereum-consensus = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-ethereum-forks = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-evm = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-exex = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-exex-test-utils = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-execution-errors = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-execution-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-fs-util = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-metrics = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-network = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-network-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-network-peers = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-node-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-node-builder = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-node-core = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-node-events = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-node-metrics = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-payload-builder-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-payload-util = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-payload-validator = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-provider = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-prune = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-prune-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-revm = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-rpc = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-rpc-builder = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-rpc-engine-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-stages = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-stages-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-static-file = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-static-file-types = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-storage-api = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-storage-errors = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-tasks = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-tracing = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-trie = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } -reth-trie-common = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e", default-features = false } -reth-trie-db = { git = "https://github.com/paradigmxyz/reth", rev = "082c36e" } +# ==================== RETH CRATES (git @ v2.0.0) ==================== +reth = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-chain-state = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-cli-commands = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-cli-runner = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-cli-util = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-consensus-common = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-db-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-db-common = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-downloaders = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-engine-local = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-engine-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-eth-wire-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-ethereum-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-ethereum-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-ethereum-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-exex = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-exex-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-execution-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-execution-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-fs-util = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-metrics = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-network-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-node-events = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-node-metrics = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-payload-builder-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-payload-util = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-payload-validator = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-prune = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-prune-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-revm = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-rpc-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-rpc-engine-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-stages = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-stages-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-static-file = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-static-file-types = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-storage-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-storage-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-trie = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } +reth-trie-common = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0", default-features = false } +reth-trie-db = { git = "https://github.com/paradigmxyz/reth", tag = "v2.0.0" } # ==================== REVM (latest: op-reth versions) ==================== revm = { version = "36.0.0", default-features = false } @@ -384,7 +389,7 @@ revm-state = { version = "10.0.0", default-features = false } revm-primitives = { version = "22.1.0", default-features = false } revm-interpreter = { version = "34.0.0", default-features = false } revm-database-interface = { version = "10.0.0", default-features = false } -op-revm = { version = "17.0.0", default-features = false } +op-revm = { version = "17.0.0", path = "op-revm/", default-features = false } revm-inspectors = "0.36.1" # ==================== ALLOY ==================== @@ -649,8 +654,3 @@ snmalloc-rs = { version = "0.3.8", features = ["build_cc"] } # K/V database rocksdb = { version = "0.24.0", default-features = false } - -[patch.crates-io] -# Duplicated by: reth-payload-primitives, reth-engine-local (reth git) -op-alloy-rpc-types-engine = { path = "op-alloy/crates/rpc-types-engine" } -op-alloy-consensus = { path = "op-alloy/crates/consensus" } diff --git a/rust/alloy-op-evm/src/block/receipt_builder.rs b/rust/alloy-op-evm/src/block/receipt_builder.rs index 7fd04314c7f..1d2717116a4 100644 --- a/rust/alloy-op-evm/src/block/receipt_builder.rs +++ b/rust/alloy-op-evm/src/block/receipt_builder.rs @@ -58,6 +58,7 @@ impl OpReceiptBuilder for OpAlloyReceiptBuilder { OpTxType::Eip2930 => OpReceiptEnvelope::Eip2930(receipt), OpTxType::Eip1559 => OpReceiptEnvelope::Eip1559(receipt), OpTxType::Eip7702 => OpReceiptEnvelope::Eip7702(receipt), + OpTxType::PostExec => OpReceiptEnvelope::PostExec(receipt), OpTxType::Deposit => unreachable!(), }) } diff --git a/rust/alloy-op-evm/src/env.rs b/rust/alloy-op-evm/src/env.rs index 79f41c12b3b..1ec9d540cb5 100644 --- a/rust/alloy-op-evm/src/env.rs +++ b/rust/alloy-op-evm/src/env.rs @@ -36,6 +36,7 @@ pub fn spec_by_timestamp_after_bedrock(chain_spec: impl OpHardforks, timestamp: } check_forks! { is_interop_active_at_timestamp => INTEROP, + is_karst_active_at_timestamp => KARST, is_jovian_active_at_timestamp => JOVIAN, is_isthmus_active_at_timestamp => ISTHMUS, is_holocene_active_at_timestamp => HOLOCENE, @@ -204,6 +205,7 @@ mod tests { fake_hardfork_constructors! { timestamp: interop => Interop, + karst => Karst, jovian => Jovian, isthmus => Isthmus, holocene => Holocene, @@ -229,6 +231,7 @@ mod tests { } #[test_case::test_case(FakeHardfork::interop(), OpSpecId::INTEROP; "Interop")] + #[test_case::test_case(FakeHardfork::karst(), OpSpecId::KARST; "Karst")] #[test_case::test_case(FakeHardfork::jovian(), OpSpecId::JOVIAN; "Jovian")] #[test_case::test_case(FakeHardfork::isthmus(), OpSpecId::ISTHMUS; "Isthmus")] #[test_case::test_case(FakeHardfork::holocene(), OpSpecId::HOLOCENE; "Holocene")] diff --git a/rust/alloy-op-evm/src/tx.rs b/rust/alloy-op-evm/src/tx.rs index b519914e9c9..01ea931377d 100644 --- a/rust/alloy-op-evm/src/tx.rs +++ b/rust/alloy-op-evm/src/tx.rs @@ -2,13 +2,13 @@ use crate::block::OpTxEnv; use alloy_consensus::{ - Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, + Signed, Transaction, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, }; use alloy_eips::{Encodable2718, Typed2718, eip7594::Encodable7594}; use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv, TransactionEnvMut}; use alloy_primitives::{Address, B256, Bytes, TxKind, U256}; use core::ops::{Deref, DerefMut}; -use op_alloy::consensus::{OpTxEnvelope, TxDeposit}; +use op_alloy::consensus::{OpTxEnvelope, TxDeposit, TxPostExec}; use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; use revm::context::TxEnv; @@ -133,6 +133,7 @@ impl FromTxWithEncoded for OpTx { OpTxEnvelope::Eip2930(tx) => Self::from_encoded_tx(tx, caller, encoded), OpTxEnvelope::Eip7702(tx) => Self::from_encoded_tx(tx, caller, encoded), OpTxEnvelope::Deposit(tx) => Self::from_encoded_tx(tx.inner(), caller, encoded), + OpTxEnvelope::PostExec(tx) => Self::from_encoded_tx(tx.inner(), caller, encoded), } } } @@ -216,6 +217,20 @@ impl FromTxWithEncoded for OpTx { } } +impl FromRecoveredTx for OpTx { + fn from_recovered_tx(tx: &TxPostExec, _sender: Address) -> Self { + let encoded = tx.encoded_2718(); + Self::from_encoded_tx(tx, Address::ZERO, encoded.into()) + } +} + +impl FromTxWithEncoded for OpTx { + fn from_encoded_tx(tx: &TxPostExec, caller: Address, encoded: Bytes) -> Self { + let base = TxEnv { tx_type: tx.ty(), caller, kind: tx.kind(), ..Default::default() }; + Self(OpTransaction { base, enveloped_tx: Some(encoded), deposit: Default::default() }) + } +} + impl TransactionEnvMut for OpTx { fn set_gas_limit(&mut self, gas_limit: u64) { self.0.base.gas_limit = gas_limit; diff --git a/rust/alloy-op-hardforks/src/lib.rs b/rust/alloy-op-hardforks/src/lib.rs index 756ed70e426..a079c14b5d0 100644 --- a/rust/alloy-op-hardforks/src/lib.rs +++ b/rust/alloy-op-hardforks/src/lib.rs @@ -166,21 +166,6 @@ impl OpHardfork { ] } - /// Devnet list of hardforks. - pub const fn devnet() -> [(Self, ForkCondition); 9] { - [ - (Self::Bedrock, ForkCondition::ZERO_BLOCK), - (Self::Regolith, ForkCondition::ZERO_TIMESTAMP), - (Self::Canyon, ForkCondition::ZERO_TIMESTAMP), - (Self::Ecotone, ForkCondition::ZERO_TIMESTAMP), - (Self::Fjord, ForkCondition::ZERO_TIMESTAMP), - (Self::Granite, ForkCondition::ZERO_TIMESTAMP), - (Self::Holocene, ForkCondition::ZERO_TIMESTAMP), - (Self::Isthmus, ForkCondition::ZERO_TIMESTAMP), - (Self::Jovian, ForkCondition::Timestamp(1762185600)), - ] - } - /// Returns index of `self` in sorted canonical array. pub const fn idx(&self) -> usize { *self as usize @@ -303,11 +288,6 @@ impl OpChainHardforks { Self::new(OpHardfork::base_sepolia()) } - /// Creates a new [`OpChainHardforks`] with devnet configuration. - pub fn devnet() -> Self { - Self::new(OpHardfork::devnet()) - } - /// Returns `true` if this is an OP mainnet instance. pub fn is_op_mainnet(&self) -> bool { self[OpHardfork::Bedrock] == ForkCondition::Block(OP_MAINNET_BEDROCK_BLOCK) @@ -316,8 +296,8 @@ impl OpChainHardforks { impl EthereumHardforks for OpChainHardforks { fn ethereum_fork_activation(&self, fork: EthereumHardfork) -> ForkCondition { - use EthereumHardfork::{Cancun, Prague, Shanghai}; - use OpHardfork::{Canyon, Ecotone, Isthmus}; + use EthereumHardfork::{Cancun, Osaka, Prague, Shanghai}; + use OpHardfork::{Canyon, Ecotone, Isthmus, Karst}; if self.forks.is_empty() { return ForkCondition::Never; @@ -329,6 +309,7 @@ impl EthereumHardforks for OpChainHardforks { Shanghai if forks_len <= Canyon.idx() => ForkCondition::Never, Cancun if forks_len <= Ecotone.idx() => ForkCondition::Never, Prague if forks_len <= Isthmus.idx() => ForkCondition::Never, + Osaka if forks_len <= Karst.idx() => ForkCondition::Never, _ => self[fork], } } @@ -378,14 +359,14 @@ impl Index for OpChainHardforks { Constantinople, Dao, Frontier, GrayGlacier, Homestead, Istanbul, London, MuirGlacier, Osaka, Paris, Petersburg, Prague, Shanghai, SpuriousDragon, Tangerine, }; - use OpHardfork::{Bedrock, Canyon, Ecotone, Isthmus}; + use OpHardfork::{Bedrock, Canyon, Ecotone, Isthmus, Karst}; match hf { // Dao Hardfork is not needed for OpChainHardforks - Dao | Osaka | Bpo1 | Bpo2 | Bpo3 | Bpo4 | Bpo5 | Amsterdam => &ForkCondition::Never, + Dao | Bpo1 | Bpo2 | Bpo3 | Bpo4 | Bpo5 | Amsterdam => &ForkCondition::Never, Berlin if self.is_op_mainnet() => &ForkCondition::Block(OP_MAINNET_BERLIN_BLOCK), - Frontier | Homestead | Tangerine | SpuriousDragon | Byzantium | Constantinople | - Petersburg | Istanbul | MuirGlacier | Berlin => &ForkCondition::ZERO_BLOCK, + Frontier | Homestead | Tangerine | SpuriousDragon | Byzantium | Constantinople + | Petersburg | Istanbul | MuirGlacier | Berlin => &ForkCondition::ZERO_BLOCK, London | ArrowGlacier | GrayGlacier => &self[Bedrock], Paris if self.is_op_mainnet() => &ForkCondition::TTD { activation_block_number: OP_MAINNET_BEDROCK_BLOCK, @@ -400,6 +381,7 @@ impl Index for OpChainHardforks { Shanghai => &self[Canyon], Cancun => &self[Ecotone], Prague => &self[Isthmus], + Osaka => &self[Karst], _ => unreachable!(), } } diff --git a/rust/justfile b/rust/justfile index 46d0fc21ec6..85d0ae8769f 100644 --- a/rust/justfile +++ b/rust/justfile @@ -1,6 +1,6 @@ set positional-arguments -NIGHTLY := "nightly-2026-02-20" +NIGHTLY := `grep -oE 'nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}' ../mise.toml | head -1` # Aliases alias t := test @@ -28,14 +28,24 @@ build *args='': build-release *args='': cargo build --workspace --release {{args}} -# Build the rollup node -build-node: +# Build kona-node +build-kona-node: cargo build --release --bin kona-node +# Build kona-node in debug mode (faster compilation for local E2E test iteration) +build-kona-node-debug: + cargo build --bin kona-node + +alias build-node := build-kona-node + # Build op-reth build-op-reth: cargo build --release --bin op-reth +# Build op-reth in debug mode (faster compilation for local E2E test iteration) +build-op-reth-debug: + cargo build --bin op-reth + ############################### Test ################################ # Run all tests (unit + doc tests) @@ -111,6 +121,9 @@ check-no-std: op-alloy-consensus op-alloy-rpc-types op-alloy-rpc-types-engine + + # op-revm + op-revm ) # We need to install the riscv32imac-unknown-none-elf target before starting to build the no-std crates. diff --git a/rust/kona/README.md b/rust/kona/README.md index f3c86b4be84..d5719f1198b 100644 --- a/rust/kona/README.md +++ b/rust/kona/README.md @@ -55,7 +55,6 @@ getting started with building your own programs, and a reference for the librari - [`driver`](./crates/proof/driver): Stateful derivation pipeline driver. - [`interop`](./crates/protocol/interop): Core functionality and primitives for the [Interop feature](https://specs.optimism.io/interop/overview.html) of the OP Stack. - [`registry`](./crates/protocol/registry): Rust bindings for the [superchain-registry][superchain-registry]. -- [`comp`](./crates/batcher/comp): Compression types for the OP Stack. - [`hardforks`](./crates/protocol/hardforks): Consensus layer hardfork types for the OP Stack including network upgrade transactions. **Proof** diff --git a/rust/kona/bin/client/src/fpvm_evm/precompiles/provider.rs b/rust/kona/bin/client/src/fpvm_evm/precompiles/provider.rs index 8d97fd8ad59..7f2d287b230 100644 --- a/rust/kona/bin/client/src/fpvm_evm/precompiles/provider.rs +++ b/rust/kona/bin/client/src/fpvm_evm/precompiles/provider.rs @@ -8,7 +8,7 @@ use alloy_primitives::{Address, Bytes}; use kona_preimage::{HintWriterClient, PreimageOracleClient}; use op_revm::{ OpSpecId, - precompiles::{fjord, granite, isthmus}, + precompiles::{fjord, granite, isthmus, jovian, karst}, }; use revm::{ context::{Cfg, ContextTr}, @@ -48,7 +48,9 @@ where OpSpecId::ECOTONE) => Precompiles::new(spec.into_eth_spec().into()), OpSpecId::FJORD => fjord(), OpSpecId::GRANITE | OpSpecId::HOLOCENE => granite(), - OpSpecId::ISTHMUS | OpSpecId::INTEROP | OpSpecId::OSAKA | OpSpecId::JOVIAN => isthmus(), + OpSpecId::ISTHMUS => isthmus(), + OpSpecId::JOVIAN => jovian(), + OpSpecId::KARST | OpSpecId::INTEROP => karst(), }; let accelerated_precompiles = match spec { @@ -57,10 +59,8 @@ where } OpSpecId::ECOTONE | OpSpecId::FJORD => accelerated_ecotone::(), OpSpecId::GRANITE | OpSpecId::HOLOCENE => accelerated_granite::(), - OpSpecId::ISTHMUS | OpSpecId::INTEROP | OpSpecId::OSAKA => { - accelerated_isthmus::() - } - OpSpecId::JOVIAN => accelerated_jovian::(), + OpSpecId::ISTHMUS => accelerated_isthmus::(), + OpSpecId::JOVIAN | OpSpecId::KARST | OpSpecId::INTEROP => accelerated_jovian::(), }; Self { @@ -438,6 +438,77 @@ mod test { assert_eq!(interpreter_result.result, InstructionResult::PrecompileOOG); } + #[test] + fn test_post_jovian_specs_use_jovian_precompiles() { + let (hint_chan, preimage_chan) = ( + kona_preimage::BidirectionalChannel::new().unwrap(), + kona_preimage::BidirectionalChannel::new().unwrap(), + ); + let hint_writer = kona_preimage::HintWriter::new(hint_chan.client); + let oracle_reader = kona_preimage::OracleReader::new(preimage_chan.client); + + let jovian_provider = OpFpvmPrecompiles::new_with_spec( + OpSpecId::JOVIAN, + hint_writer.clone(), + oracle_reader.clone(), + ); + let interop_provider = OpFpvmPrecompiles::new_with_spec( + OpSpecId::INTEROP, + hint_writer.clone(), + oracle_reader.clone(), + ); + let karst_provider = OpFpvmPrecompiles::new_with_spec( + OpSpecId::KARST, + hint_writer.clone(), + oracle_reader.clone(), + ); + let isthmus_provider = + OpFpvmPrecompiles::new_with_spec(OpSpecId::ISTHMUS, hint_writer, oracle_reader); + + // Post-Jovian specs must have the same accelerated precompile addresses as Jovian. + let jovian_addrs: Vec<_> = { + let mut addrs: Vec<_> = + jovian_provider.accelerated_precompiles.keys().copied().collect(); + addrs.sort(); + addrs + }; + let interop_addrs: Vec<_> = { + let mut addrs: Vec<_> = + interop_provider.accelerated_precompiles.keys().copied().collect(); + addrs.sort(); + addrs + }; + let osaka_addrs: Vec<_> = { + let mut addrs: Vec<_> = + karst_provider.accelerated_precompiles.keys().copied().collect(); + addrs.sort(); + addrs + }; + assert_eq!( + jovian_addrs, interop_addrs, + "INTEROP should use Jovian accelerated precompiles" + ); + assert_eq!(jovian_addrs, osaka_addrs, "OSAKA should use Jovian accelerated precompiles"); + + // Verify the non-accelerated precompile sets point to the correct static instances. + assert!( + core::ptr::eq(jovian_provider.inner.precompiles, jovian()), + "JOVIAN should use jovian() precompiles" + ); + assert!( + core::ptr::eq(isthmus_provider.inner.precompiles, isthmus()), + "ISTHMUS should use isthmus() precompiles" + ); + assert!( + core::ptr::eq(karst_provider.inner.precompiles, karst()), + "KARST should use karst() precompiles" + ); + assert!( + core::ptr::eq(interop_provider.inner.precompiles, karst()), + "INTEROP should use karst() precompiles" + ); + } + #[test] fn test_run_with_shared_buffer_empty() { let (hint_chan, preimage_chan) = ( diff --git a/rust/kona/bin/client/src/fpvm_evm/tx.rs b/rust/kona/bin/client/src/fpvm_evm/tx.rs index e1b281a368c..561063bf38f 100644 --- a/rust/kona/bin/client/src/fpvm_evm/tx.rs +++ b/rust/kona/bin/client/src/fpvm_evm/tx.rs @@ -1,14 +1,14 @@ //! [`FpvmOpTx`] newtype wrapper around [`OpTransaction`]. use alloy_consensus::{ - Signed, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, + Signed, Transaction, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, }; use alloy_eips::{Encodable2718, Typed2718, eip7594::Encodable7594}; use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv}; use alloy_op_evm::block::OpTxEnv; use alloy_primitives::{Address, B256, Bytes, TxKind, U256}; use core::ops::{Deref, DerefMut}; -use op_alloy_consensus::{OpTxEnvelope, TxDeposit}; +use op_alloy_consensus::{OpTxEnvelope, TxDeposit, TxPostExec}; use op_revm::{OpTransaction, transaction::deposit::DepositTransactionParts}; use revm::context::TxEnv; @@ -136,6 +136,7 @@ impl FromTxWithEncoded for FpvmOpTx { OpTxEnvelope::Eip2930(tx) => Self::from_encoded_tx(tx, caller, encoded), OpTxEnvelope::Eip7702(tx) => Self::from_encoded_tx(tx, caller, encoded), OpTxEnvelope::Deposit(tx) => Self::from_encoded_tx(tx.inner(), caller, encoded), + OpTxEnvelope::PostExec(tx) => Self::from_encoded_tx(tx.inner(), caller, encoded), } } } @@ -215,3 +216,17 @@ impl FromTxWithEncoded for FpvmOpTx { Self(OpTransaction { base, enveloped_tx: Some(encoded), deposit }) } } + +impl FromRecoveredTx for FpvmOpTx { + fn from_recovered_tx(tx: &TxPostExec, _sender: Address) -> Self { + let encoded = tx.encoded_2718(); + Self::from_encoded_tx(tx, Address::ZERO, encoded.into()) + } +} + +impl FromTxWithEncoded for FpvmOpTx { + fn from_encoded_tx(tx: &TxPostExec, caller: Address, encoded: Bytes) -> Self { + let base = TxEnv { tx_type: tx.ty(), caller, kind: tx.kind(), ..Default::default() }; + Self(OpTransaction { base, enveloped_tx: Some(encoded), deposit: Default::default() }) + } +} diff --git a/rust/kona/crates/node/service/src/actors/sequencer/actor.rs b/rust/kona/crates/node/service/src/actors/sequencer/actor.rs index 0722c031683..72bc5673a79 100644 --- a/rust/kona/crates/node/service/src/actors/sequencer/actor.rs +++ b/rust/kona/crates/node/service/src/actors/sequencer/actor.rs @@ -13,7 +13,7 @@ use crate::{ update_conductor_commitment_duration_metrics, update_seal_duration_metrics, update_total_transactions_sequenced, }, - origin_selector::OriginSelector, + origin_selector::{L1OriginSelectorError, OriginSelector}, }, }, }; @@ -225,6 +225,15 @@ where .await { Ok(l1_origin) => l1_origin, + Err(L1OriginSelectorError::OriginNotFound(hash)) => { + warn!( + target: "sequencer", + %hash, + "L1 origin block not found, resetting engine" + ); + self.engine_client.reset_engine_forkchoice().await?; + return Ok(None); + } Err(err) => { warn!( target: "sequencer", diff --git a/rust/kona/crates/node/service/src/actors/sequencer/origin_selector.rs b/rust/kona/crates/node/service/src/actors/sequencer/origin_selector.rs index 5ff8cdd8631..097e4eb0b37 100644 --- a/rust/kona/crates/node/service/src/actors/sequencer/origin_selector.rs +++ b/rust/kona/crates/node/service/src/actors/sequencer/origin_selector.rs @@ -70,7 +70,7 @@ impl OriginSelector for L1OriginSelec } let Some(current) = self.current else { - unreachable!("Current L1 origin should always be set by `select_origins`"); + return Err(L1OriginSelectorError::OriginNotFound(unsafe_head.l1_origin.hash)); }; let max_seq_drift = self.cfg.max_sequencer_drift(current.timestamp); @@ -128,7 +128,12 @@ impl L1OriginSelector

{ in_recovery_mode: bool, ) -> Result<(), L1OriginSelectorError> { if in_recovery_mode { - self.current = self.l1.get_block_by_hash(unsafe_head.l1_origin.hash).await?; + self.current = Some( + self.l1 + .get_block_by_hash(unsafe_head.l1_origin.hash) + .await? + .ok_or(L1OriginSelectorError::OriginNotFound(unsafe_head.l1_origin.hash))?, + ); self.next = self.l1.get_block_by_number(unsafe_head.l1_origin.number + 1).await?; return Ok(()); } @@ -141,9 +146,12 @@ impl L1OriginSelector

{ self.next = None; } else { // Find the current origin block, as it is missing. - let current = self.l1.get_block_by_hash(unsafe_head.l1_origin.hash).await?; - - self.current = current; + self.current = Some( + self.l1 + .get_block_by_hash(unsafe_head.l1_origin.hash) + .await? + .ok_or(L1OriginSelectorError::OriginNotFound(unsafe_head.l1_origin.hash))?, + ); self.next = None; } @@ -185,6 +193,9 @@ pub enum L1OriginSelectorError { "Waiting for more L1 data to be available to select the next L1 origin block. Current L1 origin: {0:?}" )] NotEnoughData(BlockInfo), + /// The L1 origin block could not be found by its hash. + #[error("L1 origin block not found for hash: {0}")] + OriginNotFound(B256), } /// L1 [`BlockInfo`] provider interface for the [`L1OriginSelector`]. @@ -493,4 +504,129 @@ mod test { assert!(matches!(next_err, L1OriginSelectorError::NotEnoughData(_))); } } + + #[tokio::test] + async fn test_next_l1_origin_recovery_mode_found() { + const L2_BLOCK_TIME: u64 = 2; + + let cfg = Arc::new(RollupConfig { + block_time: L2_BLOCK_TIME, + max_sequencer_drift: 600, + ..Default::default() + }); + + let mut provider = MockOriginSelectorProvider::default(); + provider.with_block(BlockInfo { + parent_hash: B256::ZERO, + hash: B256::with_last_byte(1), + number: 1, + timestamp: 12, + }); + provider.with_block(BlockInfo { + parent_hash: B256::with_last_byte(1), + hash: B256::with_last_byte(2), + number: 2, + timestamp: 24, + }); + + let mut selector = L1OriginSelector::new(cfg, provider); + + let unsafe_head = L2BlockInfo { + block_info: BlockInfo { + hash: B256::ZERO, + number: 5, + timestamp: 10, + ..Default::default() + }, + l1_origin: NumHash { number: 1, hash: B256::with_last_byte(1) }, + seq_num: 0, + }; + + let origin = selector.next_l1_origin(unsafe_head, true).await.unwrap(); + assert_eq!(origin.number, 1); + assert_eq!(origin.hash, B256::with_last_byte(1)); + } + + #[tokio::test] + async fn test_next_l1_origin_recovery_mode_not_found() { + const L2_BLOCK_TIME: u64 = 2; + + let cfg = Arc::new(RollupConfig { + block_time: L2_BLOCK_TIME, + max_sequencer_drift: 600, + ..Default::default() + }); + + let provider = MockOriginSelectorProvider::default(); + let mut selector = L1OriginSelector::new(cfg, provider); + + let unsafe_head = L2BlockInfo { + block_info: BlockInfo { + hash: B256::ZERO, + number: 5, + timestamp: 10, + ..Default::default() + }, + l1_origin: NumHash { number: 1, hash: B256::with_last_byte(1) }, + seq_num: 0, + }; + + let result = selector.next_l1_origin(unsafe_head, true).await; + assert!(matches!( + result, + Err(L1OriginSelectorError::OriginNotFound(hash)) if hash == B256::with_last_byte(1) + )); + } + + #[tokio::test] + async fn test_next_l1_origin_normal_mode_origin_not_found() { + const L2_BLOCK_TIME: u64 = 2; + + let cfg = Arc::new(RollupConfig { + block_time: L2_BLOCK_TIME, + max_sequencer_drift: 600, + ..Default::default() + }); + + let mut provider = MockOriginSelectorProvider::default(); + provider.with_block(BlockInfo { + parent_hash: B256::ZERO, + hash: B256::ZERO, + number: 0, + timestamp: 0, + }); + + let mut selector = L1OriginSelector::new(cfg, provider); + + // First call: set current to block 0. + let unsafe_head_epoch0 = L2BlockInfo { + block_info: BlockInfo { + hash: B256::ZERO, + number: 0, + timestamp: 0, + ..Default::default() + }, + l1_origin: NumHash { number: 0, hash: B256::ZERO }, + seq_num: 0, + }; + let _ = selector.next_l1_origin(unsafe_head_epoch0, false).await.unwrap(); + + // Second call: reference a non-existent L1 origin hash, triggering the else branch. + let unsafe_head_missing = L2BlockInfo { + block_info: BlockInfo { + hash: B256::ZERO, + number: 1, + timestamp: L2_BLOCK_TIME, + ..Default::default() + }, + l1_origin: NumHash { number: 99, hash: B256::with_last_byte(0xFF) }, + seq_num: 0, + }; + + let result = selector.next_l1_origin(unsafe_head_missing, false).await; + assert!(matches!( + result, + Err(L1OriginSelectorError::OriginNotFound(hash)) if hash == B256::with_last_byte(0xFF) + )); + } } diff --git a/rust/kona/crates/proof/proof-interop/src/boot.rs b/rust/kona/crates/proof/proof-interop/src/boot.rs index 8960648e581..c17e0dae580 100644 --- a/rust/kona/crates/proof/proof-interop/src/boot.rs +++ b/rust/kona/crates/proof/proof-interop/src/boot.rs @@ -133,10 +133,12 @@ impl BootInfo { { chain_ids.iter().map(|id| (*id, ROLLUP_CONFIGS[id].clone())).collect() } else { + let missing_ids: Vec = + chain_ids.into_iter().filter(|id| !ROLLUP_CONFIGS.contains_key(id)).collect(); warn!( target: "boot_loader", "No rollup config found for chain IDs {:?}, falling back to preimage oracle. This is insecure in production without additional validation!", - chain_ids + missing_ids ); let ser_cfg = oracle .get(PreimageKey::new_local(L2_ROLLUP_CONFIG_KEY.to())) diff --git a/rust/kona/crates/protocol/derive/src/stages/batch/batch_queue.rs b/rust/kona/crates/protocol/derive/src/stages/batch/batch_queue.rs index cc337a8ad57..0908607a221 100644 --- a/rust/kona/crates/protocol/derive/src/stages/batch/batch_queue.rs +++ b/rust/kona/crates/protocol/derive/src/stages/batch/batch_queue.rs @@ -484,7 +484,7 @@ mod tests { let file_contents = &(&*file_contents)[..file_contents.len() - 1]; let data = alloy_primitives::hex::decode(file_contents).unwrap(); let bytes: alloy_primitives::Bytes = data.into(); - BatchReader::new(bytes, MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize) + BatchReader::new(bytes, MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize, 0) } #[test] diff --git a/rust/kona/crates/protocol/derive/src/stages/channel/channel_assembler.rs b/rust/kona/crates/protocol/derive/src/stages/channel/channel_assembler.rs index 9c597142945..2eb1275aef8 100644 --- a/rust/kona/crates/protocol/derive/src/stages/channel/channel_assembler.rs +++ b/rust/kona/crates/protocol/derive/src/stages/channel/channel_assembler.rs @@ -14,14 +14,13 @@ use core::fmt::Debug; use kona_genesis::{ MAX_RLP_BYTES_PER_CHANNEL_BEDROCK, MAX_RLP_BYTES_PER_CHANNEL_FJORD, RollupConfig, SystemConfig, }; -use kona_protocol::{BlockInfo, Channel}; +use kona_protocol::{BlockInfo, OrderedChannel}; /// The [`ChannelAssembler`] stage is responsible for assembling the [`Frame`]s from the -/// [`FrameQueue`] stage into a raw compressed [`Channel`]. +/// [`FrameQueue`] stage into a raw compressed [`OrderedChannel`]. /// /// [`Frame`]: kona_protocol::Frame /// [`FrameQueue`]: crate::stages::FrameQueue -/// [`Channel`]: kona_protocol::Channel #[derive(Debug)] pub struct ChannelAssembler

where @@ -31,8 +30,8 @@ where pub cfg: Arc, /// The previous stage of the derivation pipeline. pub prev: P, - /// The current [`Channel`] being assembled. - pub channel: Option, + /// The current [`OrderedChannel`] being assembled. + pub channel: Option, } impl

ChannelAssembler

@@ -90,7 +89,7 @@ where hex::encode(next_frame.id), origin.number ); - self.channel = Some(Channel::new(next_frame.id, origin)); + self.channel = Some(OrderedChannel::new(next_frame.id, origin)); } let _count = if self.channel.is_some() { 1 } else { 0 }; @@ -148,7 +147,7 @@ where // If the channel is ready, forward the channel to the next stage. if channel.is_ready() { let channel_bytes = - channel.frame_data().ok_or(PipelineError::ChannelNotFound.crit())?; + channel.data().map_err(|_| PipelineError::ChannelNotFound.crit())?; info!( target: "channel_assembler", diff --git a/rust/kona/crates/protocol/derive/src/stages/channel/channel_reader.rs b/rust/kona/crates/protocol/derive/src/stages/channel/channel_reader.rs index cadfe05e92b..ae5afc3253c 100644 --- a/rust/kona/crates/protocol/derive/src/stages/channel/channel_reader.rs +++ b/rust/kona/crates/protocol/derive/src/stages/channel/channel_reader.rs @@ -68,8 +68,11 @@ where MAX_RLP_BYTES_PER_CHANNEL_BEDROCK }; - self.next_batch = - Some(BatchReader::new(&channel[..], max_rlp_bytes_per_channel as usize)); + self.next_batch = Some(BatchReader::new( + &channel[..], + max_rlp_bytes_per_channel as usize, + origin.timestamp, + )); kona_macros::set!(gauge, crate::metrics::Metrics::PIPELINE_BATCH_READER_SET, 1); } Ok(()) @@ -227,6 +230,7 @@ mod test { reader.next_batch = Some(BatchReader::new( new_compressed_batch_data(), MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize, + 0, )); reader.flush_channel().await.unwrap(); assert!(reader.next_batch.is_none()); @@ -239,6 +243,7 @@ mod test { reader.next_batch = Some(BatchReader::new( vec![0x00, 0x01, 0x02], MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize, + 0, )); assert!(!reader.prev.reset); reader.reset(BlockNumHash::default(), SystemConfig::default()).await.unwrap(); diff --git a/rust/kona/crates/protocol/derive/src/stages/traversal/indexed.rs b/rust/kona/crates/protocol/derive/src/stages/traversal/indexed.rs index 6c7f0cfb9e3..40253c46819 100644 --- a/rust/kona/crates/protocol/derive/src/stages/traversal/indexed.rs +++ b/rust/kona/crates/protocol/derive/src/stages/traversal/indexed.rs @@ -99,9 +99,17 @@ impl IndexedTraversal { block_info.number, ); + let prev_block_holocene = self.rollup_config.is_holocene_active(block.timestamp); + let next_block_holocene = self.rollup_config.is_holocene_active(block_info.timestamp); + // Update the origin block. self.update_origin(block_info); + // If Holocene activates on this block, flag it so the pipeline driver resets. + if !prev_block_holocene && next_block_holocene { + return Err(ResetError::HoloceneActivation.reset()); + } + Ok(()) } } @@ -157,7 +165,7 @@ mod tests { use alloc::vec; use alloy_consensus::Receipt; use alloy_primitives::{B256, Bytes, Log, LogData, address, b256, hex}; - use kona_genesis::{CONFIG_UPDATE_EVENT_VERSION_0, CONFIG_UPDATE_TOPIC}; + use kona_genesis::{CONFIG_UPDATE_EVENT_VERSION_0, CONFIG_UPDATE_TOPIC, HardForkConfig}; const L1_SYS_CONFIG_ADDR: Address = address!("1337000000000000000000000000000000000000"); @@ -324,4 +332,32 @@ mod tests { let expected = address!("000000000000000000000000000000000000bEEF"); assert_eq!(traversal.system_config.batcher_address, expected); } + + #[tokio::test] + async fn test_managed_traversal_holocene_activation_reset() { + let first = b256!("3333333333333333333333333333333333333333333333333333333333333333"); + let second = b256!("4444444444444444444444444444444444444444444444444444444444444444"); + // Block before Holocene activation (timestamp 99), block at activation (timestamp 100). + let block1 = BlockInfo { hash: first, timestamp: 99, ..BlockInfo::default() }; + let block2 = BlockInfo { number: 1, hash: second, parent_hash: first, timestamp: 100 }; + + let mut provider = TestChainProvider::default(); + provider.insert_block(0, block1); + provider.insert_block(1, block2); + provider.insert_receipts(second, vec![]); + + let rollup_config = RollupConfig { + l1_system_config_address: L1_SYS_CONFIG_ADDR, + hardforks: HardForkConfig { holocene_time: Some(100), ..Default::default() }, + ..RollupConfig::default() + }; + let mut traversal = IndexedTraversal::new(provider, Arc::new(rollup_config)); + traversal.block = Some(block1); + traversal.done = true; + + let err = traversal.provide_next_block(block2).await.unwrap_err(); + assert_eq!(err, ResetError::HoloceneActivation.reset()); + // Origin should still be updated despite the reset error. + assert_eq!(traversal.origin(), Some(block2)); + } } diff --git a/rust/kona/crates/protocol/genesis/Cargo.toml b/rust/kona/crates/protocol/genesis/Cargo.toml index 87446eca090..0e0d017d056 100644 --- a/rust/kona/crates/protocol/genesis/Cargo.toml +++ b/rust/kona/crates/protocol/genesis/Cargo.toml @@ -51,6 +51,7 @@ alloy-primitives = { workspace = true, features = ["rand", "arbitrary"] } [features] default = [] +rollup_config_override = [] revm = [ "dep:op-revm" ] tabled = [ "dep:tabled", "std" ] std = [ diff --git a/rust/kona/crates/protocol/genesis/src/chain/config.rs b/rust/kona/crates/protocol/genesis/src/chain/config.rs index b2cf56dc440..8a1958526ed 100644 --- a/rust/kona/crates/protocol/genesis/src/chain/config.rs +++ b/rust/kona/crates/protocol/genesis/src/chain/config.rs @@ -5,6 +5,8 @@ use alloy_chains::Chain; use alloy_eips::eip1559::BaseFeeParams; use alloy_primitives::Address; +#[cfg(feature = "rollup_config_override")] +use crate::FJORD_MAX_SEQUENCER_DRIFT; use crate::{ AddressList, AltDAConfig, BaseFeeConfig, ChainGenesis, GRANITE_CHANNEL_TIMEOUT, HardForkConfig, Roles, RollupConfig, SuperchainLevel, base_fee_params, base_fee_params_canyon, @@ -176,6 +178,8 @@ impl ChainConfig { // necessary. channel_timeout: 300, granite_channel_timeout: GRANITE_CHANNEL_TIMEOUT, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, chain_op_config: self.base_fee_config(), alt_da_config: self.alt_da.clone(), } diff --git a/rust/kona/crates/protocol/genesis/src/rollup.rs b/rust/kona/crates/protocol/genesis/src/rollup.rs index a3785e25c4a..4c88d69b0f6 100644 --- a/rust/kona/crates/protocol/genesis/src/rollup.rs +++ b/rust/kona/crates/protocol/genesis/src/rollup.rs @@ -23,6 +23,14 @@ const fn default_granite_channel_timeout() -> u64 { GRANITE_CHANNEL_TIMEOUT } +/// The max sequencer drift needs to be changes for some chains, e.g. those that build only on +/// finalized L1 blocks, where L1 finality delays can exceed the standard +/// [`FJORD_MAX_SEQUENCER_DRIFT`]. +#[cfg(all(feature = "serde", feature = "rollup_config_override"))] +const fn default_fjord_max_sequencer_drift() -> u64 { + FJORD_MAX_SEQUENCER_DRIFT +} + /// The Rollup configuration. #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -47,6 +55,10 @@ pub struct RollupConfig { /// The channel timeout after the Granite hardfork. #[cfg_attr(feature = "serde", serde(default = "default_granite_channel_timeout"))] pub granite_channel_timeout: u64, + /// The max sequencer drift after the Fjord hardfork. + #[cfg(feature = "rollup_config_override")] + #[cfg_attr(feature = "serde", serde(default = "default_fjord_max_sequencer_drift"))] + pub fjord_max_sequencer_drift: u64, /// The L1 chain ID pub l1_chain_id: u64, /// The L2 chain ID @@ -103,6 +115,8 @@ impl<'a> arbitrary::Arbitrary<'a> for RollupConfig { seq_window_size: u.arbitrary()?, channel_timeout: u.arbitrary()?, granite_channel_timeout: u.arbitrary()?, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: u.arbitrary()?, l1_chain_id: u.arbitrary()?, l2_chain_id: u.arbitrary()?, hardforks: HardForkConfig::arbitrary(u)?, @@ -129,6 +143,8 @@ impl Default for RollupConfig { seq_window_size: 0, channel_timeout: 0, granite_channel_timeout: GRANITE_CHANNEL_TIMEOUT, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, l1_chain_id: 0, l2_chain_id: Chain::from_id(0), hardforks: HardForkConfig::default(), @@ -329,10 +345,12 @@ impl RollupConfig { /// Returns the max sequencer drift for the given timestamp. pub fn max_sequencer_drift(&self, timestamp: u64) -> u64 { if self.is_fjord_active(timestamp) { - FJORD_MAX_SEQUENCER_DRIFT - } else { - self.max_sequencer_drift + #[cfg(feature = "rollup_config_override")] + return self.fjord_max_sequencer_drift; + #[cfg(not(feature = "rollup_config_override"))] + return FJORD_MAX_SEQUENCER_DRIFT; } + self.max_sequencer_drift } /// Returns the max rlp bytes per channel for the given timestamp. @@ -890,6 +908,8 @@ mod tests { seq_window_size: 3600, channel_timeout: 300, granite_channel_timeout: GRANITE_CHANNEL_TIMEOUT, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, l1_chain_id: 3151908, l2_chain_id: Chain::from_id(1337), hardforks: HardForkConfig { @@ -975,4 +995,41 @@ mod tests { assert_eq!(cfg.block_number_from_timestamp(20), 5); assert_eq!(cfg.block_number_from_timestamp(30), 10); } + + #[cfg(feature = "rollup_config_override")] + mod rollup_config_override_tests { + use super::*; + + #[test] + fn test_max_sequencer_drift_override() { + let mut config = RollupConfig { + max_sequencer_drift: 100, + fjord_max_sequencer_drift: 2892, + hardforks: HardForkConfig { fjord_time: Some(10), ..Default::default() }, + ..Default::default() + }; + assert_eq!(config.max_sequencer_drift(0), 100); + assert_eq!(config.max_sequencer_drift(10), 2892); + config.fjord_max_sequencer_drift = 3600; + assert_eq!(config.max_sequencer_drift(10), 3600); + } + + #[test] + #[cfg(feature = "serde")] + fn test_serde_fjord_max_sequencer_drift_override() { + // Default value survives round-trip. + let config = RollupConfig::default(); + assert_eq!(config.fjord_max_sequencer_drift, FJORD_MAX_SEQUENCER_DRIFT); + let serialized = serde_json::to_string(&config).unwrap(); + let deserialized: RollupConfig = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized.fjord_max_sequencer_drift, FJORD_MAX_SEQUENCER_DRIFT); + + // Custom value survives round-trip. + let mut config = config; + config.fjord_max_sequencer_drift = 2892; + let serialized = serde_json::to_string(&config).unwrap(); + let deserialized: RollupConfig = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized.fjord_max_sequencer_drift, 2892); + } + } } diff --git a/rust/kona/crates/protocol/hardforks/Cargo.toml b/rust/kona/crates/protocol/hardforks/Cargo.toml index 0210f18d8c1..4547c63b004 100644 --- a/rust/kona/crates/protocol/hardforks/Cargo.toml +++ b/rust/kona/crates/protocol/hardforks/Cargo.toml @@ -27,8 +27,8 @@ op-alloy-consensus.workspace = true [dev-dependencies] alloy-primitives = { workspace = true, features = ["rand", "arbitrary"] } -revm = { version = "34.0.0", default-features = false } -op-revm = { version = "15.0.0", default-features = false } +revm.workspace = true +op-revm.workspace = true [features] default = [] diff --git a/rust/kona/crates/protocol/protocol/src/batch/core.rs b/rust/kona/crates/protocol/protocol/src/batch/core.rs index 7ac31bb08ac..0ebb9a50513 100644 --- a/rust/kona/crates/protocol/protocol/src/batch/core.rs +++ b/rust/kona/crates/protocol/protocol/src/batch/core.rs @@ -42,7 +42,7 @@ impl Batch { } // Read the batch type - let batch_type = BatchType::from(r[0]); + let batch_type = BatchType::try_from(r[0]).map_err(BatchDecodingError::UnknownBatchType)?; r.advance(1); match batch_type { @@ -131,6 +131,13 @@ mod tests { }), decoded); } + #[test] + fn test_unknown_batch_type_returns_error() { + let data = [0xFF, 0x00]; // unknown batch type 0xFF followed by dummy data + let result = Batch::decode(&mut data.as_slice(), &RollupConfig::default()); + assert_eq!(result, Err(BatchDecodingError::UnknownBatchType(0xFF))); + } + #[test] fn test_empty_span_batch() { let mut out = Vec::new(); diff --git a/rust/kona/crates/protocol/protocol/src/batch/errors.rs b/rust/kona/crates/protocol/protocol/src/batch/errors.rs index 33f621af5de..650c6949b28 100644 --- a/rust/kona/crates/protocol/protocol/src/batch/errors.rs +++ b/rust/kona/crates/protocol/protocol/src/batch/errors.rs @@ -40,6 +40,9 @@ pub enum BatchDecodingError { /// Empty buffer #[error("Empty buffer")] EmptyBuffer, + /// Unknown batch type + #[error("Unknown batch type: {0}")] + UnknownBatchType(u8), /// Error decoding an Alloy RLP #[error("Error decoding an Alloy RLP: {0}")] AlloyRlpError(alloy_rlp::Error), diff --git a/rust/kona/crates/protocol/protocol/src/batch/reader.rs b/rust/kona/crates/protocol/protocol/src/batch/reader.rs index 1a32205eb53..23ffabb7a20 100644 --- a/rust/kona/crates/protocol/protocol/src/batch/reader.rs +++ b/rust/kona/crates/protocol/protocol/src/batch/reader.rs @@ -25,7 +25,7 @@ pub enum DecompressionError { } /// Batch Reader provides a function that iteratively consumes batches from the reader. -/// The `L1Inclusion` block is also provided at creation time. +/// The L1 origin timestamp is provided at creation time and used for hardfork activation checks. /// Warning: the batch reader can read every batch-type. /// The caller of the batch-reader should filter the results. #[derive(Debug)] @@ -40,6 +40,8 @@ pub struct BatchReader { pub max_rlp_bytes_per_channel: usize, /// Whether brotli decompression was used. pub brotli_used: bool, + /// The L1 origin block timestamp, used for hardfork activation checks. + pub origin_timestamp: u64, } impl BatchReader { @@ -52,9 +54,9 @@ impl BatchReader { /// Brotli Compression Channel Version. pub const CHANNEL_VERSION_BROTLI: u8 = 1; - /// Creates a new [`BatchReader`] from the given data and max decompressed RLP bytes per - /// channel. - pub fn new(data: T, max_rlp_bytes_per_channel: usize) -> Self + /// Creates a new [`BatchReader`] from the given data, max decompressed RLP bytes per + /// channel, and the L1 origin block timestamp (used for hardfork activation checks). + pub fn new(data: T, max_rlp_bytes_per_channel: usize, origin_timestamp: u64) -> Self where T: Into>, { @@ -64,6 +66,7 @@ impl BatchReader { cursor: 0, max_rlp_bytes_per_channel, brotli_used: false, + origin_timestamp, } } @@ -133,8 +136,8 @@ impl BatchReader { return None; }; - // Confirm that brotli decompression was performed *after* the Fjord hardfork. - if self.brotli_used && !cfg.is_fjord_active(batch.timestamp()) { + // Accept brotli only after Fjord activation (per L1 origin timestamp). + if self.brotli_used && !cfg.is_fjord_active(self.origin_timestamp) { return None; } @@ -167,7 +170,7 @@ mod test { fn test_batch_reader() { let raw = new_compressed_batch_data(); let decompressed_len = decompress_to_vec_zlib(&raw).unwrap().len(); - let mut reader = BatchReader::new(raw, MAX_RLP_BYTES_PER_CHANNEL_BEDROCK as usize); + let mut reader = BatchReader::new(raw, MAX_RLP_BYTES_PER_CHANNEL_BEDROCK as usize, 0); reader.next_batch(&RollupConfig::default()).unwrap(); assert_eq!(reader.cursor, decompressed_len); } @@ -176,7 +179,7 @@ mod test { fn test_batch_reader_fjord() { let raw = new_compressed_batch_data(); let decompressed_len = decompress_to_vec_zlib(&raw).unwrap().len(); - let mut reader = BatchReader::new(raw, MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize); + let mut reader = BatchReader::new(raw, MAX_RLP_BYTES_PER_CHANNEL_FJORD as usize, 0); reader .next_batch(&RollupConfig { hardforks: HardForkConfig { fjord_time: Some(0), ..Default::default() }, @@ -208,7 +211,7 @@ mod test { // Set limit below decompressed size — should truncate, not error. let limit = decompressed_len / 2; - let mut reader = BatchReader::new(raw, limit); + let mut reader = BatchReader::new(raw, limit, 0); assert!(reader.decompress().is_ok()); assert_eq!(reader.decompressed.len(), limit); } @@ -220,7 +223,7 @@ mod test { let single_batch_len = full_len / n; // Full decompression should yield all n batches. - let mut reader = BatchReader::new(compressed.clone(), full_len); + let mut reader = BatchReader::new(compressed.clone(), full_len, 0); let mut count = 0; while reader.next_batch(&RollupConfig::default()).is_some() { count += 1; @@ -229,7 +232,7 @@ mod test { // Truncate to just under the last batch — should yield n-1 batches. let limit = full_len - 1; - let mut reader = BatchReader::new(compressed, limit); + let mut reader = BatchReader::new(compressed, limit, 0); let mut count = 0; while reader.next_batch(&RollupConfig::default()).is_some() { count += 1; diff --git a/rust/kona/crates/protocol/protocol/src/batch/type.rs b/rust/kona/crates/protocol/protocol/src/batch/type.rs index 9bb9a481d9c..7ce2cacf96e 100644 --- a/rust/kona/crates/protocol/protocol/src/batch/type.rs +++ b/rust/kona/crates/protocol/protocol/src/batch/type.rs @@ -28,12 +28,14 @@ pub enum BatchType { Span = SPAN_BATCH_TYPE, } -impl From for BatchType { - fn from(val: u8) -> Self { +impl TryFrom for BatchType { + type Error = u8; + + fn try_from(val: u8) -> Result { match val { - SINGLE_BATCH_TYPE => Self::Single, - SPAN_BATCH_TYPE => Self::Span, - _ => panic!("Invalid batch type: {val}"), + SINGLE_BATCH_TYPE => Ok(Self::Single), + SPAN_BATCH_TYPE => Ok(Self::Span), + _ => Err(val), } } } @@ -51,7 +53,7 @@ impl Encodable for BatchType { impl Decodable for BatchType { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { let val = u8::decode(buf)?; - Ok(Self::from(val)) + Self::try_from(val).map_err(|_| alloy_rlp::Error::Custom("invalid batch type")) } } @@ -68,4 +70,25 @@ mod test { let decoded = BatchType::decode(&mut buf.as_slice()).unwrap(); assert_eq!(batch_type, decoded); } + + #[test] + fn test_try_from_valid_types() { + assert_eq!(BatchType::try_from(SINGLE_BATCH_TYPE), Ok(BatchType::Single)); + assert_eq!(BatchType::try_from(SPAN_BATCH_TYPE), Ok(BatchType::Span)); + } + + #[test] + fn test_try_from_unknown_type_returns_error() { + assert_eq!(BatchType::try_from(0xFF), Err(0xFF)); + assert_eq!(BatchType::try_from(0x02), Err(0x02)); + } + + #[test] + fn test_rlp_decode_unknown_type_returns_error() { + let mut buf = Vec::new(); + // RLP-encode an invalid batch type byte + 0xFFu8.encode(&mut buf); + let result = BatchType::decode(&mut buf.as_slice()); + assert!(result.is_err()); + } } diff --git a/rust/kona/crates/protocol/protocol/src/channel.rs b/rust/kona/crates/protocol/protocol/src/channel.rs index 65e53b1e7aa..52eb39f5cef 100644 --- a/rust/kona/crates/protocol/protocol/src/channel.rs +++ b/rust/kona/crates/protocol/protocol/src/channel.rs @@ -34,6 +34,14 @@ pub enum ChannelError { /// The frame number is beyond the end frame. #[error("Frame number {0} is beyond end frame")] FrameBeyondEndFrame(usize), + /// The frame is out of order. + #[error("Frame out of order: expected {expected}, got {got}")] + FrameOutOfOrder { + /// The expected frame number. + expected: u16, + /// The actual frame number. + got: u16, + }, } /// A Channel is a set of batches that are split into at least one, but possibly multiple frames. diff --git a/rust/kona/crates/protocol/protocol/src/lib.rs b/rust/kona/crates/protocol/protocol/src/lib.rs index 7a8c05facc6..4cf537a49ad 100644 --- a/rust/kona/crates/protocol/protocol/src/lib.rs +++ b/rust/kona/crates/protocol/protocol/src/lib.rs @@ -49,6 +49,9 @@ pub use channel::{ MAX_RLP_BYTES_PER_CHANNEL, }; +mod ordered_channel; +pub use ordered_channel::{OrderedChannel, ReadError}; + mod deposits; pub use deposits::{ DEPOSIT_EVENT_ABI, DEPOSIT_EVENT_ABI_HASH, DEPOSIT_EVENT_VERSION_0, DepositError, diff --git a/rust/kona/crates/protocol/protocol/src/ordered_channel.rs b/rust/kona/crates/protocol/protocol/src/ordered_channel.rs new file mode 100644 index 00000000000..07ead660af5 --- /dev/null +++ b/rust/kona/crates/protocol/protocol/src/ordered_channel.rs @@ -0,0 +1,285 @@ +//! Ordered Channel Type +//! +//! An [`OrderedChannel`] enforces strict sequential frame ordering, rejecting any frame whose +//! number does not match the expected next frame. This is the post-Holocene channel type, matching +//! `op-node`'s `requireInOrder` behavior. + +use alloc::vec::Vec; +use alloy_primitives::Bytes; + +use crate::{BlockInfo, ChannelError, ChannelId, Frame}; + +/// An error returned when reading data from an [`OrderedChannel`]. +#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ReadError { + /// The channel is not ready (not all frames have been received). + #[error("Channel is not ready")] + NotReady, + /// The channel has no frames. + #[error("Channel is empty")] + Empty, +} + +/// An ordered channel that enforces strict sequential frame ingestion. +/// +/// Unlike [`Channel`], which accepts frames out of order and checks contiguity at read time, +/// `OrderedChannel` rejects any frame whose number does not equal the current frame count. +/// This matches `op-node`'s Holocene behavior where `requireInOrder` is true. +/// +/// [`Channel`]: crate::Channel +#[derive(Debug, Clone)] +pub struct OrderedChannel { + /// The unique identifier for this channel. + pub id: ChannelId, + /// The block that the channel was opened at. + pub open_block: BlockInfo, + /// Estimated memory size, used to drop the channel if we have too much data. + pub estimated_size: usize, + /// True if the last frame has been buffered. + pub closed: bool, + /// Frames stored in sequential order. + pub inputs: Vec, + /// The highest L1 inclusion block that a frame was included in. + pub highest_l1_inclusion_block: BlockInfo, +} + +impl OrderedChannel { + /// Create a new [`OrderedChannel`] with the given [`ChannelId`] and [`BlockInfo`]. + pub fn new(id: ChannelId, open_block: BlockInfo) -> Self { + Self { + id, + open_block, + estimated_size: 0, + closed: false, + inputs: Vec::new(), + highest_l1_inclusion_block: BlockInfo::default(), + } + } + + /// Returns the [`ChannelId`]. + pub const fn id(&self) -> ChannelId { + self.id + } + + /// Returns the number of frames ingested. + pub const fn len(&self) -> usize { + self.inputs.len() + } + + /// Returns if the channel is empty. + pub const fn is_empty(&self) -> bool { + self.inputs.is_empty() + } + + /// Returns the block number of the L1 block that contained the first [`Frame`]. + pub const fn open_block_number(&self) -> u64 { + self.open_block.number + } + + /// Returns the estimated size of the channel including [`Frame`] overhead. + pub const fn size(&self) -> usize { + self.estimated_size + } + + /// Add a frame to the channel. The frame number must equal the current frame count + /// (strict sequential ordering). + pub fn add_frame( + &mut self, + frame: Frame, + l1_inclusion_block: BlockInfo, + ) -> Result<(), ChannelError> { + if frame.id != self.id { + return Err(ChannelError::FrameIdMismatch); + } + if self.closed { + return Err(ChannelError::ChannelClosed); + } + + let expected = self.inputs.len() as u16; + if frame.number != expected { + return Err(ChannelError::FrameOutOfOrder { expected, got: frame.number }); + } + + if frame.is_last { + self.closed = true; + } + + if self.highest_l1_inclusion_block.number < l1_inclusion_block.number { + self.highest_l1_inclusion_block = l1_inclusion_block; + } + + self.estimated_size += frame.size(); + self.inputs.push(frame); + Ok(()) + } + + /// Returns `true` if the channel is ready to be read. + /// Since frames are ingested in order, the channel is ready as soon as it is closed. + pub const fn is_ready(&self) -> bool { + self.closed + } + + /// Returns all of the channel's [`Frame`] data concatenated together. + /// + /// Returns an error if the channel is empty or not yet ready. + pub fn data(&self) -> Result { + if self.inputs.is_empty() { + return Err(ReadError::Empty); + } + if !self.closed { + return Err(ReadError::NotReady); + } + Ok(self.inputs.iter().flat_map(|f| &f.data).copied().collect::>().into()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::string::ToString; + + fn test_id() -> ChannelId { + [0xFF; 16] + } + + fn frame(id: ChannelId, number: u16, data: &[u8], is_last: bool) -> Frame { + Frame { id, number, data: data.to_vec(), is_last } + } + + #[test] + fn test_ordered_channel_accessors() { + let id = test_id(); + let block = BlockInfo { number: 42, timestamp: 0, ..Default::default() }; + let channel = OrderedChannel::new(id, block); + + assert_eq!(channel.id(), id); + assert_eq!(channel.open_block_number(), 42); + assert_eq!(channel.size(), 0); + assert_eq!(channel.len(), 0); + assert!(channel.is_empty()); + assert!(!channel.is_ready()); + } + + #[test] + fn test_ordered_frames_accepted() { + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + assert!(channel.add_frame(frame(id, 0, b"hello", false), block).is_ok()); + assert!(channel.add_frame(frame(id, 1, b"world", true), block).is_ok()); + assert!(channel.is_ready()); + assert_eq!(channel.len(), 2); + assert_eq!(channel.data().unwrap().as_ref(), b"helloworld"); + } + + #[test] + fn test_wrong_channel_id_rejected() { + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + let err = channel.add_frame(frame([0xEE; 16], 0, b"bad", false), block).unwrap_err(); + assert_eq!(err, ChannelError::FrameIdMismatch); + } + + #[test] + fn test_out_of_order_frame_rejected() { + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + // Frame 0 succeeds + assert!(channel.add_frame(frame(id, 0, b"first", false), block).is_ok()); + + // Frame 2 (skipping 1) is rejected + let err = channel.add_frame(frame(id, 2, b"skip", false), block).unwrap_err(); + assert_eq!(err, ChannelError::FrameOutOfOrder { expected: 1, got: 2 }); + assert_eq!(channel.len(), 1); + } + + #[test] + fn test_frame_after_close_rejected() { + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + assert!(channel.add_frame(frame(id, 0, b"only", true), block).is_ok()); + assert!(channel.is_ready()); + + let err = channel.add_frame(frame(id, 1, b"extra", false), block).unwrap_err(); + assert_eq!(err, ChannelError::ChannelClosed); + } + + #[test] + fn test_attack_scenario_cross_tx_out_of_order() { + // Attack from the finding: T1=[F0,F1,F2], T2=[F4,F5,F6(is_last)], T3=[F3] + // OrderedChannel should accept F0-F2 then reject F4 (expected F3). + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + // T1 frames arrive in order + assert!(channel.add_frame(frame(id, 0, b"f0", false), block).is_ok()); + assert!(channel.add_frame(frame(id, 1, b"f1", false), block).is_ok()); + assert!(channel.add_frame(frame(id, 2, b"f2", false), block).is_ok()); + + // T2 starts at frame 4 — out of order, rejected + let err = channel.add_frame(frame(id, 4, b"f4", false), block).unwrap_err(); + assert_eq!(err, ChannelError::FrameOutOfOrder { expected: 3, got: 4 }); + + // Channel is not ready and not closed + assert!(!channel.is_ready()); + assert_eq!(channel.len(), 3); + } + + #[test] + fn test_single_frame_channel() { + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + assert!(channel.add_frame(frame(id, 0, b"all", true), block).is_ok()); + assert!(channel.is_ready()); + assert_eq!(channel.data().unwrap().as_ref(), b"all"); + } + + #[test] + fn test_data_empty_channel() { + let id = test_id(); + let block = BlockInfo::default(); + let channel = OrderedChannel::new(id, block); + + assert_eq!(channel.data(), Err(ReadError::Empty)); + } + + #[test] + fn test_data_not_ready() { + let id = test_id(); + let block = BlockInfo::default(); + let mut channel = OrderedChannel::new(id, block); + + assert!(channel.add_frame(frame(id, 0, b"partial", false), block).is_ok()); + assert_eq!(channel.data(), Err(ReadError::NotReady)); + } + + #[test] + fn test_l1_inclusion_block_tracking() { + let id = test_id(); + let block1 = BlockInfo { number: 10, ..Default::default() }; + let block2 = BlockInfo { number: 20, ..Default::default() }; + let mut channel = OrderedChannel::new(id, block1); + + assert!(channel.add_frame(frame(id, 0, b"a", false), block1).is_ok()); + assert_eq!(channel.highest_l1_inclusion_block.number, 10); + + assert!(channel.add_frame(frame(id, 1, b"b", true), block2).is_ok()); + assert_eq!(channel.highest_l1_inclusion_block.number, 20); + } + + #[test] + fn test_error_display() { + let err = ChannelError::FrameOutOfOrder { expected: 3, got: 5 }; + assert_eq!(err.to_string(), "Frame out of order: expected 3, got 5"); + } +} diff --git a/rust/kona/crates/protocol/registry/Cargo.toml b/rust/kona/crates/protocol/registry/Cargo.toml index ffe2511d58d..71611127e0f 100644 --- a/rust/kona/crates/protocol/registry/Cargo.toml +++ b/rust/kona/crates/protocol/registry/Cargo.toml @@ -47,6 +47,7 @@ alloy-eips.workspace = true [features] default = [] +rollup_config_override = ["kona-genesis/rollup_config_override"] tabled = [ "dep:tabled", "std" ] std = [ "alloy-chains/std", diff --git a/rust/kona/crates/protocol/registry/src/test_utils/base_mainnet.rs b/rust/kona/crates/protocol/registry/src/test_utils/base_mainnet.rs index 9333caa3f75..6338c580954 100644 --- a/rust/kona/crates/protocol/registry/src/test_utils/base_mainnet.rs +++ b/rust/kona/crates/protocol/registry/src/test_utils/base_mainnet.rs @@ -8,6 +8,8 @@ use alloy_op_hardforks::{ BASE_MAINNET_ISTHMUS_TIMESTAMP, BASE_MAINNET_JOVIAN_TIMESTAMP, }; use alloy_primitives::{address, b256, uint}; +#[cfg(feature = "rollup_config_override")] +use kona_genesis::FJORD_MAX_SEQUENCER_DRIFT; use kona_genesis::{ BASE_MAINNET_BASE_FEE_CONFIG, ChainGenesis, HardForkConfig, RollupConfig, SystemConfig, }; @@ -44,6 +46,8 @@ pub const BASE_MAINNET_CONFIG: RollupConfig = RollupConfig { seq_window_size: 3600, channel_timeout: 300, granite_channel_timeout: 50, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, l1_chain_id: 1, l2_chain_id: Chain::base_mainnet(), hardforks: HardForkConfig { diff --git a/rust/kona/crates/protocol/registry/src/test_utils/base_sepolia.rs b/rust/kona/crates/protocol/registry/src/test_utils/base_sepolia.rs index 613eadbb293..2cbc40c14bc 100644 --- a/rust/kona/crates/protocol/registry/src/test_utils/base_sepolia.rs +++ b/rust/kona/crates/protocol/registry/src/test_utils/base_sepolia.rs @@ -8,6 +8,8 @@ use alloy_op_hardforks::{ BASE_SEPOLIA_ISTHMUS_TIMESTAMP, BASE_SEPOLIA_JOVIAN_TIMESTAMP, }; use alloy_primitives::{address, b256, uint}; +#[cfg(feature = "rollup_config_override")] +use kona_genesis::FJORD_MAX_SEQUENCER_DRIFT; use kona_genesis::{ BASE_SEPOLIA_BASE_FEE_CONFIG, ChainGenesis, HardForkConfig, RollupConfig, SystemConfig, }; @@ -44,6 +46,8 @@ pub const BASE_SEPOLIA_CONFIG: RollupConfig = RollupConfig { seq_window_size: 3600, channel_timeout: 300, granite_channel_timeout: 50, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, l1_chain_id: 11155111, l2_chain_id: Chain::base_sepolia(), chain_op_config: BASE_SEPOLIA_BASE_FEE_CONFIG, diff --git a/rust/kona/crates/protocol/registry/src/test_utils/op_mainnet.rs b/rust/kona/crates/protocol/registry/src/test_utils/op_mainnet.rs index 656501f0e0e..d0f08c36124 100644 --- a/rust/kona/crates/protocol/registry/src/test_utils/op_mainnet.rs +++ b/rust/kona/crates/protocol/registry/src/test_utils/op_mainnet.rs @@ -8,6 +8,8 @@ use alloy_op_hardforks::{ OP_MAINNET_JOVIAN_TIMESTAMP, }; use alloy_primitives::{address, b256, uint}; +#[cfg(feature = "rollup_config_override")] +use kona_genesis::FJORD_MAX_SEQUENCER_DRIFT; use kona_genesis::{ ChainGenesis, HardForkConfig, OP_MAINNET_BASE_FEE_CONFIG, RollupConfig, SystemConfig, }; @@ -44,6 +46,8 @@ pub const OP_MAINNET_CONFIG: RollupConfig = RollupConfig { seq_window_size: 3600_u64, channel_timeout: 300_u64, granite_channel_timeout: 50, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, l1_chain_id: 1_u64, l2_chain_id: Chain::optimism_mainnet(), chain_op_config: OP_MAINNET_BASE_FEE_CONFIG, diff --git a/rust/kona/crates/protocol/registry/src/test_utils/op_sepolia.rs b/rust/kona/crates/protocol/registry/src/test_utils/op_sepolia.rs index b49c3433ecf..8923796817a 100644 --- a/rust/kona/crates/protocol/registry/src/test_utils/op_sepolia.rs +++ b/rust/kona/crates/protocol/registry/src/test_utils/op_sepolia.rs @@ -8,6 +8,8 @@ use alloy_op_hardforks::{ OP_SEPOLIA_JOVIAN_TIMESTAMP, }; use alloy_primitives::{address, b256, uint}; +#[cfg(feature = "rollup_config_override")] +use kona_genesis::FJORD_MAX_SEQUENCER_DRIFT; use kona_genesis::{ ChainGenesis, HardForkConfig, OP_SEPOLIA_BASE_FEE_CONFIG, RollupConfig, SystemConfig, }; @@ -44,6 +46,8 @@ pub const OP_SEPOLIA_CONFIG: RollupConfig = RollupConfig { seq_window_size: 3600, channel_timeout: 300, granite_channel_timeout: 50, + #[cfg(feature = "rollup_config_override")] + fjord_max_sequencer_drift: FJORD_MAX_SEQUENCER_DRIFT, l1_chain_id: 11155111, l2_chain_id: Chain::optimism_sepolia(), chain_op_config: OP_SEPOLIA_BASE_FEE_CONFIG, diff --git a/rust/kona/docker/apps/README.md b/rust/kona/docker/apps/README.md index 12ad4a0b840..0c0c72c03a1 100644 --- a/rust/kona/docker/apps/README.md +++ b/rust/kona/docker/apps/README.md @@ -57,7 +57,7 @@ target "" { } ``` -The [docker release workflow](../../.github/workflows/docker.yaml) will **first** check if a target is available, +The docker release workflow will **first** check if a target is available, using that target for the docker build. If the workflow can't find the target, it will fallback to the "generic" target specified in the [`docker-bake.hcl`](../docker-bake.hcl). diff --git a/rust/op-alloy/crates/consensus/src/lib.rs b/rust/op-alloy/crates/consensus/src/lib.rs index 02a15b95d88..24185307345 100644 --- a/rust/op-alloy/crates/consensus/src/lib.rs +++ b/rust/op-alloy/crates/consensus/src/lib.rs @@ -35,6 +35,9 @@ pub use eip1559::{ decode_jovian_extra_data, encode_holocene_extra_data, encode_jovian_extra_data, }; +pub mod sdm; +pub use sdm::*; + mod source; pub use source::*; diff --git a/rust/op-alloy/crates/consensus/src/receipts/envelope.rs b/rust/op-alloy/crates/consensus/src/receipts/envelope.rs index d0efcea1583..d5d5fba4ac7 100644 --- a/rust/op-alloy/crates/consensus/src/receipts/envelope.rs +++ b/rust/op-alloy/crates/consensus/src/receipts/envelope.rs @@ -42,6 +42,9 @@ pub enum OpReceiptEnvelope { /// [EIP-7702]: https://eips.ethereum.org/EIPS/eip-7702 #[cfg_attr(feature = "serde", serde(rename = "0x4", alias = "0x04"))] Eip7702(ReceiptWithBloom>), + /// Receipt envelope with type flag 125, containing a synthetic post-exec receipt. + #[cfg_attr(feature = "serde", serde(rename = "0x7d", alias = "0x7D"))] + PostExec(ReceiptWithBloom>), /// Receipt envelope with type flag 126, containing a [deposit] receipt. /// /// [deposit]: https://specs.optimism.io/protocol/deposits.html @@ -76,6 +79,9 @@ impl OpReceiptEnvelope { OpTxType::Eip7702 => { Self::Eip7702(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) } + OpTxType::PostExec => { + Self::PostExec(ReceiptWithBloom { receipt: inner_receipt, logs_bloom }) + } OpTxType::Deposit => { let inner = OpDepositReceiptWithBloom { receipt: OpDepositReceipt { @@ -99,6 +105,7 @@ impl OpReceiptEnvelope { Self::Eip2930(_) => OpTxType::Eip2930, Self::Eip1559(_) => OpTxType::Eip1559, Self::Eip7702(_) => OpTxType::Eip7702, + Self::PostExec(_) => OpTxType::PostExec, Self::Deposit(_) => OpTxType::Deposit, } } @@ -127,6 +134,7 @@ impl OpReceiptEnvelope { Self::Eip2930(r) => OpReceiptEnvelope::Eip2930(r.map_logs(f)), Self::Eip1559(r) => OpReceiptEnvelope::Eip1559(r.map_logs(f)), Self::Eip7702(r) => OpReceiptEnvelope::Eip7702(r.map_logs(f)), + Self::PostExec(r) => OpReceiptEnvelope::PostExec(r.map_logs(f)), Self::Deposit(r) => OpReceiptEnvelope::Deposit(r.map_receipt(|r| r.map_logs(f))), } } @@ -144,9 +152,11 @@ impl OpReceiptEnvelope { /// Return the receipt's bloom. pub const fn logs_bloom(&self) -> &Bloom { match self { - Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::Eip7702(t) => { - &t.logs_bloom - } + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::PostExec(t) => &t.logs_bloom, Self::Deposit(t) => &t.logs_bloom, } } @@ -180,7 +190,11 @@ impl OpReceiptEnvelope { /// Consumes the type and returns the underlying [`Receipt`]. pub fn into_receipt(self) -> Receipt { match self { - Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::Eip7702(t) => t.receipt, + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::PostExec(t) => t.receipt, Self::Deposit(t) => t.receipt.into_inner(), } } @@ -189,9 +203,11 @@ impl OpReceiptEnvelope { /// receipt types may be added. pub const fn as_receipt(&self) -> Option<&Receipt> { match self { - Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::Eip7702(t) => { - Some(&t.receipt) - } + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::PostExec(t) => Some(&t.receipt), Self::Deposit(t) => Some(&t.receipt.inner), } } @@ -201,7 +217,11 @@ impl OpReceiptEnvelope { /// Get the length of the inner receipt in the 2718 encoding. pub fn inner_length(&self) -> usize { match self { - Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::Eip7702(t) => t.length(), + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::PostExec(t) => t.length(), Self::Deposit(t) => t.length(), } } @@ -278,6 +298,7 @@ impl Typed2718 for OpReceiptEnvelope { Self::Eip2930(_) => OpTxType::Eip2930, Self::Eip1559(_) => OpTxType::Eip1559, Self::Eip7702(_) => OpTxType::Eip7702, + Self::PostExec(_) => OpTxType::PostExec, Self::Deposit(_) => OpTxType::Deposit, }; ty as u8 @@ -302,9 +323,11 @@ impl Encodable2718 for OpReceiptEnvelope { } match self { Self::Deposit(t) => t.encode(out), - Self::Legacy(t) | Self::Eip2930(t) | Self::Eip1559(t) | Self::Eip7702(t) => { - t.encode(out) - } + Self::Legacy(t) | + Self::Eip2930(t) | + Self::Eip1559(t) | + Self::Eip7702(t) | + Self::PostExec(t) => t.encode(out), } } } @@ -319,6 +342,7 @@ impl Decodable2718 for OpReceiptEnvelope { OpTxType::Eip1559 => Ok(Self::Eip1559(Decodable::decode(buf)?)), OpTxType::Eip7702 => Ok(Self::Eip7702(Decodable::decode(buf)?)), OpTxType::Eip2930 => Ok(Self::Eip2930(Decodable::decode(buf)?)), + OpTxType::PostExec => Ok(Self::PostExec(Decodable::decode(buf)?)), OpTxType::Deposit => Ok(Self::Deposit(Decodable::decode(buf)?)), } } @@ -349,10 +373,12 @@ where T: arbitrary::Arbitrary<'a>, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - match u.int_in_range(0..=4)? { + match u.int_in_range(0..=5)? { 0 => Ok(Self::Legacy(ReceiptWithBloom::arbitrary(u)?)), 1 => Ok(Self::Eip2930(ReceiptWithBloom::arbitrary(u)?)), 2 => Ok(Self::Eip1559(ReceiptWithBloom::arbitrary(u)?)), + 3 => Ok(Self::Eip7702(ReceiptWithBloom::arbitrary(u)?)), + 4 => Ok(Self::PostExec(ReceiptWithBloom::arbitrary(u)?)), _ => Ok(Self::Deposit(OpDepositReceiptWithBloom::arbitrary(u)?)), } } @@ -427,4 +453,15 @@ mod tests { assert_eq!(receipt.deposit_nonce(), Some(1)); assert_eq!(receipt.deposit_receipt_version(), Some(2)); } + + #[test] + fn post_exec_receipt_from_parts() { + let receipt = + OpReceiptEnvelope::from_parts(true, 100, vec![], OpTxType::PostExec, None, None); + assert!(receipt.status()); + assert_eq!(receipt.cumulative_gas_used(), 100); + assert_eq!(receipt.logs().len(), 0); + assert_eq!(receipt.tx_type(), OpTxType::PostExec); + assert!(matches!(receipt, OpReceiptEnvelope::PostExec(_))); + } } diff --git a/rust/op-alloy/crates/consensus/src/receipts/receipt.rs b/rust/op-alloy/crates/consensus/src/receipts/receipt.rs index 21b2b2ad632..56b9e8f403b 100644 --- a/rust/op-alloy/crates/consensus/src/receipts/receipt.rs +++ b/rust/op-alloy/crates/consensus/src/receipts/receipt.rs @@ -33,6 +33,9 @@ pub enum OpReceipt { /// EIP-7702 receipt #[cfg_attr(feature = "serde", serde(rename = "0x4", alias = "0x04"))] Eip7702(Receipt), + /// Post-exec receipt + #[cfg_attr(feature = "serde", serde(rename = "0x7d", alias = "0x7D"))] + PostExec(Receipt), /// Deposit receipt #[cfg_attr(feature = "serde", serde(rename = "0x7e", alias = "0x7E"))] Deposit(OpDepositReceipt), @@ -46,6 +49,7 @@ impl OpReceipt { Self::Eip2930(_) => OpTxType::Eip2930, Self::Eip1559(_) => OpTxType::Eip1559, Self::Eip7702(_) => OpTxType::Eip7702, + Self::PostExec(_) => OpTxType::PostExec, Self::Deposit(_) => OpTxType::Deposit, } } @@ -56,7 +60,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => receipt, + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt, Self::Deposit(receipt) => &receipt.inner, } } @@ -67,7 +72,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => receipt, + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt, Self::Deposit(receipt) => &mut receipt.inner, } } @@ -78,7 +84,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => receipt, + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt, Self::Deposit(receipt) => receipt.inner, } } @@ -92,6 +99,7 @@ impl OpReceipt { Self::Eip2930(receipt) => OpReceipt::Eip2930(receipt.map_logs(f)), Self::Eip1559(receipt) => OpReceipt::Eip1559(receipt.map_logs(f)), Self::Eip7702(receipt) => OpReceipt::Eip7702(receipt.map_logs(f)), + Self::PostExec(receipt) => OpReceipt::PostExec(receipt.map_logs(f)), Self::Deposit(receipt) => OpReceipt::Deposit(receipt.map_logs(f)), } } @@ -105,7 +113,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom), + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom), Self::Deposit(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom), } } @@ -119,7 +128,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out), + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out), Self::Deposit(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out), } } @@ -170,6 +180,11 @@ impl OpReceipt { RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; Ok(ReceiptWithBloom { receipt: Self::Eip7702(receipt), logs_bloom }) } + OpTxType::PostExec => { + let ReceiptWithBloom { receipt, logs_bloom } = + RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; + Ok(ReceiptWithBloom { receipt: Self::PostExec(receipt), logs_bloom }) + } OpTxType::Deposit => { let ReceiptWithBloom { receipt, logs_bloom } = RlpDecodableReceipt::rlp_decode_with_bloom(buf)?; @@ -188,7 +203,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => { + Self::Eip7702(receipt) | + Self::PostExec(receipt) => { receipt.status.encode(out); receipt.cumulative_gas_used.encode(out); receipt.logs.encode(out); @@ -217,7 +233,8 @@ impl OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => { + Self::Eip7702(receipt) | + Self::PostExec(receipt) => { receipt.status.length() + receipt.cumulative_gas_used.length() + receipt.logs.length() @@ -258,6 +275,7 @@ impl OpReceipt { OpTxType::Eip2930 => Ok(Self::Eip2930(Receipt { status, cumulative_gas_used, logs })), OpTxType::Eip1559 => Ok(Self::Eip1559(Receipt { status, cumulative_gas_used, logs })), OpTxType::Eip7702 => Ok(Self::Eip7702(Receipt { status, cumulative_gas_used, logs })), + OpTxType::PostExec => Ok(Self::PostExec(Receipt { status, cumulative_gas_used, logs })), OpTxType::Deposit => Ok(Self::Deposit(OpDepositReceipt { inner: Receipt { status, cumulative_gas_used, logs }, deposit_nonce, @@ -402,7 +420,8 @@ impl> TxReceipt for OpReceipt receipt.logs, + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt.logs, Self::Deposit(receipt) => receipt.inner.logs, } } @@ -443,6 +462,7 @@ impl From for OpReceipt { super::OpReceiptEnvelope::Eip2930(receipt) => Self::Eip2930(receipt.receipt), super::OpReceiptEnvelope::Eip1559(receipt) => Self::Eip1559(receipt.receipt), super::OpReceiptEnvelope::Eip7702(receipt) => Self::Eip7702(receipt.receipt), + super::OpReceiptEnvelope::PostExec(receipt) => Self::PostExec(receipt.receipt), super::OpReceiptEnvelope::Deposit(receipt) => Self::Deposit(OpDepositReceipt { deposit_nonce: receipt.receipt.deposit_nonce, deposit_receipt_version: receipt.receipt.deposit_receipt_version, @@ -460,6 +480,9 @@ impl From>> for OpReceiptEnvelope { OpReceipt::Eip2930(receipt) => Self::Eip2930(ReceiptWithBloom { receipt, logs_bloom }), OpReceipt::Eip1559(receipt) => Self::Eip1559(ReceiptWithBloom { receipt, logs_bloom }), OpReceipt::Eip7702(receipt) => Self::Eip7702(ReceiptWithBloom { receipt, logs_bloom }), + OpReceipt::PostExec(receipt) => { + Self::PostExec(ReceiptWithBloom { receipt, logs_bloom }) + } OpReceipt::Deposit(receipt) => Self::Deposit(ReceiptWithBloom { receipt, logs_bloom }), } } @@ -496,6 +519,8 @@ pub(crate) mod serde_bincode_compat { Eip1559(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>), /// EIP-7702 receipt Eip7702(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>), + /// Post-exec receipt + PostExec(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>), /// Deposit receipt Deposit(crate::serde_bincode_compat::OpDepositReceipt<'a, alloy_primitives::Log>), } @@ -507,6 +532,7 @@ pub(crate) mod serde_bincode_compat { super::OpReceipt::Eip2930(receipt) => Self::Eip2930(receipt.into()), super::OpReceipt::Eip1559(receipt) => Self::Eip1559(receipt.into()), super::OpReceipt::Eip7702(receipt) => Self::Eip7702(receipt.into()), + super::OpReceipt::PostExec(receipt) => Self::PostExec(receipt.into()), super::OpReceipt::Deposit(receipt) => Self::Deposit(receipt.into()), } } @@ -519,6 +545,7 @@ pub(crate) mod serde_bincode_compat { OpReceipt::Eip2930(receipt) => Self::Eip2930(receipt.into()), OpReceipt::Eip1559(receipt) => Self::Eip1559(receipt.into()), OpReceipt::Eip7702(receipt) => Self::Eip7702(receipt.into()), + OpReceipt::PostExec(receipt) => Self::PostExec(receipt.into()), OpReceipt::Deposit(receipt) => Self::Deposit(receipt.into()), } } @@ -581,7 +608,7 @@ pub(crate) mod serde_bincode_compat { mod tests { use super::*; use alloc::vec; - use alloy_eips::Encodable2718; + use alloy_eips::{Decodable2718, Encodable2718}; use alloy_primitives::{Bytes, address, b256, bytes, hex_literal::hex}; use alloy_rlp::Encodable; @@ -766,4 +793,22 @@ mod tests { "Encoded length for legacy receipt should match the actual encoded data length" ); } + + #[test] + fn post_exec_receipt_roundtrip() { + let receipt: ReceiptWithBloom = ReceiptWithBloom { + receipt: OpReceipt::PostExec(Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 0, + logs: vec![], + }), + logs_bloom: Bloom::default(), + }; + + let encoded = receipt.encoded_2718(); + let decoded = ReceiptWithBloom::::decode_2718(&mut encoded.as_ref()).unwrap(); + + assert_eq!(decoded, receipt); + assert_eq!(decoded.receipt.tx_type(), OpTxType::PostExec); + } } diff --git a/rust/op-alloy/crates/consensus/src/reth_codec.rs b/rust/op-alloy/crates/consensus/src/reth_codec.rs index d3bd124f584..96fc0da8a47 100644 --- a/rust/op-alloy/crates/consensus/src/reth_codec.rs +++ b/rust/op-alloy/crates/consensus/src/reth_codec.rs @@ -11,10 +11,14 @@ //! - `Compress`/`Decompress` impls for `OpTxEnvelope` and `OpReceipt` are added here since they //! were previously provided by reth's in-tree codecs crate. -use crate::{OpReceipt, OpTxEnvelope, OpTxType, OpTypedTransaction, TxDeposit}; +use crate::{ + OpReceipt, OpTxEnvelope, OpTxType, OpTypedTransaction, POST_EXEC_TX_TYPE_ID, TxDeposit, + TxPostExec, +}; use alloc::vec::Vec; -use alloy_consensus::{Receipt, Signed}; +use alloy_consensus::{Receipt, Signed, Transaction}; use alloy_primitives::{Address, B256, Bytes, Log, Signature, TxKind, U256}; +use alloy_rlp::Decodable; use reth_codecs::{ Compact, alloy::transaction::{CompactEnvelope, Envelope, FromTxCompact, ToTxCompact}, @@ -40,6 +44,10 @@ impl Compact for OpTxType { buf.put_u8(crate::DEPOSIT_TX_TYPE_ID); COMPACT_EXTENDED_IDENTIFIER_FLAG } + Self::PostExec => { + buf.put_u8(POST_EXEC_TX_TYPE_ID); + COMPACT_EXTENDED_IDENTIFIER_FLAG + } } } @@ -54,6 +62,7 @@ impl Compact for OpTxType { let ty = match extended_identifier { alloy_consensus::constants::EIP7702_TX_TYPE_ID => Self::Eip7702, crate::DEPOSIT_TX_TYPE_ID => Self::Deposit, + POST_EXEC_TX_TYPE_ID => Self::PostExec, _ => panic!("Unsupported OpTxType identifier: {extended_identifier}"), }; (ty, buf) @@ -126,6 +135,22 @@ impl Compact for TxDeposit { } } +impl Compact for TxPostExec { + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + self.input().to_compact(buf) + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + let (input, buf) = Bytes::from_compact(buf, len); + let mut slice = input.as_ref(); + let tx = Self::decode(&mut slice).expect("valid compact post-exec tx"); + (tx, buf) + } +} + // --- OpTypedTransaction --- impl Compact for OpTypedTransaction { @@ -151,6 +176,9 @@ impl Compact for OpTypedTransaction { Self::Deposit(tx) => { tx.to_compact(buf); } + Self::PostExec(tx) => { + tx.to_compact(buf); + } } identifier } @@ -178,6 +206,10 @@ impl Compact for OpTypedTransaction { let (tx, buf) = TxDeposit::from_compact(buf, buf.len()); (Self::Deposit(tx), buf) } + OpTxType::PostExec => { + let (tx, buf) = TxPostExec::from_compact(buf, buf.len()); + (Self::PostExec(tx), buf) + } } } } @@ -191,7 +223,7 @@ impl Envelope for OpTxEnvelope { Self::Eip2930(tx) => tx.signature(), Self::Eip1559(tx) => tx.signature(), Self::Eip7702(tx) => tx.signature(), - Self::Deposit(_) => { + Self::Deposit(_) | Self::PostExec(_) => { const DEPOSIT_SIG: Signature = Signature::new(U256::ZERO, U256::ZERO, false); &DEPOSIT_SIG } @@ -223,6 +255,9 @@ impl ToTxCompact for OpTxEnvelope { Self::Deposit(tx) => { tx.inner().to_compact(buf); } + Self::PostExec(tx) => { + tx.inner().to_compact(buf); + } }; } } @@ -257,6 +292,10 @@ impl FromTxCompact for OpTxEnvelope { let (tx, buf) = TxDeposit::from_compact(buf, buf.len()); (Self::Deposit(alloy_consensus::Sealed::new(tx)), buf) } + OpTxType::PostExec => { + let (tx, buf) = TxPostExec::from_compact(buf, buf.len()); + (Self::PostExec(alloy_consensus::Sealed::new(tx)), buf) + } } } } @@ -337,6 +376,7 @@ impl From for OpReceipt { OpTxType::Eip2930 => Self::Eip2930(receipt), OpTxType::Eip1559 => Self::Eip1559(receipt), OpTxType::Eip7702 => Self::Eip7702(receipt), + OpTxType::PostExec => Self::PostExec(receipt), OpTxType::Deposit => Self::Deposit(crate::OpDepositReceipt { inner: receipt, deposit_nonce: compact.deposit_nonce, diff --git a/rust/op-alloy/crates/consensus/src/reth_core.rs b/rust/op-alloy/crates/consensus/src/reth_core.rs index 585eca1cb60..030d2449d88 100644 --- a/rust/op-alloy/crates/consensus/src/reth_core.rs +++ b/rust/op-alloy/crates/consensus/src/reth_core.rs @@ -41,7 +41,8 @@ impl InMemorySize for OpReceipt { Self::Legacy(receipt) | Self::Eip2930(receipt) | Self::Eip1559(receipt) | - Self::Eip7702(receipt) => receipt.size(), + Self::Eip7702(receipt) | + Self::PostExec(receipt) => receipt.size(), Self::Deposit(receipt) => receipt.size(), } } @@ -55,6 +56,7 @@ impl InMemorySize for OpTypedTransaction { Self::Eip1559(tx) => tx.size(), Self::Eip7702(tx) => tx.size(), Self::Deposit(tx) => tx.size(), + Self::PostExec(tx) => tx.size(), } } } @@ -78,6 +80,9 @@ impl InMemorySize for OpTxEnvelope { Self::Eip1559(tx) => tx.size(), Self::Eip7702(tx) => tx.size(), Self::Deposit(tx) => core::mem::size_of::() + tx.inner().size(), + Self::PostExec(tx) => { + core::mem::size_of::() + tx.inner().size() + } } } } diff --git a/rust/op-alloy/crates/consensus/src/sdm.rs b/rust/op-alloy/crates/consensus/src/sdm.rs new file mode 100644 index 00000000000..b359cf4a141 --- /dev/null +++ b/rust/op-alloy/crates/consensus/src/sdm.rs @@ -0,0 +1,395 @@ +//! Post-execution transaction types. + +use alloc::vec::Vec; +use alloy_consensus::{Sealable, Transaction, Typed2718, transaction::RlpEcdsaEncodableTx}; +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718, IsTyped2718}, + eip2930::AccessList, +}; +use alloy_primitives::{Address, B256, Bytes, ChainId, TxHash, TxKind, U256, keccak256}; +use alloy_rlp::{BufMut, Decodable, Encodable, Header, RlpDecodable, RlpEncodable}; + +/// Type byte for the post-execution transaction. +pub const POST_EXEC_TX_TYPE_ID: u8 = 0x7D; + +/// Per-transaction gas refund entry within a [`PostExecPayload`]. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, RlpEncodable, RlpDecodable)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct SDMGasEntry { + /// Transaction index within the block. + pub index: u64, + /// Gas refund from post-execution warming settlement. + pub gas_refund: u64, +} + +/// Payload for the post-execution transaction. +/// +/// Today this only carries the SDM gas refund data, but additional post-exec fields may +/// be added in the future. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, RlpEncodable, RlpDecodable)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct PostExecPayload { + /// Format version. + pub version: u64, + /// L2 block number this synthetic payload is anchored to. + pub block_number: u64, + /// Initial SDM gas refund entries keyed by transaction index. + pub gas_refund_entries: Vec, +} + +impl PostExecPayload { + /// Look up refund for a given tx index. + pub fn gas_refund_for_idx(&self, index: u64) -> Option { + self.gas_refund_entries.iter().find(|e| e.index == index).map(|e| e.gas_refund) + } + + /// RLP-encode the payload into bytes. + pub fn to_rlp_bytes(&self) -> Bytes { + let mut buf = Vec::new(); + self.encode(&mut buf); + buf.into() + } + + /// Decode a payload from RLP bytes. + pub fn from_rlp_bytes(data: &[u8]) -> alloy_rlp::Result { + let mut buf = data; + let payload = Self::decode(&mut buf)?; + if !buf.is_empty() { + return Err(alloy_rlp::Error::UnexpectedLength); + } + Ok(payload) + } +} + +/// Post-execution transaction carrying a [`PostExecPayload`]. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(into = "PostExecPayload", from = "PostExecPayload"))] +pub struct TxPostExec { + /// Decoded payload. + pub payload: PostExecPayload, + /// RLP-encoded payload bytes used as the transaction input. + pub input: Bytes, +} + +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for TxPostExec { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + // Keep `payload` and the cached `input` bytes in sync for fuzzed values. + Ok(Self::new(PostExecPayload::arbitrary(u)?)) + } +} + +impl From for TxPostExec { + fn from(payload: PostExecPayload) -> Self { + Self::new(payload) + } +} + +impl From for PostExecPayload { + fn from(tx: TxPostExec) -> Self { + tx.payload + } +} + +impl TxPostExec { + /// Construct a post-exec transaction from its decoded payload. + pub fn new(payload: PostExecPayload) -> Self { + let input = payload.to_rlp_bytes(); + Self { payload, input } + } + + /// Returns the canonical signature for post-exec transactions, which don't include a + /// signature. + pub const fn signature() -> alloy_primitives::Signature { + alloy_primitives::Signature::new(U256::ZERO, U256::ZERO, false) + } + + /// Returns the canonical signer address for post-exec transactions. + pub const fn signer_address(&self) -> Address { + Address::ZERO + } + + /// Encoded length of the transaction body. + pub fn rlp_encoded_length(&self) -> usize { + self.input.len() + } + + /// Encoded length including the type byte. + pub fn eip2718_encoded_length(&self) -> usize { + self.rlp_encoded_length() + 1 + } + + fn network_header(&self) -> Header { + Header { list: false, payload_length: self.eip2718_encoded_length() } + } + + /// Encoded length including the outer network RLP header. + pub fn network_encoded_length(&self) -> usize { + self.network_header().length_with_payload() + } + + /// Network encode the transaction. + pub fn network_encode(&self, out: &mut dyn BufMut) { + self.network_header().encode(out); + self.encode_2718(out); + } + + /// Calculates a heuristic for the in-memory size of the transaction. + pub fn size(&self) -> usize { + core::mem::size_of::() + + self.input.len() + + self.payload.gas_refund_entries.len() * core::mem::size_of::() + } + + /// Calculate the transaction hash. + pub fn tx_hash(&self) -> TxHash { + let mut buf = Vec::with_capacity(self.eip2718_encoded_length()); + self.encode_2718(&mut buf); + keccak256(&buf) + } +} + +impl Typed2718 for TxPostExec { + fn ty(&self) -> u8 { + POST_EXEC_TX_TYPE_ID + } +} + +impl IsTyped2718 for TxPostExec { + fn is_type(ty: u8) -> bool { + ty == POST_EXEC_TX_TYPE_ID + } +} + +impl RlpEcdsaEncodableTx for TxPostExec { + fn rlp_encoded_fields_length(&self) -> usize { + self.input.len() + } + + fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) { + // `input` already stores the canonical RLP-encoded payload, so the transaction fields are + // written as-is instead of being wrapped in an additional RLP list. + out.put_slice(self.input.as_ref()); + } +} + +impl Transaction for TxPostExec { + fn chain_id(&self) -> Option { + None + } + fn nonce(&self) -> u64 { + 0 + } + fn gas_limit(&self) -> u64 { + 0 + } + fn gas_price(&self) -> Option { + None + } + fn max_fee_per_gas(&self) -> u128 { + 0 + } + fn max_priority_fee_per_gas(&self) -> Option { + None + } + fn max_fee_per_blob_gas(&self) -> Option { + None + } + fn priority_fee_or_price(&self) -> u128 { + 0 + } + fn effective_gas_price(&self, _: Option) -> u128 { + 0 + } + fn is_dynamic_fee(&self) -> bool { + false + } + fn kind(&self) -> TxKind { + // Post-exec transactions do not carry a destination like deposits do, so expose them as a + // synthetic zero-address call placeholder. + TxKind::Call(Default::default()) + } + fn is_create(&self) -> bool { + false + } + fn value(&self) -> U256 { + U256::ZERO + } + fn input(&self) -> &Bytes { + &self.input + } + fn access_list(&self) -> Option<&AccessList> { + None + } + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } + fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> { + None + } +} + +impl Encodable2718 for TxPostExec { + fn type_flag(&self) -> Option { + Some(POST_EXEC_TX_TYPE_ID) + } + fn encode_2718_len(&self) -> usize { + self.eip2718_encoded_length() + } + fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) { + out.put_u8(POST_EXEC_TX_TYPE_ID); + out.put_slice(self.input.as_ref()); + } +} + +impl Decodable2718 for TxPostExec { + fn typed_decode(ty: u8, data: &mut &[u8]) -> Eip2718Result { + if ty != POST_EXEC_TX_TYPE_ID { + return Err(Eip2718Error::UnexpectedType(ty)); + } + Ok(Self::new(PostExecPayload::decode(data)?)) + } + + fn fallback_decode(data: &mut &[u8]) -> Eip2718Result { + Ok(Self::new(PostExecPayload::decode(data)?)) + } +} + +impl Encodable for TxPostExec { + fn encode(&self, out: &mut dyn BufMut) { + out.put_slice(self.input.as_ref()); + } + + fn length(&self) -> usize { + self.rlp_encoded_length() + } +} + +impl Decodable for TxPostExec { + fn decode(data: &mut &[u8]) -> alloy_rlp::Result { + Ok(Self::new(PostExecPayload::decode(data)?)) + } +} + +impl Sealable for TxPostExec { + fn hash_slow(&self) -> B256 { + self.tx_hash() + } +} + +#[cfg(feature = "alloy-compat")] +impl From for alloy_rpc_types_eth::TransactionRequest { + fn from(tx: TxPostExec) -> Self { + Self { + from: Some(tx.signer_address()), + transaction_type: Some(POST_EXEC_TX_TYPE_ID), + gas: Some(0), + nonce: Some(0), + value: Some(U256::ZERO), + input: tx.input.into(), + ..Default::default() + } + } +} + +/// Build a post-execution transaction from a block number and refund entries. +pub fn build_post_exec_tx(block_number: u64, gas_refund_entries: Vec) -> TxPostExec { + TxPostExec::new(PostExecPayload { version: 1, block_number, gas_refund_entries }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn post_exec_payload_rlp_roundtrip_preserves_block_number() { + let payload = PostExecPayload { + version: 1, + block_number: 42, + gas_refund_entries: vec![SDMGasEntry { index: 3, gas_refund: 7 }], + }; + + let encoded = payload.to_rlp_bytes(); + let decoded = PostExecPayload::from_rlp_bytes(encoded.as_ref()).expect("decode payload"); + + assert_eq!(decoded, payload); + } + + #[test] + fn post_exec_payload_rlp_decode_rejects_trailing_bytes() { + let payload = PostExecPayload { + version: 1, + block_number: 42, + gas_refund_entries: vec![SDMGasEntry { index: 3, gas_refund: 7 }], + }; + + let mut encoded = payload.to_rlp_bytes().to_vec(); + encoded.push(0); + + let err = PostExecPayload::from_rlp_bytes(&encoded).expect_err("reject trailing bytes"); + assert_eq!(err, alloy_rlp::Error::UnexpectedLength); + } + + #[test] + fn post_exec_tx_hash_depends_on_block_number() { + let entries = vec![SDMGasEntry { index: 3, gas_refund: 7 }]; + let tx_a = build_post_exec_tx(42, entries.clone()); + let tx_b = build_post_exec_tx(43, entries); + + assert_ne!(tx_a.tx_hash(), tx_b.tx_hash()); + } + + #[test] + fn post_exec_tx_eip2718_roundtrip() { + let tx = build_post_exec_tx( + 99, + vec![ + SDMGasEntry { index: 0, gas_refund: 100 }, + SDMGasEntry { index: 5, gas_refund: 200 }, + ], + ); + + let mut buf = Vec::new(); + tx.encode_2718(&mut buf); + + let decoded = TxPostExec::decode_2718(&mut buf.as_slice()).expect("decode 2718"); + assert_eq!(decoded, tx); + assert_eq!(decoded.tx_hash(), tx.tx_hash()); + } + + #[test] + fn post_exec_tx_eip2718_roundtrip_empty_refunds() { + let tx = build_post_exec_tx(1, vec![]); + + let mut buf = Vec::new(); + tx.encode_2718(&mut buf); + + let decoded = TxPostExec::decode_2718(&mut buf.as_slice()).expect("decode 2718"); + assert_eq!(decoded, tx); + } + + #[cfg(feature = "serde")] + #[test] + fn post_exec_tx_serde_serializes_as_payload() { + let tx = build_post_exec_tx(42, vec![SDMGasEntry { index: 3, gas_refund: 7 }]); + let value = serde_json::to_value(&tx).expect("serialize tx"); + + assert_eq!(value, serde_json::to_value(&tx.payload).expect("serialize payload")); + } + + #[cfg(feature = "serde")] + #[test] + fn post_exec_tx_serde_roundtrip_preserves_cached_input() { + let tx = build_post_exec_tx(42, vec![SDMGasEntry { index: 3, gas_refund: 7 }]); + let value = serde_json::to_value(&tx).expect("serialize tx"); + + let decoded: TxPostExec = serde_json::from_value(value).expect("deserialize tx"); + assert_eq!(decoded, tx); + assert_eq!(decoded.input, decoded.payload.to_rlp_bytes()); + } +} diff --git a/rust/op-alloy/crates/consensus/src/transaction/envelope.rs b/rust/op-alloy/crates/consensus/src/transaction/envelope.rs index afe73b2474a..305e3878d05 100644 --- a/rust/op-alloy/crates/consensus/src/transaction/envelope.rs +++ b/rust/op-alloy/crates/consensus/src/transaction/envelope.rs @@ -1,5 +1,5 @@ use crate::{ - OpPooledTransaction, TxDeposit, + OpPooledTransaction, TxDeposit, TxPostExec, transaction::{OpDepositInfo, OpTransactionInfo}, }; use alloy_consensus::{ @@ -41,17 +41,24 @@ pub enum OpTxEnvelope { #[envelope(ty = 126)] #[serde(serialize_with = "crate::serde_deposit_tx_rpc")] Deposit(Sealed), + /// A [`TxPostExec`] tagged with type 0x7D. + #[envelope(ty = 125)] + PostExec(Sealed), } /// Represents an Optimism transaction envelope. /// -/// Compared to Ethereum it can tell whether the transaction is a deposit. +/// Compared to Ethereum it can tell whether the transaction is a deposit or post-exec synthetic +/// transaction. pub trait OpTransaction { /// Returns `true` if the transaction is a deposit. fn is_deposit(&self) -> bool; /// Returns `Some` if the transaction is a deposit. fn as_deposit(&self) -> Option<&Sealed>; + + /// Returns `Some` if the transaction is a post-exec transaction. + fn as_post_exec(&self) -> Option<&Sealed>; } impl OpTransaction for OpTxEnvelope { @@ -62,6 +69,10 @@ impl OpTransaction for OpTxEnvelope { fn as_deposit(&self) -> Option<&Sealed> { self.as_deposit() } + + fn as_post_exec(&self) -> Option<&Sealed> { + self.as_post_exec() + } } impl OpTransaction for Extended @@ -82,6 +93,13 @@ where Self::Other(t) => t.as_deposit(), } } + + fn as_post_exec(&self) -> Option<&Sealed> { + match self { + Self::BuiltIn(b) => b.as_post_exec(), + Self::Other(t) => t.as_post_exec(), + } + } } impl AsRef for OpTxEnvelope { @@ -141,6 +159,7 @@ impl From> for OpTxEnvelope { Self::Eip7702(tx) } OpTypedTransaction::Deposit(tx) => Self::Deposit(Sealed::new_unchecked(tx, hash)), + OpTypedTransaction::PostExec(tx) => Self::PostExec(Sealed::new_unchecked(tx, hash)), } } } @@ -157,6 +176,18 @@ impl From> for OpTxEnvelope { } } +impl From for OpTxEnvelope { + fn from(v: TxPostExec) -> Self { + v.seal_slow().into() + } +} + +impl From> for OpTxEnvelope { + fn from(v: Sealed) -> Self { + Self::PostExec(v) + } +} + impl From for Extended { fn from(value: OpTxEnvelope) -> Self { Self::BuiltIn(value) @@ -187,6 +218,7 @@ impl From for alloy_rpc_types_eth::TransactionRequest { OpTxEnvelope::Eip1559(tx) => tx.into_parts().0.into(), OpTxEnvelope::Eip7702(tx) => tx.into_parts().0.into(), OpTxEnvelope::Deposit(tx) => tx.into_inner().into(), + OpTxEnvelope::PostExec(tx) => tx.into_inner().into(), OpTxEnvelope::Legacy(tx) => tx.into_parts().0.into(), } } @@ -242,15 +274,18 @@ impl OpTxEnvelope { /// Attempts to convert the envelope into the pooled variant. /// /// Returns an error if the envelope's variant is incompatible with the pooled format: - /// [`TxDeposit`]. + /// [`TxDeposit`] and [`TxPostExec`]. pub fn try_into_pooled(self) -> Result> { match self { Self::Legacy(tx) => Ok(tx.into()), Self::Eip2930(tx) => Ok(tx.into()), Self::Eip1559(tx) => Ok(tx.into()), Self::Eip7702(tx) => Ok(tx.into()), - Self::Deposit(tx) => { - Err(ValueError::new(tx.into(), "Deposit transactions cannot be pooled")) + tx @ Self::Deposit(_) => { + Err(ValueError::new(tx, "Deposit transactions cannot be pooled")) + } + tx @ Self::PostExec(_) => { + Err(ValueError::new(tx, "PostExec transactions cannot be pooled")) } } } @@ -258,7 +293,7 @@ impl OpTxEnvelope { /// Attempts to convert the envelope into the ethereum pooled variant. /// /// Returns an error if the envelope's variant is incompatible with the pooled format: - /// [`TxDeposit`]. + /// [`TxDeposit`] and [`TxPostExec`]. pub fn try_into_eth_pooled( self, ) -> Result> { @@ -278,6 +313,10 @@ impl OpTxEnvelope { tx, "Deposit transactions cannot be converted to ethereum transaction", )), + tx @ Self::PostExec(_) => Err(ValueError::new( + tx, + "PostExec transactions cannot be converted to ethereum transaction", + )), } } @@ -316,6 +355,10 @@ impl OpTxEnvelope { /// Returns mutable access to the input bytes. /// /// Caution: modifying this will cause side-effects on the hash. + /// + /// For [`TxPostExec`], this mutates the cached encoded payload bytes directly and may leave + /// them out of sync with [`TxPostExec::payload`]. Rebuild the transaction with + /// [`TxPostExec::new`] if you need to restore that invariant after mutating the input. #[doc(hidden)] pub const fn input_mut(&mut self) -> &mut Bytes { match self { @@ -324,6 +367,7 @@ impl OpTxEnvelope { Self::Legacy(tx) => &mut tx.tx_mut().input, Self::Eip7702(tx) => &mut tx.tx_mut().input, Self::Deposit(tx) => &mut tx.inner_mut().input, + Self::PostExec(tx) => &mut tx.inner_mut().input, } } @@ -357,6 +401,12 @@ impl OpTxEnvelope { matches!(self, Self::Deposit(_)) } + /// Returns true if the transaction is a post-exec transaction. + #[inline] + pub const fn is_post_exec(&self) -> bool { + matches!(self, Self::PostExec(_)) + } + /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction. pub const fn as_legacy(&self) -> Option<&Signed> { match self { @@ -381,7 +431,7 @@ impl OpTxEnvelope { } } - /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction. + /// Returns the [`TxDeposit`] variant if the transaction is a deposit transaction. pub const fn as_deposit(&self) -> Option<&Sealed> { match self { Self::Deposit(tx) => Some(tx), @@ -389,16 +439,24 @@ impl OpTxEnvelope { } } + /// Returns the [`TxPostExec`] variant if the transaction is a post-exec transaction. + pub const fn as_post_exec(&self) -> Option<&Sealed> { + match self { + Self::PostExec(tx) => Some(tx), + _ => None, + } + } + /// Return the reference to signature. /// - /// Returns `None` if this is a deposit variant. + /// Returns `None` for unsigned variants: [`TxDeposit`] and [`TxPostExec`]. pub const fn signature(&self) -> Option<&Signature> { match self { Self::Legacy(tx) => Some(tx.signature()), Self::Eip2930(tx) => Some(tx.signature()), Self::Eip1559(tx) => Some(tx.signature()), Self::Eip7702(tx) => Some(tx.signature()), - Self::Deposit(_) => None, + Self::Deposit(_) | Self::PostExec(_) => None, } } @@ -410,6 +468,7 @@ impl OpTxEnvelope { Self::Eip1559(_) => OpTxType::Eip1559, Self::Eip7702(_) => OpTxType::Eip7702, Self::Deposit(_) => OpTxType::Deposit, + Self::PostExec(_) => OpTxType::PostExec, } } @@ -421,6 +480,7 @@ impl OpTxEnvelope { Self::Eip2930(tx) => tx.hash(), Self::Eip7702(tx) => tx.hash(), Self::Deposit(tx) => tx.hash_ref(), + Self::PostExec(tx) => tx.hash_ref(), } } @@ -437,6 +497,7 @@ impl OpTxEnvelope { Self::Eip1559(t) => t.eip2718_encoded_length(), Self::Eip7702(t) => t.eip2718_encoded_length(), Self::Deposit(t) => t.eip2718_encoded_length(), + Self::PostExec(t) => t.eip2718_encoded_length(), } } } @@ -460,13 +521,19 @@ impl alloy_consensus::transaction::SignerRecoverable for OpTxEnvelope { // Optimism's Deposit transaction does not have a signature. Directly return the // `from` address. Self::Deposit(tx) => return Ok(tx.from), + // Post-exec transactions are unsigned synthetic system transactions. They use a + // canonical zero-address signer rather than a cryptographic signature. + Self::PostExec(tx) => return Ok(tx.inner().signer_address()), }; let signature = match self { Self::Legacy(tx) => tx.signature(), Self::Eip2930(tx) => tx.signature(), Self::Eip1559(tx) => tx.signature(), Self::Eip7702(tx) => tx.signature(), - Self::Deposit(_) => unreachable!("Deposit transactions should not be handled here"), + // Deposit and PostExec are unsigned and handled via early return above. + Self::Deposit(_) | Self::PostExec(_) => { + unreachable!("non-signed transactions should not be handled here") + } }; alloy_consensus::crypto::secp256k1::recover_signer(signature, signature_hash) } @@ -482,13 +549,17 @@ impl alloy_consensus::transaction::SignerRecoverable for OpTxEnvelope { // Optimism's Deposit transaction does not have a signature. Directly return the // `from` address. Self::Deposit(tx) => return Ok(tx.from), + // Post-exec transactions are unsigned synthetic system transactions. They use a + // canonical zero-address signer rather than a cryptographic signature. + Self::PostExec(tx) => return Ok(tx.inner().signer_address()), }; let signature = match self { Self::Legacy(tx) => tx.signature(), Self::Eip2930(tx) => tx.signature(), Self::Eip1559(tx) => tx.signature(), Self::Eip7702(tx) => tx.signature(), - Self::Deposit(_) => unreachable!("Deposit transactions should not be handled here"), + // Deposit and PostExec are unsigned and handled via early return above. + Self::Deposit(_) | Self::PostExec(_) => unreachable!(), }; alloy_consensus::crypto::secp256k1::recover_signer_unchecked(signature, signature_hash) } @@ -510,7 +581,9 @@ impl alloy_consensus::transaction::SignerRecoverable for OpTxEnvelope { Self::Eip7702(tx) => { alloy_consensus::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf) } + // Deposit and PostExec are unsigned; return their canonical signer directly. Self::Deposit(tx) => Ok(tx.from), + Self::PostExec(tx) => Ok(tx.inner().signer_address()), } } } @@ -518,7 +591,7 @@ impl alloy_consensus::transaction::SignerRecoverable for OpTxEnvelope { /// Bincode-compatible serde implementation for `OpTxEnvelope`. #[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] pub mod serde_bincode_compat { - use crate::serde_bincode_compat::TxDeposit; + use crate::{TxPostExec, serde_bincode_compat::TxDeposit}; use alloy_consensus::{ Sealed, Signed, transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, @@ -565,6 +638,13 @@ pub mod serde_bincode_compat { /// Borrowed deposit transaction data. transaction: TxDeposit<'a>, }, + /// Post-exec variant. + PostExec { + /// Precomputed hash. + hash: B256, + /// Owned post-exec transaction data. + transaction: TxPostExec, + }, } impl<'a> From<&'a super::OpTxEnvelope> for OpTxEnvelope<'a> { @@ -590,6 +670,10 @@ pub mod serde_bincode_compat { hash: sealed_deposit.seal(), transaction: sealed_deposit.inner().into(), }, + super::OpTxEnvelope::PostExec(sealed_post_exec) => Self::PostExec { + hash: sealed_post_exec.seal(), + transaction: sealed_post_exec.inner().clone(), + }, } } } @@ -612,6 +696,9 @@ pub mod serde_bincode_compat { OpTxEnvelope::Deposit { hash, transaction } => { Self::Deposit(Sealed::new_unchecked(transaction.into(), hash)) } + OpTxEnvelope::PostExec { hash, transaction } => { + Self::PostExec(Sealed::new_unchecked(transaction, hash)) + } } } } @@ -639,11 +726,43 @@ pub mod serde_bincode_compat { #[cfg(test)] mod tests { use super::*; + use alloy_consensus::{Sealed, Signed, TxEip1559, TxEip2930, TxEip7702, TxLegacy}; use arbitrary::Arbitrary; use rand::Rng; use serde::{Deserialize, Serialize}; use serde_with::serde_as; + fn arbitrary_op_tx_envelope( + u: &mut arbitrary::Unstructured<'_>, + ) -> arbitrary::Result { + Ok(match u.int_in_range(0..=5)? { + 0 => super::super::OpTxEnvelope::Legacy(Signed::new_unhashed( + TxLegacy::arbitrary(u)?, + Signature::arbitrary(u)?, + )), + 1 => super::super::OpTxEnvelope::Eip2930(Signed::new_unhashed( + TxEip2930::arbitrary(u)?, + Signature::arbitrary(u)?, + )), + 2 => super::super::OpTxEnvelope::Eip1559(Signed::new_unhashed( + TxEip1559::arbitrary(u)?, + Signature::arbitrary(u)?, + )), + 3 => super::super::OpTxEnvelope::Eip7702(Signed::new_unhashed( + TxEip7702::arbitrary(u)?, + Signature::arbitrary(u)?, + )), + 4 => super::super::OpTxEnvelope::Deposit(Sealed::new_unchecked( + crate::TxDeposit::arbitrary(u)?, + B256::arbitrary(u)?, + )), + _ => super::super::OpTxEnvelope::PostExec(Sealed::new_unchecked( + crate::TxPostExec::arbitrary(u)?, + B256::arbitrary(u)?, + )), + }) + } + /// Tests a bincode round-trip for `OpTxEnvelope` using an arbitrary instance. #[test] fn test_op_tx_envelope_bincode_roundtrip_arbitrary() { @@ -655,14 +774,16 @@ pub mod serde_bincode_compat { envelope: super::super::OpTxEnvelope, } - let mut bytes = [0u8; 1024]; - rand::rng().fill(bytes.as_mut_slice()); - let data = Data { - envelope: super::super::OpTxEnvelope::arbitrary(&mut arbitrary::Unstructured::new( - &bytes, - )) - .unwrap(), - }; + let mut rng = rand::rng(); + let data = (0..128) + .find_map(|_| { + let mut bytes = [0u8; 4096]; + rng.fill(bytes.as_mut_slice()); + arbitrary_op_tx_envelope(&mut arbitrary::Unstructured::new(&bytes)) + .ok() + .map(|envelope| Data { envelope }) + }) + .expect("failed to generate arbitrary OpTxEnvelope"); let encoded = bincode::serde::encode_to_vec(&data, bincode::config::legacy()).unwrap(); let (decoded, _) = diff --git a/rust/op-alloy/crates/consensus/src/transaction/tx_type.rs b/rust/op-alloy/crates/consensus/src/transaction/tx_type.rs index fb6d864b662..899466f2ec5 100644 --- a/rust/op-alloy/crates/consensus/src/transaction/tx_type.rs +++ b/rust/op-alloy/crates/consensus/src/transaction/tx_type.rs @@ -21,14 +21,15 @@ impl Display for OpTxType { Self::Eip1559 => write!(f, "eip1559"), Self::Eip7702 => write!(f, "eip7702"), Self::Deposit => write!(f, "deposit"), + Self::PostExec => write!(f, "post-exec"), } } } impl OpTxType { /// List of all variants. - pub const ALL: [Self; 5] = - [Self::Legacy, Self::Eip2930, Self::Eip1559, Self::Eip7702, Self::Deposit]; + pub const ALL: [Self; 6] = + [Self::Legacy, Self::Eip2930, Self::Eip1559, Self::Eip7702, Self::Deposit, Self::PostExec]; /// Returns `true` if the type is [`OpTxType::Deposit`]. pub const fn is_deposit(&self) -> bool { @@ -44,13 +45,14 @@ mod tests { #[test] fn test_all_tx_types() { - assert_eq!(OpTxType::ALL.len(), 5); + assert_eq!(OpTxType::ALL.len(), 6); let all = vec![ OpTxType::Legacy, OpTxType::Eip2930, OpTxType::Eip1559, OpTxType::Eip7702, OpTxType::Deposit, + OpTxType::PostExec, ]; assert_eq!(OpTxType::ALL.to_vec(), all); } diff --git a/rust/op-alloy/crates/consensus/src/transaction/typed.rs b/rust/op-alloy/crates/consensus/src/transaction/typed.rs index 4cf2feaf6e5..5ab4da91342 100644 --- a/rust/op-alloy/crates/consensus/src/transaction/typed.rs +++ b/rust/op-alloy/crates/consensus/src/transaction/typed.rs @@ -1,5 +1,5 @@ pub use crate::transaction::envelope::OpTypedTransaction; -use crate::{OpTxEnvelope, OpTxType, TxDeposit}; +use crate::{OpTxEnvelope, OpTxType, TxDeposit, TxPostExec}; use alloy_consensus::{ EthereumTypedTransaction, SignableTransaction, Signed, TxEip1559, TxEip2930, TxEip7702, TxLegacy, Typed2718, TypedTransaction, error::ValueError, transaction::RlpEcdsaEncodableTx, @@ -37,6 +37,12 @@ impl From for OpTypedTransaction { } } +impl From for OpTypedTransaction { + fn from(tx: TxPostExec) -> Self { + Self::PostExec(tx) + } +} + impl From for OpTypedTransaction { fn from(envelope: OpTxEnvelope) -> Self { match envelope { @@ -45,6 +51,7 @@ impl From for OpTypedTransaction { OpTxEnvelope::Eip1559(tx) => Self::Eip1559(tx.strip_signature()), OpTxEnvelope::Eip7702(tx) => Self::Eip7702(tx.strip_signature()), OpTxEnvelope::Deposit(tx) => Self::Deposit(tx.into_inner()), + OpTxEnvelope::PostExec(tx) => Self::PostExec(tx.into_inner()), } } } @@ -66,6 +73,7 @@ impl From for alloy_rpc_types_eth::TransactionRequest { OpTypedTransaction::Eip1559(tx) => tx.into(), OpTypedTransaction::Eip7702(tx) => tx.into(), OpTypedTransaction::Deposit(tx) => tx.into(), + OpTypedTransaction::PostExec(tx) => tx.into(), } } } @@ -79,19 +87,21 @@ impl OpTypedTransaction { Self::Eip1559(_) => OpTxType::Eip1559, Self::Eip7702(_) => OpTxType::Eip7702, Self::Deposit(_) => OpTxType::Deposit, + Self::PostExec(_) => OpTxType::PostExec, } } /// Calculates the signing hash for the transaction. /// - /// Returns `None` if the tx is a deposit transaction. + /// Returns `None` for unsigned transaction variants: [`TxDeposit`] and [`TxPostExec`]. pub fn checked_signature_hash(&self) -> Option { match self { Self::Legacy(tx) => Some(tx.signature_hash()), Self::Eip2930(tx) => Some(tx.signature_hash()), Self::Eip1559(tx) => Some(tx.signature_hash()), Self::Eip7702(tx) => Some(tx.signature_hash()), - Self::Deposit(_) => None, + // Deposit and PostExec are unsigned synthetic transactions with no signature hash. + Self::Deposit(_) | Self::PostExec(_) => None, } } @@ -127,6 +137,14 @@ impl OpTypedTransaction { } } + /// Return the inner post-exec transaction if it exists. + pub const fn post_exec(&self) -> Option<&TxPostExec> { + match self { + Self::PostExec(tx) => Some(tx), + _ => None, + } + } + /// Returns `true` if transaction is deposit transaction. pub const fn is_deposit(&self) -> bool { matches!(self, Self::Deposit(_)) @@ -134,7 +152,8 @@ impl OpTypedTransaction { /// Calculate the transaction hash for the given signature. /// - /// Note: Returns the regular tx hash if this is a deposit variant + /// Note: returns the regular transaction hash for unsigned variants: [`TxDeposit`] and + /// [`TxPostExec`]. In those cases the provided signature is ignored. pub fn tx_hash(&self, signature: &Signature) -> TxHash { match self { Self::Legacy(tx) => tx.tx_hash(signature), @@ -142,28 +161,30 @@ impl OpTypedTransaction { Self::Eip1559(tx) => tx.tx_hash(signature), Self::Eip7702(tx) => tx.tx_hash(signature), Self::Deposit(tx) => tx.tx_hash(), + Self::PostExec(tx) => tx.tx_hash(), } } /// Convenience function to convert this typed transaction into an [`OpTxEnvelope`]. /// - /// Note: If this is a [`OpTypedTransaction::Deposit`] variant, the signature will be ignored. + /// Note: for unsigned variants ([`OpTypedTransaction::Deposit`] and + /// [`OpTypedTransaction::PostExec`]), the provided signature is ignored. pub fn into_envelope(self, signature: Signature) -> OpTxEnvelope { self.into_signed(signature).into() } /// Attempts to convert the optimism variant into an ethereum [`TypedTransaction`]. /// - /// Returns the typed transaction as error if it is a variant unsupported on ethereum: - /// [`TxDeposit`] + /// Returns the typed transaction as an error if it is a variant unsupported on ethereum: + /// [`TxDeposit`] or [`TxPostExec`]. pub fn try_into_eth(self) -> Result> { self.try_into_eth_variant() } /// Attempts to convert the optimism variant into an ethereum [`TypedTransaction`]. /// - /// Returns the typed transaction as error if it is a variant unsupported on ethereum: - /// [`TxDeposit`] + /// Returns the typed transaction as an error if it is a variant unsupported on ethereum: + /// [`TxDeposit`] or [`TxPostExec`]. pub fn try_into_eth_variant( self, ) -> Result, ValueError> { @@ -176,6 +197,10 @@ impl OpTypedTransaction { tx, "Deposit transactions cannot be converted to ethereum transaction", )), + tx @ Self::PostExec(_) => Err(ValueError::new( + tx, + "PostExec transactions cannot be converted to ethereum transaction", + )), } } } @@ -188,6 +213,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.rlp_encoded_fields_length(), Self::Eip7702(tx) => tx.rlp_encoded_fields_length(), Self::Deposit(tx) => tx.rlp_encoded_fields_length(), + Self::PostExec(tx) => tx.rlp_encoded_fields_length(), } } @@ -198,6 +224,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.rlp_encode_fields(out), Self::Eip7702(tx) => tx.rlp_encode_fields(out), Self::Deposit(tx) => tx.rlp_encode_fields(out), + Self::PostExec(tx) => tx.rlp_encode_fields(out), } } @@ -208,6 +235,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), Self::Eip7702(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out), Self::Deposit(tx) => tx.encode_2718(out), + Self::PostExec(tx) => tx.encode_2718(out), } } @@ -218,6 +246,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.eip2718_encode(signature, out), Self::Eip7702(tx) => tx.eip2718_encode(signature, out), Self::Deposit(tx) => tx.encode_2718(out), + Self::PostExec(tx) => tx.encode_2718(out), } } @@ -228,6 +257,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.network_encode_with_type(signature, tx.ty(), out), Self::Eip7702(tx) => tx.network_encode_with_type(signature, tx.ty(), out), Self::Deposit(tx) => tx.network_encode(out), + Self::PostExec(tx) => tx.network_encode(out), } } @@ -238,6 +268,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.network_encode(signature, out), Self::Eip7702(tx) => tx.network_encode(signature, out), Self::Deposit(tx) => tx.network_encode(out), + Self::PostExec(tx) => tx.network_encode(out), } } @@ -248,6 +279,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.tx_hash_with_type(signature, tx.ty()), Self::Eip7702(tx) => tx.tx_hash_with_type(signature, tx.ty()), Self::Deposit(tx) => tx.tx_hash(), + Self::PostExec(tx) => tx.tx_hash(), } } @@ -258,6 +290,7 @@ impl RlpEcdsaEncodableTx for OpTypedTransaction { Self::Eip1559(tx) => tx.tx_hash(signature), Self::Eip7702(tx) => tx.tx_hash(signature), Self::Deposit(tx) => tx.tx_hash(), + Self::PostExec(tx) => tx.tx_hash(), } } } @@ -269,7 +302,7 @@ impl SignableTransaction for OpTypedTransaction { Self::Eip2930(tx) => tx.set_chain_id(chain_id), Self::Eip1559(tx) => tx.set_chain_id(chain_id), Self::Eip7702(tx) => tx.set_chain_id(chain_id), - Self::Deposit(_) => {} + Self::Deposit(_) | Self::PostExec(_) => {} } } @@ -279,7 +312,7 @@ impl SignableTransaction for OpTypedTransaction { Self::Eip2930(tx) => tx.encode_for_signing(out), Self::Eip1559(tx) => tx.encode_for_signing(out), Self::Eip7702(tx) => tx.encode_for_signing(out), - Self::Deposit(_) => {} + Self::Deposit(_) | Self::PostExec(_) => {} } } @@ -289,7 +322,7 @@ impl SignableTransaction for OpTypedTransaction { Self::Eip2930(tx) => tx.payload_len_for_signature(), Self::Eip1559(tx) => tx.payload_len_for_signature(), Self::Eip7702(tx) => tx.payload_len_for_signature(), - Self::Deposit(_) => 0, + Self::Deposit(_) | Self::PostExec(_) => 0, } } diff --git a/rust/op-alloy/crates/network/src/lib.rs b/rust/op-alloy/crates/network/src/lib.rs index 0b942701b86..aae8c75a48e 100644 --- a/rust/op-alloy/crates/network/src/lib.rs +++ b/rust/op-alloy/crates/network/src/lib.rs @@ -143,6 +143,7 @@ impl TransactionBuilder for OpTransactionRequest { fn complete_type(&self, ty: OpTxType) -> Result<(), Vec<&'static str>> { match ty { OpTxType::Deposit => Err(vec!["not implemented for deposit tx"]), + OpTxType::PostExec => Err(vec!["not implemented for post-exec tx"]), _ => { let ty = TxType::try_from(ty as u8).unwrap(); self.as_ref().complete_type(ty) diff --git a/rust/op-alloy/crates/rpc-types/src/genesis.rs b/rust/op-alloy/crates/rpc-types/src/genesis.rs index e49556be590..0bd74e63642 100644 --- a/rust/op-alloy/crates/rpc-types/src/genesis.rs +++ b/rust/op-alloy/crates/rpc-types/src/genesis.rs @@ -56,6 +56,8 @@ pub struct OpGenesisInfo { pub interop_time: Option, /// jovian hardfork timestamp pub jovian_time: Option, + /// karst hardfork timestamp + pub karst_time: Option, } impl OpGenesisInfo { @@ -136,6 +138,7 @@ mod tests { isthmus_time: None, interop_time: None, jovian_time: None, + karst_time: None, } ); } @@ -197,6 +200,7 @@ mod tests { isthmus_time: None, interop_time: None, jovian_time: None, + karst_time: None, }), base_fee_info: Some(OpBaseFeeInfo { eip1559_elasticity: None, @@ -222,6 +226,7 @@ mod tests { isthmus_time: None, interop_time: None, jovian_time: None, + karst_time: None, }), base_fee_info: Some(OpBaseFeeInfo { eip1559_elasticity: None, @@ -265,6 +270,7 @@ mod tests { isthmus_time: Some(0), interop_time: None, jovian_time: Some(0), + karst_time: None, }), base_fee_info: None, } diff --git a/rust/op-alloy/crates/rpc-types/src/lib.rs b/rust/op-alloy/crates/rpc-types/src/lib.rs index f77b33eeec7..d6e984e651a 100644 --- a/rust/op-alloy/crates/rpc-types/src/lib.rs +++ b/rust/op-alloy/crates/rpc-types/src/lib.rs @@ -9,6 +9,9 @@ extern crate alloc; +#[cfg(feature = "arbitrary")] +use arbitrary as _; + mod genesis; pub use genesis::{OpBaseFeeInfo, OpChainInfo, OpGenesisInfo}; diff --git a/rust/op-alloy/crates/rpc-types/src/receipt.rs b/rust/op-alloy/crates/rpc-types/src/receipt.rs index 5d7be127446..8faae24fdbe 100644 --- a/rust/op-alloy/crates/rpc-types/src/receipt.rs +++ b/rust/op-alloy/crates/rpc-types/src/receipt.rs @@ -19,6 +19,9 @@ pub struct OpTransactionReceipt { /// L1 block info of the transaction. #[serde(flatten)] pub l1_block_info: L1BlockInfo, + /// Per-transaction gas refund from post-exec block-level warming. + #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")] + pub op_gas_refund: Option, } impl alloy_network_primitives::ReceiptResponse for OpTransactionReceipt { @@ -87,6 +90,9 @@ pub struct OpTransactionReceiptFields { /// L1 block info. #[serde(flatten)] pub l1_block_info: L1BlockInfo, + /// Per-transaction gas refund from post-exec block-level warming. + #[serde(default, skip_serializing_if = "Option::is_none", with = "alloy_serde::quantity::opt")] + pub op_gas_refund: Option, /* --------------------------------------- Regolith --------------------------------------- */ /// Deposit nonce for deposit transactions. /// @@ -229,6 +235,9 @@ impl From for OpReceiptEnvelope { OpReceiptEnvelope::Eip2930(receipt) => Self::Eip2930(convert_standard_receipt(receipt)), OpReceiptEnvelope::Eip1559(receipt) => Self::Eip1559(convert_standard_receipt(receipt)), OpReceiptEnvelope::Eip7702(receipt) => Self::Eip7702(convert_standard_receipt(receipt)), + OpReceiptEnvelope::PostExec(receipt) => { + Self::PostExec(convert_standard_receipt(receipt)) + } OpReceiptEnvelope::Deposit(OpDepositReceiptWithBloom { logs_bloom, receipt }) => { let consensus_logs = receipt.inner.logs.into_iter().map(|log| log.inner).collect(); let consensus_receipt = OpDepositReceiptWithBloom { diff --git a/rust/op-alloy/crates/rpc-types/src/transaction/request.rs b/rust/op-alloy/crates/rpc-types/src/transaction/request.rs index bf621ca5a40..2f9290c688a 100644 --- a/rust/op-alloy/crates/rpc-types/src/transaction/request.rs +++ b/rust/op-alloy/crates/rpc-types/src/transaction/request.rs @@ -6,7 +6,9 @@ use alloy_eips::eip7702::SignedAuthorization; use alloy_network_primitives::TransactionBuilder7702; use alloy_primitives::{Address, Signature, TxKind, U256}; use alloy_rpc_types_eth::{AccessList, TransactionInput, TransactionRequest}; -use op_alloy_consensus::{OpTxEnvelope, OpTypedTransaction, TxDeposit}; +use op_alloy_consensus::{ + OpTxEnvelope, OpTypedTransaction, POST_EXEC_TX_TYPE_ID, TxDeposit, TxPostExec, +}; use serde::{Deserialize, Serialize}; /// Builder for [`OpTypedTransaction`]. @@ -156,6 +158,20 @@ impl From> for OpTransactionRequest { } } +impl From for OpTransactionRequest { + fn from(tx: TxPostExec) -> Self { + Self(TransactionRequest { + from: Some(tx.signer_address()), + transaction_type: Some(POST_EXEC_TX_TYPE_ID), + gas: Some(0), + nonce: Some(0), + value: Some(U256::ZERO), + input: tx.input.into(), + ..Default::default() + }) + } +} + impl From> for OpTransactionRequest where T: SignableTransaction + Into, @@ -181,6 +197,7 @@ impl From for OpTransactionRequest { OpTypedTransaction::Eip1559(tx) => Self(tx.into()), OpTypedTransaction::Eip7702(tx) => Self(tx.into()), OpTypedTransaction::Deposit(tx) => tx.into(), + OpTypedTransaction::PostExec(tx) => tx.into(), } } } @@ -192,6 +209,7 @@ impl From for OpTransactionRequest { OpTxEnvelope::Eip1559(tx) => tx.into(), OpTxEnvelope::Eip7702(tx) => tx.into(), OpTxEnvelope::Deposit(tx) => tx.into(), + OpTxEnvelope::PostExec(tx) => tx.into_inner().into(), _ => Default::default(), } } diff --git a/rust/op-reth/README.md b/rust/op-reth/README.md index e53ac1a7261..3b881bbe965 100644 --- a/rust/op-reth/README.md +++ b/rust/op-reth/README.md @@ -2,11 +2,11 @@ **Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol** -![](./assets/reth-prod.png) +![](https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-prod.png) **[Install](https://paradigmxyz.github.io/reth/installation/installation.html)** | [User Docs](https://reth.rs) -| [Developer Docs](./docs) +| [Developer Docs](https://github.com/paradigmxyz/reth/tree/main/docs) | [Crate Docs](https://reth.rs/docs) ## What is Reth? @@ -19,7 +19,7 @@ As a full Ethereum node, Reth allows users to connect to the Ethereum network an More concretely, our goals are: -1. **Modularity**: Every component of Reth is built to be used as a library: well-tested, heavily documented and benchmarked. We envision that developers will import the node's crates, mix and match, and innovate on top of them. Examples of such usage include but are not limited to spinning up standalone P2P networks, talking directly to a node's database, or "unbundling" the node into the components you need. To achieve that, we are licensing Reth under the Apache/MIT permissive license. You can learn more about the project's components [here](./docs/repo/layout.md). +1. **Modularity**: Every component of Reth is built to be used as a library: well-tested, heavily documented and benchmarked. We envision that developers will import the node's crates, mix and match, and innovate on top of them. Examples of such usage include but are not limited to spinning up standalone P2P networks, talking directly to a node's database, or "unbundling" the node into the components you need. To achieve that, we are licensing Reth under the Apache/MIT permissive license. You can learn more about the project's components [here](https://github.com/paradigmxyz/reth/blob/main/docs/repo/layout.md). 2. **Performance**: Reth aims to be fast, so we use Rust and the [Erigon staged-sync](https://erigon.substack.com/p/erigon-stage-sync-and-control-flows) node architecture. We also use our Ethereum libraries (including [Alloy](https://github.com/alloy-rs/alloy/) and [revm](https://github.com/bluealloy/revm/)) which we've battle-tested and optimized via [Foundry](https://github.com/foundry-rs/foundry/). 3. **Free for anyone to use any way they want**: Reth is free open source software, built for the community, by the community. By licensing the software under the Apache/MIT license, we want developers to use it without being bound by business licenses, or having to think about the implications of GPL-like licenses. 4. **Client Diversity**: The Ethereum protocol becomes more antifragile when no node implementation dominates. This ensures that if there's a software bug, the network does not finalize a bad block. By building a new client, we hope to contribute to Ethereum's antifragility. @@ -33,7 +33,7 @@ Reth is production ready, and suitable for usage in mission-critical environment More historical context below: - We released 1.0 "production-ready" stable Reth in June 2024. - - Reth completed an audit with [Sigma Prime](https://sigmaprime.io/), the developers of [Lighthouse](https://github.com/sigp/lighthouse), the Rust Consensus Layer implementation. Find it [here](./audit/sigma_prime_audit_v2.pdf). + - Reth completed an audit with [Sigma Prime](https://sigmaprime.io/), the developers of [Lighthouse](https://github.com/sigp/lighthouse), the Rust Consensus Layer implementation. Find it [here](https://github.com/paradigmxyz/reth/blob/main/audit/sigma_prime_audit_v2.pdf). - Revm (the EVM used in Reth) underwent an audit with [Guido Vranken](https://x.com/guidovranken) (#1 [Ethereum Bug Bounty](https://ethereum.org/en/bug-bounty)). We will publish the results soon. - We released multiple iterative beta versions, up to [beta.9](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.9) on Monday June 3, 2024, the last beta release. - We released [beta](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.1) on Monday March 4, 2024, our first breaking change to the database model, providing faster query speed, smaller database footprint, and allowing "history" to be mounted on separate drives. @@ -62,14 +62,14 @@ You can use individual crates of reth in your project. The crate docs can be found [here](https://reth.rs/docs/). -For a general overview of the crates, see [Project Layout](./docs/repo/layout.md). +For a general overview of the crates, see [Project Layout](https://github.com/paradigmxyz/reth/blob/main/docs/repo/layout.md). ### Contributing If you want to contribute, or follow along with contributor discussion, you can use our [main telegram](https://t.me/paradigm_reth) to chat with us about the development of Reth! -- Our contributor guidelines can be found in [`CONTRIBUTING.md`](./CONTRIBUTING.md). -- See our [contributor docs](./docs) for more information on the project. A good starting point is [Project Layout](./docs/repo/layout.md). +- Our contributor guidelines can be found in [`CONTRIBUTING.md`](https://github.com/paradigmxyz/reth/blob/main/CONTRIBUTING.md). +- See our [contributor docs](https://github.com/paradigmxyz/reth/tree/main/docs) for more information on the project. A good starting point is [Project Layout](https://github.com/paradigmxyz/reth/blob/main/docs/repo/layout.md). ### Building and testing @@ -117,7 +117,7 @@ If the answer is not there: ## Security -See [`SECURITY.md`](./SECURITY.md). +See [`SECURITY.md`](https://github.com/paradigmxyz/reth/blob/main/SECURITY.md). ## Acknowledgements diff --git a/rust/op-reth/bin/src/main.rs b/rust/op-reth/bin/src/main.rs index 39cf1adde01..cd1436ce6f0 100644 --- a/rust/op-reth/bin/src/main.rs +++ b/rust/op-reth/bin/src/main.rs @@ -25,7 +25,7 @@ fn main() { if let Err(err) = Cli::::parse().run(async move |builder, args| { info!(target: "reth::cli", "Launching node"); - proof_history::launch_node_with_proof_history(builder, args).await + proof_history::launch_node(builder, args).await }) { eprintln!("Error: {err:?}"); diff --git a/rust/op-reth/crates/chainspec/Cargo.toml b/rust/op-reth/crates/chainspec/Cargo.toml index 5706906e44b..2204793f66f 100644 --- a/rust/op-reth/crates/chainspec/Cargo.toml +++ b/rust/op-reth/crates/chainspec/Cargo.toml @@ -20,7 +20,7 @@ reth-network-peers.workspace = true # op-reth reth-optimism-forks.workspace = true -reth-optimism-primitives.workspace = true +reth-optimism-primitives = { workspace = true, features = ["serde", "reth-codec"] } # ethereum alloy-chains.workspace = true diff --git a/rust/op-reth/crates/chainspec/src/lib.rs b/rust/op-reth/crates/chainspec/src/lib.rs index cfa40ff5732..4662f809cf6 100644 --- a/rust/op-reth/crates/chainspec/src/lib.rs +++ b/rust/op-reth/crates/chainspec/src/lib.rs @@ -200,9 +200,16 @@ impl OpChainSpecBuilder { self } + /// Enable Karst at genesis + pub fn karst_activated(mut self) -> Self { + self = self.jovian_activated(); + self.inner = self.inner.with_fork(OpHardfork::Karst, ForkCondition::Timestamp(0)); + self + } + /// Enable Interop at genesis pub fn interop_activated(mut self) -> Self { - self = self.jovian_activated(); + self = self.karst_activated(); self.inner = self.inner.with_fork(OpHardfork::Interop, ForkCondition::Timestamp(0)); self } @@ -393,6 +400,7 @@ impl From for OpChainSpec { (OpHardfork::Holocene.boxed(), genesis_info.holocene_time), (OpHardfork::Isthmus.boxed(), genesis_info.isthmus_time), (OpHardfork::Jovian.boxed(), genesis_info.jovian_time), + (OpHardfork::Karst.boxed(), genesis_info.karst_time), (OpHardfork::Interop.boxed(), genesis_info.interop_time), ]; diff --git a/rust/op-reth/crates/chainspec/src/superchain/chain_metadata.rs b/rust/op-reth/crates/chainspec/src/superchain/chain_metadata.rs index bf6228c099a..95739bedfd1 100644 --- a/rust/op-reth/crates/chainspec/src/superchain/chain_metadata.rs +++ b/rust/op-reth/crates/chainspec/src/superchain/chain_metadata.rs @@ -27,6 +27,7 @@ pub(crate) struct HardforkConfig { pub holocene_time: Option, pub isthmus_time: Option, pub jovian_time: Option, + pub karst_time: Option, } #[derive(Clone, Debug, Deserialize)] @@ -61,6 +62,8 @@ pub(crate) struct ChainConfigExtraFields { #[serde(skip_serializing_if = "Option::is_none")] pub jovian_time: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub karst_time: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub optimism: Option, } @@ -108,7 +111,7 @@ pub(crate) fn to_genesis_chain_config(chain_config: &ChainMetadata) -> ChainConf shanghai_time: chain_config.hardforks.canyon_time, // Shanghai activates with Canyon cancun_time: chain_config.hardforks.ecotone_time, // Cancun activates with Ecotone prague_time: chain_config.hardforks.isthmus_time, // Prague activates with Isthmus - osaka_time: None, + osaka_time: chain_config.hardforks.karst_time, // Osaka activates with Karst terminal_total_difficulty: Some(U256::ZERO), terminal_total_difficulty_passed: true, ethash: None, @@ -141,6 +144,7 @@ pub(crate) fn to_genesis_chain_config(chain_config: &ChainMetadata) -> ChainConf holocene_time: chain_config.hardforks.holocene_time, isthmus_time: chain_config.hardforks.isthmus_time, jovian_time: chain_config.hardforks.jovian_time, + karst_time: chain_config.hardforks.karst_time, optimism: chain_config.optimism.as_ref().map(|o| o.into()), }; res.extra_fields = @@ -203,7 +207,8 @@ mod tests { granite_time: Some(1726070401), holocene_time: Some(1736445601), isthmus_time: Some(1746806401), - jovian_time: None, + jovian_time: Some(1764691201), + karst_time: None, optimism: Option::from(ChainConfigExtraFieldsOptimism { eip1559_elasticity: 6, eip1559_denominator: 50, @@ -220,7 +225,8 @@ mod tests { assert_eq!(value.get("graniteTime").unwrap(), 1726070401); assert_eq!(value.get("holoceneTime").unwrap(), 1736445601); assert_eq!(value.get("isthmusTime").unwrap(), 1746806401); - assert_eq!(value.get("jovianTime"), None); + assert_eq!(value.get("jovianTime").unwrap(), 1764691201); + assert_eq!(value.get("karstTime"), None); let optimism = value.get("optimism").unwrap(); assert_eq!(optimism.get("eip1559Elasticity").unwrap(), 6); assert_eq!(optimism.get("eip1559Denominator").unwrap(), 50); diff --git a/rust/op-reth/crates/cli/src/commands/op_proofs/init.rs b/rust/op-reth/crates/cli/src/commands/op_proofs/init.rs index 506b0e0758c..8cbdf4d5a28 100644 --- a/rust/op-reth/crates/cli/src/commands/op_proofs/init.rs +++ b/rust/op-reth/crates/cli/src/commands/op_proofs/init.rs @@ -6,9 +6,11 @@ use reth_cli::chainspec::ChainSpecParser; use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs}; use reth_node_core::version::version_metadata; use reth_optimism_chainspec::OpChainSpec; +use reth_optimism_node::args::ProofsStorageVersion; use reth_optimism_primitives::OpPrimitives; use reth_optimism_trie::{ - InitializationJob, OpProofsProviderRO, OpProofsStore, db::MdbxProofsStorage, + InitializationJob, OpProofsProviderRO, OpProofsStore, + db::{MdbxProofsStorage, MdbxProofsStorageV2}, }; use reth_provider::{BlockNumReader, DBProvider, DatabaseProviderFactory}; use std::{path::PathBuf, sync::Arc}; @@ -33,6 +35,14 @@ pub struct InitCommand { required = true )] pub storage_path: PathBuf, + + /// Storage schema version. Must match the version used when starting the node. + #[arg( + long = "proofs-history.storage-version", + value_name = "PROOFS_HISTORY_STORAGE_VERSION", + default_value = "v1" + )] + pub storage_version: ProofsStorageVersion, } impl> InitCommand { @@ -51,11 +61,34 @@ impl> InitCommand { // During initialization we write billions of entries; the metrics layer's // `AtomicBucket::push` (used by `Histogram::record_many`) is append-only and // would accumulate ~19 bytes per observation, causing OOM on large chains. - let storage: Arc = Arc::new( - MdbxProofsStorage::new(&self.storage_path) - .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, - ); + match self.storage_version { + ProofsStorageVersion::V1 => { + let storage: Arc = Arc::new( + MdbxProofsStorage::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, + ); + Self::run_init(&provider_factory, storage)?; + } + ProofsStorageVersion::V2 => { + let storage: Arc = Arc::new( + MdbxProofsStorageV2::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorageV2: {e}"))?, + ); + Self::run_init(&provider_factory, storage)?; + } + } + Ok(()) + } + + /// Run the initialization against the given proofs storage. + /// + /// If the storage is already initialized this is a no-op. + fn run_init(provider_factory: &F, storage: impl OpProofsStore) -> eyre::Result<()> + where + F: DatabaseProviderFactory + BlockNumReader, + F::Provider: DBProvider, + { // Check if already initialized if let Some((block_number, block_hash)) = storage.provider_ro()?.get_earliest_block_number()? @@ -79,7 +112,6 @@ impl> InitCommand { "Starting backfill job for current chain state" ); - // Run the backfill job { let db_provider = provider_factory.database_provider_ro()?.disable_long_read_transaction_safety(); diff --git a/rust/op-reth/crates/cli/src/commands/op_proofs/prune.rs b/rust/op-reth/crates/cli/src/commands/op_proofs/prune.rs index 0e0d23392c2..de3ae7dff3f 100644 --- a/rust/op-reth/crates/cli/src/commands/op_proofs/prune.rs +++ b/rust/op-reth/crates/cli/src/commands/op_proofs/prune.rs @@ -5,9 +5,11 @@ use reth_cli::chainspec::ChainSpecParser; use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs}; use reth_node_core::version::version_metadata; use reth_optimism_chainspec::OpChainSpec; +use reth_optimism_node::args::ProofsStorageVersion; use reth_optimism_primitives::OpPrimitives; use reth_optimism_trie::{ - OpProofStoragePruner, OpProofsProviderRO, OpProofsStore, db::MdbxProofsStorage, + OpProofStoragePruner, OpProofsProviderRO, OpProofsStore, + db::{MdbxProofsStorage, MdbxProofsStorageV2}, }; use std::{path::PathBuf, sync::Arc}; use tracing::info; @@ -43,6 +45,14 @@ pub struct PruneCommand { value_name = "PROOFS_HISTORY_PRUNE_BATCH_SIZE" )] pub proofs_history_prune_batch_size: u64, + + /// Storage schema version. Must match the version used when starting the node. + #[arg( + long = "proofs-history.storage-version", + value_name = "PROOFS_HISTORY_STORAGE_VERSION", + default_value = "v1" + )] + pub storage_version: ProofsStorageVersion, } impl> PruneCommand { @@ -57,11 +67,43 @@ impl> PruneCommand { // Initialize the environment with read-only access let Environment { provider_factory, .. } = self.env.init::(AccessRights::RO, runtime)?; - let storage: Arc = Arc::new( - MdbxProofsStorage::new(&self.storage_path) - .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, - ); + match self.storage_version { + ProofsStorageVersion::V1 => { + let storage: Arc = Arc::new( + MdbxProofsStorage::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, + ); + Self::run_prune( + storage, + provider_factory, + self.proofs_history_window, + self.proofs_history_prune_batch_size, + )?; + } + ProofsStorageVersion::V2 => { + let storage: Arc = Arc::new( + MdbxProofsStorageV2::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorageV2: {e}"))?, + ); + Self::run_prune( + storage, + provider_factory, + self.proofs_history_window, + self.proofs_history_prune_batch_size, + )?; + } + } + Ok(()) + } + + /// Run the pruner against the given proofs storage. + fn run_prune( + storage: impl OpProofsStore, + block_hash_reader: impl reth_provider::BlockHashReader, + proofs_history_window: u64, + prune_batch_size: u64, + ) -> eyre::Result<()> { let provider_ro = storage.provider_ro()?; let earliest_block = provider_ro.get_earliest_block_number()?; let latest_block = provider_ro.get_latest_block_number()?; @@ -71,12 +113,13 @@ impl> PruneCommand { ?latest_block, "Current proofs storage block range" ); + drop(provider_ro); let pruner = OpProofStoragePruner::new( storage, - provider_factory, - self.proofs_history_window, - self.proofs_history_prune_batch_size, + block_hash_reader, + proofs_history_window, + prune_batch_size, ); pruner.run(); Ok(()) diff --git a/rust/op-reth/crates/cli/src/commands/op_proofs/unwind.rs b/rust/op-reth/crates/cli/src/commands/op_proofs/unwind.rs index b4be959474a..18c0ebdf689 100644 --- a/rust/op-reth/crates/cli/src/commands/op_proofs/unwind.rs +++ b/rust/op-reth/crates/cli/src/commands/op_proofs/unwind.rs @@ -1,13 +1,16 @@ //! Command that unwinds the OP proofs storage to a specific block number. +use alloy_consensus::BlockHeader; use clap::Parser; use reth_cli::chainspec::ChainSpecParser; use reth_cli_commands::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs}; use reth_node_core::version::version_metadata; use reth_optimism_chainspec::OpChainSpec; +use reth_optimism_node::args::ProofsStorageVersion; use reth_optimism_primitives::OpPrimitives; use reth_optimism_trie::{ - OpProofsProviderRO, OpProofsProviderRw, OpProofsStore, db::MdbxProofsStorage, + OpProofsProviderRO, OpProofsProviderRw, OpProofsStore, + db::{MdbxProofsStorage, MdbxProofsStorageV2}, }; use reth_provider::{BlockReader, TransactionVariant}; use std::{path::PathBuf, sync::Arc}; @@ -34,6 +37,14 @@ pub struct UnwindCommand { /// All history *after* this block will be removed. #[arg(long, value_name = "TARGET_BLOCK")] pub target: u64, + + /// Storage schema version. Must match the version used when starting the node. + #[arg( + long = "proofs-history.storage-version", + value_name = "PROOFS_HISTORY_STORAGE_VERSION", + default_value = "v1" + )] + pub storage_version: ProofsStorageVersion, } impl UnwindCommand { @@ -73,12 +84,32 @@ impl> UnwindCommand { // Initialize the environment with read-only access let Environment { provider_factory, .. } = self.env.init::(AccessRights::RO, runtime)?; - // Create the proofs storage - let storage: Arc = Arc::new( - MdbxProofsStorage::new(&self.storage_path) - .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, - ); + match self.storage_version { + ProofsStorageVersion::V1 => { + let storage: Arc = Arc::new( + MdbxProofsStorage::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, + ); + self.run_unwind(storage, &provider_factory)?; + } + ProofsStorageVersion::V2 => { + let storage: Arc = Arc::new( + MdbxProofsStorageV2::new(&self.storage_path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorageV2: {e}"))?, + ); + self.run_unwind(storage, &provider_factory)?; + } + } + + Ok(()) + } + /// Validate the unwind range and unwind the proofs storage to the target block. + fn run_unwind(&self, storage: S, provider_factory: &F) -> eyre::Result<()> + where + S: OpProofsStore + Clone, + F: BlockReader, + { // Validate that the target block is within a valid range for unwinding if !self.validate_unwind_range(storage.clone())? { return Ok(()); @@ -91,7 +122,7 @@ impl> UnwindCommand { eyre::eyre!("Target block {} not found in the main database", self.target) })?; - info!(target: "reth::cli", block_number = block.number, block_hash = %block.hash(), "Unwinding to target block"); + info!(target: "reth::cli", block_number = block.number(), block_hash = %block.hash(), "Unwinding to target block"); let provider_rw = storage.provider_rw()?; provider_rw.unwind_history(block.block_with_parent())?; provider_rw.commit()?; diff --git a/rust/op-reth/crates/cli/src/ovm_file_codec.rs b/rust/op-reth/crates/cli/src/ovm_file_codec.rs index 9d4704f0a6a..5ecef32c281 100644 --- a/rust/op-reth/crates/cli/src/ovm_file_codec.rs +++ b/rust/op-reth/crates/cli/src/ovm_file_codec.rs @@ -14,7 +14,7 @@ use alloy_primitives::{ }; use alloy_rlp::{Decodable, Error as RlpError, RlpDecodable}; use derive_more::{AsRef, Deref}; -use op_alloy_consensus::{OpTxType, OpTypedTransaction, TxDeposit}; +use op_alloy_consensus::{OpTxType, OpTypedTransaction, TxDeposit, TxPostExec}; use reth_downloaders::file_client::FileClientError; use serde::{Deserialize, Serialize}; use tokio_util::codec::Decoder; @@ -248,6 +248,7 @@ impl Encodable2718 for OvmTransactionSigned { set_code_tx.eip2718_encoded_length(&self.signature) } OpTypedTransaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(), + OpTypedTransaction::PostExec(post_exec_tx) => post_exec_tx.eip2718_encoded_length(), } } @@ -276,6 +277,10 @@ impl Decodable2718 for OvmTransactionSigned { OpTypedTransaction::Deposit(TxDeposit::rlp_decode(buf)?), TxDeposit::signature(), )), + OpTxType::PostExec => Ok(Self::from_transaction_and_signature( + OpTypedTransaction::PostExec(TxPostExec::decode_2718(buf)?), + TxPostExec::signature(), + )), } } diff --git a/rust/op-reth/crates/cli/src/receipt_file_codec.rs b/rust/op-reth/crates/cli/src/receipt_file_codec.rs index 8cbb89abe09..376b69306d9 100644 --- a/rust/op-reth/crates/cli/src/receipt_file_codec.rs +++ b/rust/op-reth/crates/cli/src/receipt_file_codec.rs @@ -108,6 +108,7 @@ impl TryFrom for OpReceipt { OpTxType::Legacy => Ok(Self::Legacy(receipt)), OpTxType::Eip2930 => Ok(Self::Eip2930(receipt)), OpTxType::Eip1559 => Ok(Self::Eip1559(receipt)), + OpTxType::PostExec => Ok(Self::PostExec(receipt)), OpTxType::Eip7702 => Ok(Self::Eip7702(receipt)), OpTxType::Deposit => Ok(Self::Deposit(OpDepositReceipt { inner: receipt, diff --git a/rust/op-reth/crates/evm/src/receipts.rs b/rust/op-reth/crates/evm/src/receipts.rs index 9b95cf3a489..82072893063 100644 --- a/rust/op-reth/crates/evm/src/receipts.rs +++ b/rust/op-reth/crates/evm/src/receipts.rs @@ -35,6 +35,7 @@ impl OpReceiptBuilder for OpRethReceiptBuilder { OpTxType::Eip1559 => OpReceipt::Eip1559(receipt), OpTxType::Eip2930 => OpReceipt::Eip2930(receipt), OpTxType::Eip7702 => OpReceipt::Eip7702(receipt), + OpTxType::PostExec => OpReceipt::PostExec(receipt), OpTxType::Deposit => unreachable!(), }) } diff --git a/rust/op-reth/crates/hardforks/src/lib.rs b/rust/op-reth/crates/hardforks/src/lib.rs index f299f7a9645..40edc47a6c4 100644 --- a/rust/op-reth/crates/hardforks/src/lib.rs +++ b/rust/op-reth/crates/hardforks/src/lib.rs @@ -64,6 +64,7 @@ pub static DEV_HARDFORKS: LazyLock = LazyLock::new(|| { (EthereumHardfork::Prague.boxed(), ForkCondition::Timestamp(0)), (OpHardfork::Isthmus.boxed(), ForkCondition::Timestamp(0)), (OpHardfork::Jovian.boxed(), ForkCondition::Timestamp(0)), + (OpHardfork::Karst.boxed(), ForkCondition::Timestamp(0)), ]) }); diff --git a/rust/op-reth/crates/node/src/args.rs b/rust/op-reth/crates/node/src/args.rs index d4d8748fa82..6b411dc29e7 100644 --- a/rust/op-reth/crates/node/src/args.rs +++ b/rust/op-reth/crates/node/src/args.rs @@ -7,6 +7,17 @@ use op_alloy_consensus::interop::SafetyLevel; use std::{path::PathBuf, time::Duration}; use url::Url; +/// Storage schema version for the proofs-history database. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, clap::ValueEnum)] +pub enum ProofsStorageVersion { + /// V1 storage schema (original single-table-per-domain layout). Default. + #[default] + V1, + /// V2 storage schema with changeset and history-bitmap tables, enabling + /// history-aware reads at any block number within the proof window. + V2, +} + /// Parameters for rollup configuration #[derive(Debug, Clone, PartialEq, Eq, clap::Args)] #[command(next_help_heading = "Rollup")] @@ -139,6 +150,19 @@ pub struct RollupArgs { default_value_t = 0 )] pub proofs_history_verification_interval: u64, + + /// Storage schema version for proofs history. Defaults to v1. + /// + /// Use `v2` to enable the changeset + history-bitmap schema, which supports + /// history-aware reads at any block number within the proof window. + /// + /// CLI: `--proofs-history.storage-version v2` + #[arg( + long = "proofs-history.storage-version", + value_name = "PROOFS_HISTORY_STORAGE_VERSION", + default_value = "v1" + )] + pub proofs_history_storage_version: ProofsStorageVersion, } impl Default for RollupArgs { @@ -161,6 +185,7 @@ impl Default for RollupArgs { proofs_history_window: 1_296_000, proofs_history_prune_interval: Duration::from_secs(15), proofs_history_verification_interval: 0, + proofs_history_storage_version: ProofsStorageVersion::V1, } } } diff --git a/rust/op-reth/crates/node/src/node.rs b/rust/op-reth/crates/node/src/node.rs index 50e74c70bae..94d23f80f17 100644 --- a/rust/op-reth/crates/node/src/node.rs +++ b/rust/op-reth/crates/node/src/node.rs @@ -506,6 +506,34 @@ where ) } + /// Maps the [`EngineValidatorBuilder`] builder type. + pub fn with_engine_validator( + self, + engine_validator_builder: T, + ) -> OpAddOns { + let Self { + rpc_add_ons, + da_config, + gas_limit_config, + sequencer_url, + sequencer_headers, + enable_tx_conditional, + min_suggested_priority_fee, + historical_rpc, + .. + } = self; + OpAddOns::new( + rpc_add_ons.with_engine_validator(engine_validator_builder), + da_config, + gas_limit_config, + sequencer_url, + sequencer_headers, + historical_rpc, + enable_tx_conditional, + min_suggested_priority_fee, + ) + } + /// Sets the RPC middleware stack for processing RPC requests. /// /// This method configures a custom middleware stack that will be applied to all RPC requests diff --git a/rust/op-reth/crates/node/src/proof_history.rs b/rust/op-reth/crates/node/src/proof_history.rs index 404b938bc1b..b82c755ec32 100644 --- a/rust/op-reth/crates/node/src/proof_history.rs +++ b/rust/op-reth/crates/node/src/proof_history.rs @@ -1,6 +1,9 @@ -//! Node luncher with proof history support. +//! Node launcher with proof history support. -use crate::{OpNode, args::RollupArgs}; +use crate::{ + OpNode, + args::{ProofsStorageVersion, RollupArgs}, +}; use eyre::ErrReport; use futures_util::FutureExt; use reth_db::DatabaseEnv; @@ -12,7 +15,10 @@ use reth_optimism_rpc::{ debug::{DebugApiExt, DebugApiOverrideServer}, eth::proofs::{EthApiExt, EthApiOverrideServer}, }; -use reth_optimism_trie::{OpProofsStorage, db::MdbxProofsStorage}; +use reth_optimism_trie::{ + OpProofsStorage, OpProofsStore, + db::{MdbxProofsStorage, MdbxProofsStorageV2}, +}; use reth_tasks::TaskExecutor; use std::{sync::Arc, time::Duration}; use tokio::time::sleep; @@ -21,81 +27,107 @@ use tracing::info; /// - no proofs history (plain node), /// - in-mem proofs storage, /// - MDBX proofs storage. -pub async fn launch_node_with_proof_history( +pub async fn launch_node( builder: WithLaunchContext>, args: RollupArgs, ) -> eyre::Result<(), ErrReport> { + if !args.proofs_history { + let handle = builder.node(OpNode::new(args)).launch_with_debug_capabilities().await?; + return handle.node_exit_future.await; + } + + let path = args + .proofs_history_storage_path + .clone() + .expect("Path must be provided if not using in-memory storage"); + + match args.proofs_history_storage_version { + ProofsStorageVersion::V1 => { + info!(target: "reth::cli", "Using on-disk storage for proofs history (v1)"); + let mdbx = Arc::new( + MdbxProofsStorage::new(&path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, + ); + launch_with_proof_history(builder, args, mdbx).await + } + ProofsStorageVersion::V2 => { + info!(target: "reth::cli", "Using on-disk storage for proofs history (v2)"); + let mdbx = Arc::new( + MdbxProofsStorageV2::new(&path) + .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorageV2: {e}"))?, + ); + launch_with_proof_history(builder, args, mdbx).await + } + } +} + +/// Installs the ExEx, RPC overrides, and metrics hook for proof history, then launches the node. +async fn launch_with_proof_history( + builder: WithLaunchContext>, + args: RollupArgs, + mdbx: Arc, +) -> eyre::Result<(), ErrReport> +where + S: OpProofsStore + DatabaseMetrics + Send + Sync + 'static, +{ + let storage: OpProofsStorage> = mdbx.clone().into(); + let storage_exec = storage.clone(); + let RollupArgs { - proofs_history, proofs_history_window, proofs_history_prune_interval, proofs_history_verification_interval, .. - } = args; - - // Start from a plain OpNode builder - let mut node_builder = builder.node(OpNode::new(args.clone())); - - if proofs_history { - let path = args - .proofs_history_storage_path - .clone() - .expect("Path must be provided if not using in-memory storage"); - info!(target: "reth::cli", "Using on-disk storage for proofs history"); + } = args.clone(); - let mdbx = Arc::new( - MdbxProofsStorage::new(&path) - .map_err(|e| eyre::eyre!("Failed to create MdbxProofsStorage: {e}"))?, - ); - let storage: OpProofsStorage> = mdbx.clone().into(); - - let storage_exec = storage.clone(); + let handle = builder + .node(OpNode::new(args)) + .on_node_started(move |node| { + spawn_proofs_db_metrics( + node.task_executor, + mdbx, + node.config.metrics.push_gateway_interval, + ); + Ok(()) + }) + .install_exex("proofs-history", async move |exex_context| { + Ok(OpProofsExEx::builder(exex_context, storage_exec) + .with_proofs_history_window(proofs_history_window) + .with_proofs_history_prune_interval(proofs_history_prune_interval) + .with_verification_interval(proofs_history_verification_interval) + .build() + .run() + .boxed()) + }) + .extend_rpc_modules(move |ctx| { + info!(target: "reth::cli", "Installing proofs-history RPC overrides (eth_getProof, debug_executePayload)"); + let api_ext = EthApiExt::new(ctx.registry.eth_api().clone(), storage.clone()); + let debug_ext = DebugApiExt::new( + ctx.node().provider().clone(), + ctx.registry.eth_api().clone(), + storage, + ctx.node().task_executor().clone(), + ctx.node().evm_config().clone(), + ); + let eth_replaced = ctx.modules.replace_configured(api_ext.into_rpc())?; + let debug_replaced = ctx.modules.replace_configured(debug_ext.into_rpc())?; + info!(target: "reth::cli", eth_replaced, debug_replaced, "Proofs-history RPC overrides installed"); + Ok(()) + }) + .launch_with_debug_capabilities() + .await?; - node_builder = node_builder - .on_node_started(move |node| { - spawn_proofs_db_metrics( - node.task_executor, - mdbx, - node.config.metrics.push_gateway_interval, - ); - Ok(()) - }) - .install_exex("proofs-history", async move |exex_context| { - Ok(OpProofsExEx::builder(exex_context, storage_exec) - .with_proofs_history_window(proofs_history_window) - .with_proofs_history_prune_interval(proofs_history_prune_interval) - .with_verification_interval(proofs_history_verification_interval) - .build() - .run() - .boxed()) - }) - .extend_rpc_modules(move |ctx| { - info!(target: "reth::cli", "Installing proofs-history RPC overrides (eth_getProof, debug_executePayload)"); - let api_ext = EthApiExt::new(ctx.registry.eth_api().clone(), storage.clone()); - let debug_ext = DebugApiExt::new( - ctx.node().provider().clone(), - ctx.registry.eth_api().clone(), - storage, - ctx.node().task_executor().clone(), - ctx.node().evm_config().clone(), - ); - let eth_replaced = ctx.modules.replace_configured(api_ext.into_rpc())?; - let debug_replaced = ctx.modules.replace_configured(debug_ext.into_rpc())?; - info!(target: "reth::cli", eth_replaced, debug_replaced, "Proofs-history RPC overrides installed"); - Ok(()) - }); - } - - // In all cases (with or without proofs), launch the node. - let handle = node_builder.launch_with_debug_capabilities().await?; handle.node_exit_future.await } + /// Spawns a task that periodically reports metrics for the proofs DB. -fn spawn_proofs_db_metrics( +fn spawn_proofs_db_metrics( executor: TaskExecutor, - storage: Arc, + storage: Arc, metrics_report_interval: Duration, -) { +) where + S: DatabaseMetrics + Send + Sync + 'static, +{ executor.spawn_critical_task("op-proofs-storage-metrics", async move { info!( target: "reth::cli", diff --git a/rust/op-reth/crates/primitives/src/receipt.rs b/rust/op-reth/crates/primitives/src/receipt.rs index 21c620ae67f..2ab2486aa30 100644 --- a/rust/op-reth/crates/primitives/src/receipt.rs +++ b/rust/op-reth/crates/primitives/src/receipt.rs @@ -67,6 +67,8 @@ pub(super) mod serde_bincode_compat { Eip1559(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>), /// EIP-7702 receipt Eip7702(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>), + /// Post-exec receipt + PostExec(alloy_consensus::serde_bincode_compat::Receipt<'a, alloy_primitives::Log>), /// Deposit receipt Deposit( op_alloy_consensus::serde_bincode_compat::OpDepositReceipt<'a, alloy_primitives::Log>, @@ -80,6 +82,7 @@ pub(super) mod serde_bincode_compat { super::OpReceipt::Eip2930(receipt) => Self::Eip2930(receipt.into()), super::OpReceipt::Eip1559(receipt) => Self::Eip1559(receipt.into()), super::OpReceipt::Eip7702(receipt) => Self::Eip7702(receipt.into()), + super::OpReceipt::PostExec(receipt) => Self::PostExec(receipt.into()), super::OpReceipt::Deposit(receipt) => Self::Deposit(receipt.into()), } } @@ -92,6 +95,7 @@ pub(super) mod serde_bincode_compat { OpReceipt::Eip2930(receipt) => Self::Eip2930(receipt.into()), OpReceipt::Eip1559(receipt) => Self::Eip1559(receipt.into()), OpReceipt::Eip7702(receipt) => Self::Eip7702(receipt.into()), + OpReceipt::PostExec(receipt) => Self::PostExec(receipt.into()), OpReceipt::Deposit(receipt) => Self::Deposit(receipt.into()), } } diff --git a/rust/op-reth/crates/primitives/src/transaction/mod.rs b/rust/op-reth/crates/primitives/src/transaction/mod.rs index 306f5459046..9b6a425fe2a 100644 --- a/rust/op-reth/crates/primitives/src/transaction/mod.rs +++ b/rust/op-reth/crates/primitives/src/transaction/mod.rs @@ -6,7 +6,10 @@ mod tx_type; #[cfg(test)] mod signed; -pub use op_alloy_consensus::{OpTransaction, OpTxType, OpTypedTransaction}; +pub use op_alloy_consensus::{ + OpTransaction, OpTxEnvelope, OpTxType, OpTypedTransaction, POST_EXEC_TX_TYPE_ID, + PostExecPayload, SDMGasEntry, TxPostExec, build_post_exec_tx, +}; /// Signed transaction. -pub type OpTransactionSigned = op_alloy_consensus::OpTxEnvelope; +pub type OpTransactionSigned = OpTxEnvelope; diff --git a/rust/op-reth/crates/primitives/src/transaction/signed.rs b/rust/op-reth/crates/primitives/src/transaction/signed.rs index 6fcb6dc183c..386d653a02f 100644 --- a/rust/op-reth/crates/primitives/src/transaction/signed.rs +++ b/rust/op-reth/crates/primitives/src/transaction/signed.rs @@ -20,7 +20,9 @@ use core::{ mem, ops::Deref, }; -use op_alloy_consensus::{OpPooledTransaction, OpTxEnvelope, OpTypedTransaction, TxDeposit}; +use op_alloy_consensus::{ + OpPooledTransaction, OpTxEnvelope, OpTypedTransaction, TxDeposit, TxPostExec, +}; #[cfg(any(test, feature = "reth-codec"))] use reth_primitives_traits::{ InMemorySize, SignedTransaction, @@ -64,6 +66,7 @@ impl OpTransactionSigned { OpTypedTransaction::Eip1559(tx) => &mut tx.input, OpTypedTransaction::Eip7702(tx) => &mut tx.input, OpTypedTransaction::Deposit(tx) => &mut tx.input, + OpTypedTransaction::PostExec(tx) => &mut tx.input, } } @@ -105,40 +108,62 @@ impl OpTransactionSigned { impl SignerRecoverable for OpTransactionSigned { fn recover_signer(&self) -> Result { - // Optimism's Deposit transaction does not have a signature. Directly return the - // `from` address. - if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = self.transaction { - return Ok(from); + match &self.transaction { + // Optimism's Deposit transaction does not have a signature. Directly return the + // `from` address. + OpTypedTransaction::Deposit(TxDeposit { from, .. }) => Ok(*from), + // Post-exec transactions are unsigned synthetic system transactions. They use a + // canonical zero-address signer rather than a cryptographic signature. + OpTypedTransaction::PostExec(tx) => Ok(tx.signer_address()), + _ => { + let Self { transaction, signature, .. } = self; + let signature_hash = signature_hash(transaction); + recover_signer(signature, signature_hash) + } } - - let Self { transaction, signature, .. } = self; - let signature_hash = signature_hash(transaction); - recover_signer(signature, signature_hash) } fn recover_signer_unchecked(&self) -> Result { - // Optimism's Deposit transaction does not have a signature. Directly return the - // `from` address. - if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = &self.transaction { - return Ok(*from); + match &self.transaction { + // Optimism's Deposit transaction does not have a signature. Directly return the + // `from` address. + OpTypedTransaction::Deposit(TxDeposit { from, .. }) => Ok(*from), + // Post-exec transactions are unsigned synthetic system transactions. They use a + // canonical zero-address signer rather than a cryptographic signature. + OpTypedTransaction::PostExec(tx) => Ok(tx.signer_address()), + _ => { + let Self { transaction, signature, .. } = self; + let signature_hash = signature_hash(transaction); + recover_signer_unchecked(signature, signature_hash) + } } - - let Self { transaction, signature, .. } = self; - let signature_hash = signature_hash(transaction); - recover_signer_unchecked(signature, signature_hash) } fn recover_unchecked_with_buf(&self, buf: &mut Vec) -> Result { match &self.transaction { // Optimism's Deposit transaction does not have a signature. Directly return the // `from` address. - OpTypedTransaction::Deposit(tx) => return Ok(tx.from), - OpTypedTransaction::Legacy(tx) => tx.encode_for_signing(buf), - OpTypedTransaction::Eip2930(tx) => tx.encode_for_signing(buf), - OpTypedTransaction::Eip1559(tx) => tx.encode_for_signing(buf), - OpTypedTransaction::Eip7702(tx) => tx.encode_for_signing(buf), - }; - recover_signer_unchecked(&self.signature, keccak256(buf)) + OpTypedTransaction::Deposit(tx) => Ok(tx.from), + // Post-exec transactions are unsigned synthetic system transactions. They use a + // canonical zero-address signer rather than a cryptographic signature. + OpTypedTransaction::PostExec(tx) => Ok(tx.signer_address()), + OpTypedTransaction::Legacy(tx) => { + tx.encode_for_signing(buf); + recover_signer_unchecked(&self.signature, keccak256(buf)) + } + OpTypedTransaction::Eip2930(tx) => { + tx.encode_for_signing(buf); + recover_signer_unchecked(&self.signature, keccak256(buf)) + } + OpTypedTransaction::Eip1559(tx) => { + tx.encode_for_signing(buf); + recover_signer_unchecked(&self.signature, keccak256(buf)) + } + OpTypedTransaction::Eip7702(tx) => { + tx.encode_for_signing(buf); + recover_signer_unchecked(&self.signature, keccak256(buf)) + } + } } } @@ -177,6 +202,7 @@ impl From for OpTransactionSigned { OpTxEnvelope::Eip1559(tx) => tx.into(), OpTxEnvelope::Eip7702(tx) => tx.into(), OpTxEnvelope::Deposit(tx) => tx.into(), + OpTxEnvelope::PostExec(tx) => tx.into(), } } } @@ -188,6 +214,13 @@ impl From> for OpTransactionSigned { } } +impl From> for OpTransactionSigned { + fn from(value: Sealed) -> Self { + let (tx, hash) = value.into_parts(); + Self::new(OpTypedTransaction::PostExec(tx), TxPostExec::signature(), hash) + } +} + impl From for OpTxEnvelope { fn from(value: OpTransactionSigned) -> Self { let (tx, signature, hash) = value.into_parts(); @@ -197,6 +230,7 @@ impl From for OpTxEnvelope { OpTypedTransaction::Eip1559(tx) => Signed::new_unchecked(tx, signature, hash).into(), OpTypedTransaction::Deposit(tx) => Sealed::new_unchecked(tx, hash).into(), OpTypedTransaction::Eip7702(tx) => Signed::new_unchecked(tx, signature, hash).into(), + OpTypedTransaction::PostExec(tx) => Sealed::new_unchecked(tx, hash).into(), } } } @@ -249,6 +283,7 @@ impl Encodable2718 for OpTransactionSigned { set_code_tx.eip2718_encoded_length(&self.signature) } OpTypedTransaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(), + OpTypedTransaction::PostExec(post_exec_tx) => post_exec_tx.eip2718_encoded_length(), } } @@ -268,6 +303,7 @@ impl Encodable2718 for OpTransactionSigned { } OpTypedTransaction::Eip7702(set_code_tx) => set_code_tx.eip2718_encode(signature, out), OpTypedTransaction::Deposit(deposit_tx) => deposit_tx.encode_2718(out), + OpTypedTransaction::PostExec(post_exec_tx) => post_exec_tx.encode_2718(out), } } } @@ -298,6 +334,10 @@ impl Decodable2718 for OpTransactionSigned { OpTypedTransaction::Deposit(TxDeposit::rlp_decode(buf)?), TxDeposit::signature(), )), + op_alloy_consensus::OpTxType::PostExec => Ok(Self::new_unhashed( + OpTypedTransaction::PostExec(TxPostExec::decode_2718(buf)?), + TxPostExec::signature(), + )), } } @@ -391,7 +431,18 @@ impl Typed2718 for OpTransactionSigned { impl PartialEq for OpTransactionSigned { fn eq(&self, other: &Self) -> bool { - self.signature == other.signature && + let self_signature = match &self.transaction { + OpTypedTransaction::Deposit(_) => TxDeposit::signature(), + OpTypedTransaction::PostExec(_) => TxPostExec::signature(), + _ => self.signature, + }; + let other_signature = match &other.transaction { + OpTypedTransaction::Deposit(_) => TxDeposit::signature(), + OpTypedTransaction::PostExec(_) => TxPostExec::signature(), + _ => other.signature, + }; + + self_signature == other_signature && self.transaction == other.transaction && self.tx_hash() == other.tx_hash() } @@ -399,7 +450,11 @@ impl PartialEq for OpTransactionSigned { impl Hash for OpTransactionSigned { fn hash(&self, state: &mut H) { - self.signature.hash(state); + match &self.transaction { + OpTypedTransaction::Deposit(_) => TxDeposit::signature().hash(state), + OpTypedTransaction::PostExec(_) => TxPostExec::signature().hash(state), + _ => self.signature.hash(state), + } self.transaction.hash(state); } } @@ -416,7 +471,11 @@ impl reth_codecs::Compact for OpTransactionSigned { // The first byte uses 4 bits as flags: IsCompressed[1bit], TxType[2bits], Signature[1bit] buf.put_u8(0); - let sig_bit = self.signature.to_compact(buf) as u8; + let sig_bit = match &self.transaction { + OpTypedTransaction::Deposit(_) => TxDeposit::signature().to_compact(buf) as u8, + OpTypedTransaction::PostExec(_) => TxPostExec::signature().to_compact(buf) as u8, + _ => self.signature.to_compact(buf) as u8, + }; let zstd_bit = self.transaction.input().len() >= 32; let tx_bits = if zstd_bit { @@ -478,7 +537,11 @@ impl<'a> arbitrary::Arbitrary<'a> for OpTransactionSigned { ) .unwrap(); - let signature = if transaction.is_deposit() { TxDeposit::signature() } else { signature }; + let signature = match &transaction { + OpTypedTransaction::Deposit(_) => TxDeposit::signature(), + OpTypedTransaction::PostExec(_) => TxPostExec::signature(), + _ => signature, + }; Ok(Self::new_unhashed(transaction, signature)) } @@ -491,7 +554,7 @@ fn signature_hash(tx: &OpTypedTransaction) -> B256 { OpTypedTransaction::Eip2930(tx) => tx.signature_hash(), OpTypedTransaction::Eip1559(tx) => tx.signature_hash(), OpTypedTransaction::Eip7702(tx) => tx.signature_hash(), - OpTypedTransaction::Deposit(_) => B256::ZERO, + OpTypedTransaction::Deposit(_) | OpTypedTransaction::PostExec(_) => B256::ZERO, } } @@ -502,6 +565,22 @@ mod tests { use proptest_arbitrary_interop::arb; use reth_codecs::Compact; + fn make_input_large_enough_for_zstd(tx: &mut OpTransactionSigned) { + match &mut tx.transaction { + OpTypedTransaction::PostExec(post_exec) => { + *post_exec = TxPostExec::new(op_alloy_consensus::PostExecPayload { + version: 1, + block_number: 1, + gas_refund_entries: (0..16) + .map(|index| op_alloy_consensus::SDMGasEntry { index, gas_refund: 1 }) + .collect(), + }); + assert!(post_exec.input.len() >= 33); + } + _ => *tx.input_mut() = vec![0; 33].into(), + } + } + proptest! { #[test] fn test_roundtrip_compact_encode_envelope(reth_tx in arb::()) { @@ -519,7 +598,7 @@ mod tests { #[test] fn test_roundtrip_compact_decode_envelope_zstd(mut reth_tx in arb::()) { // zstd only kicks in if the input is large enough - *reth_tx.input_mut() = vec![0;33].into(); + make_input_large_enough_for_zstd(&mut reth_tx); let mut buf = Vec::::new(); let len = reth_tx.to_compact(&mut buf); @@ -532,7 +611,7 @@ mod tests { #[test] fn test_roundtrip_compact_encode_envelope_zstd(mut reth_tx in arb::()) { // zstd only kicks in if the input is large enough - *reth_tx.input_mut() = vec![0;33].into(); + make_input_large_enough_for_zstd(&mut reth_tx); let mut expected_buf = Vec::::new(); let expected_len = reth_tx.to_compact(&mut expected_buf); diff --git a/rust/op-reth/crates/primitives/src/transaction/tx_type.rs b/rust/op-reth/crates/primitives/src/transaction/tx_type.rs index bea52f338ed..246619ccea6 100644 --- a/rust/op-reth/crates/primitives/src/transaction/tx_type.rs +++ b/rust/op-reth/crates/primitives/src/transaction/tx_type.rs @@ -3,7 +3,7 @@ #[cfg(test)] mod tests { use alloy_consensus::constants::EIP7702_TX_TYPE_ID; - use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTxType}; + use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, OpTxType, POST_EXEC_TX_TYPE_ID}; use reth_codecs::{Compact, txtype::*}; use rstest::rstest; @@ -13,6 +13,7 @@ mod tests { #[case(OpTxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] #[case(OpTxType::Eip7702, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP7702_TX_TYPE_ID])] #[case(OpTxType::Deposit, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![DEPOSIT_TX_TYPE_ID])] + #[case(OpTxType::PostExec, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![POST_EXEC_TX_TYPE_ID])] fn test_txtype_to_compact( #[case] tx_type: OpTxType, #[case] expected_identifier: usize, @@ -34,6 +35,7 @@ mod tests { #[case(OpTxType::Eip1559, COMPACT_IDENTIFIER_EIP1559, vec![])] #[case(OpTxType::Eip7702, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![EIP7702_TX_TYPE_ID])] #[case(OpTxType::Deposit, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![DEPOSIT_TX_TYPE_ID])] + #[case(OpTxType::PostExec, COMPACT_EXTENDED_IDENTIFIER_FLAG, vec![POST_EXEC_TX_TYPE_ID])] fn test_txtype_from_compact( #[case] expected_type: OpTxType, #[case] identifier: usize, diff --git a/rust/op-reth/crates/rpc/src/eth/mod.rs b/rust/op-reth/crates/rpc/src/eth/mod.rs index ed1f59b4591..8291bde0dec 100644 --- a/rust/op-reth/crates/rpc/src/eth/mod.rs +++ b/rust/op-reth/crates/rpc/src/eth/mod.rs @@ -38,7 +38,7 @@ use reth_rpc_eth_api::{ RpcNodeCoreExt, RpcTypes, helpers::{ EthApiSpec, EthFees, EthState, LoadFee, LoadPendingBlock, LoadState, SpawnBlocking, Trace, - pending_block::BuildPendingEnv, + bal::GetBlockAccessList, pending_block::BuildPendingEnv, }, }; use reth_rpc_eth_types::{ @@ -386,6 +386,14 @@ where { } +impl GetBlockAccessList for OpEthApi +where + N: RpcNodeCore, + OpEthApiError: FromEvmError, + Rpc: RpcConvert, +{ +} + impl fmt::Debug for OpEthApi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpEthApi").finish_non_exhaustive() diff --git a/rust/op-reth/crates/rpc/src/eth/receipt.rs b/rust/op-reth/crates/rpc/src/eth/receipt.rs index 77243039e97..c7e00539df2 100644 --- a/rust/op-reth/crates/rpc/src/eth/receipt.rs +++ b/rust/op-reth/crates/rpc/src/eth/receipt.rs @@ -11,7 +11,7 @@ use reth_chainspec::{ChainSpecProvider, EthChainSpec}; use reth_node_api::NodePrimitives; use reth_optimism_evm::RethL1BlockInfo; use reth_optimism_forks::OpHardforks; -use reth_primitives_traits::SealedBlock; +use reth_primitives_traits::{BlockBody, SealedBlock}; use reth_rpc_eth_api::{ RpcConvert, helpers::LoadReceipt, @@ -86,6 +86,11 @@ where }; let mut receipts = Vec::with_capacity(inputs.len()); + let post_exec_payload = block + .body() + .transactions() + .iter() + .find_map(|tx| tx.as_post_exec().map(|tx| &tx.inner().payload)); for input in inputs { // We must clear this cache as different L2 transactions can have different @@ -93,9 +98,18 @@ where // new transaction input has changed, since otherwise the L1 cost wouldn't. l1_block_info.clear_tx_l1_cost(); + let op_gas_refund = post_exec_payload + .as_ref() + .and_then(|payload| payload.gas_refund_for_idx(input.meta.index)); + receipts.push( - OpReceiptBuilder::new(&self.provider.chain_spec(), input, &mut l1_block_info)? - .build(), + OpReceiptBuilder::new( + &self.provider.chain_spec(), + input, + &mut l1_block_info, + op_gas_refund, + )? + .build(), ); } @@ -120,6 +134,8 @@ pub struct OpReceiptFieldsBuilder { /* ---------------------------------------- Bedrock ---------------------------------------- */ /// The base fee of the L1 origin block. pub l1_base_fee: Option, + /// Post-exec block-level warming refund for this transaction. + pub op_gas_refund: Option, /* --------------------------------------- Regolith ---------------------------------------- */ /// Deposit nonce, if this is a deposit transaction. pub deposit_nonce: Option, @@ -153,6 +169,7 @@ impl OpReceiptFieldsBuilder { l1_data_gas: None, l1_fee_scalar: None, l1_base_fee: None, + op_gas_refund: None, deposit_nonce: None, deposit_receipt_version: None, l1_base_fee_scalar: None, @@ -217,6 +234,12 @@ impl OpReceiptFieldsBuilder { Ok(self) } + /// Applies post-exec block-level warming refund metadata. + pub const fn op_gas_refund(mut self, op_gas_refund: Option) -> Self { + self.op_gas_refund = op_gas_refund; + self + } + /// Applies deposit transaction metadata: deposit nonce. pub const fn deposit_nonce(mut self, nonce: Option) -> Self { self.deposit_nonce = nonce; @@ -238,6 +261,7 @@ impl OpReceiptFieldsBuilder { l1_data_gas: l1_gas_used, l1_fee_scalar, l1_base_fee: l1_gas_price, + op_gas_refund, deposit_nonce, deposit_receipt_version, l1_base_fee_scalar, @@ -261,6 +285,7 @@ impl OpReceiptFieldsBuilder { operator_fee_constant, da_footprint_gas_scalar, }, + op_gas_refund, deposit_nonce, deposit_receipt_version, } @@ -282,6 +307,7 @@ impl OpReceiptBuilder { chain_spec: &impl OpHardforks, input: ConvertReceiptInput<'_, N>, l1_block_info: &mut op_revm::L1BlockInfo, + op_gas_refund: Option, ) -> Result where N: NodePrimitives, @@ -300,6 +326,7 @@ impl OpReceiptBuilder { OpReceipt::Eip2930(receipt) => OpReceipt::Eip2930(map_logs(receipt)), OpReceipt::Eip1559(receipt) => OpReceipt::Eip1559(map_logs(receipt)), OpReceipt::Eip7702(receipt) => OpReceipt::Eip7702(map_logs(receipt)), + OpReceipt::PostExec(receipt) => OpReceipt::PostExec(map_logs(receipt)), OpReceipt::Deposit(receipt) => OpReceipt::Deposit(receipt.map_inner(map_logs)), }; mapped_receipt.into_with_bloom() @@ -322,6 +349,7 @@ impl OpReceiptBuilder { let op_receipt_fields = OpReceiptFieldsBuilder::new(timestamp, block_number) .l1_block_info(chain_spec, tx_signed, l1_block_info)? + .op_gas_refund(op_gas_refund) .build(); Ok(Self { core_receipt, op_receipt_fields }) @@ -332,25 +360,33 @@ impl OpReceiptBuilder { pub fn build(self) -> OpTransactionReceipt { let Self { core_receipt: inner, op_receipt_fields } = self; - let OpTransactionReceiptFields { l1_block_info, .. } = op_receipt_fields; + let OpTransactionReceiptFields { + l1_block_info, + op_gas_refund, + deposit_nonce: _, + deposit_receipt_version: _, + } = op_receipt_fields; - OpTransactionReceipt { inner, l1_block_info } + OpTransactionReceipt { inner, l1_block_info, op_gas_refund } } } #[cfg(test)] mod test { use super::*; - use alloy_consensus::{Block, BlockBody, Eip658Value, TxEip7702, transaction::TransactionMeta}; + use alloy_consensus::{ + Block, BlockBody, Eip658Value, Header, Receipt, Sealable, SignableTransaction, TxEip7702, + transaction::TransactionMeta, + }; use alloy_op_hardforks::{ OP_MAINNET_ISTHMUS_TIMESTAMP, OP_MAINNET_JOVIAN_TIMESTAMP, OpChainHardforks, }; use alloy_primitives::{Address, Bytes, Signature, U256, hex}; - use op_alloy_consensus::OpTypedTransaction; + use op_alloy_consensus::{OpTypedTransaction, SDMGasEntry, build_post_exec_tx}; use op_alloy_network::eip2718::Decodable2718; use reth_optimism_chainspec::{BASE_MAINNET, OP_MAINNET}; use reth_optimism_primitives::{OpPrimitives, OpTransactionSigned}; - use reth_primitives_traits::Recovered; + use reth_primitives_traits::{Recovered, SealedBlock}; /// OP Mainnet transaction at index 0 in block 124665056. /// @@ -388,6 +424,7 @@ mod test { operator_fee_constant: None, da_footprint_gas_scalar: None, }, + op_gas_refund: None, deposit_nonce: None, deposit_receipt_version: None, }; @@ -481,6 +518,63 @@ mod test { ); } + #[test] + fn convert_receipts_extracts_post_exec_gas_refund_from_embedded_payload() { + let tx_0 = OpTransactionSigned::decode_2718( + &mut TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056.as_slice(), + ) + .unwrap(); + let tx_1 = + OpTransactionSigned::decode_2718(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice()) + .unwrap(); + let post_exec = OpTransactionSigned::PostExec( + build_post_exec_tx(124665056, vec![SDMGasEntry { index: 1, gas_refund: 77 }]) + .seal_slow(), + ); + + let block = SealedBlock::new_unhashed(Block:: { + header: Header { + number: 124665056, + timestamp: BLOCK_124665056_TIMESTAMP, + ..Default::default() + }, + body: BlockBody { + transactions: vec![tx_0, tx_1.clone(), post_exec], + ..Default::default() + }, + }); + + let converter = OpReceiptConverter::new(reth_storage_api::noop::NoopProvider::< + _, + OpPrimitives, + >::new(OP_MAINNET.clone())); + let receipts = + as ReceiptConverter>::convert_receipts_with_block( + &converter, + vec![ConvertReceiptInput:: { + tx: Recovered::new_unchecked(&tx_1, Address::ZERO), + receipt: OpReceipt::Eip1559(Receipt { + status: Eip658Value::Eip658(true), + cumulative_gas_used: 100, + logs: vec![], + }), + gas_used: 100, + next_log_index: 0, + meta: TransactionMeta { + index: 1, + block_number: 124665056, + timestamp: BLOCK_124665056_TIMESTAMP, + ..Default::default() + }, + }], + &block, + ) + .unwrap(); + + assert_eq!(receipts.len(), 1); + assert_eq!(receipts[0].op_gas_refund, Some(77)); + } + #[test] fn op_non_zero_operator_fee_params_included_in_receipt() { let tx_1 = @@ -600,7 +694,7 @@ mod test { let signature = Signature::new(U256::default(), U256::default(), true); - let tx = OpTransactionSigned::new_unhashed(OpTypedTransaction::Eip7702(tx), signature); + let tx: OpTransactionSigned = OpTypedTransaction::Eip7702(tx).into_signed(signature).into(); let mut l1_block_info = op_revm::L1BlockInfo { da_footprint_gas_scalar: Some(DA_FOOTPRINT_GAS_SCALAR), @@ -635,7 +729,7 @@ mod test { let signature = Signature::new(U256::default(), U256::default(), true); - let tx = OpTransactionSigned::new_unhashed(OpTypedTransaction::Eip7702(tx), signature); + let tx: OpTransactionSigned = OpTypedTransaction::Eip7702(tx).into_signed(signature).into(); let mut l1_block_info = op_revm::L1BlockInfo { da_footprint_gas_scalar: Some(DA_FOOTPRINT_GAS_SCALAR), @@ -661,6 +755,7 @@ mod test { }, }, &mut l1_block_info, + None, ) .unwrap(); @@ -689,7 +784,7 @@ mod test { let signature = Signature::new(U256::default(), U256::default(), true); - let tx = OpTransactionSigned::new_unhashed(OpTypedTransaction::Eip7702(tx), signature); + let tx: OpTransactionSigned = OpTypedTransaction::Eip7702(tx).into_signed(signature).into(); let mut l1_block_info = op_revm::L1BlockInfo { da_footprint_gas_scalar: Some(DA_FOOTPRINT_GAS_SCALAR), @@ -715,6 +810,7 @@ mod test { }, }, &mut l1_block_info, + None, ) .unwrap(); diff --git a/rust/op-reth/crates/trie/Cargo.toml b/rust/op-reth/crates/trie/Cargo.toml index 229463b39af..e67f145d3f9 100644 --- a/rust/op-reth/crates/trie/Cargo.toml +++ b/rust/op-reth/crates/trie/Cargo.toml @@ -13,7 +13,6 @@ workspace = true [dependencies] # reth -reth-codecs.workspace = true reth-db = { workspace = true, features = ["mdbx"] } reth-evm.workspace = true reth-execution-errors.workspace = true @@ -22,6 +21,7 @@ reth-provider.workspace = true reth-revm.workspace = true reth-trie = { workspace = true, features = ["serde"] } reth-trie-common = { workspace = true, features = ["serde"] } +reth-codecs.workspace = true reth-tasks.workspace = true # workaround: reth-trie/serde-bincode-compat activates serde-bincode-compat on diff --git a/rust/op-reth/crates/trie/src/db/mod.rs b/rust/op-reth/crates/trie/src/db/mod.rs index b32f557341d..a3b7f99b269 100644 --- a/rust/op-reth/crates/trie/src/db/mod.rs +++ b/rust/op-reth/crates/trie/src/db/mod.rs @@ -15,3 +15,16 @@ mod cursor; pub use cursor::{ BlockNumberVersionedCursor, MdbxAccountCursor, MdbxStorageCursor, MdbxTrieCursor, }; + +mod store_v2; +pub use store_v2::{ + MdbxProofsProviderV2, MdbxProofsStorageV2, V2AccountCursor, V2AccountTrieCursor, + V2StorageCursor, V2StorageTrieCursor, +}; + +use alloy_eips::NumHash; + +pub(crate) struct ProofWindowValue { + pub earliest: NumHash, + pub latest: NumHash, +} diff --git a/rust/op-reth/crates/trie/src/db/models/key.rs b/rust/op-reth/crates/trie/src/db/models/key.rs new file mode 100644 index 00000000000..48441386b92 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/models/key.rs @@ -0,0 +1,394 @@ +use alloy_primitives::{B256, BlockNumber}; +use reth_db::{ + DatabaseError, + models::sharded_key::ShardedKey, + table::{Decode, Encode}, +}; +use reth_trie_common::StoredNibbles; +use serde::{Deserialize, Serialize}; + +/// Sharded key for hashed accounts history. +/// +/// Wraps `ShardedKey` to provide `Encode`/`Decode` impls needed by MDBX. +#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +pub struct HashedAccountShardedKey(pub ShardedKey); + +impl HashedAccountShardedKey { + /// Create a new sharded key for a hashed account. + pub const fn new(key: B256, highest_block_number: u64) -> Self { + Self(ShardedKey::new(key, highest_block_number)) + } +} + +impl Encode for HashedAccountShardedKey { + type Encoded = [u8; 40]; // 32 (B256) + 8 (BlockNumber) + + fn encode(self) -> Self::Encoded { + let mut buf = [0u8; 40]; + buf[..32].copy_from_slice(self.0.key.as_slice()); + buf[32..].copy_from_slice(&self.0.highest_block_number.to_be_bytes()); + buf + } +} + +impl Decode for HashedAccountShardedKey { + fn decode(value: &[u8]) -> Result { + if value.len() != 40 { + return Err(DatabaseError::Decode); + } + let key = B256::from_slice(&value[..32]); + let highest_block_number = + u64::from_be_bytes(value[32..].try_into().map_err(|_| DatabaseError::Decode)?); + Ok(Self(ShardedKey::new(key, highest_block_number))) + } +} + +/// Keys Hashed Storage History by: Hashed Address + Sharded Key (Storage Key + Sharded Block). +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)] +pub struct HashedStorageShardedKey { + /// The hashed address of the account owning the storage. + pub hashed_address: B256, + /// The sharded key combining the storage key and sharded block number. + pub sharded_key: ShardedKey, +} + +impl Encode for HashedStorageShardedKey { + type Encoded = Vec; + fn encode(self) -> Self::Encoded { + let mut buf = Vec::with_capacity(32 + 32 + 8); + buf.extend_from_slice(self.hashed_address.as_slice()); + // ShardedKey: Key (32 bytes) + BlockNumber (8 bytes BE) + buf.extend_from_slice(self.sharded_key.key.as_slice()); + buf.extend_from_slice(&self.sharded_key.highest_block_number.to_be_bytes()); + buf + } +} + +impl Decode for HashedStorageShardedKey { + fn decode(value: &[u8]) -> Result { + // 32 (Addr) + 32 (Key) + 8 (Block) = 72 bytes + if value.len() < 72 { + return Err(DatabaseError::Decode); + } + let (addr, rest) = value.split_at(32); + let hashed_address = B256::from_slice(addr); + let key = B256::from_slice(&rest[..32]); + let highest_block_number = + u64::from_be_bytes(rest[32..40].try_into().map_err(|_| DatabaseError::Decode)?); + Ok(Self { hashed_address, sharded_key: ShardedKey::new(key, highest_block_number) }) + } +} + +/// Sharded key for account trie history. +/// +/// Uses **length-prefixed encoding** to avoid sort ambiguity in MDBX: +/// +/// ```text +/// [nibble_count: 1 byte] ++ [raw nibble bytes] ++ [block_number: 8 BE bytes] +/// ``` +/// +/// See [`StorageTrieShardedKey`] for the same rationale +/// applied to per-account storage tries. +#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +pub struct AccountTrieShardedKey { + /// Trie path as nibbles. + pub key: StoredNibbles, + /// Highest block number in this shard (or `u64::MAX` for the sentinel). + pub highest_block_number: u64, +} + +impl AccountTrieShardedKey { + /// Create a new sharded key for an account trie path. + pub const fn new(key: StoredNibbles, highest_block_number: u64) -> Self { + Self { key, highest_block_number } + } +} + +impl Encode for AccountTrieShardedKey { + type Encoded = Vec; + + fn encode(self) -> Self::Encoded { + let nibble_bytes: Vec = self.key.0.iter().collect(); + let nibble_count = nibble_bytes.len() as u8; + let mut buf = Vec::with_capacity(1 + nibble_bytes.len() + 8); + buf.push(nibble_count); + buf.extend_from_slice(&nibble_bytes); + buf.extend_from_slice(&self.highest_block_number.to_be_bytes()); + buf + } +} + +impl Decode for AccountTrieShardedKey { + fn decode(value: &[u8]) -> Result { + // Minimum: 1 (count) + 0 (nibbles) + 8 (block) = 9 + if value.len() < 9 { + return Err(DatabaseError::Decode); + } + let nibble_count = value[0] as usize; + let expected_len = 1 + nibble_count + 8; + if value.len() != expected_len { + return Err(DatabaseError::Decode); + } + let nibble_bytes = &value[1..1 + nibble_count]; + let key = + StoredNibbles::from(reth_trie_common::Nibbles::from_nibbles_unchecked(nibble_bytes)); + let block_bytes = &value[1 + nibble_count..]; + let highest_block_number = + u64::from_be_bytes(block_bytes.try_into().map_err(|_| DatabaseError::Decode)?); + Ok(Self { key, highest_block_number }) + } +} + +/// Keys Storage Trie History by: Hashed Address + Nibbles + Sharded Block. +/// +/// Uses **length-prefixed encoding** for the nibble portion to avoid sort +/// ambiguity in MDBX (same rationale as [`AccountTrieShardedKey`]): +/// +/// ```text +/// [hashed_address: 32 bytes] ++ [nibble_count: 1 byte] ++ [nibble_bytes] ++ [block_number: 8 BE bytes] +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)] +pub struct StorageTrieShardedKey { + /// The hashed address of the account owning the storage trie. + pub hashed_address: B256, + /// The trie path (nibbles). + pub key: StoredNibbles, + /// Highest block number in this shard (or `u64::MAX` for the sentinel). + pub highest_block_number: u64, +} + +impl StorageTrieShardedKey { + /// Create a new storage trie sharded key. + pub const fn new(hashed_address: B256, key: StoredNibbles, highest_block_number: u64) -> Self { + Self { hashed_address, key, highest_block_number } + } +} + +impl Encode for StorageTrieShardedKey { + type Encoded = Vec; + fn encode(self) -> Self::Encoded { + let nibble_bytes: Vec = self.key.0.iter().collect(); + let nibble_count = nibble_bytes.len() as u8; + let mut buf = Vec::with_capacity(32 + 1 + nibble_bytes.len() + 8); + buf.extend_from_slice(self.hashed_address.as_slice()); + buf.push(nibble_count); + buf.extend_from_slice(&nibble_bytes); + buf.extend_from_slice(&self.highest_block_number.to_be_bytes()); + buf + } +} + +impl Decode for StorageTrieShardedKey { + fn decode(value: &[u8]) -> Result { + // Minimum: 32 (addr) + 1 (count) + 0 (nibbles) + 8 (block) = 41 + if value.len() < 41 { + return Err(DatabaseError::Decode); + } + let hashed_address = B256::from_slice(&value[..32]); + let nibble_count = value[32] as usize; + let expected_len = 32 + 1 + nibble_count + 8; + if value.len() != expected_len { + return Err(DatabaseError::Decode); + } + let nibble_bytes = &value[33..33 + nibble_count]; + let key = + StoredNibbles::from(reth_trie_common::Nibbles::from_nibbles_unchecked(nibble_bytes)); + let block_bytes = &value[33 + nibble_count..]; + let highest_block_number = + u64::from_be_bytes(block_bytes.try_into().map_err(|_| DatabaseError::Decode)?); + Ok(Self { hashed_address, key, highest_block_number }) + } +} + +/// Keys Storage `ChangeSets` by: Block Number + Hashed Address. +/// Replaces `BlockNumberAddress` which uses unhashed Address. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)] +pub struct BlockNumberHashedAddress(pub (BlockNumber, B256)); + +impl Encode for BlockNumberHashedAddress { + type Encoded = [u8; 40]; // 8 + 32 + fn encode(self) -> Self::Encoded { + let mut buf = [0u8; 40]; + buf[..8].copy_from_slice(&self.0.0.to_be_bytes()); + buf[8..].copy_from_slice(self.0.1.as_slice()); + buf + } +} + +impl Decode for BlockNumberHashedAddress { + fn decode(value: &[u8]) -> Result { + if value.len() < 40 { + return Err(DatabaseError::Decode); + } + let block_num = u64::from_be_bytes(value[..8].try_into().unwrap()); + let hash = B256::from_slice(&value[8..40]); + Ok(Self((block_num, hash))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use reth_db::table::{Decode, Encode}; + use reth_trie_common::Nibbles; + + #[test] + fn hashed_account_sharded_key_roundtrip() { + let original = HashedAccountShardedKey::new(B256::repeat_byte(0xaa), 42); + let decoded = HashedAccountShardedKey::decode(&original.clone().encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn hashed_storage_sharded_key_roundtrip() { + let original = HashedStorageShardedKey { + hashed_address: B256::repeat_byte(0xaa), + sharded_key: ShardedKey::new(B256::repeat_byte(0xbb), 100), + }; + let decoded = HashedStorageShardedKey::decode(&original.clone().encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn account_trie_sharded_key_roundtrip() { + let nibbles = StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x0a, 0x0b, 0x0c])); + let original = AccountTrieShardedKey::new(nibbles, 500); + let decoded = AccountTrieShardedKey::decode(&original.clone().encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn account_trie_sharded_key_roundtrip_empty_nibbles() { + let original = + AccountTrieShardedKey::new(StoredNibbles::from(Nibbles::default()), u64::MAX); + let decoded = AccountTrieShardedKey::decode(&original.clone().encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn storage_trie_sharded_key_roundtrip() { + let nibbles = StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x01, 0x02])); + let original = StorageTrieShardedKey::new(B256::repeat_byte(0xcc), nibbles, 999); + let decoded = StorageTrieShardedKey::decode(&original.clone().encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn storage_trie_sharded_key_roundtrip_empty_nibbles() { + let original = StorageTrieShardedKey::new( + B256::repeat_byte(0xdd), + StoredNibbles::from(Nibbles::default()), + 0, + ); + let decoded = StorageTrieShardedKey::decode(&original.clone().encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn block_number_hashed_address_roundtrip() { + let original = BlockNumberHashedAddress((42, B256::repeat_byte(0xdd))); + let decoded = BlockNumberHashedAddress::decode(&original.encode()).unwrap(); + assert_eq!(original, decoded); + } + + #[test] + fn account_trie_shorter_nibbles_sort_before_longer() { + let key_a = AccountTrieShardedKey::new( + StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x01])), + 256, + ); + let key_b = AccountTrieShardedKey::new( + StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x01, 0x00])), + 1, + ); + + assert!( + key_a.encode() < key_b.encode(), + "shorter nibble path must sort before longer in encoded form" + ); + } + + #[test] + fn account_trie_same_nibbles_ordered_by_block() { + let nibbles = StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x0a, 0x0b])); + + let lo = AccountTrieShardedKey::new(nibbles.clone(), 10); + let hi = AccountTrieShardedKey::new(nibbles, 20); + + assert!( + lo.encode() < hi.encode(), + "same nibbles: lower block must sort before higher block" + ); + } + + #[test] + fn account_trie_nibbles_resembling_block_bytes_are_unambiguous() { + let key_a = AccountTrieShardedKey::new( + StoredNibbles::from(Nibbles::from_nibbles_unchecked([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + ])), + 1, + ); + let key_b = AccountTrieShardedKey::new(StoredNibbles::from(Nibbles::default()), 5); + + let enc_a = key_a.encode(); + let enc_b = key_b.encode(); + assert_ne!(enc_a, enc_b, "different logical keys must never produce identical encodings"); + // Empty nibbles (length 0) must sort before non-empty nibbles (length 8). + assert!(enc_b < enc_a, "empty nibbles must sort before non-empty nibbles"); + } + + #[test] + fn storage_trie_shorter_nibbles_sort_before_longer() { + let addr = B256::repeat_byte(0x11); + + let key_a = StorageTrieShardedKey::new( + addr, + StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x0f])), + 256, + ); + let key_b = StorageTrieShardedKey::new( + addr, + StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x0f, 0x00])), + 1, + ); + + assert!( + key_a.encode() < key_b.encode(), + "shorter nibble path must sort before longer in encoded form" + ); + } + + #[test] + fn storage_trie_same_nibbles_ordered_by_block() { + let addr = B256::repeat_byte(0x22); + let nibbles = StoredNibbles::from(Nibbles::from_nibbles_unchecked([0x0a])); + + let lo = StorageTrieShardedKey::new(addr, nibbles.clone(), 10); + let hi = StorageTrieShardedKey::new(addr, nibbles, 20); + + assert!( + lo.encode() < hi.encode(), + "same nibbles: lower block must sort before higher block" + ); + } + + #[test] + fn storage_trie_nibbles_resembling_block_bytes_are_unambiguous() { + let addr = B256::repeat_byte(0x33); + + let key_a = StorageTrieShardedKey::new( + addr, + StoredNibbles::from(Nibbles::from_nibbles_unchecked([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + ])), + 1, + ); + let key_b = StorageTrieShardedKey::new(addr, StoredNibbles::from(Nibbles::default()), 5); + + let enc_a = key_a.encode(); + let enc_b = key_b.encode(); + assert_ne!(enc_a, enc_b, "different logical keys must never produce identical encodings"); + assert!(enc_b < enc_a, "empty nibbles must sort before non-empty nibbles"); + } +} diff --git a/rust/op-reth/crates/trie/src/db/models/kv.rs b/rust/op-reth/crates/trie/src/db/models/kv.rs index 5585336f4bb..e42bea5007b 100644 --- a/rust/op-reth/crates/trie/src/db/models/kv.rs +++ b/rust/op-reth/crates/trie/src/db/models/kv.rs @@ -1,3 +1,5 @@ +//! KV conversion helpers for trie history tables. + use crate::db::{ AccountTrieHistory, HashedAccountHistory, HashedStorageHistory, HashedStorageKey, MaybeDeleted, StorageTrieHistory, StorageTrieKey, StorageValue, VersionedValue, diff --git a/rust/op-reth/crates/trie/src/db/models/mod.rs b/rust/op-reth/crates/trie/src/db/models/mod.rs index b6d524528c6..00de0cce2ec 100644 --- a/rust/op-reth/crates/trie/src/db/models/mod.rs +++ b/rust/op-reth/crates/trie/src/db/models/mod.rs @@ -12,18 +12,22 @@ pub use version::*; mod storage; pub use storage::*; mod change_set; -pub(crate) mod kv; pub use change_set::*; +pub mod kv; pub use kv::*; +mod key; +pub use key::*; +mod value; +pub use value::*; -use alloy_primitives::B256; +use alloy_primitives::{B256, BlockNumber}; use reth_db::{ - TableSet, TableType, TableViewer, + BlockNumberList, TableSet, TableType, TableViewer, table::{DupSort, TableInfo}, tables, }; -use reth_primitives_traits::Account; -use reth_trie_common::{BranchNodeCompact, StoredNibbles}; +use reth_primitives_traits::{Account, StorageEntry}; +use reth_trie_common::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}; use std::fmt; tables! { @@ -82,4 +86,153 @@ tables! { type Key = u64; // Block number type Value = ChangeSet; } + + // ==================== V2 Tables ==================== + // + // The v2 schema uses the 3-table-per-data-type pattern. All v2 tables are + // prefixed with `V2` to clearly distinguish them from v1 tables and to ensure + // each store only reads/writes its own tables. + // + // - **Current state** tables hold the latest values for fast reads. + // - **ChangeSet** tables group changes by block number for efficient pruning/unwinding. + // - **History** tables store sharded bitmaps for historical lookups. + // + + // -------------------- Proof Window -------------------- + + /// V2 proof window tracking (independent of the v1 [`ProofWindow`] table). + table V2ProofWindow { + type Key = ProofWindowKey; + type Value = BlockNumberHash; + } + + // -------------------- Hashed Accounts -------------------- + + /// Sharded history index for hashed accounts. + /// + /// Maps `ShardedKey` (hashed address + highest block number in shard) + /// to a bitmap of block numbers that modified this account. Used for historical + /// lookups: find the relevant block in the bitmap, then read the changeset. + table V2HashedAccountsHistory { + type Key = HashedAccountShardedKey; + type Value = BlockNumberList; + } + + /// Account changesets grouped by block number. + /// + /// Each entry stores the hashed address and the account state **before** the + /// block was applied (`None` if the account didn't exist). Grouped by block + /// number for efficient pruning (delete all entries for a block in one + /// operation) and unwinding (restore old values on reorg). + table V2HashedAccountChangeSets { + type Key = BlockNumber; + type Value = HashedAccountBeforeTx; + type SubKey = B256; + } + + /// Current state of all accounts, keyed by `keccak256(address)`. + /// + /// Holds the latest account data (nonce, balance, code hash, storage root). + /// Primary read target for state root computation and proof generation — + /// no version lookup needed. + table V2HashedAccounts { + type Key = B256; + type Value = Account; + } + + // -------------------- Hashed Storages -------------------- + + /// Sharded history index for storage slots. + /// + /// Composite key of `(hashed_address, hashed_storage_key, highest_block_number)`. + /// Maps to a bitmap of block numbers that modified this storage slot. + table V2HashedStoragesHistory { + type Key = HashedStorageShardedKey; + type Value = BlockNumberList; + } + + /// Storage changesets grouped by block number and account. + /// + /// Composite key of `(block_number, hashed_address)`. Each entry stores the + /// hashed storage key and value **before** the block was applied. + /// A value of [`U256::ZERO`](alloy_primitives::U256::ZERO) means the slot + /// did not exist (needs to be removed on unwind). + table V2HashedStorageChangeSets { + type Key = BlockNumberHashedAddress; + type Value = StorageEntry; + type SubKey = B256; + } + + /// Current storage values, keyed by hashed address with hashed storage key + /// as the `DupSort` subkey. + /// + /// Holds the latest storage slot values for each account. Primary read target + /// for storage proof generation. + table V2HashedStorages { + type Key = B256; + type Value = StorageEntry; + type SubKey = B256; + } + + // -------------------- Account Trie -------------------- + + /// Sharded history index for the account state trie. + /// + /// Maps `ShardedKey` (trie path + highest block number in shard) + /// to a bitmap of block numbers that modified this path. + table V2AccountsTrieHistory { + type Key = AccountTrieShardedKey; + type Value = BlockNumberList; + } + + /// Account trie changesets grouped by block number. + /// + /// Each entry stores the trie path and the branch node value **before** the + /// block was applied (`None` if the node didn't exist). Enables efficient + /// pruning and unwinding of trie state. + table V2AccountTrieChangeSets { + type Key = BlockNumber; + type Value = TrieChangeSetsEntry; + type SubKey = StoredNibblesSubKey; + } + + /// Current state of the account Merkle Patricia Trie. + /// + /// Maps trie paths to the latest branch node. Primary read target during + /// proof generation — no version lookup needed. + table V2AccountsTrie { + type Key = StoredNibbles; + type Value = BranchNodeCompact; + } + + // -------------------- Storage Trie -------------------- + + /// Sharded history index for per-account storage tries. + /// + /// Composite key of `(hashed_address, trie_path, highest_block_number)`. + /// Maps to a bitmap of block numbers that modified this storage trie node. + table V2StoragesTrieHistory { + type Key = StorageTrieShardedKey; + type Value = BlockNumberList; + } + + /// Storage trie changesets grouped by block number and account. + /// + /// Composite key of `(block_number, hashed_address)`. Each entry stores the + /// trie path and the branch node value **before** the block was applied. + table V2StorageTrieChangeSets { + type Key = BlockNumberHashedAddress; + type Value = TrieChangeSetsEntry; + type SubKey = StoredNibblesSubKey; + } + + /// Current state of each account's storage Merkle Patricia Trie. + /// + /// Keyed by hashed account address, with the trie path as the `DupSort` subkey. + /// Holds the latest branch node for each path in each account's storage trie. + table V2StoragesTrie { + type Key = B256; + type Value = StorageTrieEntry; + type SubKey = StoredNibblesSubKey; + } } diff --git a/rust/op-reth/crates/trie/src/db/models/value.rs b/rust/op-reth/crates/trie/src/db/models/value.rs new file mode 100644 index 00000000000..1b3f68058fd --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/models/value.rs @@ -0,0 +1,210 @@ +use alloy_primitives::B256; +use bytes::BufMut; +use reth_codecs::{Compact, DecompressError}; +use reth_db::{ + DatabaseError, + table::{Compress, Decompress}, +}; +use reth_primitives_traits::{Account, ValueWithSubKey}; +use reth_trie_common::{BranchNodeCompact, StoredNibblesSubKey}; +use serde::{Deserialize, Serialize}; + +/// Account state before a block, keyed by hashed address. +/// +/// This is the hashed-address equivalent of reth's +/// `AccountBeforeTx`, designed for our v2 `AccountChangeSets` +/// table where keys are `keccak256(address)`. +/// +/// Layout: `[hashed_address: 32 bytes][account: Compact-encoded or empty]` +/// +/// - The 32-byte hashed address acts as the [`DupSort::SubKey`]. +/// - An empty remainder means the account did not exist before this block (creation). +/// - A non-empty remainder is the [`Account`] state before the block was applied. +/// +/// [`DupSort::SubKey`]: reth_db::table::DupSort::SubKey +#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct HashedAccountBeforeTx { + /// Hashed address (`keccak256(address)`). Acts as `DupSort::SubKey`. + pub hashed_address: B256, + /// Account state before the block. `None` means the account didn't exist. + pub info: Option, +} + +impl HashedAccountBeforeTx { + /// Creates a new instance. + pub const fn new(hashed_address: B256, info: Option) -> Self { + Self { hashed_address, info } + } +} + +impl ValueWithSubKey for HashedAccountBeforeTx { + type SubKey = B256; + + fn get_subkey(&self) -> Self::SubKey { + self.hashed_address + } +} + +impl Compress for HashedAccountBeforeTx { + type Compressed = Vec; + + fn compress_to_buf>(&self, buf: &mut B) { + // SubKey: raw 32 bytes (uncompressed so MDBX can seek by it) + buf.put_slice(self.hashed_address.as_slice()); + // Value: compress the account if present, otherwise write nothing + if let Some(account) = &self.info { + account.compress_to_buf(buf); + } + } +} + +impl Decompress for HashedAccountBeforeTx { + fn decompress(value: &[u8]) -> Result { + if value.len() < 32 { + return Err(DecompressError::new(DatabaseError::Decode)); + } + + let hashed_address = B256::from_slice(&value[..32]); + let info = if value.len() > 32 { Some(Account::decompress(&value[32..])?) } else { None }; + + Ok(Self { hashed_address, info }) + } +} + +/// Trie changeset entry representing the state of a trie node before a block. +/// +/// `nibbles` is the subkey when used as a value in the changeset tables. +/// This is a local definition since the upstream `reth-trie-common` crate does +/// not provide this type. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TrieChangeSetsEntry { + /// The nibbles of the intermediate node + pub nibbles: StoredNibblesSubKey, + /// Node value prior to the block being processed, None indicating it didn't exist. + pub node: Option, +} + +impl ValueWithSubKey for TrieChangeSetsEntry { + type SubKey = StoredNibblesSubKey; + + fn get_subkey(&self) -> Self::SubKey { + self.nibbles.clone() + } +} + +impl Compress for TrieChangeSetsEntry { + type Compressed = Vec; + + fn compress_to_buf>(&self, buf: &mut B) { + let _ = self.nibbles.to_compact(buf); + if let Some(ref node) = self.node { + let _ = node.to_compact(buf); + } + } +} + +impl Decompress for TrieChangeSetsEntry { + fn decompress(value: &[u8]) -> Result { + if value.is_empty() { + return Ok(Self { + nibbles: StoredNibblesSubKey::from(reth_trie_common::Nibbles::default()), + node: None, + }); + } + + let (nibbles, rest) = StoredNibblesSubKey::from_compact(value, 65); + let node = if rest.is_empty() { + None + } else { + Some(BranchNodeCompact::from_compact(rest, rest.len()).0) + }; + Ok(Self { nibbles, node }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use reth_db::table::{Compress, Decompress}; + + #[test] + fn test_hashed_account_before_tx_roundtrip_some() { + let original = HashedAccountBeforeTx { + hashed_address: B256::repeat_byte(0xaa), + info: Some(Account { + nonce: 42, + balance: alloy_primitives::U256::from(1000u64), + bytecode_hash: None, + }), + }; + + let compressed = original.clone().compress(); + assert!(compressed.len() > 32, "Should contain address + account data"); + + let decompressed = HashedAccountBeforeTx::decompress(&compressed).unwrap(); + assert_eq!(original, decompressed); + } + + #[test] + fn test_hashed_account_before_tx_roundtrip_none() { + let original = + HashedAccountBeforeTx { hashed_address: B256::repeat_byte(0xbb), info: None }; + + let compressed = original.clone().compress(); + assert_eq!(compressed.len(), 32, "None account should be just the 32-byte address"); + + let decompressed = HashedAccountBeforeTx::decompress(&compressed).unwrap(); + assert_eq!(original, decompressed); + } + + #[test] + fn test_hashed_account_before_tx_subkey() { + let addr = B256::repeat_byte(0xcc); + let entry = HashedAccountBeforeTx::new(addr, None); + assert_eq!(entry.get_subkey(), addr); + } + + #[test] + fn test_trie_changesets_entry_roundtrip_with_node() { + let nibbles = + StoredNibblesSubKey(reth_trie_common::Nibbles::from_nibbles_unchecked([0x0A, 0x0B])); + let node = BranchNodeCompact::new(0b11, 0, 0, vec![], Some(B256::repeat_byte(0xDD))); + let original = TrieChangeSetsEntry { nibbles, node: Some(node) }; + + let compressed = original.clone().compress(); + let decompressed = TrieChangeSetsEntry::decompress(&compressed).unwrap(); + assert_eq!(original, decompressed); + } + + #[test] + fn test_trie_changesets_entry_roundtrip_none_node() { + let nibbles = StoredNibblesSubKey(reth_trie_common::Nibbles::from_nibbles_unchecked([ + 0x01, 0x02, 0x03, + ])); + let original = TrieChangeSetsEntry { nibbles, node: None }; + + let compressed = original.clone().compress(); + let decompressed = TrieChangeSetsEntry::decompress(&compressed).unwrap(); + assert_eq!(original, decompressed); + } + + #[test] + fn test_trie_changesets_entry_roundtrip_empty() { + let original = TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(reth_trie_common::Nibbles::default()), + node: None, + }; + + let compressed = original.clone().compress(); + let decompressed = TrieChangeSetsEntry::decompress(&compressed).unwrap(); + assert_eq!(original, decompressed); + } + + #[test] + fn test_trie_changesets_entry_subkey() { + let nibbles = + StoredNibblesSubKey(reth_trie_common::Nibbles::from_nibbles_unchecked([0x05, 0x06])); + let entry = TrieChangeSetsEntry { nibbles: nibbles.clone(), node: None }; + assert_eq!(entry.get_subkey(), nibbles); + } +} diff --git a/rust/op-reth/crates/trie/src/db/store.rs b/rust/op-reth/crates/trie/src/db/store.rs index 3519741fe18..b3a32ac90e7 100644 --- a/rust/op-reth/crates/trie/src/db/store.rs +++ b/rust/op-reth/crates/trie/src/db/store.rs @@ -1,4 +1,4 @@ -use super::{BlockNumberHash, ProofWindow, ProofWindowKey, Tables}; +use super::{BlockNumberHash, ProofWindow, ProofWindowKey, ProofWindowValue, Tables}; use crate::{ BlockStateDiff, OpProofsStorageError, OpProofsStorageError::NoBlocksFound, @@ -35,11 +35,6 @@ use reth_trie_common::{ }; use std::{fmt::Debug, ops::RangeBounds, path::Path, sync::Arc}; -struct ProofWindowValue { - earliest: NumHash, - latest: NumHash, -} - /// Preprocessed prune plan for a target block number #[derive(Debug, Clone)] struct PrunePlan { @@ -861,6 +856,18 @@ impl OpProofsProviderRw latest_common_block: BlockNumHash, mut blocks_to_add: Vec<(BlockWithParent, BlockStateDiff)>, ) -> OpProofsStorageResult<()> { + let proof_window = self.get_proof_window_inner()?; + + if latest_common_block.number <= proof_window.earliest.number || + latest_common_block.number > proof_window.latest.number + { + return Err(OpProofsStorageError::ReorgBaseOutOfWindow { + block_number: latest_common_block.number, + earliest_block_number: proof_window.earliest.number, + latest_block_number: proof_window.latest.number, + }); + } + blocks_to_add.sort_unstable_by_key(|(bwp, _)| bwp.block.number); let history_to_delete = self.collect_history_ranged(latest_common_block.number + 1..)?; @@ -3317,6 +3324,164 @@ mod tests { } } + #[test] + fn replace_updates_rejects_lca_below_earliest() { + let dir = TempDir::new().unwrap(); + let store = MdbxProofsStorage::new(dir.path()).expect("env"); + + let addr = B256::from([0xCC; 32]); + let make_diff = |nonce: u64| { + let mut ps = HashedPostState::default(); + ps.accounts.insert(addr, Some(Account { nonce, ..Default::default() })); + BlockStateDiff { + sorted_trie_updates: TrieUpdatesSorted::default(), + sorted_post_state: ps.into_sorted(), + } + }; + + // Window: earliest=5, latest=8 + let b5 = BlockWithParent::new(B256::ZERO, NumHash::new(5, B256::random())); + let b6 = BlockWithParent::new(b5.block.hash, NumHash::new(6, B256::random())); + let b7 = BlockWithParent::new(b6.block.hash, NumHash::new(7, B256::random())); + let b8 = BlockWithParent::new(b7.block.hash, NumHash::new(8, B256::random())); + + store.set_earliest_block_number(5, b5.block.hash).expect("set earliest"); + store.store_trie_updates(b6, make_diff(60)).expect("store b6"); + store.store_trie_updates(b7, make_diff(70)).expect("store b7"); + store.store_trie_updates(b8, make_diff(80)).expect("store b8"); + + // LCA at block 3, which is below earliest (5) + let result = store.replace_updates(BlockNumHash::new(3, B256::random()), vec![]); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + matches!( + err, + OpProofsStorageError::ReorgBaseOutOfWindow { + block_number: 3, + earliest_block_number: 5, + latest_block_number: 8, + } + ), + "expected ReorgBaseOutOfWindow, got: {err:?}" + ); + } + + #[test] + fn replace_updates_rejects_lca_above_latest() { + let dir = TempDir::new().unwrap(); + let store = MdbxProofsStorage::new(dir.path()).expect("env"); + + let addr = B256::from([0xDD; 32]); + let make_diff = |nonce: u64| { + let mut ps = HashedPostState::default(); + ps.accounts.insert(addr, Some(Account { nonce, ..Default::default() })); + BlockStateDiff { + sorted_trie_updates: TrieUpdatesSorted::default(), + sorted_post_state: ps.into_sorted(), + } + }; + + // Window: earliest=1, latest=3 + let b1 = BlockWithParent::new(B256::ZERO, NumHash::new(1, B256::random())); + let b2 = BlockWithParent::new(b1.block.hash, NumHash::new(2, B256::random())); + let b3 = BlockWithParent::new(b2.block.hash, NumHash::new(3, B256::random())); + + store.set_earliest_block_number(1, b1.block.hash).expect("set earliest"); + store.store_trie_updates(b2, make_diff(20)).expect("store b2"); + store.store_trie_updates(b3, make_diff(30)).expect("store b3"); + + // LCA at block 10, which is above latest (3) + let result = store.replace_updates(BlockNumHash::new(10, B256::random()), vec![]); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + matches!( + err, + OpProofsStorageError::ReorgBaseOutOfWindow { + block_number: 10, + earliest_block_number: 1, + latest_block_number: 3, + } + ), + "expected ReorgBaseOutOfWindow, got: {err:?}" + ); + } + + #[test] + fn replace_updates_rejects_lca_at_earliest() { + let dir = TempDir::new().unwrap(); + let store = MdbxProofsStorage::new(dir.path()).expect("env"); + + let make_diff = || BlockStateDiff { + sorted_trie_updates: TrieUpdatesSorted::default(), + sorted_post_state: HashedPostState::default().into_sorted(), + }; + + // Window: earliest=1, latest=3 + let b1 = BlockWithParent::new(B256::ZERO, NumHash::new(1, B256::random())); + let b2 = BlockWithParent::new(b1.block.hash, NumHash::new(2, B256::random())); + let b3 = BlockWithParent::new(b2.block.hash, NumHash::new(3, B256::random())); + + store.set_earliest_block_number(1, b1.block.hash).expect("set earliest"); + store.store_trie_updates(b2, make_diff()).expect("store b2"); + store.store_trie_updates(b3, make_diff()).expect("store b3"); + + // LCA at earliest boundary — should be rejected, no valid anchor + let b2p = BlockWithParent::new(b1.block.hash, NumHash::new(2, B256::random())); + + let err = store + .replace_updates(BlockNumHash::new(1, b1.block.hash), vec![(b2p, make_diff())]) + .expect_err("replace_updates at earliest should fail"); + + assert!( + matches!(err, OpProofsStorageError::ReorgBaseOutOfWindow { .. }), + "expected ReorgBaseOutOfWindow, got {err:?}" + ); + } + + #[test] + fn replace_updates_accepts_lca_at_latest() { + let dir = TempDir::new().unwrap(); + let store = MdbxProofsStorage::new(dir.path()).expect("env"); + + let addr = B256::from([0xFF; 32]); + let make_diff = |nonce: u64| { + let mut ps = HashedPostState::default(); + ps.accounts.insert(addr, Some(Account { nonce, ..Default::default() })); + BlockStateDiff { + sorted_trie_updates: TrieUpdatesSorted::default(), + sorted_post_state: ps.into_sorted(), + } + }; + + // Window: earliest=1, latest=3 + let b1 = BlockWithParent::new(B256::ZERO, NumHash::new(1, B256::random())); + let b2 = BlockWithParent::new(b1.block.hash, NumHash::new(2, B256::random())); + let b3 = BlockWithParent::new(b2.block.hash, NumHash::new(3, B256::random())); + + store.set_earliest_block_number(1, b1.block.hash).expect("set earliest"); + store.store_trie_updates(b2, make_diff(20)).expect("store b2"); + store.store_trie_updates(b3, make_diff(30)).expect("store b3"); + + // LCA at latest boundary — should succeed, appending after block 3 + let b4 = BlockWithParent::new(b3.block.hash, NumHash::new(4, B256::random())); + + store + .replace_updates(BlockNumHash::new(3, b3.block.hash), vec![(b4, make_diff(400))]) + .expect("replace_updates at latest should succeed"); + + // Verify: blocks 1-3 unchanged, block 4 added + let tx = store.env.tx().expect("tx"); + let mut cur = tx.new_cursor::().expect("cursor"); + let v2 = cur.seek_by_key_subkey(addr, 2).expect("seek").expect("exists"); + assert_eq!(v2.value.0.unwrap().nonce, 20, "block 2 should be unchanged"); + let v3 = cur.seek_by_key_subkey(addr, 3).expect("seek").expect("exists"); + assert_eq!(v3.value.0.unwrap().nonce, 30, "block 3 should be unchanged"); + let v4 = cur.seek_by_key_subkey(addr, 4).expect("seek").expect("exists"); + assert_eq!(v4.value.0.unwrap().nonce, 400, "block 4 should be appended"); + } + #[test] fn test_unwind_history_basic() { let dir = TempDir::new().unwrap(); diff --git a/rust/op-reth/crates/trie/src/db/store_v2/cursor/account.rs b/rust/op-reth/crates/trie/src/db/store_v2/cursor/account.rs new file mode 100644 index 00000000000..11dc2fa46c4 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/cursor/account.rs @@ -0,0 +1,206 @@ +//! History-aware cursor over the [`V2HashedAccounts`] v2 tables. + +use alloy_primitives::B256; +use reth_db::{ + DatabaseError, + cursor::{DbCursorRO, DbDupCursorRO}, +}; +use reth_primitives_traits::Account; +use reth_trie::hashed_cursor::HashedCursor; + +use super::{ResolvedSource, find_source}; +use crate::db::models::{ + HashedAccountShardedKey, V2HashedAccountChangeSets, V2HashedAccounts, V2HashedAccountsHistory, +}; + +/// History-aware cursor over the [`V2HashedAccounts`] v2 tables. +/// +/// Uses a **dual-cursor merge** to discover all account keys that existed at +/// `max_block_number`. This is necessary because an account deleted *after* +/// the target block no longer exists in the current-state table and would be +/// missed by a walk of current state alone. The merge walks both the +/// current-state cursor and the history-bitmap cursor in sorted order, +/// yielding the minimum key from each, resolving its value at the target +/// block, and skipping keys that did not exist at that block. +#[derive(Debug)] +pub struct V2AccountCursor { + /// Current state walk cursor. + cursor: C, + /// History bitmap cursor for resolving individual keys. + history_cursor: HC, + /// History bitmap cursor for merge-walking deleted keys. + history_walk_cursor: HC, + /// Changeset cursor. + changeset_cursor: CC, + /// Target block number for historical reads. + max_block_number: u64, + /// Pre-fetched next entry from the current state walk. + cs_next: Option<(B256, Account)>, + /// Pre-fetched next unique key from the history walk. + hist_next_key: Option, + /// Whether `seek` has been called to initialize the merge cursors. + seeked: bool, + /// Fast path: when `true`, skip all history/changeset lookups and + /// read directly from the current-state table. + is_latest: bool, +} + +impl V2AccountCursor { + /// Create a new [`V2AccountCursor`]. + pub const fn new( + cursor: C, + history_cursor: HC, + history_walk_cursor: HC, + changeset_cursor: CC, + max_block_number: u64, + is_latest: bool, + ) -> Self { + Self { + cursor, + history_cursor, + history_walk_cursor, + changeset_cursor, + max_block_number, + cs_next: None, + hist_next_key: None, + seeked: false, + is_latest, + } + } +} + +impl V2AccountCursor +where + C: DbCursorRO, + HC: DbCursorRO, + CC: DbCursorRO + DbDupCursorRO, +{ + /// Resolve an account using a pre-fetched current-state value. + /// + /// Does **not** touch the walk cursor, so it is safe to call from the + /// merge loop (`find_next_live`). + fn resolve_account_merge( + &mut self, + hashed_address: B256, + cs_value: Option<&Account>, + ) -> Result, DatabaseError> { + let history_key = + HashedAccountShardedKey::new(hashed_address, self.max_block_number.saturating_add(1)); + let source = find_source::( + &mut self.history_cursor, + history_key, + self.max_block_number, + |k| k.0.key == hashed_address, + )?; + + match source { + ResolvedSource::FromChangeset(changeset_block) => { + let entry = self + .changeset_cursor + .seek_by_key_subkey(changeset_block, hashed_address)? + .filter(|e| e.hashed_address == hashed_address); + Ok(entry.and_then(|e| e.info)) + } + ResolvedSource::FromCurrentState => Ok(cs_value.copied()), + } + } + + /// Advance the history walk cursor past all shards of `key` and return + /// the next distinct key, if any. + fn advance_history_past(&mut self, key: &B256) -> Result, DatabaseError> { + let entry = self.history_walk_cursor.seek(HashedAccountShardedKey::new(*key, u64::MAX))?; + match entry { + Some((k, _)) if k.0.key == *key => { + // On the last shard of this key — one more step. + Ok(self.history_walk_cursor.next()?.map(|(k, _)| k.0.key)) + } + Some((k, _)) => Ok(Some(k.0.key)), + None => Ok(None), + } + } + + /// Merge-walk both the current-state cursor and the history-bitmap cursor, + /// yielding the next key (in ascending order) whose account is live at + /// `max_block_number`. + fn find_next_live(&mut self) -> Result, DatabaseError> { + loop { + // Step 1: Pick the minimum key from current-state and history cursors. + // If both have the same key, prefer the current-state value. + // `cs_value` is `Some` when the key exists in current state, `None` + // when it only appears in history (i.e. deleted after max_block_number). + let (min_key, cs_value) = match (&self.cs_next, &self.hist_next_key) { + (Some((cs_k, cs_v)), Some(h_k)) => { + if cs_k <= h_k { + (*cs_k, Some(*cs_v)) + } else { + (*h_k, None) + } + } + (Some((cs_k, cs_v)), None) => (*cs_k, Some(*cs_v)), + (None, Some(h_k)) => (*h_k, None), + (None, None) => return Ok(None), + }; + + // Step 2: Advance whichever cursor(s) produced this key. + // Both are advanced when they have the same key (deduplication). + if self.cs_next.as_ref().is_some_and(|(k, _)| *k == min_key) { + self.cs_next = self.cursor.next()?; + } + if self.hist_next_key.as_ref().is_some_and(|k| *k == min_key) { + self.hist_next_key = self.advance_history_past(&min_key)?; + } + + // Step 3: Resolve the value at max_block_number. + // Returns `Some` if the key was live at that block, `None` if it + // didn't exist yet or was already deleted. + if let Some(account) = self.resolve_account_merge(min_key, cs_value.as_ref())? { + return Ok(Some((min_key, account))); + } + // Key doesn't exist at max_block_number — continue to next. + } + } +} + +impl HashedCursor for V2AccountCursor +where + C: DbCursorRO + Send, + HC: DbCursorRO + Send, + CC: DbCursorRO + DbDupCursorRO + Send, +{ + type Value = Account; + + fn seek(&mut self, key: B256) -> Result, DatabaseError> { + self.seeked = true; + + if self.is_latest { + // Fast path: current state is authoritative, no history needed. + return self.cursor.seek(key); + } + + // Initialize both merge cursors at the target key. + self.cs_next = self.cursor.seek(key)?; + self.hist_next_key = self + .history_walk_cursor + .seek(HashedAccountShardedKey::new(key, 0))? + .map(|(k, _)| k.0.key); + self.find_next_live() + } + + fn next(&mut self) -> Result, DatabaseError> { + if !self.seeked { + return self.seek(B256::ZERO); + } + + if self.is_latest { + return self.cursor.next(); + } + + self.find_next_live() + } + + fn reset(&mut self) { + self.cs_next = None; + self.hist_next_key = None; + self.seeked = false; + } +} diff --git a/rust/op-reth/crates/trie/src/db/store_v2/cursor/account_trie.rs b/rust/op-reth/crates/trie/src/db/store_v2/cursor/account_trie.rs new file mode 100644 index 00000000000..d166a603277 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/cursor/account_trie.rs @@ -0,0 +1,294 @@ +//! History-aware cursor over the [`V2AccountsTrie`] v2 table. + +use reth_db::{ + DatabaseError, + cursor::{DbCursorRO, DbDupCursorRO}, +}; +use reth_trie::{ + BranchNodeCompact, Nibbles, StoredNibbles, StoredNibblesSubKey, trie_cursor::TrieCursor, +}; + +use super::{ResolvedSource, find_source}; +use crate::db::models::{ + AccountTrieShardedKey, V2AccountTrieChangeSets, V2AccountsTrie, V2AccountsTrieHistory, +}; + +/// History-aware cursor over the [`V2AccountsTrie`] v2 tables. +/// +/// Uses a **dual-cursor merge** to discover all trie paths that existed at +/// `max_block_number`. This is necessary because a key deleted *after* the +/// target block no longer exists in the current-state table and would be +/// missed by a walk of current state alone. The merge walks both the +/// current-state cursor and the history-bitmap cursor in sorted order, +/// yielding the minimum key from each, resolving its value at the target +/// block, and skipping keys that did not exist at that block. +#[derive(Debug)] +pub struct V2AccountTrieCursor { + /// Current state walk cursor. + cursor: C, + /// History bitmap cursor for resolving individual keys. + history_cursor: HC, + /// History bitmap cursor for merge-walking deleted keys. + history_walk_cursor: HC, + /// Changeset cursor. + changeset_cursor: CC, + /// Target block number. + max_block_number: u64, + /// Pre-fetched next entry from the current state walk. + cs_next: Option<(StoredNibbles, BranchNodeCompact)>, + /// Pre-fetched next unique key from the history walk. + hist_next_key: Option, + /// Last key yielded by `seek`/`next` (for `current()`). + last_key: Option, + /// Whether `seek` or `seek_exact` has been called to initialize the merge cursors. + seeked: bool, + /// Fast path: when `true`, skip all history/changeset lookups. + is_latest: bool, +} + +impl V2AccountTrieCursor { + /// Create a new [`V2AccountTrieCursor`]. + pub const fn new( + cursor: C, + history_cursor: HC, + history_walk_cursor: HC, + changeset_cursor: CC, + max_block_number: u64, + is_latest: bool, + ) -> Self { + Self { + cursor, + history_cursor, + history_walk_cursor, + changeset_cursor, + max_block_number, + cs_next: None, + hist_next_key: None, + last_key: None, + seeked: false, + is_latest, + } + } +} + +impl V2AccountTrieCursor +where + C: DbCursorRO, + HC: DbCursorRO, + CC: DbCursorRO + DbDupCursorRO, +{ + /// Resolve a key using the walk cursor for the `FromCurrentState` case. + /// + /// May disrupt the walk cursor position — only call when the walk state + /// will be re-synced immediately afterward (e.g. in `seek_exact`). + fn resolve_node_standalone( + &mut self, + path: &StoredNibbles, + ) -> Result, DatabaseError> { + let seek_key = + AccountTrieShardedKey::new(path.clone(), self.max_block_number.saturating_add(1)); + let target = path.clone(); + let source = find_source::( + &mut self.history_cursor, + seek_key, + self.max_block_number, + |k| k.key == target, + )?; + + match source { + ResolvedSource::FromChangeset(changeset_block) => { + let entry = self + .changeset_cursor + .seek_by_key_subkey(changeset_block, StoredNibblesSubKey(path.0))? + .filter(|e| e.nibbles == StoredNibblesSubKey(path.0)); + Ok(entry.and_then(|e| e.node)) + } + ResolvedSource::FromCurrentState => { + Ok(self.cursor.seek_exact(path.clone())?.map(|(_, node)| node)) + } + } + } + + /// Resolve a key using a pre-fetched current-state value. + /// + /// Does **not** touch the walk cursor, so it is safe to call from the + /// merge loop (`find_next_live`). + fn resolve_node_merge( + &mut self, + path: &StoredNibbles, + cs_value: Option<&BranchNodeCompact>, + ) -> Result, DatabaseError> { + let seek_key = + AccountTrieShardedKey::new(path.clone(), self.max_block_number.saturating_add(1)); + let target = path.clone(); + let source = find_source::( + &mut self.history_cursor, + seek_key, + self.max_block_number, + |k| k.key == target, + )?; + + match source { + ResolvedSource::FromChangeset(changeset_block) => { + let entry = self + .changeset_cursor + .seek_by_key_subkey(changeset_block, StoredNibblesSubKey(path.0))? + .filter(|e| e.nibbles == StoredNibblesSubKey(path.0)); + Ok(entry.and_then(|e| e.node)) + } + ResolvedSource::FromCurrentState => Ok(cs_value.cloned()), + } + } + + /// Advance the history walk cursor past all shards of `key` and return + /// the next distinct key, if any. + fn advance_history_past( + &mut self, + key: &StoredNibbles, + ) -> Result, DatabaseError> { + // Jump to the last shard of this key (or past it entirely). + let entry = + self.history_walk_cursor.seek(AccountTrieShardedKey::new(key.clone(), u64::MAX))?; + match entry { + Some((k, _)) if k.key == *key => { + // On the last shard of this key — one more step to reach the + // next distinct key. + Ok(self.history_walk_cursor.next()?.map(|(k, _)| k.key)) + } + Some((k, _)) => Ok(Some(k.key)), + None => Ok(None), + } + } + + /// Merge-walk both the current-state cursor and the history-bitmap cursor, + /// yielding the next key (in ascending order) whose value is live at + /// `max_block_number`. + fn find_next_live(&mut self) -> Result, DatabaseError> { + loop { + // Step 1: Pick the minimum key from current-state and history cursors. + // If both have the same key, prefer the current-state value. + // `cs_value` is `Some` when the key exists in current state, `None` + // when it only appears in history (i.e. deleted after max_block_number). + let (min_key, cs_value) = match (&self.cs_next, &self.hist_next_key) { + (Some((cs_k, cs_v)), Some(h_k)) => { + if cs_k <= h_k { + (cs_k.clone(), Some(cs_v.clone())) + } else { + (h_k.clone(), None) + } + } + (Some((cs_k, cs_v)), None) => (cs_k.clone(), Some(cs_v.clone())), + (None, Some(h_k)) => (h_k.clone(), None), + (None, None) => return Ok(None), + }; + + // Step 2: Advance whichever cursor(s) produced this key. + // Both are advanced when they have the same key (deduplication). + if self.cs_next.as_ref().is_some_and(|(k, _)| *k == min_key) { + self.cs_next = self.cursor.next()?; + } + if self.hist_next_key.as_ref().is_some_and(|k| *k == min_key) { + self.hist_next_key = self.advance_history_past(&min_key)?; + } + + // Step 3: Resolve the value at max_block_number. + // Returns `Some` if the key was live at that block, `None` if it + // didn't exist yet or was already deleted. + if let Some(node) = self.resolve_node_merge(&min_key, cs_value.as_ref())? { + self.last_key = Some(min_key.clone()); + return Ok(Some((min_key.0, node))); + } + // Key doesn't exist at max_block_number — continue to next. + } + } +} + +impl TrieCursor for V2AccountTrieCursor +where + C: DbCursorRO + Send, + HC: DbCursorRO + Send, + CC: DbCursorRO + DbDupCursorRO + Send, +{ + fn seek_exact( + &mut self, + key: Nibbles, + ) -> Result, DatabaseError> { + self.seeked = true; + + if self.is_latest { + // Fast path: direct current-state lookup. + let result = self.cursor.seek_exact(StoredNibbles(key))?; + if result.is_some() { + self.last_key = Some(StoredNibbles(key)); + } + return Ok(result.map(|(_, node)| (key, node))); + } + + let path = StoredNibbles(key); + let node = self.resolve_node_standalone(&path)?; + + // Re-sync the walk state so a subsequent next() starts after `path`. + let cs_at_key = self.cursor.seek(path.clone())?; + self.cs_next = match cs_at_key { + Some((k, _)) if k == path => self.cursor.next()?, + other => other, + }; + self.hist_next_key = self.advance_history_past(&path)?; + + if node.is_some() { + self.last_key = Some(path); + } + Ok(node.map(|n| (key, n))) + } + + fn seek( + &mut self, + key: Nibbles, + ) -> Result, DatabaseError> { + self.seeked = true; + + if self.is_latest { + // Fast path: direct current-state walk. + let result = self.cursor.seek(StoredNibbles(key))?; + if let Some((ref k, _)) = result { + self.last_key = Some(k.clone()); + } + return Ok(result.map(|(k, node)| (k.0, node))); + } + + // Initialize both merge cursors at the target key. + self.cs_next = self.cursor.seek(StoredNibbles(key))?; + self.hist_next_key = self + .history_walk_cursor + .seek(AccountTrieShardedKey::new(StoredNibbles(key), 0))? + .map(|(k, _)| k.key); + self.find_next_live() + } + + fn next(&mut self) -> Result, DatabaseError> { + if !self.seeked { + return self.seek(Nibbles::default()); + } + + if self.is_latest { + let result = self.cursor.next()?; + if let Some((ref k, _)) = result { + self.last_key = Some(k.clone()); + } + return Ok(result.map(|(k, node)| (k.0, node))); + } + + self.find_next_live() + } + + fn current(&mut self) -> Result, DatabaseError> { + Ok(self.last_key.as_ref().map(|k| k.0)) + } + + fn reset(&mut self) { + self.cs_next = None; + self.hist_next_key = None; + self.last_key = None; + self.seeked = false; + } +} diff --git a/rust/op-reth/crates/trie/src/db/store_v2/cursor/mod.rs b/rust/op-reth/crates/trie/src/db/store_v2/cursor/mod.rs new file mode 100644 index 00000000000..fca8100670d --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/cursor/mod.rs @@ -0,0 +1,92 @@ +//! V2 cursor implementations for the v2 table schema. +//! +//! These cursors implement **history-aware reads** using the v2 3-table-per-data-type pattern: +//! +//! | Purpose | Accounts | Storages | Account Trie | Storage Trie | +//! |---------|----------|----------|-------------|-------------| +//! | Current state | `V2HashedAccounts` | `V2HashedStorages` | `V2AccountsTrie` | `V2StoragesTrie` | +//! | `ChangeSets` | `V2HashedAccountChangeSets` | `V2HashedStorageChangeSets` | `V2AccountTrieChangeSets` | `V2StorageTrieChangeSets` | +//! | History | `V2HashedAccountsHistory` | `V2HashedStoragesHistory` | `V2AccountsTrieHistory` | `V2StoragesTrieHistory` | +//! +//! # Historical Lookup Strategy +//! +//! Each cursor accepts a `max_block_number` parameter. For each key encountered: +//! +//! 1. **History bitmap lookup**: Seek `ShardedKey(key, max_block_number + 1)` in the history table. +//! This lands on the first shard whose `highest_block_number > max_block_number`. The bitmap +//! tells us which blocks modified this key. +//! 2. **Find the first modification *after* `max_block_number`**: Using `rank` + `select` on the +//! bitmap. `rank(max_block_number)` counts entries ≤ the target block; `select(rank)` returns +//! the first entry strictly greater. +//! 3. **Determine where the value lives**: +//! - If a block `> max_block_number` modified this key → read the **changeset** at that block. +//! The changeset stores the value *before* that block's execution, which is the value at the +//! end of `max_block_number`. +//! - If no block after `max_block_number` modified this key → the **current state** table +//! already has the correct value. + +mod account; +mod account_trie; +mod storage; +mod storage_trie; + +pub use account::V2AccountCursor; +pub use account_trie::V2AccountTrieCursor; +pub use storage::V2StorageCursor; +pub use storage_trie::V2StorageTrieCursor; + +use reth_db::{BlockNumberList, DatabaseError, cursor::DbCursorRO, table::Table}; + +/// Enum to define where to read the value for a given key at a specific block. +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum ResolvedSource { + /// Read the "before" value from the changeset at this block. + /// The changeset stores the value *before* this block's execution, + /// which equals the value at the end of `max_block_number`. + FromChangeset(u64), + /// No modification after the target block → current state has the value. + FromCurrentState, +} + +/// Search history bitmaps to determine where to read the value for a key +/// at a given `max_block_number`. +/// +/// **Important**: `seek_key` must embed `max_block_number + 1` (not +/// `max_block_number` itself) so that the seek lands on a shard whose +/// `highest_block_number > max_block_number`. This guarantees the shard +/// contains at least one entry after the target block, eliminating the +/// need for a `cursor.next()` fallback. +/// +/// The algorithm: +/// 1. Seek the first history shard with `highest_block_number > max_block_number` (achieved by +/// embedding `max_block_number + 1` in `seek_key`). +/// 2. Within that shard, find the first block strictly `> max_block_number`. +/// 3. If found → `FromChangeset(block)`. +/// 4. Otherwise → `FromCurrentState`. +pub(crate) fn find_source( + cursor: &mut C, + seek_key: T::Key, + max_block_number: u64, + key_filter: impl Fn(&T::Key) -> bool, +) -> Result +where + T: Table, + C: DbCursorRO, +{ + // 1. Seek using the caller-provided key (which embeds max_block_number + 1), then filter to + // ensure the shard belongs to the expected key. + let shard = cursor.seek(seek_key)?.filter(|(k, _)| key_filter(k)); + let Some((_, chunk)) = shard else { + return Ok(ResolvedSource::FromCurrentState); + }; + + // 2. rank(n) = count of entries ≤ n. select(rank) = first entry > n. + let rank = chunk.rank(max_block_number); + Ok(chunk + .select(rank) + .map(ResolvedSource::FromChangeset) + .unwrap_or(ResolvedSource::FromCurrentState)) +} + +#[cfg(test)] +mod tests; diff --git a/rust/op-reth/crates/trie/src/db/store_v2/cursor/storage.rs b/rust/op-reth/crates/trie/src/db/store_v2/cursor/storage.rs new file mode 100644 index 00000000000..da4c7c74f57 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/cursor/storage.rs @@ -0,0 +1,264 @@ +//! History-aware cursor over the [`V2HashedStorages`] v2 `DupSort` table. + +use alloy_primitives::{B256, U256}; +use reth_db::{ + DatabaseError, + cursor::{DbCursorRO, DbDupCursorRO}, + models::sharded_key::ShardedKey, +}; +use reth_trie::hashed_cursor::{HashedCursor, HashedStorageCursor}; + +use super::{ResolvedSource, find_source}; +use crate::db::models::{ + BlockNumberHashedAddress, HashedStorageShardedKey, V2HashedStorageChangeSets, V2HashedStorages, + V2HashedStoragesHistory, +}; + +/// History-aware cursor over the [`V2HashedStorages`] v2 `DupSort` table. +/// +/// Uses the same dual-cursor merge strategy as [`super::V2AccountCursor`] but +/// scoped to a single `hashed_address`. Both the current-state `DupSort` +/// entries and the history-bitmap entries are walked in parallel to discover +/// storage slots that may have been deleted after `max_block_number`. +#[derive(Debug)] +pub struct V2StorageCursor { + /// Current state cursor (`DupSort`). + cursor: C, + /// History bitmap cursor for resolving individual keys. + history_cursor: HC, + /// History bitmap cursor for merge-walking deleted keys. + history_walk_cursor: HC, + /// Changeset cursor (`DupSort`). + changeset_cursor: CC, + /// Target hashed address. + hashed_address: B256, + /// Target block number for historical reads. + max_block_number: u64, + /// Pre-fetched next entry from the current state walk (within address). + cs_next: Option, + /// Pre-fetched next unique storage key from the history walk. + hist_next_key: Option, + /// Whether `seek` has been called to initialize the merge cursors. + seeked: bool, + /// Fast path: when `true`, skip all history/changeset lookups. + is_latest: bool, +} + +impl V2StorageCursor { + /// Create a new [`V2StorageCursor`]. + pub const fn new( + cursor: C, + history_cursor: HC, + history_walk_cursor: HC, + changeset_cursor: CC, + hashed_address: B256, + max_block_number: u64, + is_latest: bool, + ) -> Self { + Self { + cursor, + history_cursor, + history_walk_cursor, + changeset_cursor, + hashed_address, + max_block_number, + cs_next: None, + hist_next_key: None, + seeked: false, + is_latest, + } + } +} + +impl V2StorageCursor +where + C: DbCursorRO + DbDupCursorRO, + HC: DbCursorRO, + CC: DbCursorRO + DbDupCursorRO, +{ + /// Resolve a storage slot using a pre-fetched current-state value. + /// + /// Does **not** touch the walk cursor, so it is safe to call from the + /// merge loop (`find_next_live`). + fn resolve_storage_merge( + &mut self, + storage_key: B256, + cs_value: Option<&U256>, + ) -> Result, DatabaseError> { + let history_key = HashedStorageShardedKey { + hashed_address: self.hashed_address, + sharded_key: ShardedKey::new(storage_key, self.max_block_number.saturating_add(1)), + }; + + let addr = self.hashed_address; + let source = find_source::( + &mut self.history_cursor, + history_key, + self.max_block_number, + |k| k.hashed_address == addr && k.sharded_key.key == storage_key, + )?; + + match source { + ResolvedSource::FromChangeset(changeset_block) => { + let cs_key = BlockNumberHashedAddress((changeset_block, self.hashed_address)); + let entry = self + .changeset_cursor + .seek_by_key_subkey(cs_key, storage_key)? + .filter(|e| e.key == storage_key); + match entry { + Some(e) if e.value.is_zero() => Ok(None), + Some(e) => Ok(Some(e.value)), + None => Ok(None), + } + } + ResolvedSource::FromCurrentState => Ok(cs_value.copied().filter(|v| !v.is_zero())), + } + } + + /// Advance the history walk cursor past all shards of `key` (for this + /// address) and return the next distinct storage key, if any. + fn advance_history_past(&mut self, key: &B256) -> Result, DatabaseError> { + let seek = HashedStorageShardedKey { + hashed_address: self.hashed_address, + sharded_key: ShardedKey::new(*key, u64::MAX), + }; + let entry = self + .history_walk_cursor + .seek(seek)? + .filter(|(k, _)| k.hashed_address == self.hashed_address); + match entry { + Some((k, _)) if k.sharded_key.key == *key => { + // On the last shard of this key — advance once more. + Ok(self + .history_walk_cursor + .next()? + .filter(|(k, _)| k.hashed_address == self.hashed_address) + .map(|(k, _)| k.sharded_key.key)) + } + Some((k, _)) => Ok(Some(k.sharded_key.key)), + None => Ok(None), + } + } + + /// Merge-walk both the current-state `DupSort` cursor and the history-bitmap + /// cursor, yielding the next storage slot whose value is live at + /// `max_block_number`. + fn find_next_live(&mut self) -> Result, DatabaseError> { + loop { + // Step 1: Pick the minimum key from current-state and history cursors. + // If both have the same key, prefer the current-state value. + // `cs_value` is `Some` when the key exists in current state, `None` + // when it only appears in history (i.e. deleted after max_block_number). + let (min_key, cs_value) = match (&self.cs_next, &self.hist_next_key) { + (Some(cs_entry), Some(h_k)) => { + if cs_entry.key <= *h_k { + (cs_entry.key, Some(cs_entry.value)) + } else { + (*h_k, None) + } + } + (Some(cs_entry), None) => (cs_entry.key, Some(cs_entry.value)), + (None, Some(h_k)) => (*h_k, None), + (None, None) => return Ok(None), + }; + + // Step 2: Advance whichever cursor(s) produced this key. + // Both are advanced when they have the same key (deduplication). + if self.cs_next.as_ref().is_some_and(|e| e.key == min_key) { + self.cs_next = self.cursor.next_dup_val()?; + } + if self.hist_next_key.as_ref().is_some_and(|k| *k == min_key) { + self.hist_next_key = self.advance_history_past(&min_key)?; + } + + // Step 3: Resolve the value at max_block_number. + // Returns `Some` if the key was live at that block, `None` if it + // didn't exist yet or was already deleted. + if let Some(value) = self.resolve_storage_merge(min_key, cs_value.as_ref())? { + return Ok(Some((min_key, value))); + } + // Key doesn't exist at max_block_number — continue to next. + } + } +} + +impl HashedCursor for V2StorageCursor +where + C: DbCursorRO + DbDupCursorRO + Send, + HC: DbCursorRO + Send, + CC: DbCursorRO + DbDupCursorRO + Send, +{ + type Value = U256; + + fn seek(&mut self, subkey: B256) -> Result, DatabaseError> { + self.seeked = true; + + if self.is_latest { + // Fast path: current state is authoritative. + // Loop to skip zero-valued entries (tombstones). + let mut entry = self.cursor.seek_by_key_subkey(self.hashed_address, subkey)?; + while let Some(ref e) = entry { + if !e.value.is_zero() { + return Ok(Some((e.key, e.value))); + } + entry = self.cursor.next_dup_val()?; + } + return Ok(None); + } + + // Initialize both merge cursors at the target key. + self.cs_next = self.cursor.seek_by_key_subkey(self.hashed_address, subkey)?; + let hist_seek = HashedStorageShardedKey { + hashed_address: self.hashed_address, + sharded_key: ShardedKey::new(subkey, 0), + }; + self.hist_next_key = self + .history_walk_cursor + .seek(hist_seek)? + .filter(|(k, _)| k.hashed_address == self.hashed_address) + .map(|(k, _)| k.sharded_key.key); + self.find_next_live() + } + + fn next(&mut self) -> Result, DatabaseError> { + if !self.seeked { + return self.seek(B256::ZERO); + } + + if self.is_latest { + // Loop to skip zero-valued entries (tombstones). + while let Some(e) = self.cursor.next_dup_val()? { + if !e.value.is_zero() { + return Ok(Some((e.key, e.value))); + } + } + return Ok(None); + } + + self.find_next_live() + } + + fn reset(&mut self) { + self.cs_next = None; + self.hist_next_key = None; + self.seeked = false; + } +} + +impl HashedStorageCursor for V2StorageCursor +where + C: DbCursorRO + DbDupCursorRO + Send, + HC: DbCursorRO + Send, + CC: DbCursorRO + DbDupCursorRO + Send, +{ + fn is_storage_empty(&mut self) -> Result { + Ok(self.seek(B256::ZERO)?.is_none()) + } + + fn set_hashed_address(&mut self, hashed_address: B256) { + self.hashed_address = hashed_address; + self.cs_next = None; + self.hist_next_key = None; + self.seeked = false; + } +} diff --git a/rust/op-reth/crates/trie/src/db/store_v2/cursor/storage_trie.rs b/rust/op-reth/crates/trie/src/db/store_v2/cursor/storage_trie.rs new file mode 100644 index 00000000000..79993f3e68a --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/cursor/storage_trie.rs @@ -0,0 +1,345 @@ +//! History-aware cursor over the [`V2StoragesTrie`] v2 `DupSort` table. + +use alloy_primitives::B256; +use reth_db::{ + DatabaseError, + cursor::{DbCursorRO, DbDupCursorRO}, +}; +use reth_trie::{ + BranchNodeCompact, Nibbles, StoredNibbles, StoredNibblesSubKey, + trie_cursor::{TrieCursor, TrieStorageCursor}, +}; +use reth_trie_common::StorageTrieEntry; + +use super::{ResolvedSource, find_source}; +use crate::db::models::{ + BlockNumberHashedAddress, StorageTrieShardedKey, V2StorageTrieChangeSets, V2StoragesTrie, + V2StoragesTrieHistory, +}; + +/// History-aware cursor over the [`V2StoragesTrie`] v2 `DupSort` table. +/// +/// Uses the same dual-cursor merge strategy as [`super::V2AccountTrieCursor`] but +/// scoped to a single `hashed_address`. Both the current-state `DupSort` +/// entries and the history-bitmap entries are walked in parallel to discover +/// keys that may have been deleted after `max_block_number`. +#[derive(Debug)] +pub struct V2StorageTrieCursor { + /// Current state cursor (`DupSort`). + cursor: C, + /// History bitmap cursor for resolving individual keys. + history_cursor: HC, + /// History bitmap cursor for merge-walking deleted keys. + history_walk_cursor: HC, + /// Changeset cursor (`DupSort`). + changeset_cursor: CC, + /// Target hashed address. + hashed_address: B256, + /// Target block number. + max_block_number: u64, + /// Pre-fetched next entry from the current state walk (within address). + cs_next: Option, + /// Pre-fetched next unique nibbles key from the history walk. + hist_next_key: Option, + /// Last key yielded by `seek`/`next` (for `current()`). + last_key: Option, + /// Whether `seek` or `seek_exact` has been called to initialize the merge cursors. + seeked: bool, + /// Fast path: when `true`, skip all history/changeset lookups. + is_latest: bool, +} + +impl V2StorageTrieCursor { + /// Create a new [`V2StorageTrieCursor`]. + pub const fn new( + cursor: C, + history_cursor: HC, + history_walk_cursor: HC, + changeset_cursor: CC, + hashed_address: B256, + max_block_number: u64, + is_latest: bool, + ) -> Self { + Self { + cursor, + history_cursor, + history_walk_cursor, + changeset_cursor, + hashed_address, + max_block_number, + cs_next: None, + hist_next_key: None, + last_key: None, + seeked: false, + is_latest, + } + } +} + +impl V2StorageTrieCursor +where + C: DbCursorRO + DbDupCursorRO, + HC: DbCursorRO, + CC: DbCursorRO + DbDupCursorRO, +{ + /// Resolve a key using the walk cursor for the `FromCurrentState` case. + /// + /// May disrupt the walk cursor position — only call from `seek_exact`. + fn resolve_node_standalone( + &mut self, + path: Nibbles, + ) -> Result, DatabaseError> { + let nibbles = StoredNibbles(path); + let seek_key = StorageTrieShardedKey::new( + self.hashed_address, + nibbles.clone(), + self.max_block_number.saturating_add(1), + ); + + let addr = self.hashed_address; + let nibbles_cmp = nibbles; + let source = find_source::( + &mut self.history_cursor, + seek_key, + self.max_block_number, + |k| k.hashed_address == addr && k.key == nibbles_cmp, + )?; + + match source { + ResolvedSource::FromChangeset(changeset_block) => { + let cs_key = BlockNumberHashedAddress((changeset_block, self.hashed_address)); + let entry = self + .changeset_cursor + .seek_by_key_subkey(cs_key, StoredNibblesSubKey(path))? + .filter(|e| e.nibbles == StoredNibblesSubKey(path)); + Ok(entry.and_then(|e| e.node)) + } + ResolvedSource::FromCurrentState => Ok(self + .cursor + .seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(path))? + .filter(|e| e.nibbles == StoredNibblesSubKey(path)) + .map(|e| e.node)), + } + } + + /// Resolve a key using a pre-fetched current-state value. + /// + /// Does **not** touch the walk cursor. + fn resolve_node_merge( + &mut self, + path: Nibbles, + cs_value: Option<&BranchNodeCompact>, + ) -> Result, DatabaseError> { + let nibbles = StoredNibbles(path); + let seek_key = StorageTrieShardedKey::new( + self.hashed_address, + nibbles.clone(), + self.max_block_number.saturating_add(1), + ); + + let addr = self.hashed_address; + let nibbles_cmp = nibbles; + let source = find_source::( + &mut self.history_cursor, + seek_key, + self.max_block_number, + |k| k.hashed_address == addr && k.key == nibbles_cmp, + )?; + + match source { + ResolvedSource::FromChangeset(changeset_block) => { + let cs_key = BlockNumberHashedAddress((changeset_block, self.hashed_address)); + let entry = self + .changeset_cursor + .seek_by_key_subkey(cs_key, StoredNibblesSubKey(path))? + .filter(|e| e.nibbles == StoredNibblesSubKey(path)); + Ok(entry.and_then(|e| e.node)) + } + ResolvedSource::FromCurrentState => Ok(cs_value.cloned()), + } + } + + /// Advance the history walk cursor past all shards of `key` (for this + /// address) and return the next distinct nibbles key, if any. + fn advance_history_past( + &mut self, + key: &StoredNibbles, + ) -> Result, DatabaseError> { + let seek = StorageTrieShardedKey::new(self.hashed_address, key.clone(), u64::MAX); + let entry = self + .history_walk_cursor + .seek(seek)? + .filter(|(k, _)| k.hashed_address == self.hashed_address); + match entry { + Some((k, _)) if k.key == *key => { + // On the last shard of this key — advance once more. + Ok(self + .history_walk_cursor + .next()? + .filter(|(k, _)| k.hashed_address == self.hashed_address) + .map(|(k, _)| k.key)) + } + Some((k, _)) => Ok(Some(k.key)), + None => Ok(None), + } + } + + /// Merge-walk both the current-state `DupSort` cursor and the history-bitmap + /// cursor, yielding the next path whose node is live at `max_block_number`. + fn find_next_live(&mut self) -> Result, DatabaseError> { + loop { + // Step 1: Pick the minimum key from current-state and history cursors. + // If both have the same key, prefer the current-state value. + // `cs_node` is `Some` when the key exists in current state, `None` + // when it only appears in history (i.e. deleted after max_block_number). + let (min_nibbles, cs_node) = match (&self.cs_next, &self.hist_next_key) { + (Some(cs_entry), Some(h_k)) => { + let cs_stored = StoredNibbles(cs_entry.nibbles.0); + if cs_stored <= *h_k { + (cs_stored, Some(cs_entry.node.clone())) + } else { + (h_k.clone(), None) + } + } + (Some(cs_entry), None) => { + (StoredNibbles(cs_entry.nibbles.0), Some(cs_entry.node.clone())) + } + (None, Some(h_k)) => (h_k.clone(), None), + (None, None) => return Ok(None), + }; + + // Step 2: Advance whichever cursor(s) produced this key. + // Both are advanced when they have the same key (deduplication). + if self.cs_next.as_ref().is_some_and(|e| StoredNibbles(e.nibbles.0) == min_nibbles) { + self.cs_next = self.cursor.next_dup()?.map(|(_, v)| v); + } + if self.hist_next_key.as_ref().is_some_and(|k| *k == min_nibbles) { + self.hist_next_key = self.advance_history_past(&min_nibbles)?; + } + + // Step 3: Resolve the value at max_block_number. + // Returns `Some` if the key was live at that block, `None` if it + // didn't exist yet or was already deleted. + if let Some(node) = self.resolve_node_merge(min_nibbles.0, cs_node.as_ref())? { + self.last_key = Some(StoredNibbles(min_nibbles.0)); + return Ok(Some((min_nibbles.0, node))); + } + // Key doesn't exist at max_block_number — continue to next. + } + } +} + +impl TrieCursor for V2StorageTrieCursor +where + C: DbCursorRO + DbDupCursorRO + Send, + HC: DbCursorRO + Send, + CC: DbCursorRO + DbDupCursorRO + Send, +{ + fn seek_exact( + &mut self, + key: Nibbles, + ) -> Result, DatabaseError> { + self.seeked = true; + + if self.is_latest { + // Fast path: direct DupSort lookup. + let entry = self + .cursor + .seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(key))? + .filter(|e| e.nibbles == StoredNibblesSubKey(key)); + if entry.is_some() { + self.last_key = Some(StoredNibbles(key)); + } + return Ok(entry.map(|e| (key, e.node))); + } + + let node = self.resolve_node_standalone(key)?; + + // Re-sync walk state so a subsequent next() starts after `key`. + let cs_at_key = + self.cursor.seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(key))?; + self.cs_next = match cs_at_key { + Some(e) if e.nibbles == StoredNibblesSubKey(key) => { + self.cursor.next_dup()?.map(|(_, v)| v) + } + other => other, + }; + let path = StoredNibbles(key); + self.hist_next_key = self.advance_history_past(&path)?; + + if node.is_some() { + self.last_key = Some(path); + } + Ok(node.map(|n| (key, n))) + } + + fn seek( + &mut self, + key: Nibbles, + ) -> Result, DatabaseError> { + self.seeked = true; + + if self.is_latest { + // Fast path: direct DupSort walk. + let entry = + self.cursor.seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(key))?; + if let Some(ref e) = entry { + self.last_key = Some(StoredNibbles(e.nibbles.0)); + } + return Ok(entry.map(|e| (e.nibbles.0, e.node))); + } + + // Initialize both merge cursors at the target key. + self.cs_next = + self.cursor.seek_by_key_subkey(self.hashed_address, StoredNibblesSubKey(key))?; + let hist_seek = StorageTrieShardedKey::new(self.hashed_address, StoredNibbles(key), 0); + self.hist_next_key = self + .history_walk_cursor + .seek(hist_seek)? + .filter(|(k, _)| k.hashed_address == self.hashed_address) + .map(|(k, _)| k.key); + self.find_next_live() + } + + fn next(&mut self) -> Result, DatabaseError> { + if !self.seeked { + return self.seek(Nibbles::default()); + } + + if self.is_latest { + let entry = self.cursor.next_dup()?.map(|(_, v)| v); + if let Some(ref e) = entry { + self.last_key = Some(StoredNibbles(e.nibbles.0)); + } + return Ok(entry.map(|e| (e.nibbles.0, e.node))); + } + + self.find_next_live() + } + + fn current(&mut self) -> Result, DatabaseError> { + Ok(self.last_key.as_ref().map(|k| k.0)) + } + + fn reset(&mut self) { + self.cs_next = None; + self.hist_next_key = None; + self.last_key = None; + self.seeked = false; + } +} + +impl TrieStorageCursor for V2StorageTrieCursor +where + C: DbCursorRO + DbDupCursorRO + Send, + HC: DbCursorRO + Send, + CC: DbCursorRO + DbDupCursorRO + Send, +{ + fn set_hashed_address(&mut self, hashed_address: B256) { + self.hashed_address = hashed_address; + self.cs_next = None; + self.hist_next_key = None; + self.last_key = None; + self.seeked = false; + } +} diff --git a/rust/op-reth/crates/trie/src/db/store_v2/cursor/tests.rs b/rust/op-reth/crates/trie/src/db/store_v2/cursor/tests.rs new file mode 100644 index 00000000000..0403f972ef3 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/cursor/tests.rs @@ -0,0 +1,2322 @@ +use super::*; +use crate::db::{ + models, + models::{HashedAccountBeforeTx, TrieChangeSetsEntry}, +}; +use alloy_primitives::{B256, U256}; +use reth_db::{ + BlockNumberList, Database, DatabaseEnv, + cursor::{DbCursorRW, DbDupCursorRW}, + mdbx::{DatabaseArguments, init_db_for}, + transaction::{DbTx, DbTxMut}, +}; +use reth_primitives_traits::{Account, StorageEntry}; +use reth_trie::{ + BranchNodeCompact, Nibbles, StoredNibbles, StoredNibblesSubKey, + hashed_cursor::{HashedCursor, HashedStorageCursor}, + trie_cursor::{TrieCursor, TrieStorageCursor}, +}; +use reth_trie_common::StorageTrieEntry; +use tempfile::TempDir; + +use crate::db::models::{ + AccountTrieShardedKey, BlockNumberHashedAddress, HashedAccountShardedKey, + HashedStorageShardedKey, StorageTrieShardedKey, V2AccountTrieChangeSets, V2AccountsTrie, + V2AccountsTrieHistory, V2HashedAccountChangeSets, V2HashedAccounts, V2HashedAccountsHistory, + V2HashedStorageChangeSets, V2HashedStorages, V2HashedStoragesHistory, V2StorageTrieChangeSets, + V2StoragesTrie, V2StoragesTrieHistory, +}; +use reth_db::models::sharded_key::ShardedKey; + +fn setup_db() -> DatabaseEnv { + let tmp = TempDir::new().expect("create tmpdir"); + init_db_for::<_, models::Tables>(tmp, DatabaseArguments::default()).expect("init db") +} + +fn node() -> BranchNodeCompact { + BranchNodeCompact::new(0b11, 0, 0, vec![], Some(B256::repeat_byte(0xAB))) +} + +fn node2() -> BranchNodeCompact { + BranchNodeCompact::new(0b101, 0, 0, vec![], Some(B256::repeat_byte(0xCD))) +} + +fn sample_account(nonce: u64) -> Account { + Account { nonce, ..Default::default() } +} + +// ====================== find_source unit tests ====================== + +#[test] +fn find_source_returns_current_state_when_no_history() { + let db = setup_db(); + let addr = B256::from([0xAA; 32]); + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + let result = find_source::( + &mut cursor, + HashedAccountShardedKey::new(addr, 11), + 10, + |k| k.0.key == addr, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromCurrentState); +} + +#[test] +fn find_source_returns_changeset_when_modification_after_target() { + let db = setup_db(); + let addr = B256::from([0xBB; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(addr, u64::MAX), + &BlockNumberList::new_pre_sorted([5, 10, 15]), + ) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + // Target block 7 → first block > 7 in [5, 10, 15] is 10 + let result = find_source::( + &mut cursor, + HashedAccountShardedKey::new(addr, 8), + 7, + |k| k.0.key == addr, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromChangeset(10)); +} + +#[test] +fn find_source_returns_current_state_when_no_modification_after_target() { + let db = setup_db(); + let addr = B256::from([0xCC; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(addr, u64::MAX), + &BlockNumberList::new_pre_sorted([3, 7]), + ) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + // Target block 10 → no block > 10 in [3, 7] + let result = find_source::( + &mut cursor, + HashedAccountShardedKey::new(addr, 11), + 10, + |k| k.0.key == addr, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromCurrentState); +} + +#[test] +fn find_source_handles_exact_match_block() { + let db = setup_db(); + let addr = B256::from([0xDD; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(addr, u64::MAX), + &BlockNumberList::new_pre_sorted([5, 10, 15]), + ) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + // Target block 10 (exactly in the bitmap) → first block > 10 is 15 + let result = find_source::( + &mut cursor, + HashedAccountShardedKey::new(addr, 11), + 10, + |k| k.0.key == addr, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromChangeset(15)); +} + +// ====================== find_source with AccountTrieShardedKey tests ====================== + +#[test] +fn find_source_resolves_root_path_despite_child_history() { + // Regression test: the root trie path [] has history at blocks [10, 15]. + // A child path [0] also has history. With the old `ShardedKey` + // encoding (no length prefix), `cursor.seek` would land on the wrong path. + // With `AccountTrieShardedKey`'s length-prefixed encoding, `find_source` works + // correctly: all shards of [] sort before all shards of [0]. + let db = setup_db(); + let root_path = StoredNibbles(Nibbles::default()); + let child_path = StoredNibbles(Nibbles::from_nibbles([0])); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut cursor = wtx.cursor_write::().expect("c"); + // Root path history: modified at blocks 10, 15 + cursor + .upsert( + AccountTrieShardedKey::new(root_path.clone(), u64::MAX), + &BlockNumberList::new_pre_sorted([10, 15]), + ) + .expect("upsert root"); + // Child path [0] history: modified at blocks 10, 15 + cursor + .upsert( + AccountTrieShardedKey::new(child_path, u64::MAX), + &BlockNumberList::new_pre_sorted([10, 15]), + ) + .expect("upsert child"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + // Query at block 12 — should find changeset at block 15 + // (the first modification after block 12). + let result = find_source::( + &mut cursor, + AccountTrieShardedKey::new(root_path.clone(), 13), + 12, + |k| k.key == root_path, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromChangeset(15)); +} + +#[test] +fn find_source_trie_returns_current_state_when_no_history() { + let db = setup_db(); + let root_path = StoredNibbles(Nibbles::default()); + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + let result = find_source::( + &mut cursor, + AccountTrieShardedKey::new(root_path.clone(), 11), + 10, + |k| k.key == root_path, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromCurrentState); +} + +#[test] +fn find_source_trie_returns_current_state_when_all_modifications_before_target() { + let db = setup_db(); + let root_path = StoredNibbles(Nibbles::default()); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(root_path.clone(), u64::MAX), + &BlockNumberList::new_pre_sorted([5, 8]), + ) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + // Target block 10 — all modifications (5, 8) are ≤ 10 + let result = find_source::( + &mut cursor, + AccountTrieShardedKey::new(root_path.clone(), 11), + 10, + |k| k.key == root_path, + ) + .expect("ok"); + + assert_eq!(result, ResolvedSource::FromCurrentState); +} + +#[test] +fn find_source_handles_root_path_with_child_history() { + // Verifies the encoding fix: `find_source` with `AccountTrieShardedKey` + // correctly resolves the root path even when child path [0] has history. + // Before the length-prefix fix, this would return `FromCurrentState` + // due to encoding ambiguity. Now it correctly returns `FromChangeset(15)`. + let db = setup_db(); + let root_path = StoredNibbles(Nibbles::default()); + let child_path = StoredNibbles(Nibbles::from_nibbles([0])); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut cursor = wtx.cursor_write::().expect("c"); + cursor + .upsert( + AccountTrieShardedKey::new(root_path.clone(), u64::MAX), + &BlockNumberList::new_pre_sorted([10, 15]), + ) + .expect("upsert root"); + cursor + .upsert( + AccountTrieShardedKey::new(child_path, u64::MAX), + &BlockNumberList::new_pre_sorted([10, 15]), + ) + .expect("upsert child"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cursor = tx.cursor_read::().expect("c"); + + // find_source with AccountTrieShardedKey correctly returns FromChangeset(15) + let result = find_source::( + &mut cursor, + AccountTrieShardedKey::new(root_path.clone(), 13), + 12, + |k| k.key == root_path, + ) + .expect("ok"); + + assert_eq!( + result, + ResolvedSource::FromChangeset(15), + "find_source with AccountTrieShardedKey should correctly resolve root path" + ); +} + +// ====================== Account Cursor tests ====================== + +#[test] +fn account_cursor_reads_current_state_when_no_history() { + let db = setup_db(); + let addr = B256::from([0xAA; 32]); + let acc = sample_account(42); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::().expect("c").upsert(addr, &acc).expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + let result = cur.seek(addr).expect("ok").expect("should find"); + assert_eq!(result.0, addr); + assert_eq!(result.1.nonce, 42); +} + +#[test] +fn account_cursor_resolves_from_changeset_when_modified_after_target() { + let db = setup_db(); + let addr = B256::from([0xBB; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: nonce=10 (applied at block 5) + wtx.cursor_write::() + .expect("c") + .upsert(addr, &sample_account(10)) + .expect("upsert"); + + // History bitmap: block 5 modified this account + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(addr, u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + + // Changeset: before block 5, account had nonce=3 + wtx.cursor_dup_write::() + .expect("c") + .append_dup(5u64, HashedAccountBeforeTx::new(addr, Some(sample_account(3)))) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 4 (before the modification at block 5) + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 4, + false, + ); + + let result = cur.seek(addr).expect("ok").expect("should find"); + assert_eq!(result.0, addr); + assert_eq!(result.1.nonce, 3, "should get changeset value (before block 5)"); +} + +#[test] +fn account_cursor_returns_current_state_when_at_or_after_last_modification() { + let db = setup_db(); + let addr = B256::from([0xCC; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: nonce=20 + wtx.cursor_write::() + .expect("c") + .upsert(addr, &sample_account(20)) + .expect("upsert"); + + // History bitmap: [3, 7] + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(addr, u64::MAX), + &BlockNumberList::new_pre_sorted([3, 7]), + ) + .expect("upsert"); + + // Changeset at 3 + wtx.cursor_dup_write::() + .expect("c") + .append_dup(3u64, HashedAccountBeforeTx::new(addr, Some(sample_account(1)))) + .expect("append"); + + // Changeset at 7 + wtx.cursor_dup_write::() + .expect("c") + .append_dup(7u64, HashedAccountBeforeTx::new(addr, Some(sample_account(5)))) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 10 (after last modification at block 7) + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 10, + true, + ); + + let result = cur.seek(addr).expect("ok").expect("should find"); + assert_eq!(result.0, addr); + assert_eq!(result.1.nonce, 20, "current state (no modification after block 10)"); +} + +#[test] +fn account_cursor_returns_none_when_not_yet_created() { + let db = setup_db(); + let addr = B256::from([0xDD; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: account exists (created at block 5) + wtx.cursor_write::() + .expect("c") + .upsert(addr, &sample_account(1)) + .expect("upsert"); + + // History: first write at block 5 + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(addr, u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + + // Changeset at 5: didn't exist before (info = None) + wtx.cursor_dup_write::() + .expect("c") + .append_dup(5u64, HashedAccountBeforeTx::new(addr, None)) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 4 (before first write at 5) + // The changeset at block 5 says info=None → the cursor's resolve returns None + // → next_live_from skips this entry → seek returns None + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 4, + false, + ); + + let result = cur.seek(addr).expect("ok"); + assert!(result.is_none(), "account should not exist at block 4"); +} + +#[test] +fn account_cursor_seek_and_next_skip_dead_entries() { + let db = setup_db(); + let k1 = B256::from([0x01; 32]); + let k2 = B256::from([0x02; 32]); + let k3 = B256::from([0x03; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(k1, &sample_account(1)).expect("upsert"); + c.upsert(k2, &sample_account(2)).expect("upsert"); + c.upsert(k3, &sample_account(3)).expect("upsert"); + + // k2 was created at block 10 + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(k2, u64::MAX), + &BlockNumberList::new_pre_sorted([10]), + ) + .expect("upsert"); + + // Changeset at 10: k2 didn't exist before + wtx.cursor_dup_write::() + .expect("c") + .append_dup(10u64, HashedAccountBeforeTx::new(k2, None)) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 5 (before k2 was created) + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 5, + false, + ); + + // Seek k1 → should find k1 (no history = current state) + let result = cur.seek(k1).expect("ok").expect("should find k1"); + assert_eq!(result.0, k1); + assert_eq!(result.1.nonce, 1); + + // Next → should skip k2 (doesn't exist at block 5) and find k3 + let result = cur.next().expect("ok").expect("should skip k2, find k3"); + assert_eq!(result.0, k3); + assert_eq!(result.1.nonce, 3); +} + +/// Account was deleted (SELFDESTRUCT) after the target block, so it's not +/// in the current-state table. The history walk must discover it. +#[test] +fn account_cursor_discovers_key_deleted_after_target_block() { + let db = setup_db(); + let k1 = B256::from([0x01; 32]); + let k2 = B256::from([0x02; 32]); // deleted after target + let k3 = B256::from([0x03; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + // k1 and k3 exist in current state; k2 was deleted at block 10 + c.upsert(k1, &sample_account(1)).expect("upsert"); + c.upsert(k3, &sample_account(3)).expect("upsert"); + + // k2 history: modified at blocks [5, 10] + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(k2, u64::MAX), + &BlockNumberList::new_pre_sorted([5, 10]), + ) + .expect("upsert"); + + // Changeset at block 10: value before block 10 = nonce 7 + wtx.cursor_dup_write::() + .expect("c") + .append_dup(10u64, HashedAccountBeforeTx::new(k2, Some(sample_account(7)))) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + // Query at block 9: k2 existed with nonce=7 + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 9, + false, + ); + + let r1 = cur.seek(B256::ZERO).expect("ok").expect("k1"); + assert_eq!(r1.0, k1); + assert_eq!(r1.1.nonce, 1); + + let r2 = cur.next().expect("ok").expect("k2 from history"); + assert_eq!(r2.0, k2); + assert_eq!(r2.1.nonce, 7); + + let r3 = cur.next().expect("ok").expect("k3"); + assert_eq!(r3.0, k3); + assert_eq!(r3.1.nonce, 3); + + assert!(cur.next().expect("ok").is_none()); +} + +/// All accounts are deleted after the target block — only the history +/// walk can find them. +#[test] +fn account_cursor_all_keys_from_history() { + let db = setup_db(); + let k1 = B256::from([0x10; 32]); + let k2 = B256::from([0x20; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + // Nothing in current state. + + // k1 modified at block 5 + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(k1, u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(5u64, HashedAccountBeforeTx::new(k1, Some(sample_account(11)))) + .expect("append"); + + // k2 modified at block 8 + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(k2, u64::MAX), + &BlockNumberList::new_pre_sorted([8]), + ) + .expect("upsert"); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(8u64, HashedAccountBeforeTx::new(k2, Some(sample_account(22)))) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 4, + false, + ); + + let r1 = cur.seek(B256::ZERO).expect("ok").expect("k1"); + assert_eq!(r1.0, k1); + assert_eq!(r1.1.nonce, 11); + + let r2 = cur.next().expect("ok").expect("k2"); + assert_eq!(r2.0, k2); + assert_eq!(r2.1.nonce, 22); + + assert!(cur.next().expect("ok").is_none()); +} + +/// Duplicate key in both current state and history — the merge should +/// yield it exactly once. +#[test] +fn account_cursor_deduplicates_key_in_both_cursors() { + let db = setup_db(); + let k = B256::from([0x55; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::() + .expect("c") + .upsert(k, &sample_account(99)) + .expect("upsert"); + + wtx.cursor_write::() + .expect("c") + .upsert( + HashedAccountShardedKey::new(k, u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(5u64, HashedAccountBeforeTx::new(k, Some(sample_account(50)))) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + // At block 4 → changeset at 5 gives nonce=50 + let mut cur = V2AccountCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 4, + false, + ); + + let r = cur.seek(B256::ZERO).expect("ok").expect("one result"); + assert_eq!(r.0, k); + assert_eq!(r.1.nonce, 50); + assert!(cur.next().expect("ok").is_none(), "no duplicates"); +} + +// ====================== Account Trie Cursor tests ====================== + +#[test] +fn account_trie_cursor_reads_current_state_when_no_history() { + let db = setup_db(); + let path = Nibbles::from_nibbles([0x0A]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_write::() + .expect("c") + .upsert(StoredNibbles(path), &n) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + let out = TrieCursor::seek_exact(&mut cur, path).expect("ok").expect("some"); + assert_eq!(out.0, path); + assert_eq!(out.1, n); +} + +#[test] +fn account_trie_cursor_resolves_old_node_from_changeset() { + let db = setup_db(); + let path = Nibbles::from_nibbles([0x0B]); + let old_node = node(); + let new_node = node2(); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state has new_node (applied at block 10) + wtx.cursor_write::() + .expect("c") + .upsert(StoredNibbles(path), &new_node) + .expect("upsert"); + + // History: modified at block 10 + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(path), u64::MAX), + &BlockNumberList::new_pre_sorted([10]), + ) + .expect("upsert"); + + // Changeset at block 10: old_node was the value before + let cs_entry = TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(path), + node: Some(old_node.clone()), + }; + wtx.cursor_dup_write::() + .expect("c") + .append_dup(10u64, cs_entry) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 9 (before modification at 10) + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 9, + false, + ); + + let out = TrieCursor::seek_exact(&mut cur, path).expect("ok").expect("some"); + assert_eq!(out.0, path); + assert_eq!(out.1, old_node, "should get old node from changeset"); +} + +#[test] +fn account_trie_cursor_seek_and_next_skip_dead_nodes() { + let db = setup_db(); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + let p3 = Nibbles::from_nibbles([0x03]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p1), &node()).expect("upsert"); + c.upsert(StoredNibbles(p2), &node()).expect("upsert"); + c.upsert(StoredNibbles(p3), &node()).expect("upsert"); + + // p2 was created at block 5, didn't exist before + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(p2), u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + + let cs_entry = TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p2), node: None }; + wtx.cursor_dup_write::() + .expect("c") + .append_dup(5u64, cs_entry) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 3 (before p2 was created) + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 3, + false, + ); + + // Seek p1 → should find p1 + let out = TrieCursor::seek(&mut cur, p1).expect("ok").expect("some"); + assert_eq!(out.0, p1); + + // Next → should skip p2 (didn't exist at block 3) and find p3 + let out = TrieCursor::next(&mut cur).expect("ok").expect("some"); + assert_eq!(out.0, p3, "should skip p2 which didn't exist at block 3"); +} + +#[test] +fn account_trie_cursor_seek_returns_gte() { + let db = setup_db(); + let p_a = Nibbles::from_nibbles([0x0A]); + let p_c = Nibbles::from_nibbles([0x0C]); + let p_b = Nibbles::from_nibbles([0x0B]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p_a), &node()).expect("upsert"); + c.upsert(StoredNibbles(p_c), &node()).expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + // Seek to 0x0B → should land on 0x0C (first ≥ 0x0B) + let out = TrieCursor::seek(&mut cur, p_b).expect("ok").expect("some"); + assert_eq!(out.0, p_c); +} + +#[test] +fn account_trie_cursor_empty_returns_none() { + let db = setup_db(); + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + assert!(TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").is_none()); + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +#[test] +fn account_trie_cursor_discovers_key_deleted_after_target_block() { + // Scenario from the design discussion: + // block 2 adds [a1, b1, c1] + // block 3 adds [d1, z1] and deletes a1 + // Query at block 2 → should see a1, b1, c1 (not d1 or z1) + let db = setup_db(); + let a1 = Nibbles::from_nibbles([0x0A, 0x01]); + let b1 = Nibbles::from_nibbles([0x0B, 0x01]); + let c1 = Nibbles::from_nibbles([0x0C, 0x01]); + let d1 = Nibbles::from_nibbles([0x0D, 0x01]); + let z1 = Nibbles::from_nibbles([0x0F, 0x01]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + + // Current state after block 3: {b1, c1, d1, z1} (a1 deleted) + c.upsert(StoredNibbles(b1), &n).expect("upsert"); + c.upsert(StoredNibbles(c1), &n).expect("upsert"); + c.upsert(StoredNibbles(d1), &n).expect("upsert"); + c.upsert(StoredNibbles(z1), &n).expect("upsert"); + + // History bitmaps + let mut hc = wtx.cursor_write::().expect("c"); + // a1 modified at blocks 2 and 3 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(a1), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 3]), + ) + .expect("upsert"); + // b1 modified at block 2 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(b1), u64::MAX), + &BlockNumberList::new_pre_sorted([2]), + ) + .expect("upsert"); + // c1 modified at block 2 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(c1), u64::MAX), + &BlockNumberList::new_pre_sorted([2]), + ) + .expect("upsert"); + // d1 modified at block 3 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(d1), u64::MAX), + &BlockNumberList::new_pre_sorted([3]), + ) + .expect("upsert"); + // z1 modified at block 3 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(z1), u64::MAX), + &BlockNumberList::new_pre_sorted([3]), + ) + .expect("upsert"); + + // Changesets + let mut csc = wtx.cursor_dup_write::().expect("c"); + + // Block 2 changesets: a1, b1, c1 didn't exist before + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(a1), node: None }) + .expect("append"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(b1), node: None }) + .expect("append"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(c1), node: None }) + .expect("append"); + + // Block 3 changesets: a1 existed (deleted), d1 and z1 didn't exist + csc.append_dup( + 3u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(a1), node: Some(n.clone()) }, + ) + .expect("append"); + csc.append_dup(3u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(d1), node: None }) + .expect("append"); + csc.append_dup(3u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(z1), node: None }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 2: a1 should be visible even though it's deleted + // from current state (deleted at block 3). + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 2, + false, + ); + + // seek(default) → a1 (discovered via history walk, resolved from changeset) + let out = TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").expect("should find a1"); + assert_eq!(out.0, a1, "a1 must be visible at block 2"); + assert_eq!(out.1, n); + + // next → b1 + let out = TrieCursor::next(&mut cur).expect("ok").expect("should find b1"); + assert_eq!(out.0, b1); + + // next → c1 + let out = TrieCursor::next(&mut cur).expect("ok").expect("should find c1"); + assert_eq!(out.0, c1); + + // next → None (d1 and z1 didn't exist at block 2) + let out = TrieCursor::next(&mut cur).expect("ok"); + assert!(out.is_none(), "d1 and z1 must NOT be visible at block 2"); +} + +#[test] +fn account_trie_cursor_deleted_key_only_in_history() { + // Key exists ONLY in history (not in current state), no other keys at all. + // Ensures the history-walk alone can produce results when current state is empty. + let db = setup_db(); + let p = Nibbles::from_nibbles([0x05]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + // Current state: empty (p was deleted at block 4) + + // History: [2, 4] + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(p), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 4]), + ) + .expect("upsert"); + + // Changeset block 2: p didn't exist before + let mut csc = wtx.cursor_dup_write::().expect("c"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p), node: None }) + .expect("append"); + // Changeset block 4: p had value n before deletion + csc.append_dup( + 4u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p), node: Some(n.clone()) }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 3: p should be visible (created at 2, deleted at 4) + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 3, + false, + ); + + let out = TrieCursor::seek(&mut cur, Nibbles::default()) + .expect("ok") + .expect("should find p at block 3"); + assert_eq!(out.0, p); + assert_eq!(out.1, n, "should resolve from changeset at block 4"); + + // next → None + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); + + // Also: query at block 1 → p didn't exist yet + let mut cur2 = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 1, + false, + ); + assert!( + TrieCursor::seek(&mut cur2, Nibbles::default()).expect("ok").is_none(), + "p should not exist at block 1" + ); +} + +#[test] +fn account_trie_cursor_seek_exact_on_deleted_key() { + // seek_exact on a key that is deleted from current state but alive at + // the target block. + let db = setup_db(); + let p = Nibbles::from_nibbles([0x0A]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + // Current state: empty (p deleted at block 10) + + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(p), u64::MAX), + &BlockNumberList::new_pre_sorted([5, 10]), + ) + .expect("upsert"); + + let mut csc = wtx.cursor_dup_write::().expect("c"); + // Block 5: created (old = None) + csc.append_dup(5u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p), node: None }) + .expect("append"); + // Block 10: deleted (old = n) + csc.append_dup( + 10u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p), node: Some(n.clone()) }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // seek_exact at block 8 → should find p + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 8, + false, + ); + let out = TrieCursor::seek_exact(&mut cur, p).expect("ok").expect("should find"); + assert_eq!(out.0, p); + assert_eq!(out.1, n); + + // seek_exact at block 3 → should NOT find p (created at 5) + let mut cur2 = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 3, + false, + ); + assert!(TrieCursor::seek_exact(&mut cur2, p).expect("ok").is_none()); +} + +#[test] +fn account_trie_cursor_current_tracks_last_yielded() { + // current() should return the last key yielded by seek/next. + let db = setup_db(); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p1), &n).expect("upsert"); + c.upsert(StoredNibbles(p2), &n).expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + // Before any seek, current is None + assert!(TrieCursor::current(&mut cur).expect("ok").is_none()); + + TrieCursor::seek(&mut cur, p1).expect("ok"); + assert_eq!(TrieCursor::current(&mut cur).expect("ok"), Some(p1)); + + TrieCursor::next(&mut cur).expect("ok"); + assert_eq!(TrieCursor::current(&mut cur).expect("ok"), Some(p2)); +} + +#[test] +fn account_trie_cursor_seek_exact_then_next() { + // After seek_exact, next() should return the key after the sought key. + let db = setup_db(); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + let p3 = Nibbles::from_nibbles([0x03]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p1), &n).expect("upsert"); + c.upsert(StoredNibbles(p2), &n).expect("upsert"); + c.upsert(StoredNibbles(p3), &n).expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + // seek_exact p2 + let out = TrieCursor::seek_exact(&mut cur, p2).expect("ok").expect("some"); + assert_eq!(out.0, p2); + + // next → p3 + let out = TrieCursor::next(&mut cur).expect("ok").expect("some"); + assert_eq!(out.0, p3); + + // next → None + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +#[test] +fn account_trie_cursor_seek_gte_skips_dead_landing() { + // seek() lands on a dead key (in current state but not alive at target + // block) and must skip forward to the next live key. + let db = setup_db(); + let p_a = Nibbles::from_nibbles([0x0A]); + let p_b = Nibbles::from_nibbles([0x0B]); + let p_c = Nibbles::from_nibbles([0x0C]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p_a), &node()).expect("upsert"); + c.upsert(StoredNibbles(p_b), &node()).expect("upsert"); + c.upsert(StoredNibbles(p_c), &node()).expect("upsert"); + + // p_b was created at block 10 + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(p_b), u64::MAX), + &BlockNumberList::new_pre_sorted([10]), + ) + .expect("upsert"); + + wtx.cursor_dup_write::() + .expect("c") + .append_dup( + 10u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p_b), node: None }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // At block 5, seek(p_b) → p_b is dead → should skip to p_c + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 5, + false, + ); + + let out = TrieCursor::seek(&mut cur, p_b).expect("ok").expect("some"); + assert_eq!(out.0, p_c, "should skip dead p_b and land on p_c"); +} + +#[test] +fn account_trie_cursor_all_keys_dead() { + // Every key in current state is dead at the target block, and no + // history-only keys exist → None. + let db = setup_db(); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p1), &node()).expect("upsert"); + c.upsert(StoredNibbles(p2), &node()).expect("upsert"); + + let mut hc = wtx.cursor_write::().expect("c"); + // Both created at block 5 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(p1), u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(p2), u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + + let mut csc = wtx.cursor_dup_write::().expect("c"); + csc.append_dup(5u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p1), node: None }) + .expect("append"); + csc.append_dup(5u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p2), node: None }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // At block 3 → both dead + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 3, + false, + ); + + assert!(TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").is_none()); +} + +#[test] +fn account_trie_cursor_interleaved_current_and_history_keys() { + // Current state has {b, d}. History has {a, c, e} (all deleted after + // target block). The merge should yield a, b, c, d, e in order. + let db = setup_db(); + let a = Nibbles::from_nibbles([0x01]); + let b = Nibbles::from_nibbles([0x02]); + let c = Nibbles::from_nibbles([0x03]); + let d = Nibbles::from_nibbles([0x04]); + let e = Nibbles::from_nibbles([0x05]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut cs = wtx.cursor_write::().expect("c"); + // Current state: {b, d} + cs.upsert(StoredNibbles(b), &n).expect("upsert"); + cs.upsert(StoredNibbles(d), &n).expect("upsert"); + + let mut hc = wtx.cursor_write::().expect("c"); + // a: created block 2, deleted block 10 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(a), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 10]), + ) + .expect("upsert"); + // b: created block 2 (stays in current state) + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(b), u64::MAX), + &BlockNumberList::new_pre_sorted([2]), + ) + .expect("upsert"); + // c: created block 2, deleted block 10 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(c), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 10]), + ) + .expect("upsert"); + // d: created block 2 (stays) + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(d), u64::MAX), + &BlockNumberList::new_pre_sorted([2]), + ) + .expect("upsert"); + // e: created block 2, deleted block 10 + hc.upsert( + AccountTrieShardedKey::new(StoredNibbles(e), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 10]), + ) + .expect("upsert"); + + let mut csc = wtx.cursor_dup_write::().expect("c"); + // Block 2: all created (old = None) + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(a), node: None }) + .expect("append"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(b), node: None }) + .expect("append"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(c), node: None }) + .expect("append"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(d), node: None }) + .expect("append"); + csc.append_dup(2u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(e), node: None }) + .expect("append"); + // Block 10: a, c, e deleted (old = n) + csc.append_dup( + 10u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(a), node: Some(n.clone()) }, + ) + .expect("append"); + csc.append_dup( + 10u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(c), node: Some(n.clone()) }, + ) + .expect("append"); + csc.append_dup( + 10u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(e), node: Some(n.clone()) }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // At block 5: a, b, c, d, e all alive (a, c, e via changeset at 10) + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 5, + false, + ); + + let out = TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").expect("a"); + assert_eq!(out.0, a, "first key should be a (from history)"); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("b"); + assert_eq!(out.0, b, "second key should be b (from current state)"); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("c"); + assert_eq!(out.0, c, "third key should be c (from history)"); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("d"); + assert_eq!(out.0, d, "fourth key should be d (from current state)"); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("e"); + assert_eq!(out.0, e, "fifth key should be e (from history)"); + + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +#[test] +fn account_trie_cursor_duplicate_key_in_both_cursors() { + // Key exists in BOTH current state and history. The merge should NOT + // yield it twice. + let db = setup_db(); + let p = Nibbles::from_nibbles([0x0A]); + let n = node(); + let n2 = node2(); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: p -> n2 (updated at block 10) + wtx.cursor_write::() + .expect("c") + .upsert(StoredNibbles(p), &n2) + .expect("upsert"); + + // History: modified at block 10 + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(p), u64::MAX), + &BlockNumberList::new_pre_sorted([10]), + ) + .expect("upsert"); + + wtx.cursor_dup_write::() + .expect("c") + .append_dup( + 10u64, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p), node: Some(n.clone()) }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // At block 8: resolve from changeset at 10 → old value n + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 8, + false, + ); + + let out = TrieCursor::seek(&mut cur, p).expect("ok").expect("should find"); + assert_eq!(out.0, p); + assert_eq!(out.1, n, "should get old value from changeset"); + + // next → None (should NOT yield p again) + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +#[test] +fn account_trie_cursor_query_at_latest_block() { + // When max_block_number == u64::MAX, everything reads from current + // state — even keys with history. This exercises the + // FromCurrentState path in find_source. + let db = setup_db(); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + let n2 = node2(); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_write::().expect("c"); + c.upsert(StoredNibbles(p1), &node()).expect("upsert"); + c.upsert(StoredNibbles(p2), &n2).expect("upsert"); + + // p2 has history at block 5 + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(p2), u64::MAX), + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + + wtx.cursor_dup_write::() + .expect("c") + .append_dup(5u64, TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p2), node: None }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + u64::MAX, + true, + ); + + let out = TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").expect("p1"); + assert_eq!(out.0, p1); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("p2"); + assert_eq!(out.0, p2); + assert_eq!(out.1, n2, "should read current state value"); + + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +// ——— Storage Trie Cursor: dual-cursor merge tests ——— + +#[test] +fn storage_trie_cursor_discovers_deleted_key() { + // Same scenario as account trie deleted-key test, but for storage trie. + let db = setup_db(); + let addr = B256::from([0xAA; 32]); + let a1 = Nibbles::from_nibbles([0x0A, 0x01]); + let b1 = Nibbles::from_nibbles([0x0B, 0x01]); + let c1 = Nibbles::from_nibbles([0x0C, 0x01]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: {b1, c1} (a1 deleted at block 5) + let mut sc = wtx.cursor_dup_write::().expect("c"); + sc.upsert(addr, &StorageTrieEntry { nibbles: StoredNibblesSubKey(b1), node: n.clone() }) + .expect("upsert"); + sc.upsert(addr, &StorageTrieEntry { nibbles: StoredNibblesSubKey(c1), node: n.clone() }) + .expect("upsert"); + + let mut hc = wtx.cursor_write::().expect("c"); + // a1: created at block 2, deleted at block 5 + hc.upsert( + StorageTrieShardedKey::new(addr, StoredNibbles(a1), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 5]), + ) + .expect("upsert"); + // b1: created at block 2 + hc.upsert( + StorageTrieShardedKey::new(addr, StoredNibbles(b1), u64::MAX), + &BlockNumberList::new_pre_sorted([2]), + ) + .expect("upsert"); + // c1: created at block 2 + hc.upsert( + StorageTrieShardedKey::new(addr, StoredNibbles(c1), u64::MAX), + &BlockNumberList::new_pre_sorted([2]), + ) + .expect("upsert"); + + let mut csc = wtx.cursor_dup_write::().expect("c"); + // Block 2: all created + let cs_key2 = BlockNumberHashedAddress((2u64, addr)); + csc.append_dup( + cs_key2, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(a1), node: None }, + ) + .expect("append"); + csc.append_dup( + cs_key2, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(b1), node: None }, + ) + .expect("append"); + csc.append_dup( + cs_key2, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(c1), node: None }, + ) + .expect("append"); + // Block 5: a1 deleted (old = n) + let cs_key5 = BlockNumberHashedAddress((5u64, addr)); + csc.append_dup( + cs_key5, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(a1), node: Some(n) }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 3: a1 should be visible + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + 3, + false, + ); + + let out = TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").expect("should find a1"); + assert_eq!(out.0, a1, "a1 must be visible at block 3"); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("b1"); + assert_eq!(out.0, b1); + + let out = TrieCursor::next(&mut cur).expect("ok").expect("c1"); + assert_eq!(out.0, c1); + + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +#[test] +fn storage_trie_cursor_deleted_key_does_not_cross_address() { + // Deleted history key from addr_b must NOT appear when walking addr_a. + let db = setup_db(); + let addr_a = B256::from([0x11; 32]); + let addr_b = B256::from([0x22; 32]); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: addr_a has {p1}, addr_b is empty (p2 deleted) + let mut sc = wtx.cursor_dup_write::().expect("c"); + sc.upsert(addr_a, &StorageTrieEntry { nibbles: StoredNibblesSubKey(p1), node: n.clone() }) + .expect("upsert"); + + // addr_b: p2 history (created block 2, deleted block 5) + wtx.cursor_write::() + .expect("c") + .upsert( + StorageTrieShardedKey::new(addr_b, StoredNibbles(p2), u64::MAX), + &BlockNumberList::new_pre_sorted([2, 5]), + ) + .expect("upsert"); + + let mut csc = wtx.cursor_dup_write::().expect("c"); + csc.append_dup( + BlockNumberHashedAddress((2u64, addr_b)), + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p2), node: None }, + ) + .expect("append"); + csc.append_dup( + BlockNumberHashedAddress((5u64, addr_b)), + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(p2), node: Some(n) }, + ) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Walk addr_a at block 3 → only p1 + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr_a, + 3, + true, + ); + + let out = TrieCursor::seek(&mut cur, Nibbles::default()).expect("ok").expect("p1"); + assert_eq!(out.0, p1); + assert!( + TrieCursor::next(&mut cur).expect("ok").is_none(), + "must not leak addr_b's history into addr_a" + ); +} + +#[test] +fn storage_trie_cursor_set_hashed_address_resets_merge_state() { + // After set_hashed_address, the merge state must be reset so seek/next + // operate correctly on the new address. + let db = setup_db(); + let addr_a = B256::from([0x55; 32]); + let addr_b = B256::from([0x66; 32]); + let p1 = Nibbles::from_nibbles([0x01]); + let p2 = Nibbles::from_nibbles([0x02]); + let n = node(); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_dup_write::().expect("c"); + c.upsert(addr_a, &StorageTrieEntry { nibbles: StoredNibblesSubKey(p1), node: n.clone() }) + .expect("upsert"); + c.upsert(addr_b, &StorageTrieEntry { nibbles: StoredNibblesSubKey(p2), node: n }) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr_a, + u64::MAX, + true, + ); + + // Seek on addr_a + let out = TrieCursor::seek(&mut cur, p1).expect("ok").expect("p1"); + assert_eq!(out.0, p1); + + // Switch to addr_b + cur.set_hashed_address(addr_b); + let out = TrieCursor::seek(&mut cur, p2).expect("ok").expect("p2"); + assert_eq!(out.0, p2); + + assert!(TrieCursor::next(&mut cur).expect("ok").is_none()); +} + +// ====================== Storage Cursor tests ====================== + +#[test] +fn storage_cursor_reads_current_state_when_no_history() { + let db = setup_db(); + let addr = B256::from([0xAA; 32]); + let slot = B256::from([0x01; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_dup_write::() + .expect("c") + .upsert(addr, &StorageEntry { key: slot, value: U256::from(42u64) }) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + u64::MAX, + true, + ); + + let result = cur.seek(slot).expect("ok").expect("should find"); + assert_eq!(result, (slot, U256::from(42u64))); +} + +#[test] +fn storage_cursor_resolves_from_changeset() { + let db = setup_db(); + let addr = B256::from([0xAA; 32]); + let slot = B256::from([0x01; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: value=1000 + wtx.cursor_dup_write::() + .expect("c") + .upsert(addr, &StorageEntry { key: slot, value: U256::from(1000u64) }) + .expect("upsert"); + + // History: modified at block 8 + wtx.cursor_write::() + .expect("c") + .upsert( + HashedStorageShardedKey { + hashed_address: addr, + sharded_key: ShardedKey::new(slot, u64::MAX), + }, + &BlockNumberList::new_pre_sorted([8]), + ) + .expect("upsert"); + + // Changeset at block 8: old value was 500 + let cs_key = BlockNumberHashedAddress((8u64, addr)); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(cs_key, StorageEntry { key: slot, value: U256::from(500u64) }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 7 (before modification at 8) + let mut cur = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + 7, + false, + ); + + let result = cur.seek(slot).expect("ok").expect("should find"); + assert_eq!(result.0, slot); + assert_eq!(result.1, U256::from(500u64), "should get changeset value"); +} + +#[test] +fn storage_cursor_is_storage_empty() { + let db = setup_db(); + let addr_with = B256::from([0xBB; 32]); + let addr_without = B256::from([0xCC; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_dup_write::() + .expect("c") + .upsert( + addr_with, + &StorageEntry { key: B256::from([0x01; 32]), value: U256::from(1u64) }, + ) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + let mut cur_with = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr_with, + u64::MAX, + true, + ); + assert!(!cur_with.is_storage_empty().expect("ok")); + + let mut cur_without = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr_without, + u64::MAX, + true, + ); + assert!(cur_without.is_storage_empty().expect("ok")); +} + +/// Storage slot was zeroed (deleted from `V2HashedStorages`) after the target +/// block. The history walk must discover it. +#[test] +fn storage_cursor_discovers_slot_deleted_after_target_block() { + let db = setup_db(); + let addr = B256::from([0xAA; 32]); + let s1 = B256::from([0x01; 32]); + let s2 = B256::from([0x02; 32]); // deleted after target + let s3 = B256::from([0x03; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_dup_write::().expect("c"); + // s1 and s3 exist; s2 was zeroed at block 10 + c.upsert(addr, &StorageEntry { key: s1, value: U256::from(100u64) }).expect("upsert"); + c.upsert(addr, &StorageEntry { key: s3, value: U256::from(300u64) }).expect("upsert"); + + // s2 history: modified at [5, 10] + wtx.cursor_write::() + .expect("c") + .upsert( + HashedStorageShardedKey { + hashed_address: addr, + sharded_key: ShardedKey::new(s2, u64::MAX), + }, + &BlockNumberList::new_pre_sorted([5, 10]), + ) + .expect("upsert"); + + // Changeset at block 10: s2 = 200 before block 10 + let cs_key = BlockNumberHashedAddress((10u64, addr)); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(cs_key, StorageEntry { key: s2, value: U256::from(200u64) }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + 9, + false, + ); + + let r1 = cur.seek(B256::ZERO).expect("ok").expect("s1"); + assert_eq!(r1.0, s1); + assert_eq!(r1.1, U256::from(100u64)); + + let r2 = cur.next().expect("ok").expect("s2 from history"); + assert_eq!(r2.0, s2); + assert_eq!(r2.1, U256::from(200u64)); + + let r3 = cur.next().expect("ok").expect("s3"); + assert_eq!(r3.0, s3); + assert_eq!(r3.1, U256::from(300u64)); + + assert!(cur.next().expect("ok").is_none()); +} + +/// `is_storage_empty` must return false when storage existed at the target +/// block but has since been wiped from current state. +#[test] +fn storage_cursor_is_storage_empty_false_for_historical_only_slots() { + let db = setup_db(); + let addr = B256::from([0xDD; 32]); + let slot = B256::from([0x01; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + // No current state for addr — all storage was wiped at block 10. + + // History: slot modified at [5, 10] + wtx.cursor_write::() + .expect("c") + .upsert( + HashedStorageShardedKey { + hashed_address: addr, + sharded_key: ShardedKey::new(slot, u64::MAX), + }, + &BlockNumberList::new_pre_sorted([5, 10]), + ) + .expect("upsert"); + + // Changeset at 10: value=42 before block 10 + let cs_key = BlockNumberHashedAddress((10u64, addr)); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(cs_key, StorageEntry { key: slot, value: U256::from(42u64) }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + 9, + false, + ); + + assert!(!cur.is_storage_empty().expect("ok"), "should find historical slot"); +} + +/// History-only storage key must NOT cross into a different address. +#[test] +fn storage_cursor_deleted_slot_does_not_cross_address() { + let db = setup_db(); + let addr1 = B256::from([0x01; 32]); + let addr2 = B256::from([0x02; 32]); + let slot = B256::from([0x0A; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + // No current storage for addr1. + // addr2 has a history entry for the same slot. + wtx.cursor_write::() + .expect("c") + .upsert( + HashedStorageShardedKey { + hashed_address: addr2, + sharded_key: ShardedKey::new(slot, u64::MAX), + }, + &BlockNumberList::new_pre_sorted([5]), + ) + .expect("upsert"); + + let cs_key = BlockNumberHashedAddress((5u64, addr2)); + wtx.cursor_dup_write::() + .expect("c") + .append_dup(cs_key, StorageEntry { key: slot, value: U256::from(99u64) }) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr1, + 4, + true, + ); + + // addr1 has no storage — history walk finds addr2's entry but must filter it out. + assert!(cur.seek(B256::ZERO).expect("ok").is_none()); +} + +/// `set_hashed_address` resets merge state so the cursor works correctly +/// for the new address. +#[test] +fn storage_cursor_set_hashed_address_resets_merge_state() { + let db = setup_db(); + let addr1 = B256::from([0x01; 32]); + let addr2 = B256::from([0x02; 32]); + let slot = B256::from([0x0A; 32]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_dup_write::().expect("c"); + c.upsert(addr1, &StorageEntry { key: slot, value: U256::from(11u64) }).expect("upsert"); + c.upsert(addr2, &StorageEntry { key: slot, value: U256::from(22u64) }).expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr1, + u64::MAX, + true, + ); + + let r1 = cur.seek(B256::ZERO).expect("ok").expect("addr1 slot"); + assert_eq!(r1.1, U256::from(11u64)); + + cur.set_hashed_address(addr2); + let r2 = cur.seek(B256::ZERO).expect("ok").expect("addr2 slot"); + assert_eq!(r2.1, U256::from(22u64)); +} + +// Regression: root trie path [] with child path [0] history. +// Before the length-prefixed encoding fix, the V2AccountTrieCursor would return +// the current-state root node instead of the historical one. +#[test] +fn account_trie_cursor_root_path_resolves_historical_with_child_paths() { + let db = setup_db(); + let root_path = Nibbles::default(); + let child_path = Nibbles::from_nibbles([0]); + + let root_node_at_block5 = + BranchNodeCompact::new(0b11, 0, 0, vec![], Some(B256::repeat_byte(0x55))); + let root_node_at_block10 = + BranchNodeCompact::new(0b111, 0, 0, vec![], Some(B256::repeat_byte(0xAA))); + let child_node_at_block10 = + BranchNodeCompact::new(0b101, 0, 0, vec![], Some(B256::repeat_byte(0xBB))); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state: root has block 10's node, child has block 10's node + wtx.cursor_write::() + .expect("c") + .upsert(StoredNibbles(root_path), &root_node_at_block10) + .expect("upsert root"); + wtx.cursor_write::() + .expect("c") + .upsert(StoredNibbles(child_path), &child_node_at_block10) + .expect("upsert child"); + + // Changeset at block 10: root had block5's node before block 10 + wtx.cursor_dup_write::() + .expect("c") + .append_dup( + 10, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(root_path), + node: Some(root_node_at_block5.clone()), + }, + ) + .expect("append root cs"); + + // History: root modified at block 10 + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(root_path), u64::MAX), + &BlockNumberList::new_pre_sorted([10]), + ) + .expect("upsert root history"); + + // History: child [0] modified at block 10 + wtx.cursor_write::() + .expect("c") + .upsert( + AccountTrieShardedKey::new(StoredNibbles(child_path), u64::MAX), + &BlockNumberList::new_pre_sorted([10]), + ) + .expect("upsert child history"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2AccountTrieCursor::new( + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + 8, // max_block_number: query at block 8 (before block 10's change) + false, // is_latest = false (historical query) + ); + + // seek_exact on root path should return the historical value (block 5's node) + let out = TrieCursor::seek_exact(&mut cur, root_path) + .expect("ok") + .expect("root should exist at block 8"); + assert_eq!( + out.1, root_node_at_block5, + "Root path should return the historical node from changeset, not current state" + ); +} + +// ====================== Storage Trie Cursor tests ====================== + +#[test] +fn storage_trie_cursor_reads_current_state_when_no_history() { + let db = setup_db(); + let addr = B256::from([0x55; 32]); + let path = Nibbles::from_nibbles([0x0D]); + + { + let wtx = db.tx_mut().expect("rw tx"); + wtx.cursor_dup_write::() + .expect("c") + .upsert(addr, &StorageTrieEntry { nibbles: StoredNibblesSubKey(path), node: node() }) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + u64::MAX, + true, + ); + + let out = TrieCursor::seek_exact(&mut cur, path).expect("ok").expect("some"); + assert_eq!(out.0, path); +} + +#[test] +fn storage_trie_cursor_resolves_from_changeset() { + let db = setup_db(); + let addr = B256::from([0x55; 32]); + let path = Nibbles::from_nibbles([0x0D]); + let old_node = node(); + let new_node = node2(); + + { + let wtx = db.tx_mut().expect("rw tx"); + + // Current state + wtx.cursor_dup_write::() + .expect("c") + .upsert(addr, &StorageTrieEntry { nibbles: StoredNibblesSubKey(path), node: new_node }) + .expect("upsert"); + + // History: modified at block 6 + wtx.cursor_write::() + .expect("c") + .upsert( + StorageTrieShardedKey::new(addr, StoredNibbles(path), u64::MAX), + &BlockNumberList::new_pre_sorted([6]), + ) + .expect("upsert"); + + // Changeset at block 6: old node + let cs_key = BlockNumberHashedAddress((6u64, addr)); + let cs_entry = TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(path), + node: Some(old_node.clone()), + }; + wtx.cursor_dup_write::() + .expect("c") + .append_dup(cs_key, cs_entry) + .expect("append"); + + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + + // Query at block 5 (before modification at 6) + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr, + 5, + false, + ); + + let out = TrieCursor::seek_exact(&mut cur, path).expect("ok").expect("some"); + assert_eq!(out.0, path); + assert_eq!(out.1, old_node, "should get old node from changeset"); +} + +#[test] +fn storage_trie_cursor_respects_address_boundary() { + let db = setup_db(); + let addr_a = B256::from([0x33; 32]); + let addr_b = B256::from([0x44; 32]); + let p1 = Nibbles::from_nibbles([0x05]); + let p2 = Nibbles::from_nibbles([0x06]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_dup_write::().expect("c"); + c.upsert(addr_a, &StorageTrieEntry { nibbles: StoredNibblesSubKey(p1), node: node() }) + .expect("upsert"); + c.upsert(addr_b, &StorageTrieEntry { nibbles: StoredNibblesSubKey(p2), node: node() }) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr_a, + u64::MAX, + true, + ); + + let out = TrieCursor::seek(&mut cur, p1).expect("ok").expect("some"); + assert_eq!(out.0, p1); + + // next() should return None — crossed address boundary + let out = TrieCursor::next(&mut cur).expect("ok"); + assert!(out.is_none(), "must not cross address boundary (DupSort)"); +} + +#[test] +fn storage_trie_cursor_set_hashed_address() { + let db = setup_db(); + let addr_a = B256::from([0x55; 32]); + let addr_b = B256::from([0x66; 32]); + let path = Nibbles::from_nibbles([0x01]); + + { + let wtx = db.tx_mut().expect("rw tx"); + let mut c = wtx.cursor_dup_write::().expect("c"); + c.upsert(addr_a, &StorageTrieEntry { nibbles: StoredNibblesSubKey(path), node: node() }) + .expect("upsert"); + c.upsert(addr_b, &StorageTrieEntry { nibbles: StoredNibblesSubKey(path), node: node() }) + .expect("upsert"); + wtx.commit().expect("commit"); + } + + let tx = db.tx().expect("ro tx"); + let mut cur = V2StorageTrieCursor::new( + tx.cursor_dup_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_read::().expect("c"), + tx.cursor_dup_read::().expect("c"), + addr_a, + u64::MAX, + true, + ); + + assert!(TrieCursor::seek_exact(&mut cur, path).expect("ok").is_some()); + cur.set_hashed_address(addr_b); + assert!(TrieCursor::seek_exact(&mut cur, path).expect("ok").is_some()); +} diff --git a/rust/op-reth/crates/trie/src/db/store_v2/init.rs b/rust/op-reth/crates/trie/src/db/store_v2/init.rs new file mode 100644 index 00000000000..88da7e5d0d6 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/init.rs @@ -0,0 +1,155 @@ +//! [`OpProofsInitProvider`] implementation for [`MdbxProofsProviderV2`]. + +use super::MdbxProofsProviderV2; +use crate::{ + OpProofsStorageError, OpProofsStorageResult, + api::{InitialStateAnchor, InitialStateStatus, OpProofsInitProvider}, + db::{ + HashedStorageKey, ProofWindowKey, StorageTrieKey, V2ProofWindow, + models::{V2AccountsTrie, V2HashedAccounts, V2HashedStorages, V2StoragesTrie}, + }, +}; +use alloy_eips::BlockNumHash; +use alloy_primitives::{B256, U256}; +use reth_db::{ + cursor::{DbCursorRO, DbCursorRW, DbDupCursorRW}, + transaction::{DbTx, DbTxMut}, +}; +use reth_primitives_traits::{Account, StorageEntry}; +use reth_trie::{BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}; +use std::fmt::Debug; + +impl OpProofsInitProvider + for MdbxProofsProviderV2 +{ + fn initial_state_anchor(&self) -> OpProofsStorageResult { + let Some(block) = self.get_initial_state_anchor_inner()? else { + return Ok(InitialStateAnchor::default()); + }; + + let completed = self.get_block_number_hash_inner(ProofWindowKey::EarliestBlock)?.is_some(); + + // Scan the last entry in each current-state table to determine resume + // keys. This allows multi-step initialization: if the process is + // interrupted, the next run picks up where it left off. + let latest_hashed_account_key = + self.tx.cursor_read::()?.last()?.map(|(k, _)| k); + + let latest_hashed_storage_key = self + .tx + .cursor_read::()? + .last()? + .map(|(addr, entry)| HashedStorageKey::new(addr, entry.key)); + + let latest_account_trie_key = + self.tx.cursor_read::()?.last()?.map(|(k, _)| k); + + let latest_storage_trie_key = self + .tx + .cursor_read::()? + .last()? + .map(|(addr, entry)| StorageTrieKey::new(addr, StoredNibbles(entry.nibbles.0))); + + Ok(InitialStateAnchor { + block: Some(block), + status: if completed { + InitialStateStatus::Completed + } else { + InitialStateStatus::InProgress + }, + latest_account_trie_key, + latest_storage_trie_key, + latest_hashed_account_key, + latest_hashed_storage_key, + }) + } + + fn set_initial_state_anchor(&self, anchor: BlockNumHash) -> OpProofsStorageResult<()> { + let mut cur = self.tx.cursor_write::()?; + cur.insert(ProofWindowKey::InitialStateAnchor, &anchor.into())?; + Ok(()) + } + + fn store_account_branches( + &self, + account_nodes: Vec<(Nibbles, Option)>, + ) -> OpProofsStorageResult<()> { + if account_nodes.is_empty() { + return Ok(()); + } + + let mut cursor = self.tx.cursor_write::()?; + for (nibbles, maybe_node) in account_nodes { + if let Some(node) = maybe_node { + cursor.upsert(StoredNibbles(nibbles), &node)?; + } + } + Ok(()) + } + + fn store_storage_branches( + &self, + hashed_address: B256, + storage_nodes: Vec<(Nibbles, Option)>, + ) -> OpProofsStorageResult<()> { + if storage_nodes.is_empty() { + return Ok(()); + } + + let mut cursor = self.tx.cursor_dup_write::()?; + for (nibbles, maybe_node) in storage_nodes { + if let Some(node) = maybe_node { + cursor.append_dup( + hashed_address, + StorageTrieEntry { nibbles: StoredNibblesSubKey(nibbles), node }, + )?; + } + } + Ok(()) + } + + fn store_hashed_accounts( + &self, + accounts: Vec<(B256, Option)>, + ) -> OpProofsStorageResult<()> { + if accounts.is_empty() { + return Ok(()); + } + + let mut cursor = self.tx.cursor_write::()?; + for (hashed_address, maybe_account) in accounts { + if let Some(account) = maybe_account { + cursor.append(hashed_address, &account)?; + } + } + Ok(()) + } + + fn store_hashed_storages( + &self, + hashed_address: B256, + storages: Vec<(B256, U256)>, + ) -> OpProofsStorageResult<()> { + if storages.is_empty() { + return Ok(()); + } + + let mut cursor = self.tx.cursor_dup_write::()?; + for (storage_key, value) in storages { + cursor.append_dup(hashed_address, StorageEntry { key: storage_key, value })?; + } + Ok(()) + } + + fn commit_initial_state(&self) -> OpProofsStorageResult { + let anchor = + self.get_initial_state_anchor_inner()?.ok_or(OpProofsStorageError::NoBlocksFound)?; + self.set_earliest_block_number_inner(anchor.number, anchor.hash)?; + Ok(anchor) + } + + fn commit(self) -> OpProofsStorageResult<()> { + self.tx.commit()?; + Ok(()) + } +} diff --git a/rust/op-reth/crates/trie/src/db/store_v2/metrics.rs b/rust/op-reth/crates/trie/src/db/store_v2/metrics.rs new file mode 100644 index 00000000000..85d5a06b674 --- /dev/null +++ b/rust/op-reth/crates/trie/src/db/store_v2/metrics.rs @@ -0,0 +1,93 @@ +//! [`DatabaseMetrics`](reth_db::database_metrics::DatabaseMetrics) implementation for +//! [`MdbxProofsStorageV2`]. +//! +//! Reports per-table size, page counts, and entry counts. + +use super::{MdbxProofsStorageV2, Tables}; +use metrics::{Label, gauge}; +use reth_db::Database; + +impl reth_db::database_metrics::DatabaseMetrics for MdbxProofsStorageV2 { + fn report_metrics(&self) { + for (name, value, labels) in self.gauge_metrics() { + gauge!(name, labels).set(value); + } + } + + fn gauge_metrics(&self) -> Vec<(&'static str, f64, Vec