Skip to content

WIP arc#926

Draft
SozinM wants to merge 5 commits into
msozin/reth11.3from
msozin/ark
Draft

WIP arc#926
SozinM wants to merge 5 commits into
msozin/reth11.3from
msozin/ark

Conversation

@SozinM

@SozinM SozinM commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

📝 Summary

💡 Motivation and Context


✅ I have completed the following steps:

  • Run make lint
  • Run make test
  • Added tests (if applicable)

SozinM and others added 5 commits June 18, 2026 18:48
…ing rules

- Unify reth git deps with arc-node (tag v1.11.3, same commit as previous rev pin)
- New rbuilder 'arc' feature pulling arc-evm/arc-execution-config/arc-precompiles
- chain module: cfg-switched ChainSpec/EvmConfig/DbNodeTypes/chain parsing
- ArcCachedEvmFactory (no precompile cache: arc precompiles are stateful)
- arc_support: ProtocolConfig gas limit, EIP-2935-only pre-block calls,
  post-block SystemAccounting gas values write + extra_data base fee encoding
- finalize: chain-specific requests/extra_data handling
 asd
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- UnfinishedBlockBuildingSink(Factory) traits; MEV-Boost pipeline implements them
- SlotSource enum: MevBoost generator or external channel (engine API driver)
- EnginePayloadSink: finalizes best blocks into EthBuiltPayload via a
  watch-cell registry keyed by engine PayloadId (for engine_getPayload)
- TimingsConfig::arc() for sub-second engine-driven slots
- StateProviderFactory now requires Debug (needed for boxed sink factories)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New binary launching ArcNode with the payload service wrapped by
RbuilderPayloadServiceBuilder:
- engine FCU payload jobs are forwarded to rbuilder as building slots
  (ProtocolConfig gas limit computed on parent state)
- engine_getPayload resolves with rbuilder's best finalized block,
  falling back to the stock Arc payload builder for liveness
- rbuilder consumes the node's txpool and provider directly (no relays,
  no bidding)
- docs/ARC.md + example config

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- arc_support tests: named chain parsing, base fee decoded from parent
  extra_data + exact ProtocolConfig gas limit, EIP-7708 transfer log
  emitted through the rbuilder EVM factory
- bundle refund/simulation tests encode Ethereum SELFDESTRUCT/refund
  semantics; ignored under the arc feature with reason
- test fixtures wrap chain specs via chain::chain_spec_from_inner so the
  test build compiles for both flavors

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lding

- rbuilder now uses the node's chain spec instead of the config 'chain'
  value (a mismatch made every order fail simulation with InvalidChainId)
- log failed order simulations (trace) and non-critically discarded
  engine-payload blocks (debug) — both were previously silent
- sim-path regression test executing a signed tx through the ctx EVM

Verified end-to-end against arc-localdev driving the engine API like
Malachite: FCU(attrs) -> rbuilder builds (mempool txs + payout) ->
getPayload returns rbuilder's block -> newPayload validates it VALID
(arc executor re-checks state root, ADR-004 extra_data base fee,
SystemAccounting gas values) -> head advances to rbuilder's block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@SozinM SozinM changed the base branch from develop to msozin/reth11.3 June 18, 2026 15:08
@claude

claude Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @SozinM's task in 3m 40s —— View job


PR Review: WIP arc

