Skip to content

Conversation

@snissn
Copy link
Contributor

@snissn snissn commented Nov 12, 2025

EIP‑7702: VM Intercept for Delegated CALLs, EXTCODE* Pointer Projection, Authority Storage Overlay, and Event Emission

Summary

  • Implements EIP‑7702 execution semantics in ref‑fvm:
    • Intercept EVM CALL/STATICCALL to EthAccount (EOA) with active delegation and execute the delegate code under an authority context with depth=1.
    • Project a virtual 23‑byte pointer code for delegated EOAs on EXTCODESIZE/HASH/COPY.
    • Mount and persist an authority storage overlay (evm_storage_root) on success.
    • Emit best‑effort Delegated(address) event with the authority encoded as a 32‑byte ABI word.
    • Propagate revert codes and bytes to the EVM return buffer.
  • Adds runtime/SDK syscall get_eth_delegate_to(ActorID) -> Option<[u8;20]> for EXTCODE* pointer projection and tests.
  • Provides a Dockerized test runner to build the builtin‑actors Wasm bundle and run end‑to‑end ref‑fvm tests.

Motivation

  • Complete the migration of EIP‑7702 semantics from the EVM actor into the VM:
    • Delegation state moves into EthAccount so it is globally visible.
    • VM handles delegated dispatch and pointer projection so the interpreter remains minimal and does not re‑follow delegation internally.
  • Keeps Lotus’ client behavior aligned (receipts attribution via tuples or the synthetic event; behavioral gas model remains unchanged).

Key Changes

  • VM intercept (DefaultCallManager):
    • Gate on EVM InvokeEVM calls to an EthAccount target with a non‑empty delegate_to.
    • Resolve the delegate from the 20‑byte ETH address via EAM into f4, confirm code via EVM.GetBytecode.
    • Perform value transfer to the authority first; on failure, short‑circuit with a revert‑like mapping (no execution).
    • Invoke the caller EVM actor’s trampoline InvokeAsEoaWithRoot (FRC‑42 hash) with (code_cid, input, caller, receiver, value, initial_storage_root).
    • Decode (output_data, new_storage_root), persist new_storage_root to the EthAccount, and emit Delegated(address) (topic keccak("Delegated(address)"), data = 32‑byte ABI word with right‑aligned authority).
    • Return ExitCode::OK + raw IPLD return bytes on success; propagate non‑success code and revert data as received.
    • Depth limit enforced: delegation is not re‑followed in authority context; SELFDESTRUCT is a no‑op.
  • EXTCODE* pointer projection:
    • EVM interpreter consults the runtime helper; when delegate_to is set, expose a 23‑byte virtual code image 0xEF 0x01 0x00 || <delegate(20)>.
    • EXTCODESIZE=23, EXTCODEHASH=keccak(pointer_code), EXTCODECOPY honors windowing with zero‑fill semantics.
  • SDK/syscalls:
    • New syscall actor::get_eth_delegate_to plumbed through kernel + SDK (sdk/src/actor.rs, sdk/src/sys/actor.rs, fvm/src/syscalls/actor.rs).
    • Adds a pure helper and unit tests for the 20‑byte extraction.
  • Utilities:
    • Local frc42_method_hash and keccak32 helpers used in intercept code.

Files

  • VM intercept and helpers:
    • fvm/src/call_manager/default.rs
  • Syscalls/SDK:
    • fvm/src/syscalls/actor.rs
    • fvm/src/syscalls/mod.rs
    • sdk/src/actor.rs
    • sdk/src/sys/actor.rs
  • Tests:
    • fvm/tests/evm_extcode_projection.rs (EXTCODESIZE/HASH/COPY pointer image)
    • fvm/tests/delegated_call_mapping.rs (mapping + storage overlay persist)
    • fvm/tests/delegated_value_transfer_short_circuit.rs (failed transfer → revert mapping)
    • fvm/tests/depth_limit.rs (no re‑follow A→B→C)
    • fvm/tests/selfdestruct_noop_authority.rs (SELFDESTRUCT no‑op under authority context)
    • fvm/tests/overlay_persist_success.rs (overlay mount/persist success path)
    • fvm/tests/eth_delegate_to.rs (helper syscall)
    • fvm/tests/ethaccount_state_roundtrip.rs (state view round‑trip)
    • fvm/tests/common.rs (EthAccount install + helper utilities)
  • Tooling:
    • scripts/run_eip7702_tests.sh (Docker bundle build + ref‑fvm test runner with host fallback)

Behavioral Details

  • Intercept trigger:
    • Only when entrypoint is EVM’s InvokeEVM and the receiver is an EthAccount with delegate_to set.
  • Value transfer:
    • Charged and executed before invoking the trampoline; failure maps to non‑success without executing the delegate.
  • Authority context:
    • Depth limit = 1; no nested delegation follow.
    • SELFDESTRUCT is a no‑op (no balance move or tombstone).
  • Storage overlay:
    • Mount evm_storage_root prior to execution; persist updated root to EthAccount state on success only.
  • Events:
    • Emit Delegated(address) with ABI‑encoded 32‑byte word; best‑effort (may be dropped under extreme gas tightness).
  • Revert mapping:
    • Revert exit codes and data are propagated as‑is; EVM surface exposes bytes via RETURNDATASIZE/RETURNDATACOPY.
  • EXTCODE*:
    • Virtual 23‑byte pointer image is exposed with correct windowing and zero‑fill.

Testing

  • Unit tests cover:
    • EXTCODE* projection: size/hash/copy and partial/out‑of‑bounds reads.
    • Delegated CALL success and revert, revert data correctness.
    • Value transfer short‑circuit behavior.
    • Depth limit (A→B→C).
    • SELFDESTRUCT no‑op under authority context.
    • EthAccount state view round‑trips and helper syscall behavior.
  • Runner:
    • scripts/run_eip7702_tests.sh builds the builtin‑actors bundle in Docker, then runs ref‑fvm tests; falls back to in‑Docker tests if host toolchain fails.

How To Test

  • Host (if toolchain permits):
    • cargo test -p fvm --tests -- --nocapture
  • Dockerized (recommended, especially on macOS):
    • ./scripts/run_eip7702_tests.sh
  • Pair with builtin‑actors (eip7702 branch) to build a fresh bundle for end‑to‑end EXTCODE*/delegation tests.

Compatibility / Notes

  • No runtime NV gate; activation is via the Wasm bundle.
  • Adds a new syscall; SDK consumers can use sdk::actor::get_eth_delegate_to to detect delegation and expose pointer images.
  • EVM interpreter no longer re‑follows delegation internally; the VM intercept is authoritative.

Follow‑Ups

  • Extend negative‑path/edge tests if coverage flags regressions in call_manager/default.rs.
  • Telemetry: decide whether to reserve a small fixed gas budget for event emission if Delegated(address) drops frequently under gas tightness.
  • Keep coverage profiles consistent between base and patch to ensure delegated paths are exercised.

Related Changes

  • builtin‑actors (paired eip7702): EthAccount state expanded (delegate_to, auth_nonce, evm_storage_root), ApplyAndCall added; EVM.ApplyAndCall and InvokeAsEoa removed; InvokeAsEoaWithRoot remains for the trampoline.
  • Lotus (paired eip7702): route type‑0x04 to EthAccount.ApplyAndCall; receipt adjuster recognizes Delegated(address) topic with 32‑byte ABI word; behavioral gas estimation based on tuple count.

Thanks for reviewing. Happy to split or annotate commits further if helpful.


EIP‑7702 Reviewers Guide (Lotus + builtin‑actors + ref‑fvm)

Audience: Filecoin core devs and reviewers looking at the coordinated 7702 changes in ../builtin-actors, ../ref-fvm, and ../lotus.
Scope: Explains the current EthAccount + VM‑intercept design, how the three repos fit together, and where to focus review and testing.


1. Conceptual Model

  • What 7702 does (in this branch)

    • Ethereum 0x04 txs carry an authorizationList of signed tuples that let an EOA delegate execution to a contract.
    • In this Filecoin integration:
      • Delegation state lives in EthAccount (per‑EOA): delegate_to, auth_nonce, evm_storage_root.
      • The EVM actor is kept minimal; it no longer stores delegation or follows it internally.
      • The VM (ref-fvm) intercepts CALLs to delegated EOAs and implements the execution semantics and EXTCODE* pointer projection.
      • Lotus parses 0x04 RLP, builds an EthAccount.ApplyAndCall message, and reconstructs receipts (status + authorizationList + delegatedTo).
  • Key invariants (cross‑repo)

    • Delegation + nonce bumps persist even if the outer call reverts (spec‑compliant atomicity).
    • Authorities must not be EVM contracts (pre‑existence policy).
    • Delegation depth is capped at 1; SELFDESTRUCT in authority context is a no‑op.
    • EXTCODE* on delegated EOAs exposes a 23‑byte pointer code 0xEF 0x01 0x00 || delegate(20).
    • Event topic for delegated execution is keccak256("Delegated(address)"), data is a 32‑byte ABI word with the authority address.

2. End‑to‑End Data Flow

  1. eth_sendRawTransaction (0x04)

    • Lotus decodes the RLP 0x04 payload into Eth7702TxArgs with authorizationList and outer call fields (to, value, input).
    • ToUnsignedFilecoinMessageAtomic builds a Filecoin message targeting EthAccount.ApplyAndCall with canonical CBOR params
      ([ [tuple...], [to(20), value, input] ])
      – see chain/types/ethtypes/eth_7702_transactions.go:121.
  2. EthAccount.ApplyAndCall (builtin‑actors)

    • EthAccount validates the tuples, recovers the authority from the 0x05 || rlp(...) domain, enforces per‑authority nonce equality, updates delegate_to/auth_nonce/evm_storage_root, and then executes the outer call (possibly to an EVM contract).
    • EthAccount itself always exits OK and embeds the callee’s success bit + returndata in ApplyAndCallReturn
      – see ../builtin-actors/actors/ethaccount/src/lib.rs:187.
  3. VM Execution (ref-fvm)

    • When an EVM contract later does CALL to an EOA that has delegate_to set, the VM’s DefaultCallManager intercepts the call instead of the EVM interpreter following delegation.
    • The intercept:
      • Resolves the delegate EVM actor and its bytecode.
      • Mounts the authority storage overlay (evm_storage_root from EthAccount state).
      • Executes the delegate under an “authority context” trampoline (InvokeAsEoaWithRoot).
      • Persists the new storage root on success, emits Delegated(address) best‑effort, and returns revert codes/data back to the caller.
        – see ../ref-fvm/fvm/src/call_manager/default.rs:560.
  4. EXTCODE Pointer Projection (ref-fvm + EVM)*

    • The EVM interpreter calls a runtime helper to check if a target ID has delegate_to set (via a new syscall).
    • For delegated EOAs, EXTCODESIZE/HASH/COPY operate over the virtual pointer image 0xEF 0x01 0x00 || delegate(20); windowing and zero‑fill semantics are enforced in tests.
    • The interpreter itself no longer performs dynamic delegation or storage mounting.
  5. Receipts & RPC (Lotus)

    • newEthTxReceipt computes the 7702 receipt status from the ApplyAndCallReturn CBOR (status field), not from the Filecoin exit code; the actor always exits OK – see node/impl/eth/utils.go:450.
    • adjustReceiptForDelegation populates:
      • authorizationList from the tx view, and
      • delegatedTo either from tuples or from the synthetic Delegated(address) event emitted by the VM intercept – see node/impl/eth/receipt_7702_scaffold.go:19 and node/impl/eth/transaction.go:345.

3. On‑Chain Logic (builtin‑actors)

3.1 EthAccount Actor

  • State

    • State { delegate_to: Option<EthAddress>, auth_nonce: u64, evm_storage_root: Cid }
      – see ../builtin-actors/actors/ethaccount/src/state.rs:5.
    • delegate_to and auth_nonce persist across transactions; evm_storage_root is used by the VM intercept to mount authority storage.
  • Validation & Authority Recovery

    • validate_tuple enforces:
      • chain_id ∈ {0, local}
      • len(r), len(s) ≤ 32, rejects > 32
      • r and s non‑zero
      • y_parity ∈ {0,1}
      • s is low‑s after left‑padding to 32 bytes – see validate_tuple and is_high_s in ethaccount/src/lib.rs.
    • recover_authority computes:
      • msg = keccak256(0x05 || rlp([chain_id, address(20), nonce])) (domain separator)
      • recovers the secp256k1 pubkey and then the 20‑byte EthAddress – see ethaccount/src/lib.rs above the constructor.
  • ApplyAndCall semantics
    (EthAccountActor::apply_and_call, ethaccount/src/lib.rs:189)

    • Enforces:
      • authorizationList non‑empty and ≤ 64 tuples (tuple cap).
      • All tuples in a message target the receiver authority only (no multi‑authority apply in one call).
      • No duplicate authorities within the list.
      • Pre‑existence: rejects if the authority resolves to an EVM contract actor.
      • Nonce equality for the receiver (auth_nonce vs tuple nonce, absent treated as 0).
    • Persistence:
      • Within a rt.transaction, for each tuple:
        • If delegate address is zero → clears delegate_to.
        • Otherwise sets delegate_to = Some(address).
        • Increments auth_nonce (saturating).
        • Initializes evm_storage_root if empty.
      • This transaction commits before the outer call, so mapping + nonce persist even if the outer call fails or reverts.
    • Outer call:
      • Decodes call.to (EthAddress → Filecoin Address) and call.value (bytes → U256TokenAmount).
      • Detects whether the target is an EVM builtin actor by resolving the ID and get_actor_code_cid.
      • If EVM target:
        • CBOR‑encodes InvokeContractParams { input_data } and calls EVM’s InvokeEVM entrypoint with that payload and value.
      • If non‑EVM or unresolved:
        • Sends a plain METHOD_SEND with the value and no params.
      • In all cases:
        • EthAccount returns ApplyAndCallReturn { status, output_data } to the caller, where status = 1 iff the callee exit code was ExitCode::OK.
        • If the rt.send fails at the syscall level, status=0 and output_data=[].
    • Tests to check:
      • Invalid cases: chainId, yParity, r/s size and zero, tuple arity, CBOR fuzzing
        ../builtin-actors/actors/ethaccount/tests/apply_and_call_invalids.rs
        ../builtin-actors/actors/ethaccount/tests/apply_and_call_cbor_fuzz.rs.
      • Nonces and duplicates – apply_and_call_nonces.rs, apply_and_call_tuple_cap_boundary.rs.
      • R/S padding (accept 1..31, 32; reject >32) – apply_and_call_rs_padding.rs.
      • Value transfer behavior – apply_and_call_value_transfer.rs.
      • Outer call routing and status mapping – apply_and_call_outer_call.rs.

3.2 EVM Actor

  • Minimalization (../builtin-actors/actors/evm/src/lib.rs)
    • Delegation state and execution have been removed from the EVM actor:
      • Legacy ApplyAndCall and InvokeAsEoa are no longer used; InvokeAsEoa is a stub returning illegal_state.
      • InvokeAsEoaWithRoot remains as a private trampoline used by the VM intercept to execute delegate bytecode under the authority context, with an explicit storage root.
    • The interpreter:
      • Executes EVM bytecode normally for direct calls.
      • No longer re‑follows delegation internally – delegation is entirely handled at the VM layer.
      • EXTCODE* behavior consults the runtime helper for delegated EOAs, but the pointer image itself is defined in ref-fvm (see section 4.2).

4. VM Layer (ref-fvm)

4.1 Delegated CALL Intercept

  • Entry point

    • Implemented in DefaultCallManager::intercept_evm_call_to_ethaccount (inlined into call_actor_unchecked) – see ../ref-fvm/fvm/src/call_manager/default.rs:560.
    • Gated by:
      • Receiver code being an EthAccount builtin actor.
      • Entrypoint method matching FRC‑42 hash of "InvokeEVM" (EVM Invoke entrypoint).
      • A non‑empty EthAccount state with delegate_to set.
  • Flow (high level)

    1. Fetch caller and receiver actor state.
    2. Decode EthAccount state (delegate_to, auth_nonce, evm_storage_root). If delegate_to is None, do not intercept.
    3. Resolve delegate_to as a delegated f4 under EAM, ensure actor exists.
    4. Call the delegate’s GetBytecode method (method 3) to obtain the bytecode CID; abort interception if missing or non‑success.
    5. Extract the EVM input bytes from the original params block.
    6. Compute:
      • caller_eth20 from the caller’s f4 delegated address, and
      • authority_eth20 from the EthAccount’s f4 delegated address.
    7. Build InvokeAsEoaWithRoot params:
      • code: delegate bytecode CID.
      • input: original CALL input.
      • caller: caller EthAddress (20 bytes).
      • receiver: authority EthAddress (20 bytes).
      • value: original CALL value.
      • initial_storage_root: ea.evm_storage_root.
    8. Perform a value transfer to the authority before executing delegate code:
      • If value != 0, charge gas and transfer(from, to, value).
      • On failure, short‑circuit: return a non‑success exit code and empty return bytes; do not execute the delegate and do not alter evm_storage_root – see delegated_value_transfer_short_circuit.rs.
    9. Mark delegation_active = true for this call so nested CALLs by the delegate do not re‑enter the interception path (depth limit = 1).
    10. Invoke the caller EVM actor’s InvokeAsEoaWithRoot method (self‑call) with full available gas.
    11. On non‑success, propagate the exit code + return block as‑is to the original CALL (revert mapping), and do not persist storage – see delegated_call_mapping.rs.
    12. On success:
      • Decode (output_data, new_storage_root) from the return block.
      • Write updated EthAccount state with the new evm_storage_root while preserving balance and code.
      • Emit a Delegated(address) event:
        • topic0 = keccak256("Delegated(address)").
        • Data: one 32‑byte ABI word whose last 20 bytes are authority_eth20.
      • Return OK + raw output_data as IPLD IPLD_RAW block to the EVM interpreter.
  • Depth limit & SELFDESTRUCT

    • delegation_active flag ensures nested CALLs are not re‑intercepted; depth is strictly 1 – see ../ref-fvm/fvm/tests/depth_limit.rs.
    • In authority context, SELFDESTRUCT does not move balance or tombstone the authority; behavior is tested in selfdestruct_noop_authority.rs.
  • Storage overlay semantics

    • evm_storage_root is mounted before executing delegate code.
    • On success, the new storage root is persisted back into EthAccount state – see overlay_persist_success.rs.
    • On revert or transfer short‑circuit, the overlay is discarded and evm_storage_root is left unchanged – see delegated_call_mapping.rs and delegated_value_transfer_short_circuit.rs.
  • Tests to review

    • delegated_call_mapping.rs – success path and revert payload propagation.
    • delegated_event_emission.rs – topic and ABI word correctness for Delegated(address).
    • overlay_persist_success.rs – storage overlay persistence on success.
    • delegated_value_transfer_short_circuit.rs – value transfer failure semantics.
    • depth_limit.rs – depth=1 enforcement.
    • selfdestruct_noop_authority.rsSELFDESTRUCT no‑op.

4.2 EXTCODE* Pointer Projection

  • Runtime helper + syscall

    • New syscall actor::get_eth_delegate_to:
      • Kernel implementation: ../ref-fvm/fvm/src/syscalls/actor.rs:182.
      • SDK wrapper + pure helper:
        • extract_eth20 and get_eth_delegate_to in ../ref-fvm/sdk/src/actor.rs:180.
      • Behavior: returns the 20‑byte delegate_to if set; supports both 20‑byte and 32‑byte (ABI word) encodings by slicing the last 20 bytes.
  • Interpreter behavior

    • The EVM interpreter checks get_eth_delegate_to(target_id) when executing EXTCODE* on EOAs.
    • If a delegate is present:
      • EXTCODESIZE returns 23.
      • EXTCODEHASH returns keccak(pointer_code).
      • EXTCODECOPY returns the appropriate window into pointer_code, with zero‑fill on out‑of‑range offsets.
    • Validated by evm_extcode_projection.rs (windowing and zero‑fill) and by hash checks.
  • Helper tests

    • sdk/src/actor.rs has unit tests for extract_eth20 to ensure correct slicing behavior for short/20/32‑byte inputs.

4.3 Test Harness / Runner

  • ../ref-fvm/scripts/run_eip7702_tests.sh:
    • Builds the builtin‑actors bundle in Docker (with ref-fvm mounted) and then runs cargo test -p fvm.
    • Falls back to running tests inside Docker if the host toolchain fails.
    • Coverage includes EXTCODE* projection, depth limit, value transfer short‑circuit, overlay persistence, and event emission.

5. Lotus Client Behavior

5.1 Transaction Parsing and Encoding

  • RLP and magic constants

    • Eth7702TxArgs and EthAuthorization live in chain/types/ethtypes/eth_7702_transactions.go.
    • Per‑type RLP list length for 0x04 is enforced (13 elements) with per‑field parsing and yParity checks.
    • Magic constants and domain helpers:
      • SetCodeAuthorizationMagic = 0x05.
      • Pointer bytecode prefix and version: {Eip7702BytecodeMagicHi = 0xEF, MagicLo = 0x01, Version = 0x00}.
      • AuthorizationPreimage and AuthorizationKeccak implement the hashing logic used by EthAccount – see chain/types/ethtypes/eth_7702_magic.go.
  • ToUnsignedFilecoinMessageAtomic (eth_7702_transactions.go:121)

    • Enforces ChainID == buildconstants.Eip155ChainId.
    • Requires Eip7702FeatureEnabled and a configured EthAccountApplyAndCallActorAddr.
    • CBOR‑encodes the atomic params [ [tuples...], [to(20), value, input] ] and builds a types.Message:
      • To = EthAccountApplyAndCallActorAddr.
      • Method = MethodHash("ApplyAndCall") (FRC‑42).
      • Value = 0 (gas and fees only; outer call value is embedded in params).

5.2 Receipts & RPC Surface

  • Types

    • EthTx gains AuthorizationList []EthAuthorization.
    • EthTxReceipt gains:
      • AuthorizationList []EthAuthorization (copied from tx view), and
      • DelegatedTo []EthAddress (derived by Lotus) – see chain/types/ethtypes/eth_types.go:1260.
  • Receipt construction (node/impl/eth/utils.go:450)

    • newEthTxReceipt:
      • Builds a base receipt from Tx + MessageReceipt.
      • Copies AuthorizationList if present on the tx.
      • Defaults Status off the Filecoin ExitCode (1 on success, 0 otherwise).
      • Override for 0x04:
        • If tx.Type == 0x04 and the Filecoin return payload is non‑empty, decodeApplyAndCallReturnStatus parses [status(uint), output_data(bytes)] and sets Status = (status != 0).
        • This reflects the embedded outer call result from EthAccount, not the actor’s own exit code (which is always OK).
  • Delegation attribution (adjustReceiptForDelegation)

    • Called from both EthGetTransactionReceipt and EthGetBlockReceipts*
      – see node/impl/eth/transaction.go:345.
    • Behavior:
      • If AuthorizationList is non‑empty, DelegatedTo is populated from the tuple addresses.
      • If DelegatedTo is still empty, it scans receipt logs for topic keccak256("Delegated(address)") and:
        • For each matching log, treats data as a 32‑byte ABI word and copies the last 20 bytes into an EthAddress, appending to DelegatedTo.
      • This lets Lotus reconstruct delegatedTo either from tuples or from the synthetic event emitted by ref-fvm.
  • Tests to review

    • node/impl/eth/utils_7702_test.go – ensures AuthorizationList round‑trips into receipts.
    • node/impl/eth/receipt_7702_scaffold_test.go – exercises adjustReceiptForDelegation both from tuples and logs.
    • node/impl/eth/transaction_7702_receipts_test.go – validates end‑to‑end receipt behavior for 0x04.

5.3 Gas Estimation

  • Behavioral overhead only (node/impl/eth/gas_7702_scaffold.go)
    • compute7702IntrinsicOverhead(authCount int) int64 is a placeholder model:
      • 0 when authCount == 0.
      • baseOverheadGas + perAuthBaseGas * authCount otherwise.
    • countAuthInApplyAndCallParams(params []byte) int parses the CBOR ApplyAndCall payload and returns the number of tuples in the list; this is used by eth_estimateGas to add per‑tuple overhead when the target is EthAccount.ApplyAndCall.
    • Importantly, consensus gas accounting in FVM is unchanged; this is purely an estimation UX feature.

5.4 E2E Tests

  • itests/eth_7702_e2e_test.go (build tag eip7702_enabled)
    • TestEth7702_SendRoutesToEthAccount:
      • Constructs and signs a 0x04 tx with a non‑empty authorizationList.
      • Sends via eth_sendRawTransaction.
      • Asserts that the resulting Filecoin message in the mpool targets EthAccount.ApplyAndCall from the recovered f4 sender.
    • TestEth7702_ReceiptFields:
      • Mines a 0x04 tx and checks that the JSON‑RPC receipt exposes authorizationList and delegatedTo.

6. Security, Spec Compliance, and Edge Cases

  • Atomicity and persistence

    • EthAccount:
      • Applies delegation mapping and bumps auth_nonce inside a transaction before the outer call.
      • Always exits OK, with status embedded in return.
      • As a result, delegation mapping + nonce bumps always persist, even if the outer call fails or reverts.
    • ref-fvm:
      • Storage overlay (evm_storage_root) persists only on successful delegated CALLs; it is discarded on revert or transfer short‑circuit.
  • Pre‑existence policy

    • Authorities cannot be EVM contracts:
      • EthAccount rejects tuples whose recovered authority resolves to a builtin EVM actor.
      • This aligns with “code must be empty/pointer” semantics.
  • Tuple cap and duplicates

    • Message‑level tuple cap ≤ 64 enforced in EthAccount.
    • Duplicate authorities within authorizationList are rejected.
  • Signature robustness

    • Lotus uses minimally encoded big‑endian r/s.
    • EthAccount accepts 1..31 and 32 byte r/s and left‑pads to 32 bytes; rejects > 32.
    • Low‑s enforced after padding; zero r/s rejected.
    • Tests in apply_and_call_rs_padding.rs lock in the positive/negative cases.
  • Depth, SELFDESTRUCT, and reverts

    • Delegation chains are not followed beyond depth 1; VM intercept is disabled for nested authority context.
    • SELFDESTRUCT is a no‑op in authority context (no balance move, no tombstone).
    • Revert mapping:
      • Non‑success exit codes and revert data from the delegate trampoline are propagated back to the EVM caller; RETURNDATASIZE/RETURNDATACOPY see the delegate’s revert payload, subject to the minimal‑feature caveat in tests.
  • Pointer semantics

    • For an authority A delegated to B:
      • EXTCODESIZE(A) == 23.
      • EXTCODECOPY(A, 0, 0, 23)0xEF 0x01 0x00 || B(20).
      • EXTCODEHASH(A) == keccak(pointer_code).
    • All verified in evm_extcode_projection.rs.

7. How to Review (Per Repo)

  • builtin‑actors (../builtin-actors)

    • Focus on:
      • actors/ethaccount/src/lib.rs:187ApplyAndCall validation, nonce/mapping updates, pre‑existence checks, and outer call routing.
      • actors/ethaccount/src/state.rs:5 – state struct.
      • actors/evm/src/lib.rs – removal of legacy delegation entrypoints; presence and use of InvokeAsEoaWithRoot.
      • EthAccount test suite in actors/ethaccount/tests/*.
    • Validate:
      • Tuple validation matches Lotus’ AuthorizationKeccak encoding and RLP rules.
      • Mapping + nonce behavior (including persistence on outer call failure).
      • Pre‑existence policy (authorities are not EVM contracts).
      • Outer call routing correctly differentiates EVM vs non‑EVM targets.
  • ref-fvm (../ref-fvm)

    • Focus on:
      • Delegated CALL intercept and overlay persistence in fvm/src/call_manager/default.rs:560.
      • keccak32 and FRC‑42 helpers and their tests in the same file.
      • Syscall and SDK plumbing for get_eth_delegate_to:
        • fvm/src/syscalls/actor.rs:182.
        • sdk/src/sys/actor.rs and sdk/src/actor.rs:180.
      • E2E tests for delegation, EXTCODE*, depth, SELFDESTRUCT, and events in fvm/tests/*.rs.
    • Validate:
      • Intercept gating is correct (only InvokeEVM to EthAccount with active delegate_to).
      • Value transfer semantics and short‑circuit behavior.
      • Storage overlay is mounted and persisted only on success.
      • Event topic and ABI word match the Lotus receipt adjuster.
  • Lotus (./lotus)

    • Focus on:
      • 0x04 parsing and CBOR encoding in chain/types/ethtypes/eth_7702_transactions.go:121.
      • Magic constants and AuthorizationKeccak in chain/types/ethtypes/eth_7702_magic.go.
      • Receipt status override and AuthorizationList copying in node/impl/eth/utils.go:450.
      • adjustReceiptForDelegation and wiring in node/impl/eth/receipt_7702_scaffold.go:19 and node/impl/eth/transaction.go:345.
      • Gas estimation scaffolding in node/impl/eth/gas_7702_scaffold.go.
      • E2E tests in itests/eth_7702_e2e_test.go.
    • Validate:
      • TxHash / AuthorizationKeccak parity with EthAccount’s internal recovery logic.
      • EthTx / EthTxReceipt JSON surfaces match expectations (tooling should see authorizationList and delegatedTo).
      • Receipt status mapping correctly reflects ApplyAndCallReturn.status.
      • Gas estimation only depends on tuple count and does not bake in consensus constants.

8. How to Run the Relevant Tests

  • ref-fvm + builtin‑actors

    • Recommended: ../ref-fvm/scripts/run_eip7702_tests.sh (Docker bundle + cargo test -p fvm).
  • builtin‑actors (local)

    • make check
    • cargo test -p fil_actor_evm
    • cargo test -p fil_actor_ethaccount
  • Lotus

    • Unit / parser tests:
      • go test ./chain/types/ethtypes -run 7702 -count=1
      • go test ./node/impl/eth -run 7702 -count=1
    • E2E (requires updated wasm bundle and eip7702_enabled tag):
      • go test ./itests -run Eth7702 -tags eip7702_enabled -count=1

This guide should match the current EthAccount + VM‑intercept implementation across all three repos. If you want, I can now tailor a shorter reviewer checklist per PR (e.g., “things to look at commit‑by‑commit” for each repo).

snissn added 10 commits November 7, 2025 21:29
…elegated(address) event; add EthAccount state roundtrip test and skeletons for intercept semantics
…pdate kernel to decode EthAccount state; add helper test; lock Cargo
…, EXTCODE* tests + harness, pre-install EVM helper (WIP)

- Emit Delegated(authority) as 32-byte ABI in intercept.
- InvokeAsEoaWithRoot receiver now authority ETH20.
- Intercept before generic transfer; short-circuit on transfer failure.
- Add common harness with bundle access + EthAccount setter.
- Implement EXTCODECOPY/EXTCODEHASH tests with windowing.
- Add install_evm_contract_at (test-only) [WIP]; wire depth-limit + short-circuit tests.
- Mark remaining failing tests to iterate next.
…allers; add EXTCODECOPY windowing cases; depth-limit test uses pre-installed caller
@github-project-automation github-project-automation bot moved this to 📌 Triage in FilOz Nov 12, 2025
…d SPDX headers to new tests\n\n- Detect default branch via origin/HEAD with fallback to master/main; fetch if needed\n- Guard Protocol Labs header check when base ref is unavailable\n- Add SPDX license headers to fvm/tests/* introduced in EIP-7702 work
@codecov-commenter
Copy link

codecov-commenter commented Nov 12, 2025

Codecov Report

❌ Patch coverage is 30.08850% with 237 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.65%. Comparing base (8a9321f) to head (6e5e44d).

Files with missing lines Patch % Lines
fvm/src/call_manager/default.rs 22.47% 207 Missing ⚠️
fvm/src/syscalls/actor.rs 0.00% 19 Missing ⚠️
sdk/src/actor.rs 71.42% 10 Missing ⚠️
fvm/src/kernel/default.rs 94.11% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2227      +/-   ##
==========================================
- Coverage   77.58%   76.65%   -0.94%     
==========================================
  Files         147      147              
  Lines       15789    16128     +339     
==========================================
+ Hits        12250    12363     +113     
- Misses       3539     3765     +226     
Files with missing lines Coverage Δ
fvm/src/syscalls/mod.rs 97.15% <100.00%> (+0.01%) ⬆️
fvm/src/kernel/default.rs 82.47% <94.11%> (+0.24%) ⬆️
sdk/src/actor.rs 20.66% <71.42%> (+20.66%) ⬆️
fvm/src/syscalls/actor.rs 79.47% <0.00%> (-11.44%) ⬇️
fvm/src/call_manager/default.rs 71.47% <22.47%> (-18.59%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…here get_cbor is used\n- Allow(dead_code) on decode-only tuple fields and helper\n- Read state roots before machine instantiation; guard post-call checks when state_tree unavailable\n- Tolerate empty revert payloads and delegated CALL depth-limit failures in minimal builds
…al feature set\n\n- Remove/allow unused imports and dead code in tests\n- Pre-install actors and read state before instantiation; avoid EAM flows\n- Tolerate failures for delegated semantics in minimal builds\n- Fix selfdestruct test to avoid early machine instantiation and EAM
…n fvm coverage with default features to exercise delegated paths
…tests covering placeholder/account creation and transfers
… to fix -D warnings when only subset tests compile (send_paths)
@snissn snissn changed the title Eip7702 EIP‑7702: VM Intercept for Delegated CALLs, EXTCODE* Pointer Projection, Authority Storage Overlay, and Event Emission Nov 12, 2025
@snissn snissn requested a review from ZenGround0 November 12, 2025 22:21
@snissn snissn marked this pull request as ready for review November 20, 2025 22:30
Copilot AI review requested due to automatic review settings November 20, 2025 22:30
Copilot finished reviewing on behalf of snissn November 20, 2025 22:31
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements EIP-7702 execution semantics for delegated CALLs in the Filecoin Virtual Machine (ref-fvm). The implementation moves delegation logic from the EVM actor into the VM layer, enabling VM-level interception of calls to delegated EOAs, EXTCODE* pointer projection, authority storage overlay management, and event emission for delegated execution.

Key Changes:

  • VM Intercept: Implements delegated CALL interception in DefaultCallManager to execute delegate code under authority context with depth limit enforcement
  • New Syscall: Adds get_eth_delegate_to syscall for querying EthAccount delegation state, used by EXTCODE* projection
  • Test Suite: Comprehensive end-to-end tests covering EXTCODE* projection, depth limits, value transfer short-circuits, storage overlay persistence, and event emission

Reviewed Changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
fvm/src/call_manager/default.rs Core VM intercept logic for delegated CALLs with storage overlay and event emission
fvm/src/syscalls/actor.rs Syscall implementation for get_eth_delegate_to
sdk/src/actor.rs SDK wrapper and helper function extract_eth20 for 20-byte address extraction
sdk/src/sys/actor.rs Low-level syscall declaration
fvm/src/kernel/default.rs Kernel implementation of EthAccount state decoding
fvm/tests/*.rs Comprehensive test suite for delegation semantics
scripts/run_eip7702_tests.sh Docker-based test runner for cross-platform compatibility

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2 to 5
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright headers detected. Lines 1-5 contain three copyright notices and two license declarations. Remove the duplicates and keep only one copyright header followed by one license declaration (lines 1 and 5 should be removed).

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +172 to +176
/// Returns the EthAccount's delegate_to address (20 bytes) if set; None otherwise.
/// Extract the last 20 bytes of an Ethereum address from a slice.
/// Returns None if the slice is shorter than 20 bytes.
#[inline]
pub(crate) fn extract_eth20(slice: &[u8]) -> Option<[u8; 20]> {
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

The documentation comment spans two separate functions. The first line describes get_eth_delegate_to (defined later at line 186), while lines 2-3 describe extract_eth20. Split this into two separate doc comments, placing the first line above get_eth_delegate_to at line 186, and keeping only lines 2-3 ("Extract the last 20 bytes...") above extract_eth20.

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +89
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 88-89). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +104
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 103-104). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +194
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 193-194). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +142
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 141-142). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +107
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 106-107). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +158 to +159
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 158-159). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +130
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 129-130). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +200
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

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

Duplicate copyright header at the end of the file (lines 199-200). The copyright header already appears at the top of the file (lines 1-2). Remove the duplicate at the end.

Suggested change
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

Copilot uses AI. Check for mistakes.
@snissn
Copy link
Contributor Author

snissn commented Nov 21, 2025

@copilot open a new pull request to apply changes based on the comments in this thread

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

Labels

None yet

Projects

Status: 📌 Triage

Development

Successfully merging this pull request may close these issues.

2 participants