Reviewed the diff against the base branch. The chain-abstraction approach (crates/rbuilder/src/chain.rs compile-time swap, #[cfg(feature = "arc")] gating) is clean and keeps the Ethereum path untouched. Comments below are the issues worth attention; I posted 3 inline.

Blocking / high priority

  • Workspace no longer builds without an external arc-node checkout. The arc deps in Cargo.toml (L194–201) are relative path deps into ../../arc-node/, and crates/arc-rbuilder is added to default-members (L31). A fresh clone running cargo check / make lint / make test will fail for any contributor or CI runner without that sibling checkout. The arc feature itself is correctly optional — but default-members undoes that isolation. Recommend dropping arc-rbuilder from default-members (keep it in members) and/or pinning git revs instead of relative paths. (inline)

Correctness to verify

  • Empty-requests hash on Prague-active Arc blocks (building/mod.rs L955–959): the arc branch produces Some(Requests::default()), which emits a requests_hash in the header. If arc-node's assembler leaves it None, headers won't be byte-identical and blocks get rejected. Worth a regression test pinning this field. (inline)
  • expected_block_gas_limit / post_block_call swallow ProtocolConfig read errors (arc_support.rs) and fall back to defaults. This mirrors arc-node, but a divergence here silently produces a block with the wrong gas limit / base fee that the node will reject rather than fail loudly. Acceptable if intentional; flagging for awareness.

Performance

  • Sink mutex held across finalize_block (engine_payload_sink.rs L136–163): the lock spans state-root computation, so multiple builder threads serialize on finalization. For the sub-second TimingsConfig::arc() path this caps finalization to one block at a time. Consider finalizing outside the lock and re-checking best_value on commit. (inline)

Minor

  • block_output/mod.rs: pub mod engine_payload_sink; breaks the alphabetical module ordering (inserted before bidding_service_interface).
  • EnginePayloadSink ignores built_block_cache (cross-builder coordination cache) — fine if Arc builders don't use it, but worth a one-line note.
  • PR checklist (make lint, make test, tests) is unchecked — given the default-members issue above, please confirm these pass on a clean checkout.

Note: I confirmed gas_used passed to post_block_call includes the payout tx (payout is inserted in finalize_block_execution before partial_block.finalizeprocess_requests), and that merge_transitions runs after the post-block state write — both correct. Passing the full true_block_value as the payout value is consistent with the existing no_competition_bid pattern.

Comment thread Cargo.toml
Comment on lines +194 to +201
arc-evm = { path = "../../arc-node/crates/evm" }
arc-execution-config = { path = "../../arc-node/crates/execution-config" }
arc-precompiles = { path = "../../arc-node/crates/precompiles" }
arc-evm-node = { path = "../../arc-node/crates/evm-node" }
arc-execution-payload = { path = "../../arc-node/crates/execution-payload" }
arc-execution-txpool = { path = "../../arc-node/crates/execution-txpool" }
arc-shared = { path = "../../arc-node/crates/shared" }
arc-execution-validation = { path = "../../arc-node/crates/execution-validation" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Workspace becomes unbuildable without an external checkout. These are path deps into a sibling ../../arc-node checkout, and crates/arc-rbuilder (which depends on them unconditionally) is in default-members (line 31). That means a plain cargo build / cargo check / make lint / make test at the repo root will fail for anyone — and CI — that doesn't have arc-node checked out at exactly that relative path. The arc feature on the rbuilder crate is properly optional, but adding arc-rbuilder to default-members defeats that isolation.

Consider one of:

  • Remove arc-rbuilder from default-members (keep it in members), so default builds skip it.
  • Gate the whole arc workspace behind an env/profile, or use git deps with a pinned rev instead of relative path deps.

Either way, please confirm cargo check on a fresh clone (no arc-node sibling) still succeeds before merge.

Comment on lines +136 to +163
let mut state = self.state.lock();
let true_block_value = block.true_block_value;
if state
.best_value
.is_some_and(|best| true_block_value <= best)
{
return;
}
let builder_name = block.block().builder_name().to_string();
let mut helper = block.into_building_helper();
let result = match helper.finalize_block(
&mut state.local_ctx,
true_block_value,
I256::ZERO,
CompetitionBidContext::no_competition_bid(),
) {
Ok(result) => result,
Err(err) => {
if err.is_critical() {
error!(?err, "Failed to finalize block for engine payload");
} else {
tracing::debug!(?err, "Discarded block for engine payload");
}
return;
}
};
state.best_value = Some(true_block_value);
drop(state);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state mutex is held across the entire finalize_block call, which includes state-root computation — the most expensive step. With multiple building-algorithm threads feeding this sink and sub-second Arc blocks (TimingsConfig::arc), every thread serializes on this lock during finalization, so effective finalization throughput is single-threaded.

The cheap best_value comparison must stay under the lock, but consider releasing it before finalize_block and re-acquiring to commit best_value (re-checking the bound after finalize, since another thread may have advanced it). local_ctx would need to move out of the shared state for that. Not blocking, but worth measuring for the fast-block path.

Comment on lines +955 to +959
let requests = ctx
.chain_spec
.is_prague_active_at_timestamp(ctx.attributes.timestamp())
.then(Requests::default);
(requests, post_block.extra_data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Prague is active this yields Some(Requests::default()) (empty), producing a non-None requests_hash in the header (hash of an empty requests list). If arc-node's ArcBlockAssembler omits requests_hash entirely (sets it to None) for these blocks, the header will not be byte-identical and the block will be rejected. Please confirm arc-node sets the empty-requests hash here too (you mention E2E byte-identical verification — worth a regression test pinning this specific field across a Prague-active Arc block).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant