Skip to content

feat: implement eip-7702#196

Merged
FletcherMan merged 17 commits intomainfrom
feat/eip-7702
Oct 17, 2025
Merged

feat: implement eip-7702#196
FletcherMan merged 17 commits intomainfrom
feat/eip-7702

Conversation

@FletcherMan
Copy link
Copy Markdown
Collaborator

@FletcherMan FletcherMan commented Sep 2, 2025

1. Purpose or design rationale of this PR

This PR implements EIP-7702 (Account Delegation) following the specification at https://eips.ethereum.org/EIPS/eip-7702. The implementation is based entirely on Ethereum's go-ethereum reference implementation.

2. PR title

Your PR title must follow conventional commits (as we are doing squash merge for each PR), so it must start with one of the following types:

  • build: Changes that affect the build system or external dependencies (example scopes: yarn, eslint, typescript)
  • ci: Changes to our CI configuration files and scripts (example scopes: vercel, github, cypress)
  • docs: Documentation-only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that doesn't fix a bug, or add a feature, or improves performance
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • test: Adding missing tests or correcting existing tests

3. Deployment tag versioning

Has the version in params/version.go been updated?

  • This PR doesn't involve a new deployment, git tag, docker image tag, and it doesn't affect traces
  • Yes

4. Breaking change label

Does this PR have the breaking-change label?

  • This PR is not a breaking change
  • Yes

Summary by CodeRabbit

  • New Features

    • Support for SetCode transactions with authorization lists exposed in RPC/ethclient and GraphQL.
  • Improvements

    • Tracing rebuilt: hook-based tracers, per-transaction output files, tracer registry/config and richer JSON trace formats.
    • Time-aware behavior: block timestamp used for signing, fee/receipt computation and tracing.
    • Gas accounting: intrinsic gas now accounts for code authorizations; improved EIP‑7702 error reporting.
  • Chores

    • CLI flags, tests and fixtures updated for tracing and fork timing (Morph300/Viridian).

@FletcherMan FletcherMan requested a review from a team as a code owner September 2, 2025 07:13
@FletcherMan FletcherMan requested review from twcctop and removed request for a team September 2, 2025 07:13
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 2, 2025

Walkthrough

MakeSigner and receipt APIs become time-aware (blockTime passed/propagated). EIP‑7702 SetCode authorization data is exposed and wired into IntrinsicGas, tx-pool, RPC and clients. A Hooks-based tracing system with state-change reasons and widespread VM/state/rawdb/test updates is introduced.

Changes

Cohort / File(s) Summary
Time-aware signer & receipts
core/types/transaction_signing.go, core/types/*.go, accounts/abi/bind/backends/simulated.go, core/bench_test.go, core/blockchain*.go, core/blockchain_reader.go, core/rawdb/*, eth/*/*, les/*/*, light/*/*, rollup/tracing/tracing.go, cmd/evm/internal/t8ntool/...
MakeSigner gained a blockTime parameter; callers updated to pass header.Time/block.Time. ReadReceipts and receipts derivation now accept block time and baseFee; callers read headers for time/baseFee.
EIP‑7702 SetCode & authorization plumbing
core/state_transition.go, core/tx_pool.go, core/types/transaction.go, core/types/tx_setcode.go, internal/ethapi/*.go, interfaces.go, ethclient/*, graphql/graphql.go, accounts/abi/bind/backends/simulated.go, internal/ethapi/transaction_args.go
Adds SetCodeTxType and SetCodeAuthorization flow: Message/CallMsg expose AuthorizationList; IntrinsicGas accepts auth list; txpool enforces Morph/EIP‑7702 gating and per-authority in‑flight limits; RPC and ethclient include authorizationList and YParity fields.
Tracing Hooks & state-change reasons
core/tracing/hooks.go, core/state/statedb.go, core/state/statedb_hooked.go, core/state/*, core/vm/*, eth/tracers/*, cmd/evm/*, cmd/evm/internal/t8ntool/*, cmd/evm/file_tracer.go
Introduces Hooks API and enums for balance/gas/nonce reasons; StateDB mutation APIs extended with reason args/returns; NewHookedState emits hooks; OnTxStart/OnTxEnd/OnGasChange integrated; tracer registry and file/result writers added.
VM delegation & Morph300 support
core/vm/evm.go, core/vm/operations_acl.go, core/vm/eips.go, core/vm/jump_table.go, core/vm/interpreter.go, eth/tracers/native/prestate.go
Adds one-level delegation resolution under Morph300/Viridian, Viridian instruction set, EIP‑7702 gas variants, and tracing instrumentation across call/create paths.
IntrinsicGas / tx helpers & sig hashing
core/state_transition.go, cmd/evm/internal/t8ntool/transaction.go, core/bench_test.go, tests/*, core/types/{dynamic_fee_tx.go,access_list_tx.go,legacy_tx.go,l1_message_tx.go,tx_blob.go}
IntrinsicGas signature extended to accept code-authorizations; TxData gains effectiveGasPrice and sigHash helpers; tests updated.
State mutation API & journaling returns
core/state/state_object.go, core/state/statedb.go, core/state/statedb_hooked.go, core/state_processor.go, many core/*_test.go
Set/Add/SubBalance, SetNonce, SetCode, SetState signatures changed to include tracing reasons or return previous values; journaling adjusted and tests adapted.
TxPool authority tracking & helpers
core/tx_pool.go, core/tx_list.go
TxPool adds eip7702 gating, authority tracking (auths map) and helpers (add/remove/hasAuthorities); txList.Contains added.
Forks, params & protocol constants
params/config.go, params/forks/forks.go, params/protocol_params.go, eth/ethconfig/*, cmd/utils/flags.go, cmd/geth/*
Adds Morph300/ViridianTime fields and checks, new Fork enum entry, TxAuthTupleGas constant, CLI flags and override plumbing for Viridian/Morph300.
VM contract / precompiled gas tracing
core/vm/contract.go, core/vm/contracts.go, core/vm/*_test.go
Contract.UseGas/RefundGas now accept tracer & GasChangeReason; RunPrecompiledContract accepts a tracer; contract/precompile gas-change tracing hooked.
Errors & validation
core/error.go
New EIP‑7702-related errors added (authorization, delegation, in‑flight limits, informational auth errors).
Tracing harness, tracers registry & fixtures
eth/tracers/*, eth/tracers/internal/tracetest/*
New tracers directory/registry (DefaultDirectory), logger.Config migration, many tracer tests and fixtures added/updated to hooks/logger model; tracer constructors and registry implemented.
RPC / encoding / tooling changes
internal/ethapi/*, ethclient/*, graphql/graphql.go, eth/gasprice/gasprice.go, miner/worker.go, consensus/*, rollup/*
RPCTransaction extended with AuthorizationList and YParity; NewRPCTransaction gains blockTime; multiple callers build signer with block.Time; gasprice sampling derives signer per-block; miner/consensus balance ops annotated with tracing reasons.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant RPC
  participant TxPool
  participant Chain
  participant StateTx as StateTransition
  participant EVM
  participant VM

  Client->>RPC: submit tx (SetCode or normal)
  RPC->>TxPool: validate (IntrinsicGas with authList)
  TxPool-->>RPC: accept/reject
  Note right of Chain: Block processing uses blockTime (header.Time)
  Chain->>StateTx: apply tx (signer := MakeSigner(cfg, blockNum, blockTime))
  StateTx->>StateTx: pre-checks & apply authorizations (if any)
  StateTx->>EVM: execute message (hooked StateDB if tracer present)
  EVM->>VM: resolveCode(addr) (Morph300 lookup if enabled)
  VM-->>EVM: code (possibly delegated)
  EVM-->>StateTx: result (logs, gas, l1DataFee)
  StateTx->>Chain: create receipt (DeriveFields uses blockTime, baseFee)
Loading
sequenceDiagram
  autonumber
  participant Reader as ReceiptReader
  participant DB
  participant HeaderStore
  participant Types as Receipts.DeriveFields

  Reader->>DB: ReadHeader(hash, number)
  DB-->>Reader: header (time, baseFee)
  Reader->>DB: ReadReceipts(hash, number, header.Time, cfg)
  DB-->>Reader: raw receipts
  Reader->>Types: DeriveFields(cfg, hash, number, header.Time, header.BaseFee, txs)
  Types-->>Reader: enriched receipts
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • twcctop
  • curryxbo
  • ryanmorphl2

Poem

I nibble at timestamps, hop through code and trace,
I tuck auth lists gently in their proper place.
Receipts line up tidy, signers mark the time,
Hooks hum softly, gas reasons keep the rhyme.
A rabbit's small patch — neat commits, light and prime 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: implement eip-7702" correctly identifies a primary and significant feature being added to the changeset: the implementation of EIP-7702 (Account Delegation). The title accurately reflects real and substantial changes including new SetCode transaction types, authorization lists, delegation resolution, and related functionality. However, the changeset also includes equally substantial supporting infrastructure changes, such as a comprehensive new tracing API (core/tracing/hooks.go), pervasive block-time awareness additions (MakeSigner, receipts, etc.), and a new fork configuration (Morph300/Viridian). The title captures the main feature but does not reflect the full scope of significant infrastructure enhancements that accompany the EIP-7702 implementation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/eip-7702

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (11)
ethclient/gethclient/gethclient.go (1)

95-95: Fix JSON tag typo: keccakodeHash → keccakCodeHash

This breaks decoding of GetProof results.

-		KeccakCodeHash   common.Hash     `json:"keccakodeHash"`
+		KeccakCodeHash   common.Hash     `json:"keccakCodeHash"`
core/types/tx_blob.go (2)

177-179: Potential nil deref in rawSignatureValues.
If V/R/S are nil (e.g., partially constructed/decoded tx), ToBig() will panic.

-func (tx *BlobTx) rawSignatureValues() (v, r, s *big.Int) {
-    return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig()
-}
+func (tx *BlobTx) rawSignatureValues() (v, r, s *big.Int) {
+    if tx.V == nil || tx.R == nil || tx.S == nil {
+        return new(big.Int), new(big.Int), new(big.Int)
+    }
+    return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig()
+}

181-186: setSignatureValues assumes non-nil fields.
ChainID/V/R/S may be nil; SetFromBig on nil panics. Allocate defensively.

 func (tx *BlobTx) setSignatureValues(chainID, v, r, s *big.Int) {
-    tx.ChainID.SetFromBig(chainID)
-    tx.V.SetFromBig(v)
-    tx.R.SetFromBig(r)
-    tx.S.SetFromBig(s)
+    if tx.ChainID == nil { tx.ChainID = new(uint256.Int) }
+    if tx.V == nil { tx.V = new(uint256.Int) }
+    if tx.R == nil { tx.R = new(uint256.Int) }
+    if tx.S == nil { tx.S = new(uint256.Int) }
+    tx.ChainID.SetFromBig(chainID)
+    tx.V.SetFromBig(v)
+    tx.R.SetFromBig(r)
+    tx.S.SetFromBig(s)
 }
les/downloader/queue_test.go (1)

50-56: Fix incorrect timestamp passed to MakeSigner in makeChain

parent.Time()+10 is only correct for the first generated block; later blocks get the wrong signer when time-gated. Pass each block’s actual timestamp.

Preferred (if available on BlockGen):

-            signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+10)
+            signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Header().Time)

Fallback (matching GenerateChain’s 10s step):

-            signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+10)
+            signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+uint64((i+1)*10))
eth/fetcher/block_fetcher_test.go (1)

54-61: Replace all parent.Time()+10 with block.Time() in MakeSigner calls across tests

The following tests still use a constant timestamp; update each to use the actual block timestamp:

  • les/fetcher/block_fetcher_test.go:55
  • les/downloader/queue_test.go:50
  • les/downloader/testchain_test.go:129
  • eth/downloader/queue_test.go:51
  • eth/downloader/testchain_test.go:165
  • eth/fetcher/block_fetcher_test.go:56

Preferred replacement:

-	signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+10)
+	signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())

Fallback (if block.Time() isn’t exposed):

-	signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+10)
+	signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.PrevBlock(0).Time()+10)
rollup/tracing/tracing.go (2)

281-286: Use block hash, not tx root, in tx context.

BlockHash should be the block’s hash; TxHash is different (tx Merkle root). Fix to avoid misleading consumers.

Apply:

-	txctx := &Context{
-		BlockHash: block.TxHash(),
+	txctx := &Context{
+		BlockHash: block.Hash(),
 		TxIndex:   index,
 		TxHash:    tx.Hash(),
 	}

451-456: Avoid method call on possibly missing map entry (nil-safe ZkTrieTracer access).

env.ZkTrieTracer[addrStr].Available() may read a zero value; guard with ok check.

Apply:

-			if zktrieTracer.Available() && !env.ZkTrieTracer[addrStr].Available() {
-				env.ZkTrieTracer[addrStr] = state.NewProofTracer(trie)
-			}
+			if zktrieTracer.Available() {
+				if v, ok := env.ZkTrieTracer[addrStr]; !ok || !v.Available() {
+					env.ZkTrieTracer[addrStr] = state.NewProofTracer(trie)
+				}
+			}

Also applies to: 459-461

params/config.go (2)

858-924: Add Morph300 to compatibility checks.

Without this, config mismatches for Morph300 won’t be detected, risking consensus splits.

Apply:

 	if isForkTimestampIncompatible(c.Morph203Time, newcfg.Morph203Time, headTimestamp) {
 		return newTimestampCompatError("Morph203Time fork timestamp", c.Morph203Time, newcfg.Morph203Time)
 	}
+	if isForkTimestampIncompatible(c.Morph300Time, newcfg.Morph300Time, headTimestamp) {
+		return newTimestampCompatError("Morph300Time fork timestamp", c.Morph300Time, newcfg.Morph300Time)
+	}
 	return nil

791-856: Include Morph300 in fork-order validation.

Prevents invalid scheduling in configs.

Apply:

 	for _, cur := range []fork{
@@
-		{name: "morph203Time", timestamp: c.Morph203Time, optional: true},
+		{name: "morph203Time", timestamp: c.Morph203Time, optional: true},
+		{name: "morph300Time", timestamp: c.Morph300Time, optional: true},
 	} {
core/rawdb/accessors_chain.go (1)

686-687: Fix log fields: missing value for "hash"

The structured log omits the actual hash value.

-        log.Error("Invalid receipt array RLP", "hash", "err", err)
+        log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
internal/ethapi/transaction_args.go (1)

80-185: Fail-fast validations for SetCode in RPC and fix gas estimation undercharge

  • Return errors early if AuthorizationList is used with contract creation (nil To), with legacy gasPrice, or before Morph300 activation.
  • Include AuthorizationList in DoEstimateGas to account for intrinsic auth gas.
 func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
+    // Validate SetCode preconditions early
+    head := b.CurrentHeader()
+    if args.AuthorizationList != nil {
+        if args.To == nil {
+            return errors.New("authorizationList requires 'to' (SetCode tx cannot create contracts)")
+        }
+        if args.GasPrice != nil {
+            return errors.New("authorizationList cannot be combined with gasPrice; use maxFeePerGas/maxPriorityFeePerGas")
+        }
+        if !b.ChainConfig().IsMorph300(head.Number, head.Time) {
+            return errors.New("authorizationList not supported before Morph300")
+        }
+    }
-    // After curie, default to 1559 unless gasPrice is set
-    head := b.CurrentHeader()
+    // After curie, default to 1559 unless gasPrice is set
+    // (head already fetched)
@@
     if args.Gas == nil {
@@
-        callArgs := TransactionArgs{
+        callArgs := TransactionArgs{
             From:                 args.From,
             To:                   args.To,
             GasPrice:             args.GasPrice,
             MaxFeePerGas:         args.MaxFeePerGas,
             MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
             Value:                args.Value,
             Data:                 (*hexutil.Bytes)(&data),
             AccessList:           args.AccessList,
+            AuthorizationList:    args.AuthorizationList,
         }
🧹 Nitpick comments (46)
core/tx_list.go (1)

293-294: Docstring nit: tighten wording and spacing

Use standard Go doc style and remove the extra space.

Apply this diff:

-// Contains returns whether the  list contains a transaction
-// with the provided nonce.
+// Contains reports whether the list contains a transaction with the given nonce.
core/vm/contracts_test.go (1)

191-193: Avoid double-formatting in Errorf

Pass format and args to Errorf directly; Sprintf is redundant.

-			bench.Errorf(fmt.Sprintf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)))
+			bench.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
core/types/legacy_tx.go (1)

107-109: Nil-safety in effectiveGasPrice.

Guard against a nil GasPrice to avoid a potential panic.

 func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
-	return dst.Set(tx.GasPrice)
+	if tx.GasPrice == nil {
+		return dst.SetUint64(0)
+	}
+	return dst.Set(tx.GasPrice)
 }
light/txpool.go (1)

97-105: Make TxPool.signer update on new heads
Currently pool.signer is set once via types.LatestSigner(config) in NewTxPool and never updated (see lines 98–105 and 311–318). To ensure the signer reflects time-based fork rules, assign

pool.signer = types.MakeSigner(pool.config, head.Number, head.Time)

inside setNewHead.

core/vm/eips.go (1)

276-282: 7702 gas hooks wired to CALL-family opcodes.

Looks correct provided gasCallEIP7702* implement the intended semantics. Please ensure a targeted test verifies CALL/CALLCODE/STATICCALL/DELEGATECALL gas under Morph300.

core/types/l1_message_tx.go (1)

70-72: Panic on sigHash; add guardrails or doc.

If this method is accidentally reached, it will crash tests/nodes. Either add a comment asserting it's unreachable, or return a zero hash and let callers gate by type. At minimum, add a clarifying comment.

-func (tx *L1MessageTx) sigHash(chainID *big.Int) common.Hash {
-	panic("sigHash called on L1MessageTx")
-}
+// sigHash is undefined for L1MessageTx; L1 messages are not user-signed.
+// Callers must not invoke this for L1MessageTx.
+func (tx *L1MessageTx) sigHash(chainID *big.Int) common.Hash {
+	panic("sigHash called on L1MessageTx (unreachable)")
+}
les/downloader/testchain_test.go (1)

129-131: Prefer block.Time() for signer to avoid header-time drift

Using parent.Time()+10 risks divergence if BlockGen adjusts the current block timestamp (e.g., via OffsetTime). Using block.Time() ties the signer gating to the actual header time.

Apply:

-			signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+10)
+			signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
eth/gasprice/gasprice.go (1)

256-260: Docstring name is stale

Comment says getBlockPrices but function is getBlockValues. Rename for clarity.

-// getBlockPrices calculates the lowest transaction gas price in a given block
+// getBlockValues calculates effective gas tips for a given block
core/types/tx_blob.go (1)

46-48: Typo in comment ("sigining").
Fix spelling.

-// is used to create a transaction for sigining.
+// is used to create a transaction for signing.
core/vm/operations_acl.go (1)

227-285: Avoid potential double-charge on self-delegation; add fast-path to skip full code fetch

  • If a contract delegates to itself, charging an extra warm read (100) may double-count the same address. Skip the extra charge when target == addr.
  • Micro perf: Check code size (== 23) before fetching full code to parse the delegation stub.

Please add unit tests covering: warm/cold target, cold caller+warm target, and self-delegation.

Apply this minimal diff to guard resolution and self-delegation:

@@
-        // Check if code is a delegation and if so, charge for resolution.
-        if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
+        // Check if code is a delegation and if so, charge for resolution.
+        // Fast-path: avoid allocating code for non-7702 accounts
+        if evm.StateDB.GetCodeSize(addr) == 23 {
+            if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok && target != addr {
                 var cost uint64
-                if evm.StateDB.AddressInAccessList(target) {
-                    cost = params.WarmStorageReadCostEIP2929
-                } else {
-                    evm.StateDB.AddAddressToAccessList(target)
-                    cost = params.ColdAccountAccessCostEIP2929
-                }
-                if !contract.UseGas(cost) {
-                    return 0, ErrOutOfGas
-                }
-                total += cost
-        }
+                if evm.StateDB.AddressInAccessList(target) {
+                    cost = params.WarmStorageReadCostEIP2929
+                } else {
+                    evm.StateDB.AddAddressToAccessList(target)
+                    cost = params.ColdAccountAccessCostEIP2929
+                }
+                if !contract.UseGas(cost) {
+                    return 0, ErrOutOfGas
+                }
+                total += cost
+            }
+        }

Optional readability nit (rename accumulator to avoid shadowing on return):

@@
-            total uint64 // total dynamic gas used
+            extra uint64 // extra dynamic gas used for EIP-7702 resolution & cold access
@@
-            total += coldCost
+            extra += coldCost
@@
-            total += cost
+            extra += cost
@@
-        contract.Gas += total
+        contract.Gas += extra
@@
-        if total, overflow = math.SafeAdd(old, total); overflow {
+        if extra, overflow = math.SafeAdd(old, extra); overflow {
             return 0, ErrGasUintOverflow
         }
-        return total, nil
+        return extra, nil
core/blockchain_test.go (1)

687-687: Avoid shadowing the time package; rename local var.

Minor readability fix; prevents accidental package shadowing in-loop.

-    num, hash, time := blocks[i].NumberU64(), blocks[i].Hash(), blocks[i].Time()
+    num, hash, blockTime := blocks[i].NumberU64(), blocks[i].Hash(), blocks[i].Time()
@@
-    freceipts := rawdb.ReadReceipts(fastDb, hash, num, time, fast.Config())
-    anreceipts := rawdb.ReadReceipts(ancientDb, hash, num, time, fast.Config())
-    areceipts := rawdb.ReadReceipts(archiveDb, hash, num, time, fast.Config())
+    freceipts := rawdb.ReadReceipts(fastDb, hash, num, blockTime, fast.Config())
+    anreceipts := rawdb.ReadReceipts(ancientDb, hash, num, blockTime, fast.Config())
+    areceipts := rawdb.ReadReceipts(archiveDb, hash, num, blockTime, fast.Config())

Also applies to: 710-713

miner/worker.go (5)

320-321: Consider handling Sender() errors to skip malformed txs early.

Ignoring the error can waste cycles and spam logs. If derivation fails, drop the tx and continue.

Apply:

- from, _ := types.Sender(env.signer, tx)
+ from, errSender := types.Sender(env.signer, tx)
+ if errSender != nil {
+   log.Trace("Skipping tx with unresolvable sender", "hash", tx.Hash(), "err", errSender)
+   txs.Pop()
+   continue
+ }

324-324: Fix typo in log message: “reply” → “replay”.

- log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", miner.chainConfig.EIP155Block)
+ log.Trace("Ignoring replay-protected transaction", "hash", tx.Hash(), "eip155", miner.chainConfig.EIP155Block)

355-355: Fix typo in log message: “hight” → “high”.

- log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
+ log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce())

81-81: Spelling nit: “timstamp” → “timestamp”.

- timestamp    uint64             // The timstamp for sealing task
+ timestamp    uint64             // The timestamp for sealing task

432-432: Comment clarity nit.

- // Giving up involving L2 transactions if it is simulation for L1Messages
+ // Skip L2 transactions when simulating L1 messages
tests/state_test_util.go (1)

358-360: Add coverage for non-nil AuthorizationList (EIP-7702).

Include a test vector with a minimal valid SetCode authorization to exercise NewMessage plumbing through ApplyMessage.

I can draft a table-driven test that builds a Message with a single authorization and asserts state changes and gas accounting—want me to add it?

core/types/access_list_tx.go (1)

111-114: Optional: document unused baseFee param to avoid confusion.

-func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
+// For EIP-2930 tx, effective gas price equals GasPrice; baseFee is ignored.
+func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
core/types/tx_setcode.go (2)

108-114: Name the 0x05 domain prefix to avoid magic numbers.

-func (a *SetCodeAuthorization) SigHash() common.Hash {
-	return prefixedRlpHash(0x05, []any{
+func (a *SetCodeAuthorization) SigHash() common.Hash {
+	return prefixedRlpHash(AuthorizationSigHashPrefix, []any{

Add this constant near the transaction type declarations:

// AuthorizationSigHashPrefix is the typed-data prefix for SetCode authorizations (EIP-7702).
const AuthorizationSigHashPrefix = byte(0x05)

108-114: Optional: unit test SigHash/Authority roundtrip.

Add tests that:

  • build an auth, sign with a known key via SignSetCode,
  • assert a.SigHash() matches a known vector,
  • assert Authority() recovers the signer.

I can contribute a test using the dev key (0x...deadbeef) and a fixed chainId/nonce to pin the hash.

core/vm/jump_table.go (1)

69-73: Morph300 inherits Curie and enables 7702 — OK.

Minor: consider a brief comment noting which semantics 7702 toggles here for quick grepability.

light/odr_util.go (1)

171-182: Avoid reassembling the block just to compute receipt fields.

Minor perf: fetch header/body directly and pass their fields to DeriveFields to skip NewBlock/WithBody.

Apply:

-        block, err := GetBlock(ctx, odr, hash, number)
+        header, err := GetHeaderByNumber(ctx, odr, number)
         if err != nil {
             return nil, err
         }
-        genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
-        config := rawdb.ReadChainConfig(odr.Database(), genesis)
-
-        if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), block.Transactions()); err != nil {
+        body, err := GetBody(ctx, odr, hash, number)
+        if err != nil {
+            return nil, err
+        }
+        genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
+        config := rawdb.ReadChainConfig(odr.Database(), genesis)
+        if err := receipts.DeriveFields(config, header.Hash(), header.Number.Uint64(), header.Time, header.BaseFee, body.Transactions); err != nil {
             return nil, err
         }
         rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
core/rawdb/accessors_indexes.go (1)

133-139: Header requirement before ReadReceipts may change behavior

Early-returning when header is missing tightens invariants but could regress lookups if receipts exist while headers are pruned/corrupt. Consider a graceful fallback (time=0 via ReadReceipts) or log at warn-level and continue, if compatible with your storage guarantees.

- header := ReadHeader(db, blockHash, *blockNumber)
- if header == nil {
-   return nil, common.Hash{}, 0, 0
- }
- receipts := ReadReceipts(db, blockHash, *blockNumber, header.Time, config)
+ if header := ReadHeader(db, blockHash, *blockNumber); header != nil {
+   receipts := ReadReceipts(db, blockHash, *blockNumber, header.Time, config)
+   ...
+ } else {
+   // Optional: fallback if you want legacy tolerance; otherwise keep current behavior.
+   // receipts := ReadReceipts(db, blockHash, *blockNumber, 0, config)
+   return nil, common.Hash{}, 0, 0
+ }
les/fetcher/block_fetcher_test.go (1)

55-56: Prefer block.Time() over parent.Time()+10

Using the child block’s timestamp avoids edge cases around fork-time thresholds and reads clearer in tests.

- signer := types.MakeSigner(params.TestChainConfig, block.Number(), parent.Time()+10)
+ signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Time())
cmd/evm/internal/t8ntool/transaction.go (1)

116-116: Use actual block timestamp when creating signer
Replace the hardcoded 0 at cmd/evm/internal/t8ntool/transaction.go:116 with the real block timestamp (from CLI or input) to ensure the correct signer is chosen around Morph300/EIP-7702.

rollup/tracing/tracing.go (1)

546-563: Fix typos in log messages (“intrinstic” → “intrinsic”).

Keeps logs professional and searchable.

Apply:

-				log.Error("Proof for intrinstic address not available", "error", err, "address", addr)
+				log.Error("Proof for intrinsic address not available", "error", err, "address", addr)
@@
-					log.Error("Storage proof for intrinstic address not available", "error", err, "address", addr)
+					log.Error("Storage proof for intrinsic address not available", "error", err, "address", addr)
@@
-					log.Error("Get storage proof for intrinstic address failed", "error", err, "address", addr, "slot", slot)
+					log.Error("Get storage proof for intrinsic address failed", "error", err, "address", addr, "slot", slot)
params/config.go (2)

544-544: Fix comment typo for Morph300.

“Morph303Time” should be “Morph300Time”.

Apply:

-	Morph300Time        *uint64  `json:"morph300Time,omitempty"`        // Morph303Time switch time (nil = no fork, 0 = already on morph303)
+	Morph300Time        *uint64  `json:"morph300Time,omitempty"`        // Morph300Time switch time (nil = no fork, 0 = already on morph300)

625-660: Consider printing Morph300Time in ChainConfig.String().

Helps diagnostics when debugging signer and fork selection.

Apply:

 	return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Archimedes: %v, Shanghai: %v, Bernoulli: %v, Curie: %v, Morph203: %v, Engine: %v, Morph config: %v}",
@@
-		c.Morph203Time,
+		c.Morph203Time,
+		/* Morph300: */ c.Morph300Time,
core/vm/evm.go (1)

231-240: Use of resolveCode/resolveCodeHash in call paths — nice; consider minor perf tweak

This centralizes delegation logic. To shave a state read on Morph300 non-delegated accounts, resolveCodeHash can avoid fetching full code by first checking code size. See diff below.

Also applies to: 299-300, 342-343, 398-399

core/blockchain.go (2)

1403-1404: Partition sender recovery by signer to handle time-gated forks

Using a single signer built from chain[0]’s time can misrecover senders when the batch crosses a fork boundary gated by time (e.g., Morph300). Consider partitioning the batch by signer changes and invoking recovery per segment.

-	senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()), chain)
+	{
+		start := 0
+		for start < len(chain) {
+			s := types.MakeSigner(bc.chainConfig, chain[start].Number(), chain[start].Time())
+			end := start + 1
+			for end < len(chain) {
+				s2 := types.MakeSigner(bc.chainConfig, chain[end].Number(), chain[end].Time())
+				if reflect.TypeOf(s) != reflect.TypeOf(s2) {
+					break
+				}
+				end++
+			}
+			senderCacher.recoverFromBlocks(s, chain[start:end])
+			start = end
+		}
+	}

Add import (outside this hunk):

import "reflect"

1845-1849: Avoid redundant header reads for receipts derivation

ReadReceipts re-reads the header internally to fetch BaseFee; you already have header here. Consider a helper that accepts header to avoid an extra DB hit on reorg hot path.

core/types/dynamic_fee_tx.go (3)

39-41: Recheck JSON/gen-code tags removal for V/R/S

Dropping JSON/gencodec tags may alter RPC serialization behavior. If RPC never marshals DynamicFeeTx directly, fine; otherwise restore tags to avoid breaking clients.

-	V *big.Int
-	R *big.Int
-	S *big.Int
+	V *big.Int `json:"v" gencodec:"required"`
+	R *big.Int `json:"r" gencodec:"required"`
+	S *big.Int `json:"s" gencodec:"required"`

99-108: Clamp negative tip for robustness when baseFee > feeCap

While included blocks satisfy baseFee <= feeCap, calls/sims may not. Clamping avoids negative tip intermediates and matches min(max(feeCap-baseFee,0), tipCap)+baseFee.

  func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
  	if baseFee == nil {
  		return dst.Set(tx.GasFeeCap)
  	}
- 	tip := dst.Sub(tx.GasFeeCap, baseFee)
+ 	tip := dst.Sub(tx.GasFeeCap, baseFee)
+ 	if tip.Sign() < 0 {
+ 		tip.SetUint64(0)
+ 	}
  	if tip.Cmp(tx.GasTipCap) > 0 {
  		tip.Set(tx.GasTipCap)
  	}
  	return tip.Add(tip, baseFee)
  }

126-140: Defensively align chainID with tx payload

Prevent accidental domain mismatch by preferring tx.ChainID when provided; fall back to param if needed.

 func (tx *DynamicFeeTx) sigHash(chainID *big.Int) common.Hash {
+	if tx.ChainID != nil {
+		if chainID == nil || tx.ChainID.Cmp(chainID) != 0 {
+			chainID = tx.ChainID
+		}
+	}
 	return prefixedRlpHash(
 		DynamicFeeTxType,
 		[]any{
 			chainID,
 			tx.Nonce,
 			tx.GasTipCap,
 			tx.GasFeeCap,
 			tx.Gas,
 			tx.To,
 			tx.Value,
 			tx.Data,
 			tx.AccessList,
 		})
 }
core/tx_pool.go (3)

719-723: Use the canonical error for empty auth-list.

Prefer the shared error (defined in core/error.go) to keep error handling consistent.

Apply:

-        if len(tx.SetCodeAuthorizations()) == 0 {
-            return errors.New("set code tx must have at least one authorization tuple")
-        }
+        if len(tx.SetCodeAuthorizations()) == 0 {
+            return ErrEmptyAuthList
+        }

732-753: Delegation limit logic: looks fine, but add tests for corner cases.

Constraints for delegated/pending-delegation accounts and allowance for replacement by nonce are sensible. Please add unit tests covering:

  • delegated sender with no pending tx -> only next-nonce allowed;
  • replacement path (same nonce) succeeds;
  • non-delegated sender unaffected.

I can draft targeted txpool tests for these scenarios if helpful.


1845-1855: Authority tracking implementation is reasonable; minor perf note.

Slice-based tracking with slices.Contains/Index is OK given the “≤1 in-flight per authority” policy bounds list sizes. If this grows later, consider a small set structure to avoid O(n) scans.

Also applies to: 2007-2052

core/types/transaction_signing.go (3)

71-89: LatestSigner(nil ChainID) returns nil Signer

Consider returning HomesteadSigner when ChainID is nil to avoid nil deref at call sites expecting a non-nil signer.

-    }
-    return signer
+    }
+    if signer == nil {
+        signer = HomesteadSigner{}
+    }
+    return signer

265-281: Typed tx: accepting chainID=0 — confirm spec intent

SignatureValues allows chainID==0 (treated as “unspecified”). For EIP-2930/1559/4844, chainID is typically mandatory. If this exception is only for SetCode, gate it by tx type.

- if tx.inner.chainID().Sign() != 0 && tx.inner.chainID().Cmp(s.chainID) != 0 {
+ if tt != SetCodeTxType && tx.inner.chainID().Sign() == 0 {
+     return nil, nil, nil, fmt.Errorf("%w: missing chain id", ErrInvalidChainId)
+ }
+ if tx.inner.chainID().Sign() != 0 && tx.inner.chainID().Cmp(s.chainID) != 0 {

346-360: Consistent error details for chain ID mismatch

EIP155Signer.Sender returns ErrInvalidChainId without “have/want” context. Mirror modernSigner’s detailed message for easier debugging.

-    if tx.ChainId().Cmp(s.chainId) != 0 {
-        return common.Address{}, ErrInvalidChainId
-    }
+    if tx.ChainId().Cmp(s.chainId) != 0 {
+        return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainId)
+    }
core/state_transition.go (3)

331-339: Precheck for SetCode: no creation and non-empty list

Solid. Consider mirroring this validation in RPC (setDefaults) to fail fast.


424-442: Authorization application: errors ignored and warming of final delegation

If per-spec invalid auths must be ignored (not revert), this is correct. Please confirm; otherwise, log at TRACE for diagnosability.

-            // Note errors are ignored, we simply skip invalid authorizations here.
-            st.applyAuthorization(&auth)
+            if err := st.applyAuthorization(&auth); err != nil {
+                log.Trace("Skipped invalid EIP-7702 authorization", "err", err)
+            }

518-544: Refund math relies on EIP-3529 cap; confirm net gas matches intended schedule

The refund of (CallNewAccountGas - TxAuthTupleGas) for existing accounts is fine if intrinsic charged “max upfront.” Ensure the final net cost matches the intended tuple/new-account economics under refund caps.

I can add unit tests covering: existing vs new authority, invalid signature, and duplicate tuples to verify gas deltas and state changes.

internal/ethapi/transaction_args.go (1)

57-59: Make AuthorizationList omitempty

Avoids emitting an empty array by default in JSON-RPC responses.

-    AuthorizationList []types.SetCodeAuthorization `json:"authorizationList"`
+    AuthorizationList []types.SetCodeAuthorization `json:"authorizationList,omitempty"`
core/types/transaction.go (2)

492-514: Minor cleanup: use struct{} set and pre-size map.

Saves a few bytes per entry and avoids bool allocations. Behavior unchanged.

Apply:

- var (
-   marks = make(map[common.Address]bool)
-   auths = make([]common.Address, 0, len(setcodetx.AuthList))
- )
+ var (
+   marks = make(map[common.Address]struct{}, len(setcodetx.AuthList))
+   auths = make([]common.Address, 0, len(setcodetx.AuthList))
+ )
  for _, auth := range setcodetx.AuthList {
    if addr, err := auth.Authority(); err == nil {
-     if marks[addr] {
+     if _, seen := marks[addr]; seen {
        continue
      }
-     marks[addr] = true
+     marks[addr] = struct{}{}
      auths = append(auths, addr)
    }
  }

774-787: Prefer centralized gas price derivation to avoid drift.

Since TxData has effectiveGasPrice now, reuse it to compute msg.gasPrice with baseFee.

Apply:

   // If baseFee provided, set gasPrice to effectiveGasPrice.
-  if baseFee != nil {
-    msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
-  }
+  if baseFee != nil {
+    msg.gasPrice = tx.inner.effectiveGasPrice(msg.gasPrice, baseFee)
+  }

Also applies to: 789-791

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c59ecb3 and e295ad8.

📒 Files selected for processing (73)
  • accounts/abi/bind/backends/simulated.go (4 hunks)
  • accounts/external/backend.go (1 hunks)
  • cmd/evm/internal/t8ntool/execution.go (1 hunks)
  • cmd/evm/internal/t8ntool/transaction.go (2 hunks)
  • cmd/evm/internal/t8ntool/transition.go (1 hunks)
  • core/bench_test.go (3 hunks)
  • core/blockchain.go (2 hunks)
  • core/blockchain_l2.go (1 hunks)
  • core/blockchain_reader.go (1 hunks)
  • core/blockchain_test.go (2 hunks)
  • core/error.go (1 hunks)
  • core/rawdb/accessors_chain.go (4 hunks)
  • core/rawdb/accessors_chain_test.go (5 hunks)
  • core/rawdb/accessors_indexes.go (1 hunks)
  • core/state/journal.go (2 hunks)
  • core/state/state_object.go (1 hunks)
  • core/state_prefetcher.go (1 hunks)
  • core/state_processor.go (2 hunks)
  • core/state_transition.go (8 hunks)
  • core/tx_list.go (1 hunks)
  • core/tx_pool.go (10 hunks)
  • core/types/access_list_tx.go (2 hunks)
  • core/types/dynamic_fee_tx.go (3 hunks)
  • core/types/l1_message_tx.go (2 hunks)
  • core/types/l2trace.go (1 hunks)
  • core/types/legacy_tx.go (2 hunks)
  • core/types/receipt.go (4 hunks)
  • core/types/receipt_test.go (1 hunks)
  • core/types/transaction.go (3 hunks)
  • core/types/transaction_signing.go (5 hunks)
  • core/types/tx_blob.go (2 hunks)
  • core/types/tx_blob_test.go (1 hunks)
  • core/types/tx_setcode.go (3 hunks)
  • core/vm/contracts_test.go (1 hunks)
  • core/vm/eips.go (2 hunks)
  • core/vm/evm.go (6 hunks)
  • core/vm/interpreter.go (1 hunks)
  • core/vm/jump_table.go (1 hunks)
  • core/vm/operations_acl.go (2 hunks)
  • eth/catalyst/api.go (1 hunks)
  • eth/downloader/queue_test.go (1 hunks)
  • eth/downloader/testchain_test.go (1 hunks)
  • eth/fetcher/block_fetcher_test.go (1 hunks)
  • eth/filters/filter_system_test.go (1 hunks)
  • eth/gasprice/gasprice.go (4 hunks)
  • eth/state_accessor.go (1 hunks)
  • eth/tracers/api.go (5 hunks)
  • eth/tracers/api_test.go (1 hunks)
  • eth/tracers/internal/tracetest/calltrace_test.go (2 hunks)
  • eth/tracers/js/tracer.go (1 hunks)
  • eth/tracers/native/prestate.go (2 hunks)
  • ethclient/ethclient.go (1 hunks)
  • ethclient/gethclient/gethclient.go (1 hunks)
  • graphql/graphql.go (2 hunks)
  • interfaces.go (1 hunks)
  • internal/ethapi/api.go (11 hunks)
  • internal/ethapi/transaction_args.go (4 hunks)
  • les/downloader/queue_test.go (1 hunks)
  • les/downloader/testchain_test.go (1 hunks)
  • les/fetcher/block_fetcher_test.go (1 hunks)
  • les/handler_test.go (1 hunks)
  • les/odr_test.go (3 hunks)
  • les/state_accessor.go (1 hunks)
  • light/odr_test.go (2 hunks)
  • light/odr_util.go (1 hunks)
  • light/txpool.go (2 hunks)
  • miner/worker.go (1 hunks)
  • params/config.go (4 hunks)
  • params/forks/forks.go (1 hunks)
  • params/protocol_params.go (1 hunks)
  • rollup/tracing/tracing.go (2 hunks)
  • tests/state_test_util.go (1 hunks)
  • tests/transaction_test_util.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (58)
eth/tracers/native/prestate.go (2)
params/config.go (1)
  • ChainConfig (515-556)
core/types/tx_setcode.go (1)
  • ParseDelegation (37-42)
core/vm/contracts_test.go (1)
common/bytes.go (1)
  • Bytes2Hex (71-73)
core/types/l2trace.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
graphql/graphql.go (1)
core/types/transaction.go (2)
  • DynamicFeeTxType (52-52)
  • SetCodeTxType (54-54)
accounts/external/backend.go (1)
core/types/transaction.go (2)
  • DynamicFeeTxType (52-52)
  • SetCodeTxType (54-54)
interfaces.go (1)
core/types/tx_setcode.go (1)
  • SetCodeAuthorization (72-79)
core/types/tx_blob_test.go (1)
core/types/transaction_signing.go (1)
  • NewCurieSigner (300-302)
core/vm/eips.go (2)
core/vm/jump_table.go (1)
  • JumpTable (67-67)
core/vm/opcodes.go (4)
  • CALL (217-217)
  • CALLCODE (218-218)
  • STATICCALL (223-223)
  • DELEGATECALL (220-220)
core/blockchain_reader.go (1)
core/rawdb/accessors_chain.go (2)
  • ReadHeader (317-328)
  • ReadReceipts (565-589)
cmd/evm/internal/t8ntool/execution.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/tracers/js/tracer.go (1)
core/state_transition.go (1)
  • IntrinsicGas (133-183)
miner/worker.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/filters/filter_system_test.go (1)
core/rawdb/accessors_chain.go (2)
  • ReadHeader (317-328)
  • ReadReceipts (565-589)
core/vm/evm.go (2)
core/vm/interface.go (1)
  • StateDB (28-88)
core/types/tx_setcode.go (1)
  • ParseDelegation (37-42)
eth/state_accessor.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/fetcher/block_fetcher_test.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • TestChainConfig (398-427)
core/types/receipt.go (2)
core/types/transaction.go (6)
  • DynamicFeeTxType (52-52)
  • AccessListTxType (51-51)
  • BlobTxType (53-53)
  • L1MessageTxType (56-56)
  • SetCodeTxType (54-54)
  • Transactions (575-575)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/catalyst/api.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/state_processor.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/access_list_tx.go (1)
core/types/transaction.go (1)
  • AccessListTxType (51-51)
core/blockchain_test.go (1)
core/rawdb/accessors_chain.go (1)
  • ReadReceipts (565-589)
eth/tracers/internal/tracetest/calltrace_test.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/tracers/api_test.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/downloader/queue_test.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • TestChainConfig (398-427)
core/types/tx_blob.go (1)
core/types/transaction.go (1)
  • BlobTxType (53-53)
cmd/evm/internal/t8ntool/transition.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
eth/downloader/testchain_test.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • TestChainConfig (398-427)
les/state_accessor.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/rawdb/accessors_indexes.go (1)
core/rawdb/accessors_chain.go (2)
  • ReadHeader (317-328)
  • ReadReceipts (565-589)
core/types/tx_setcode.go (1)
core/types/gen_authorization.go (2)
  • SetCodeAuthorization (18-25)
  • SetCodeAuthorization (38-45)
core/rawdb/accessors_chain.go (4)
ethdb/database.go (1)
  • Reader (128-131)
params/config.go (1)
  • ChainConfig (515-556)
core/types/receipt.go (1)
  • Receipts (418-418)
rlp/decode.go (1)
  • DecodeBytes (92-106)
les/fetcher/block_fetcher_test.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • TestChainConfig (398-427)
les/downloader/queue_test.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • TestChainConfig (398-427)
rollup/tracing/tracing.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/l2trace.go (1)
  • NewTransactionData (150-180)
core/types/dynamic_fee_tx.go (2)
core/types/transaction.go (1)
  • DynamicFeeTxType (52-52)
core/types/access_list_tx.go (1)
  • AccessList (30-30)
light/txpool.go (1)
core/state_transition.go (1)
  • IntrinsicGas (133-183)
core/state_prefetcher.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/blockchain.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/rawdb/accessors_chain.go (1)
  • ReadReceipts (565-589)
les/handler_test.go (1)
core/rawdb/accessors_chain.go (1)
  • ReadReceipts (565-589)
internal/ethapi/transaction_args.go (2)
core/types/tx_setcode.go (2)
  • SetCodeAuthorization (72-79)
  • SetCodeTx (51-67)
core/types/transaction.go (6)
  • NewMessage (754-770)
  • LegacyTxType (50-50)
  • SetCodeTxType (54-54)
  • DynamicFeeTxType (52-52)
  • AccessListTxType (51-51)
  • TxData (80-111)
cmd/evm/internal/t8ntool/transaction.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/state_transition.go (1)
  • IntrinsicGas (133-183)
core/tx_pool.go (5)
core/types/transaction.go (3)
  • SetCodeTxType (54-54)
  • ErrTxTypeNotSupported (39-39)
  • Transaction (60-68)
core/error.go (5)
  • ErrTxTypeNotSupported (89-89)
  • ErrIntrinsicGas (85-85)
  • ErrOutOfOrderTxFromDelegated (122-122)
  • ErrInflightTxLimitReached (118-118)
  • ErrAuthorityReserved (127-127)
core/state_transition.go (1)
  • IntrinsicGas (133-183)
core/types/transaction_signing.go (1)
  • Sender (146-162)
crypto/codehash/codehash.go (1)
  • EmptyKeccakCodeHash (10-10)
les/downloader/testchain_test.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • TestChainConfig (398-427)
eth/gasprice/gasprice.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • ChainConfig (515-556)
eth/tracers/api.go (2)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
params/config.go (1)
  • ChainConfig (515-556)
core/state_transition.go (3)
core/types/tx_setcode.go (3)
  • SetCodeAuthorization (72-79)
  • ParseDelegation (37-42)
  • AddressToDelegation (45-47)
params/protocol_params.go (2)
  • CallNewAccountGas (31-31)
  • TxAuthTupleGas (93-93)
core/error.go (8)
  • ErrSenderNoEOA (108-108)
  • ErrSetCodeTxCreate (114-114)
  • ErrEmptyAuthList (113-113)
  • ErrAuthorizationWrongChainID (133-133)
  • ErrAuthorizationNonceOverflow (134-134)
  • ErrAuthorizationInvalidSignature (135-135)
  • ErrAuthorizationDestinationHasCode (136-136)
  • ErrAuthorizationNonceMismatch (137-137)
core/vm/operations_acl.go (7)
core/vm/evm.go (1)
  • EVM (107-133)
core/vm/interface.go (1)
  • StateDB (28-88)
params/protocol_params.go (2)
  • ColdAccountAccessCostEIP2929 (63-63)
  • WarmStorageReadCostEIP2929 (65-65)
core/vm/errors.go (1)
  • ErrOutOfGas (26-26)
core/types/tx_setcode.go (1)
  • ParseDelegation (37-42)
common/math/integer.go (1)
  • SafeAdd (89-92)
core/error.go (1)
  • ErrGasUintOverflow (81-81)
core/types/receipt_test.go (1)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/transaction_signing.go (4)
params/config.go (1)
  • ChainConfig (515-556)
params/forks/forks.go (7)
  • Fork (20-20)
  • SpuriousDragon (28-28)
  • Homestead (25-25)
  • Berlin (34-34)
  • London (35-35)
  • Curie (40-40)
  • Morph300 (42-42)
core/types/transaction.go (7)
  • LegacyTxType (50-50)
  • AccessListTxType (51-51)
  • DynamicFeeTxType (52-52)
  • BlobTxType (53-53)
  • SetCodeTxType (54-54)
  • Transaction (60-68)
  • ErrTxTypeNotSupported (39-39)
cmd/evm/internal/t8ntool/transaction.go (1)
  • Transaction (68-184)
internal/ethapi/api.go (3)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/tx_setcode.go (1)
  • SetCodeAuthorization (72-79)
core/types/transaction.go (2)
  • Transaction (60-68)
  • SetCodeTxType (54-54)
core/bench_test.go (3)
core/state_transition.go (1)
  • IntrinsicGas (133-183)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/rawdb/accessors_chain.go (1)
  • ReadReceipts (565-589)
light/odr_util.go (1)
core/types/transaction.go (1)
  • Transactions (575-575)
les/odr_test.go (3)
core/rawdb/accessors_chain.go (2)
  • ReadHeader (317-328)
  • ReadReceipts (565-589)
core/types/transaction.go (1)
  • NewMessage (754-770)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
light/odr_test.go (4)
core/rawdb/accessors_chain.go (2)
  • ReadHeader (317-328)
  • ReadReceipts (565-589)
core/types/transaction.go (1)
  • NewMessage (754-770)
core/evm.go (1)
  • NewEVMBlockContext (40-68)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/transaction.go (3)
core/types/tx_setcode.go (1)
  • SetCodeAuthorization (72-79)
core/types/gen_authorization.go (2)
  • SetCodeAuthorization (18-25)
  • SetCodeAuthorization (38-45)
core/state_transition.go (1)
  • Message (78-94)
core/rawdb/accessors_chain_test.go (2)
core/rawdb/accessors_chain.go (2)
  • ReadReceipts (565-589)
  • WriteReceipts (592-606)
core/types/receipt.go (1)
  • Receipts (418-418)
tests/transaction_test_util.go (2)
core/state_transition.go (1)
  • IntrinsicGas (133-183)
core/types/access_list_tx.go (1)
  • AccessList (30-30)
accounts/abi/bind/backends/simulated.go (4)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/tx_setcode.go (1)
  • SetCodeAuthorization (72-79)
interfaces.go (1)
  • CallMsg (129-147)
core/rawdb/accessors_chain.go (2)
  • ReadHeader (317-328)
  • ReadReceipts (565-589)

Comment on lines +221 to 224
case types.DynamicFeeTxType, types.SetCodeTxType:
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
default:
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.

💡 Verification agent

🧩 Analysis chain

SetCodeTxType gas handling OK; also propagate AuthorizationList to signer args

Dynamic-fee handling for SetCode is good. But the authorization list is not forwarded to clef, risking data loss when signing SetCode txs.

@@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
-		accessList := tx.AccessList()
-		args.AccessList = &accessList
+		accessList := tx.AccessList()
+		args.AccessList = &accessList
+		// For EIP-7702 SetCodeTxType, forward authorizations to the external signer (requires apitypes support).
+		if tx.Type() == types.SetCodeTxType {
+			auth := tx.SetCodeAuthorizations()
+			if len(auth) > 0 {
+				args.AuthorizationList = &auth
+			}
+		}

Verification:


🏁 Script executed:

#!/bin/bash
# Check if apitypes.SendTxArgs supports AuthorizationList; if not, add it.
rg -nC2 'type\s+SendTxArgs\s+struct' signer/core/apitypes | sed -n '1,200p'
rg -n 'AuthorizationList' --type=go signer/core/apitypes

Length of output: 563


🏁 Script executed:

#!/bin/bash
# Extract the full SendTxArgs struct definition to verify AuthorizationList support
sed -n '/type SendTxArgs struct {/,/^}/p' signer/core/apitypes/types.go

Length of output: 1056


Add AuthorizationList to SendTxArgs and forward SetCode authorizations

The SendTxArgs struct in signer/core/apitypes/types.go doesn’t include an AuthorizationList field. Add it just above ChainID:

 type SendTxArgs struct {
     … 
     AccessList *types.AccessList    `json:"accessList,omitempty"`
+    // For EIP-7702 SetCode transactions
+    AuthorizationList *[]types.Authorization `json:"authorizationList,omitempty"`
     ChainID    *hexutil.Big         `json:"chainId,omitempty"`
 }

Then in accounts/external/backend.go inside SignTx, propagate the authorizations for SetCodeTxType:

     accessList := tx.AccessList()
     args.AccessList = &accessList
+    if tx.Type() == types.SetCodeTxType {
+        auth := tx.SetCodeAuthorizations()
+        if len(auth) > 0 {
+            args.AuthorizationList = &auth
+        }
+    }

This ensures the external signer (e.g., clef) receives the authorization list when signing SetCode transactions.

Comment on lines +576 to +584
header := ReadHeader(db, hash, number)

var baseFee *big.Int
if header == nil {
baseFee = big.NewInt(0)
} else {
baseFee = header.BaseFee
}
if err := receipts.DeriveFields(config, hash, number, time, baseFee, body.Transactions); err != nil {
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.

🛠️ Refactor suggestion

Base fee nil vs zero: preserve pre-London semantics

Passing big.NewInt(0) when the header is missing subtly changes semantics for effective gas price (nil typically means “pre-London”). Use nil unless header.BaseFee exists.

-    var baseFee *big.Int
-    if header == nil {
-        baseFee = big.NewInt(0)
-    } else {
-        baseFee = header.BaseFee
-    }
+    var baseFee *big.Int
+    if header != nil {
+        baseFee = header.BaseFee // may be nil pre-London
+    }
🤖 Prompt for AI Agents
In core/rawdb/accessors_chain.go around lines 576 to 584, the code currently
sets baseFee to big.NewInt(0) when the header is missing which changes
semantics; instead preserve pre‑London semantics by setting baseFee to nil when
header == nil and only assign header.BaseFee when header exists, so
receipts.DeriveFields receives nil for pre‑London blocks rather than a zero
big.Int.

Comment thread core/state_prefetcher.go
blockContext = NewEVMBlockContext(header, p.bc, p.config, nil)
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
signer = types.MakeSigner(p.config, header.Number)
signer = types.MakeSigner(p.config, header.Number, header.Time)
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.

💡 Verification agent

🧩 Analysis chain

Time-aware signer: confirm all call sites updated and consistent with header time

Looks correct. Please verify there are no remaining two-arg MakeSigner calls and that all callers provide the correct block time (not 0 or nil-equivalent).

Run:


🏁 Script executed:

#!/bin/bash
# Find legacy 2-arg MakeSigner calls (should return nothing)
rg -nP --glob '!**/vendor/**' 'MakeSigner\s*\([^,]+,\s*[^,]+\s*\)'

# List all 3-arg calls for a quick scan
rg -nP --glob '!**/vendor/**' 'MakeSigner\s*\([^)]*,\s*[^)]*,\s*[^)]+\)'

# Flag suspicious time arg (0 or nil)
rg -nP --glob '!**/vendor/**' 'MakeSigner\([^,]+,\s*[^,]+,\s*(0|nil)\s*\)'

Length of output: 4884


Replace zero block time in MakeSigner call
In cmd/evm/internal/t8ntool/transaction.go:116 the call

signer := types.MakeSigner(chainConfig, new(big.Int), 0)

passes 0 for the block time (nil-equivalent), defeating time‐aware signing. Supply the actual timestamp (e.g. prestate.Env.Timestamp) to this and any similar callers.

🤖 Prompt for AI Agents
core/state_prefetcher.go around line 58: the MakeSigner call must not receive a
zero/nil block time; replace any hardcoded 0 arguments (e.g. in
cmd/evm/internal/t8ntool/transaction.go:116) with the actual block timestamp
(use prestate.Env.Timestamp or the header.Time available in context), converting
to the expected type if necessary, and audit other MakeSigner callers to pass
the real timestamp instead of 0.

Comment thread core/tx_pool.go
Comment on lines +763 to 779
// For symmetry, allow at most one in-flight tx for any authority with a
// pending transaction.
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
for _, auth := range auths {
var count int
if pending := pool.pending[auth]; pending != nil {
count += pending.Len()
}
if queue := pool.queue[auth]; queue != nil {
count += queue.Len()
}
if count > 1 {
return ErrAuthorityReserved
}
}
}
return nil
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.

🛠️ Refactor suggestion

⚠️ Potential issue

Authority reservation check is incorrect source; may under/over-enforce.

Current logic counts txs by “from = authority” in pending/queue, but the constraint is about transactions that reference an authority, regardless of the sender. This can:

  • miss conflicts from other senders referencing the same authority, and
  • falsely block when the authority just has unrelated txs.

Use the txLookup auth index to detect real conflicts and allow same-sender/same-nonce replacement.

Apply:

-    // For symmetry, allow at most one in-flight tx for any authority with a
-    // pending transaction.
-    if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
-        for _, auth := range auths {
-            var count int
-            if pending := pool.pending[auth]; pending != nil {
-                count += pending.Len()
-            }
-            if queue := pool.queue[auth]; queue != nil {
-                count += queue.Len()
-            }
-            if count > 1 {
-                return ErrAuthorityReserved
-            }
-        }
-    }
+    // For symmetry, allow at most one in-flight tx per authority across the pool,
+    // but permit replacement by the same sender+nonce.
+    from, _ := types.Sender(pool.signer, tx) // already validated
+    if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
+        for _, auth := range auths {
+            for _, h := range pool.all.AuthTxHashes(auth) {
+                if other := pool.all.Get(h); other != nil {
+                    ofrom, _ := types.Sender(pool.signer, other)
+                    if ofrom != from || other.Nonce() != tx.Nonce() {
+                        return ErrAuthorityReserved
+                    }
+                }
+            }
+        }
+    }

Add this helper to txLookup (outside this hunk):

// AuthTxHashes returns a copy of tx hashes that reference the given authority.
func (t *txLookup) AuthTxHashes(addr common.Address) []common.Hash {
    t.lock.RLock()
    defer t.lock.RUnlock()
    list := t.auths[addr]
    if len(list) == 0 {
        return nil
    }
    out := make([]common.Hash, len(list))
    copy(out, list)
    return out
}

Comment on lines +50 to +53
func (tx *L1MessageTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return new(big.Int)
}

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.

🛠️ Refactor suggestion

Avoid allocation; honor dst parameter.

Return dst set to 0 instead of allocating a new big.Int to match the TxData contract used by other tx types.

 func (tx *L1MessageTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
-	return new(big.Int)
+	return dst.SetUint64(0)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (tx *L1MessageTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return new(big.Int)
}
func (tx *L1MessageTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.SetUint64(0)
}
🤖 Prompt for AI Agents
In core/types/l1_message_tx.go around lines 50-53, the effectiveGasPrice
implementation incorrectly allocates a new big.Int instead of using the provided
dst; change it to set dst to zero and return dst (e.g., dst.SetInt64(0); return
dst) so the function honors the dst parameter and avoids allocation to match the
TxData contract used by other tx types; if your codebase allows dst to be nil,
guard for nil and allocate only in that edge case before setting to zero.

Comment thread internal/ethapi/api.go
Comment on lines +1442 to 1458
case types.SetCodeTxType:
al := tx.AccessList()
yparity := hexutil.Uint64(v.Sign())
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
result.YParity = &yparity
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
result.GasPrice = (*hexutil.Big)(price)
} else {
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
}
result.AuthorizationList = tx.SetCodeAuthorizations()
}
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.

⚠️ Potential issue

Fix yParity derivation; v.Sign() is not robust.

v.Sign() yields 1 for any non-zero v, collapsing 1/27/28 into 1. Derive parity from the least-significant bit.

Apply:

-    yparity := hexutil.Uint64(v.Sign())
+    y := v.Uint64()
+    yparity := hexutil.Uint64(y & 1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case types.SetCodeTxType:
al := tx.AccessList()
yparity := hexutil.Uint64(v.Sign())
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
result.YParity = &yparity
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
result.GasPrice = (*hexutil.Big)(price)
} else {
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
}
result.AuthorizationList = tx.SetCodeAuthorizations()
}
case types.SetCodeTxType:
al := tx.AccessList()
// derive yParity from the least-significant bit of v
y := v.Uint64()
yparity := hexutil.Uint64(y & 1)
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
result.YParity = &yparity
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
// if the transaction has been mined, compute the effective gas price
if baseFee != nil && blockHash != (common.Hash{}) {
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
result.GasPrice = (*hexutil.Big)(price)
} else {
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
}
result.AuthorizationList = tx.SetCodeAuthorizations()
}
🤖 Prompt for AI Agents
internal/ethapi/api.go around lines 1442 to 1458: the code uses v.Sign() to set
yParity which collapses any non-zero v to 1; instead derive parity from v's
least-significant bit. Replace the yparity assignment to compute parity as
v.Bit(0) (or v.Uint64() & 1) so it yields 0 or 1 correctly, and use that value
to set yParity while keeping the rest of the case block unchanged.

Comment thread internal/ethapi/api.go Outdated
Comment thread internal/ethapi/api.go Outdated
Comment thread internal/ethapi/transaction_args.go
Comment thread params/forks/forks.go
Comment on lines +54 to +74
var forkToString = map[Fork]string{
Frontier: "Frontier",
FrontierThawing: "Frontier Thawing",
Homestead: "Homestead",
DAO: "DAO",
TangerineWhistle: "Tangerine Whistle",
SpuriousDragon: "Spurious Dragon",
Byzantium: "Byzantium",
Constantinople: "Constantinople",
Petersburg: "Petersburg",
Istanbul: "Istanbul",
MuirGlacier: "Muir Glacier",
Berlin: "Berlin",
London: "London",
ArrowGlacier: "Arrow Glacier",
Archimedes: "Archimedes",
Shanghai: "Shanghai",
Bernoulli: "Bernoulli",
Curie: "Curie",
Morph203: "Morph203",
}
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.

⚠️ Potential issue

Missing String() mapping for Morph300.
String() returns "Unknown fork" for Morph300; impacts logs/metrics/tests.

 var forkToString = map[Fork]string{
@@
     Curie:            "Curie",
     Morph203:         "Morph203",
+    Morph300:         "Morph300",
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var forkToString = map[Fork]string{
Frontier: "Frontier",
FrontierThawing: "Frontier Thawing",
Homestead: "Homestead",
DAO: "DAO",
TangerineWhistle: "Tangerine Whistle",
SpuriousDragon: "Spurious Dragon",
Byzantium: "Byzantium",
Constantinople: "Constantinople",
Petersburg: "Petersburg",
Istanbul: "Istanbul",
MuirGlacier: "Muir Glacier",
Berlin: "Berlin",
London: "London",
ArrowGlacier: "Arrow Glacier",
Archimedes: "Archimedes",
Shanghai: "Shanghai",
Bernoulli: "Bernoulli",
Curie: "Curie",
Morph203: "Morph203",
}
var forkToString = map[Fork]string{
Frontier: "Frontier",
FrontierThawing: "Frontier Thawing",
Homestead: "Homestead",
DAO: "DAO",
TangerineWhistle: "Tangerine Whistle",
SpuriousDragon: "Spurious Dragon",
Byzantium: "Byzantium",
Constantinople: "Constantinople",
Petersburg: "Petersburg",
Istanbul: "Istanbul",
MuirGlacier: "Muir Glacier",
Berlin: "Berlin",
London: "London",
ArrowGlacier: "Arrow Glacier",
Archimedes: "Archimedes",
Shanghai: "Shanghai",
Bernoulli: "Bernoulli",
Curie: "Curie",
Morph203: "Morph203",
Morph300: "Morph300",
}
🤖 Prompt for AI Agents
In params/forks/forks.go around lines 54 to 74, the forkToString map is missing
an entry for Morph300 which causes String() to return "Unknown fork"; add a new
map entry mapping Morph300 to "Morph300" (i.e. forkToString[Morph300] =
"Morph300") in the same style as the other entries, then run tests/lint to
ensure logs/metrics/tests pick up the new string.

* refactor tracer

* fix getBlockTrace

* fix nil pointer

* fix tracer api

* use origin evm.StateDB to applyMessage
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 40

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (9)
eth/tracers/api_blocktrace.go (2)

86-92: Default logger.Config is built but never used.

You create &logger.Config{...} on nil input, but nothing in this function (or the wrapper call below) consumes it. As-is, these toggles (DisableStorage/Stack, EnableMemory/ReturnData) have no effect on the produced trace.

Two options:

  • Wire the config through: plumb *TraceConfig (or *logger.Config) into the tracing path and have the wrapper honor it.
  • Or, if config is intentionally deprecated here, remove this block to avoid a misleading no-op and document the new source of truth for tracer settings.

111-113: Tracer config not propagated to execution wrapper.

CreateTraceEnvAndGetBlockTrace(...) receives no config, so any user-supplied trace options (except Reexec, handled above) are ignored. This is likely an unintended regression vs. the previous VM LogConfig plumbing.

Proposed interface/threading change within this file (wrapper implementations must be updated accordingly):

- type morphTracerWrapper interface {
-   CreateTraceEnvAndGetBlockTrace(*params.ChainConfig, core.ChainContext, consensus.Engine, ethdb.Database, *state.StateDB, *types.Block, *types.Block, bool) (*types.BlockTrace, error)
- }
+ type morphTracerWrapper interface {
+   CreateTraceEnvAndGetBlockTrace(*params.ChainConfig, core.ChainContext, consensus.Engine, ethdb.Database, *state.StateDB, *types.Block, *types.Block, bool, *TraceConfig) (*types.BlockTrace, error)
+ }
@@
- return api.morphTracerWrapper.CreateTraceEnvAndGetBlockTrace(api.backend.ChainConfig(), api.chainContext(ctx), api.backend.Engine(), chaindb, statedb, parent, block, true)
+ return api.morphTracerWrapper.CreateTraceEnvAndGetBlockTrace(api.backend.ChainConfig(), api.chainContext(ctx), api.backend.Engine(), chaindb, statedb, parent, block, true, config)

If widening the interface is not desirable, consider constructing the concrete tracer from config.Config here and attaching it to the env the wrapper uses.

consensus/ethash/consensus.go (1)

660-671: Miner gets underpaid: final credit uses r (last per-uncle share) instead of total reward.

After the uncles loop, the miner should receive reward (blockReward plus 1/32 per uncle). Current code credits only r (which ends as blockReward/32, or 0 with no uncles).

Apply:

-    state.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle)
+    state.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle)
@@
-  state.AddBalance(header.Coinbase, r, tracing.BalanceIncreaseRewardMineBlock)
+  state.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock)

Optional clarity (avoid reuse of r):

-  r := new(big.Int)
+  r := new(big.Int)            // per-uncle reward (temporary)

Please add/adjust tests covering 0, 1, 2 uncles to prevent regressions.

I can draft tests asserting miner and uncle payouts across these cases.

core/tx_pool_test.go (2)

1083-1088: Fix test assertion message: expected count is wrong

The code checks queued != 2 but the message says “want 3”. Make the “want” match 2.

Apply:

- if queued != 2 {
-   t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
- }
+ if queued != 2 {
+   t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
+ }

1979-1982: Fix expected total tx count: should be 20, not 10

GlobalSlots=10 and GlobalQueue=10 ⇒ pending+queued should be 20.

Apply:

- if pending+queued != 20 {
-   t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 10)
- }
+ if pending+queued != 20 {
+   t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 20)
+ }
core/state/statedb_test.go (1)

178-181: Bug: updating the wrong state object in copy-of-copy test

ccopy updates with copyObj instead of ccopyObj, breaking the test’s intent.

Apply:

-   ccopy.updateStateObject(copyObj)
+   ccopy.updateStateObject(ccopyObj)
eth/tracers/api_test.go (1)

493-497: Unexported field prevents JSON population — test silently ignores returnValue.

encoding/json ignores unexported fields; both have and want will default to empty and the assertion won't check returnValue. Export the field (and add tags) to ensure it’s validated.

- type res struct {
-   Gas         int
-   Failed      bool
-   returnValue string
- }
+ type res struct {
+   Gas         int    `json:"gas"`
+   Failed      bool   `json:"failed"`
+   ReturnValue string `json:"returnValue"`
+ }
core/state_processor.go (1)

174-179: Add ReturnValue field to types.Receipt
state_processor.go sets receipt.ReturnValue but core/types/receipt.go’s Receipt struct (and its JSON/RLP marshaler) lacks this field—define ReturnValue (e.g. []byte with appropriate json/rlp tags) to prevent compile errors and ensure correct serialization.

eth/tracers/api.go (1)

895-899: Nil deref risk: guard StateOverrides.

config.StateOverrides may be nil; calling .Apply will panic.

- if config != nil {
-   if err := config.StateOverrides.Apply(statedb); err != nil {
-     return nil, err
-   }
- }
+ if config != nil && config.StateOverrides != nil {
+   if err := config.StateOverrides.Apply(statedb); err != nil {
+     return nil, err
+   }
+ }
♻️ Duplicate comments (2)
core/vm/evm.go (2)

535-547: Update fork name in comments; add self-delegation guard.

Comment still says “Prague”; here it’s gated by IsMorph300. Also avoid pointless self-delegation lookup.

-// resolveCode returns the code associated with the provided account. After
-// Prague, it can also resolve code pointed to by a delegation designator.
+// resolveCode returns the code associated with the provided account.
+// On Morph300, it can also resolve code pointed to by a delegation designator.
 func (evm *EVM) resolveCode(addr common.Address) []byte {
-    code := evm.StateDB.GetCode(addr)
-    if !evm.chainRules.IsMorph300 {
-        return code
-    }
-    if target, ok := types.ParseDelegation(code); ok {
-        // Note we only follow one level of delegation.
-        return evm.StateDB.GetCode(target)
-    }
-    return code
+    code := evm.StateDB.GetCode(addr)
+    if !evm.chainRules.IsMorph300 {
+        return code
+    }
+    if target, ok := types.ParseDelegation(code); ok {
+        if target == addr { // avoid self-delegation loop
+            return code
+        }
+        // Note we only follow one level of delegation.
+        return evm.StateDB.GetCode(target)
+    }
+    return code
 }

549-562: Avoid full code fetch on common path; handle self-delegation.

Check size first (23-byte designator), then fetch code. Guard self-delegation.

-// resolveCodeHash returns the code hash associated with the provided address.
-// After Prague, it can also resolve code hash of the account pointed to by a
-// delegation designator. Although this is not accessible in the EVM it is used
-// internally to associate jumpdest analysis to code.
+// resolveCodeHash returns the code hash associated with the provided address.
+// On Morph300, it can also resolve the code hash of a delegated target.
 func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
-    if evm.chainRules.IsMorph300 {
-        code := evm.StateDB.GetCode(addr)
-        if target, ok := types.ParseDelegation(code); ok {
-            // Note we only follow one level of delegation.
-            return evm.StateDB.GetKeccakCodeHash(target)
-        }
-    }
-    return evm.StateDB.GetKeccakCodeHash(addr)
+    if evm.chainRules.IsMorph300 {
+        // Quick size check to avoid fetching full code in the common case.
+        if evm.StateDB.GetCodeSize(addr) == 23 {
+            code := evm.StateDB.GetCode(addr)
+            if target, ok := types.ParseDelegation(code); ok {
+                if target == addr {
+                    return evm.StateDB.GetKeccakCodeHash(addr)
+                }
+                // Note we only follow one level of delegation.
+                return evm.StateDB.GetKeccakCodeHash(target)
+            }
+        }
+    }
+    return evm.StateDB.GetKeccakCodeHash(addr)
 }
🧹 Nitpick comments (97)
eth/tracers/internal/tracetest/testdata/call_tracer_legacy/throw.json (1)

48-48: Genesis timestamp change — confirm header/hash consistency.

Changing the timestamp without updating the adjacent header fields (notably "hash" at Line 42) can make this fixture internally inconsistent. If the harness reads/validates these fields, recompute the hash or drop unused/derived fields from the fixture to avoid brittle tests.

eth/tracers/internal/tracetest/testdata/call_tracer_legacy/inner_instafail.json (1)

66-66: Remove stray whitespace in JSON field.

There's an extra space after the colon in "error": "internal failure". Normalize to avoid noisy diffs.

-        "error":  "internal failure",
+        "error": "internal failure",
eth/tracers/internal/tracetest/testdata/call_tracer_flat/result_output.json (2)

62-62: Clarify tx type assumptions (legacy vs 1559)

Input RLP appears legacy (no maxFee fields). If any signer/time-aware changes conditionally affect decoding, note it near the fixture or include a README hint to prevent future mismatches.


1-107: Add a lightweight schema/invariant check to prevent future fixture rot

Automate validation that parent/child counts and basic keys exist; catches silent schema drifts early.

Proposed script (safe, read‑only):

#!/bin/bash
set -euo pipefail
f='eth/tracers/internal/tracetest/testdata/call_tracer_flat/result_output.json'

# 1) Required keys on each trace row
jq -e '
  .result as $r |
  all($r[]; has("type") and has("action") and has("result") and has("traceAddress")
              and has("subtraces") and has("transactionHash") and has("blockNumber") and has("blockHash"))
' "$f" >/dev/null

# 2) Parent/child subtrace count consistency for traceAddress [ ]
jq -e '
  .result as $r |
  ($r | map(select(.traceAddress|length==0)) | length) == 1 and
  ($r[] | select(.traceAddress|length==0).subtraces) ==
  ($r | map(select(.traceAddress|length==1 and .traceAddress[0]==0)) | length)
' "$f" >/dev/null

# 3) Context vs genesis numbering: context.number == genesis.number + 1
jq -e '((.context.number|tonumber) - (.genesis.number|tonumber))==1' "$f" >/dev/null

echo "Schema/invariants OK"

If you’d like, I can wire this into a small go:generate or a make target.

eth/tracers/internal/tracetest/testdata/call_tracer_flat/delegatecall_parent_value.json (1)

79-86: Delegatecall action.value — confirm schema intent

Most “flat” trace schemas omit value on DELEGATECALL or set it to 0 since no value is transferred. If this fixture intentionally surfaces msg.value for delegatecall (parent value), please confirm the tracer was updated accordingly and align other fixtures/docs. If not intentional, drop the value field.

Proposed fix if omission is desired:

-        "to": "0x6ab9dd83108698b9ca8d03af3c7eb91c0e54c3fc",
-        "value": "0x8ac7230489e80000"
+        "to": "0x6ab9dd83108698b9ca8d03af3c7eb91c0e54c3fc"
eth/tracers/internal/tracetest/testdata/call_tracer_flat/include_precompiled.json (4)

73-75: Align file name with config key (“includePrecompiles”).

The file is named include_precompiled.json but the config flag is includePrecompiles. Recommend renaming the file to include_precompiles.json for consistency and easier grepping.


3-13: Normalize number encodings for header fields.

Mixed decimal/hex encodings (e.g., difficulty/timestamp as decimal strings vs nonce/mixHash/extraData as hex). Prefer a consistent convention used by other tracer fixtures in this repo to reduce parser surprises.


14-25: Account nonce format may be inconsistent with other fixtures.

alloc[0x877b…].nonce is a decimal string ("77947"). Most fixtures use hex (e.g., "0x130bb"). Consider normalizing to hex for consistency.


76-76: Document how to regenerate this fixture.

Given the size and specificity of input data, add a short README.md next to this file with the exact command(s) to regenerate it from the tracer (client/build, block/time, tx crafting). This keeps the fixture from drifting.

I can draft the README with commands if you share the exact generation steps.

eth/tracers/internal/tracetest/testdata/call_tracer_flat/oog.json (1)

2-8: Normalize numeric types for consistency (optional).

Minor: context.number/timestamp are strings while blockNumber is numeric. Aligning them to numbers improves consistency and avoids accidental string/number comparisons in future tooling.

Apply if desired:

-    "number": "2294631",
-    "timestamp": "1513675366"
+    "number": 2294631,
+    "timestamp": 1513675366
@@
-    "number": "2294630",
+    "number": 2294630,
@@
-    "timestamp": "1513675347"
+    "timestamp": 1513675347

Also applies to: 44-47, 59-59

eth/tracers/internal/tracetest/makeTest.js (4)

21-24: Add basic input validation for tx/block lookups

Guard against missing transactions/blocks to fail fast with actionable errors instead of opaque exceptions from downstream calls.

-    var block   = eth.getBlock(eth.getTransaction(tx).blockHash);
-    var genesis = eth.getBlock(block.parentHash);
+    var txObj = eth.getTransaction(tx);
+    if (!txObj) {
+        throw new Error("Transaction not found: " + tx);
+    }
+    var block = eth.getBlock(txObj.blockHash);
+    if (!block) {
+        throw new Error("Block not found for tx: " + tx);
+    }
+    var genesis = eth.getBlock(block.parentHash);
+    if (!genesis) {
+        throw new Error("Parent block not found for block: " + block.hash);
+    }

25-36: Strip Cancun/Prague-era header fields too

To keep the synthetic genesis minimal and forward-compatible, also remove fields introduced by EIP-4844/EIP-4788 where present.

     delete genesis.withdrawals;
     delete genesis.withdrawalsRoot;
     delete genesis.baseFeePerGas;
+    // Post-Cancun / Proto-Danksharding and beacon-root fields
+    delete genesis.blobGasUsed;
+    delete genesis.excessBlobGas;
+    delete genesis.parentBeaconBlockRoot;

Please verify on your target chain that these keys exist and that removing them matches the expectations of the tracer test harness.


42-48: Normalize alloc fields and harden iteration

Prestate balances can surface as non-strings on some networks/console builds; also guard against prototype keys.

-    for (var key in genesis.alloc) {
-        var nonce = genesis.alloc[key].nonce;
-        if (nonce) {
-            genesis.alloc[key].nonce = nonce.toString();
-        }
-    }
+    for (var key in genesis.alloc) {
+        if (!Object.prototype.hasOwnProperty.call(genesis.alloc, key)) continue;
+        var acct = genesis.alloc[key];
+        if (acct.nonce != null) {
+            acct.nonce = acct.nonce.toString();
+        }
+        if (acct.balance != null && typeof acct.balance !== "string") {
+            acct.balance = acct.balance.toString();
+        }
+    }

77-77: Terminate the function assignment with a semicolon

Minor ASI nit to avoid edge cases when concatenating scripts.

-}
+};
eth/tracers/internal/tracetest/README.md (2)

3-4: Tighten wording and punctuation

Minor grammar/punctuation improvement.

-To fill test cases for the built-in tracers, the `makeTest.js` script can be used. Given a transaction on a dev/test network, `makeTest.js` will fetch its prestate and then traces with the given configuration.
-In the Geth console do:
+To fill test cases for the built-in tracers, use the `makeTest.js` script. Given a transaction on a dev/test network, `makeTest.js` fetches its prestate and then traces it with the given configuration.
+In the Geth console, run:

6-10: Add a short note on prerequisites

Mention the required modules to avoid confusion when running from a restricted console.

 ```terminal
 let tx = '0x...'
 loadScript('makeTest.js')
 makeTest(tx, { tracer: 'callTracer' })

+Prerequisites: start the console with admin and debug APIs enabled (e.g., --http.api admin,debug,eth,net,web3 if using HTTP) so that admin.nodeInfo and debug.traceTransaction are available.


</blockquote></details>
<details>
<summary>eth/ethconfig/config.go (1)</summary><blockquote>

`187-190`: **Clarify semantics and add TOML omitempty tags for new fields**

"Enables VM tracing" suggests a boolean, but `VMTrace` is a string. Clarify that a non-empty tracer name enables tracing, and avoid persisting empty strings in generated TOML.


```diff
-	// Enables VM tracing
-	VMTrace       string
-	VMTraceConfig string
+	// VMTrace selects a VM tracer by name; if non-empty, VM tracing is enabled (e.g., "callTracer").
+	VMTrace       string `toml:",omitempty"`
+	// VMTraceConfig is a JSON/TOML string passed to the tracer.
+	VMTraceConfig string `toml:",omitempty"`
eth/tracers/internal/tracetest/testdata/call_tracer/simple.json (1)

55-55: Extend header-time alignment checks to all tracer fixtures. Verified that simple.json passes (genesis timestamp ≤ context timestamp, block number increment by 1); apply the same validation across every JSON fixture under eth/tracers/internal/tracetest/testdata to catch any misalignments and prevent flaky time-aware tracing.

eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json (3)

67-67: transactionPosition likely should be 0 for single-tx block

Unless this fixture intentionally mirrors a real block’s 15th tx, set to 0 for consistency with common testdata.

Apply:

-      "transactionPosition": 14,
+      "transactionPosition": 0,

And similarly at Line 96.

Also applies to: 96-96


16-16: Use uniform hex encoding for account nonces

Other quantities here are hex. Consider encoding nonces as hex for consistency and to match typical genesis parsing.

-        "nonce": "0",
+        "nonce": "0x0"
-        "nonce": "260140",
+        "nonce": "0x3f82c",

Ensure the tracetest loader accepts hex for nonce in alloc accounts.

Also applies to: 22-22


61-63: Clarify empty code on create result

Returning "code": "0x" is fine if the init selfdestructs; if the harness treats absence differently, consider omitting the field instead.

Compare with adjacent call_tracer_flat fixtures for consistency.

eth/tracers/internal/tracetest/testdata/call_tracer_flat/suicide.json (1)

16-16: Use hex for alloc nonce for consistency with other fields.

Minor consistency tweak; avoids ambiguity in parsers expecting hex.

Apply:

-        "nonce": "701",
+        "nonce": "0x2bd",
eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create2_action_gas.json (1)

1-94: Add a companion EIP-7702 tracer fixture to guard recent signer/state-change refactors.

Given this PR introduces EIP-7702 behaviors, consider adding a small flat-call tracer fixture that includes a SetCode authorization path and validates any new fields (e.g., state change reasons) to prevent regressions.

I can propose a minimal tx/state and expected trace JSON if you’d like.

eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_precompiled_wrong_gas.json (3)

14-29: Nonce formatting inconsistency in alloc.

Account nonces are decimal strings (e.g., "77947" and "1"). Most fixtures use hex (0x-prefixed). Consider normalizing for consistency to avoid parser surprises across tools.


38-49: Precompile accounts in alloc are likely unnecessary.

Precompiles are built-ins and don’t require alloc entries. Keeping them is harmless, but you can drop 0x000...0002 and 0x000...0004 to reduce noise, unless a past bug relied on their explicit presence.


1-218: Add a brief rationale in the directory README for “wrong_gas”.

Optional: a one-liner in a README.md next to these fixtures noting the specific inner-precompile gas bug this guards against will aid future maintainers.

Happy to draft the README snippet if you want.

eth/tracers/internal/tracetest/prestate_test.go (5)

106-109: Populate TxContext with message-derived fields for accurate tracing.

Empty TxContext may produce incomplete trace metadata (Origin/To/GasPrice).

-			evm := vm.NewEVM(context, vm.TxContext{}, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
+			txctx := vm.TxContext{
+				Origin:   msg.From(),
+				To:       msg.To(),
+				GasPrice: msg.GasPrice(),
+			}
+			evm := vm.NewEVM(context, txctx, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})

97-101: Ensure tracer cleanup to avoid goroutine/resource leaks.

Call Stop after use.

 			tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
 			if err != nil {
 				t.Fatalf("failed to create call tracer: %v", err)
 			}
+			defer func() {
+				if tracer.Stop != nil {
+					tracer.Stop(nil)
+				}
+			}()

Also applies to: 112-116


120-126: Handle (un)marshal errors during legacy normalization.

Avoid ignoring JSON errors; fail fast for clearer signal.

 			if strings.HasSuffix(dirPath, "_legacy") {
 				// This is a tweak to make it deterministic. Can be removed when
 				// we remove the legacy tracer.
 				var x prestateTrace
-				json.Unmarshal(res, &x)
-				res, _ = json.Marshal(x)
+				if err := json.Unmarshal(res, &x); err != nil {
+					t.Fatalf("failed to decode legacy result: %v", err)
+				}
+				if res, err = json.Marshal(x); err != nil {
+					t.Fatalf("failed to normalize legacy result: %v", err)
+				}
 			}

97-101: Error message nit: say “prestate tracer” instead of “call tracer”.

The created tracer here is the prestate tracer.

-				t.Fatalf("failed to create call tracer: %v", err)
+				t.Fatalf("failed to create prestate tracer: %v", err)

102-113: Optional: add a nil-check guard before invoking tracer hooks.

Defensive check helps catch mis-registered tracers early.

-			tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
+			if tracer.Hooks == nil || tracer.OnTxStart == nil || tracer.OnTxEnd == nil {
+				t.Fatalf("tracer hooks not initialized")
+			}
+			tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json (1)

59-68: Schema consistency: add explicit top-level "output" (if still expected) and confirm inner gasUsed semantics

  • The inner CALL frame looks correct (2300 stipend shown as gas 0x8fc, EOA target, and “insufficient balance for transfer” error).
  • If consumers still expect an explicit top-level "output": "0x" on success-with-no-return, add it for consistency; otherwise ensure downstream readers/tests were updated to handle its absence.
  • Double-check that reporting gasUsed: "0x0" for a precheck-failed value transfer is the intended tracer behavior.

Apply if needed:

@@
-    "input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000",
+    "input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000",
+    "output": "0x",
     "calls": [{
eth/tracers/internal/tracetest/testdata/call_tracer/delegatecall.json (1)

88-95: Top-level CALL missing output — consider adding "output": "0x" for schema consistency.

Most callTracer frames include output even if empty. Omitting it can break strict schema validations. Suggest adding an explicit empty output.

Apply:

       "input": "0x7065cb480000000000000000000000001523e55a1ca4efbae03355775ae89f8d7699ad9e",
       "to": "0x269296dddce321a6bcbaa2f0181127593d732cba",
       "type": "CALL",
+      "output": "0x",
       "value": "0x0"
eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.md (2)

1-2: Tighten wording.

“ This test tests out …” is redundant. Prefer concise phrasing.

Apply:

-This test tests out the trace generated by the deployment of this contract:
+This test validates the trace generated by deploying the following contract:

18-19: Improve grammar and clarity.

Use “top-level call” and avoid “as well as”.

Apply:

-The trace should show a revert, with the revert reason for both the top-call as well
-as the inner call.
+The trace should show a revert, including the revert reason for both the top-level call
+and the inner call.
core/vm/errors.go (2)

98-107: Avoid double-wrapping VMError

If err is already *VMError, return it as-is to keep code stable and error chains shallow.

 func VMErrorFromErr(err error) error {
   if err == nil {
     return nil
   }
-  return &VMError{
-    error: err,
-    code:  vmErrorCodeFromErr(err),
-  }
+  if v, ok := err.(*VMError); ok {
+    return v
+  }
+  return &VMError{error: err, code: vmErrorCodeFromErr(err)}
 }

143-146: Reserve a small sentinel for VMErrorCodeUnknown
math.MaxInt-1 can surprise consumers expecting error codes in a small, fixed-width range (e.g. JSON-RPC). Use 0 or a smaller high-value (e.g. 1<<30) instead and verify downstream serialization/RPC layers handle it correctly.

core/vm/contracts_test.go (2)

343-350: Use os.ReadFile; ioutil is deprecated

Modernize file reads in tests.

-import "io/ioutil"
+import "os"
@@
-  data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name))
+  data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name))
@@
-  data, err := ioutil.ReadFile(fmt.Sprintf("testdata/precompiles/fail-%v.json", name))
+  data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/fail-%v.json", name))

Also applies to: 352-360


227-235: Typo: "identiy" → "identity"

Minor comment fix.

-// Benchmarks the sample inputs from the identiy precompile.
+// Benchmarks the sample inputs from the identity precompile.
eth/tracers/api_blocktrace.go (2)

93-96: Silently discarding config.Tracer is a breaking API behavior change.

Setting config.Tracer = nil and just warning can lead to surprising results for API clients. Prefer returning a 400-style error to the caller when an unsupported param is supplied, or explicitly mark the field deprecated in the type’s doc and ignore only if it’s empty.

If you keep ignoring it, please add a test asserting that providing Tracer causes a deterministic error response.


95-95: Minor wording nit in warning.

“Tracer params is unsupported” → “Tracer param is unsupported” or “Tracer params are unsupported”.

eth/api_test.go (1)

82-83: Reason value looks acceptable; consider a more specific reason if available.

tracing.BalanceChangeUnspecified compiles (per snippet), but if there’s a dedicated reason for state-test initialization, prefer that for clearer traces.

cmd/evm/staterunner.go (1)

70-71: tracerFromFlags must preserve prior Debug/Machine semantics.

Switching to Tracer: tracerFromFlags(ctx) is fine if tracerFromFlags maps the old flags to an equivalent tracer. Verify that:

  • --debug still produces per-op traces (previously via vm.Config.Debug + StructLogger).
  • --json --machine output remains unchanged.

If you intend to deprecate vm.Config.Debug, update help text/docs and add a fallback in tracerFromFlags to construct a StructLogger when --debug is set but no explicit tracer is requested.

core/vm/runtime/runtime.go (3)

128-130: Include GasPrice when faking the tx for OnTxStart to avoid nil access in tracers.

Some tracers read GasPrice; keep it non-nil.

-    cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
+    cfg.EVMConfig.Tracer.OnTxStart(
+      vmenv.GetVMContext(),
+      types.NewTx(&types.LegacyTx{
+        To:       &address,
+        Data:     input,
+        Value:    cfg.Value,
+        Gas:      cfg.GasLimit,
+        GasPrice: cfg.GasPrice,
+      }),
+      cfg.Origin,
+    )

165-167: Creation path: populate GasPrice in the synthetic LegacyTx too.

Consistency with Execute and to avoid tracer assumptions.

-    cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
+    cfg.EVMConfig.Tracer.OnTxStart(
+      vmenv.GetVMContext(),
+      types.NewTx(&types.LegacyTx{
+        Data:     input,
+        Value:    cfg.Value,
+        Gas:      cfg.GasLimit,
+        GasPrice: cfg.GasPrice,
+      }),
+      cfg.Origin,
+    )

197-199: Call path: include GasPrice in the synthetic tx passed to the tracer.

Prevents nil deref risks in tracer code paths that inspect price.

-    cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
+    cfg.EVMConfig.Tracer.OnTxStart(
+      vmenv.GetVMContext(),
+      types.NewTx(&types.LegacyTx{
+        To:       &address,
+        Data:     input,
+        Value:    cfg.Value,
+        Gas:      cfg.GasLimit,
+        GasPrice: cfg.GasPrice,
+      }),
+      cfg.Origin,
+    )
eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json (1)

79-86: Normalize “init” for the failing CREATE.

Given the CREATE fails with “insufficient balance,” consider using an empty init field ("0x") to avoid implying a 32-byte payload of zeros.

-        "init": "0x0000000000000000000000000000000000000000000000000000000000000000",
+        "init": "0x",
core/vm/contracts.go (1)

47-49: Fix docstring typo (“RequiredPrice” → “RequiredGas”).

-	RequiredGas(input []byte) uint64  // RequiredPrice calculates the contract gas use
+	RequiredGas(input []byte) uint64  // RequiredGas calculates the contract gas use
core/vm/contract.go (2)

180-190: Add GoDoc and keep overflow invariant in mind.

Consider a short comment documenting when RefundGas is used and that gas is uint64-bounded by EVM invariants.

-// RefundGas refunds gas to the contract
+// RefundGas refunds gas to the contract and emits a gas-change event (if enabled).
+// Note: EVM invariants guarantee c.Gas+gas does not overflow uint64.
 func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {

173-187: Optional: factor guard into a tiny helper to reduce repetition.

Minor DRY: a private helper like emitGasChange(logger, old, new, reason) could centralize the nil/reason checks.

core/vm/interface.go (2)

41-41: SetCode now returns []byte (previous code?). Document semantics.

Clarify whether the return is old or new code and whether callers may mutate it.


51-51: SetState now returns common.Hash (previous value?). Document semantics.

Add brief comments to the interface to prevent misuse.

core/vm/operations_acl.go (3)

249-262: Micro-opt: check code size before fetching code bytes

ParseDelegation only succeeds on 23-byte payloads. Avoid fetching full code unless size matches.

-        if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
+        if evm.StateDB.GetCodeSize(addr) == 23 {
+            if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
               ...
-            }
+            }
+        }

249-262: Use a distinct GasChange reason for delegation resolution

Reusing GasChangeCallStorageColdAccess for delegation target costs may confuse tracers. Consider adding GasChangeCallDelegationResolution (warm/cold) to disambiguate.

Happy to add the new enum and thread it through tracing.Hooks if desired.


274-285: Rename shadowed variable for clarity

Shadowing total in SafeAdd makes the final return less readable. Use sum to avoid confusion.

-        var overflow bool
-        if total, overflow = math.SafeAdd(old, total); overflow {
+        var (
+            sum      uint64
+            overflow bool
+        )
+        if sum, overflow = math.SafeAdd(old, total); overflow {
             return 0, ErrGasUintOverflow
         }
-        return total, nil
+        return sum, nil
eth/tracers/internal/tracetest/flat_calltrace_test.go (2)

166-174: Parallel subtests: capture loop variable to avoid flakiness on older Go

t.Run closures can capture the loop variable. Defensive copy avoids intermittent failures on Go versions before 1.22.

-        t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) {
+        name := file.Name()
+        t.Run(camel(strings.TrimSuffix(name, ".json")), func(t *testing.T) {
             t.Parallel()
-
-            err := flatCallTracerTestRunner(tracerName, file.Name(), dirPath, t)
+            err := flatCallTracerTestRunner(tracerName, name, dirPath, t)
             if err != nil {
                 t.Fatal(err)
             }
         })

104-121: Consider deferring tracer termination if the implementation requires it

Some tracers may buffer and flush on Stop. If the tracer returned by DefaultDirectory.New sets Tracer.Stop, consider deferring it to ensure resources are released.

-    tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
+    tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
     if err != nil {
         return fmt.Errorf("failed to create call tracer: %v", err)
     }
+    if tracer.Stop != nil {
+        defer tracer.Stop(nil)
+    }
cmd/evm/internal/t8ntool/transition.go (2)

221-245: Tracer setup path: ensure tracer context isn’t required (nil passed)

You pass nil for the tracers.Context. If any tracer expects non-nil contextual inputs, this may panic. Consider passing &tracers.Context{} for safety, mirroring the test harness.

-        tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
-            nil, config, chainConfig)
+        tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
+            new(tracers.Context), config, chainConfig)

361-371: Replace deprecated ioutil.WriteFile

Use os.WriteFile to avoid deprecated API and redundant import.

-    if err = ioutil.WriteFile(location, b, 0644); err != nil {
+    if err = os.WriteFile(location, b, 0644); err != nil {

And remove the ioutil import.

core/state/statedb_hooked.go (4)

37-44: Guard against nil StateDB in constructor

Avoid latent panics from nil receivers; validate input.

 func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB {
-	s := &hookedStateDB{stateDb, hooks}
+	if stateDb == nil {
+		panic("NewHookedState: stateDb is nil")
+	}
+	s := &hookedStateDB{stateDb, hooks}
 	if s.hooks == nil {
 		s.hooks = new(tracing.Hooks)
 	}
 	return s
 }

170-180: Avoid aliasing code slices passed to hooks

Hooks should not observe future mutations; pass copies.

 func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte {
 	prev := s.inner.SetCode(address, code)
 	if s.hooks.OnCodeChange != nil {
 		prevHash := codehash.EmptyKeccakCodeHash
 		if len(prev) != 0 {
 			prevHash = crypto.Keccak256Hash(prev)
 		}
-		s.hooks.OnCodeChange(address, prevHash, prev, crypto.Keccak256Hash(code), code)
+		prevCopy := append([]byte(nil), prev...)
+		codeCopy := append([]byte(nil), code...)
+		s.hooks.OnCodeChange(address, prevHash, prevCopy, crypto.Keccak256Hash(codeCopy), codeCopy)
 	}
 	return prev
 }

210-224: Event ordering of selfdestruct-burn vs Finalise

You emit BalanceDecreaseSelfdestructBurn before inner.Finalise runs (due to defer). Verify consumers expect the event pre-commit. If not, move the hook after Finalise by snapshotting addresses first.


142-149: Current balance‐hook logic is correct; optional future-proof refactor

The existing AddBalance/SubBalance implementations return the previous balance and compute the post‐state via prev ± amount, which matches the actual new value (they allocate new big.Int instances and don’t mutate the old one in place, and the zero‐amount branch is skipped by the hook). No changes are required for correctness.

If you’d like to guard against any future in-place mutations or clamping semantics, you can optionally clone both values and re-read the post-state before invoking the hook:

prev := s.inner.SubBalance(addr, amount, reason)
if s.hooks.OnBalanceChange != nil && amount.Sign() != 0 {
    prevCopy   := new(big.Int).Set(prev)
    newBalance := new(big.Int).Set(s.inner.GetBalance(addr))
    s.hooks.OnBalanceChange(addr, prevCopy, newBalance, reason)
}
return prev
eth/tracers/dir.go (3)

38-45: Comment mismatch: Tracer is a struct, not an interface

Tighten the doc to avoid confusion.

-// Tracer interface extends vm.EVMLogger and additionally
-// allows collecting the tracing result.
+// Tracer aggregates tracing hooks and result/control functions
+// for collecting and controlling a trace.
 type Tracer struct {

92-101: Consider jsEval presence in IsJS

Unknown name should only be “JS” if an evaluator exists.

 func (d *directory) IsJS(name string) bool {
 	if elem, ok := d.elems[name]; ok {
 		return elem.isJS
 	}
-	// JS eval will execute JS code
-	return true
+	return d.jsEval != nil
 }

55-76: Registry not concurrency-safe

If tracers are registered at init() only, you’re fine. Otherwise add a RWMutex around elems/jsEval reads/writes.

I can send a follow-up diff adding sync.RWMutex if you expect concurrent Register/New calls.

Also applies to: 66-71, 78-90, 92-101

eth/tracers/api_test.go (2)

255-268: Result schema change acknowledged; consider normalizing l1DataFee expectation.

Some cases omit l1DataFee while others expect "0x0". To avoid flakiness from omitempty behavior, prefer asserting essential fields only or include l1DataFee consistently.


335-337: Prefer errors.Is over string equality for error checks.

String matching is brittle and locale/format dependent. Use errors.Is when possible.

- if !reflect.DeepEqual(err.Error(), testspec.expectErr.Error()) {
+ if !errors.Is(err, testspec.expectErr) {
core/state/state_object.go (2)

451-460: Document SetBalance return value explicitly (previous balance).

The behavior is fine, but the doc should state “returns the previous balance” to avoid misuse.

-// SetBalance sets the balance for the object, and returns the previous balance.
+// SetBalance sets the balance for the object and returns the previous balance.

513-521: SetCode returns previous code — OK; add doc for clarity.

- func (s *stateObject) SetCode(code []byte) []byte {
+ // SetCode sets the code and returns the previous code bytes.
+ func (s *stateObject) SetCode(code []byte) []byte {
core/state_processor.go (1)

188-195: Receipt fields population (logs, bloom, indices, L1Fee) looks correct.

Consider whether EffectiveGasPrice is set here or derived later via types.DeriveFields; ensure consistency across readers.

cmd/evm/internal/t8ntool/flags.go (2)

34-41: Consistent flag style and validation.

  • Mixed pointer/value usage for flags (pointers here; values elsewhere). Consider standardizing for readability.
  • If --trace.jsonconfig must be JSON, validate early (flag parse time) and fail fast with a clear error.

54-57: Document callframes interplay with other trace flags.

Note when trace.callframes is effective (which tracers honor it) and how it interacts with stack/memory/returndata toggles.

eth/tracers/internal/tracetest/calltrace_test.go (4)

133-134: Guard optional hooks to avoid nil-call panics.

Some tracers may not provide OnTxStart.

-            tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
+            if tracer.OnTxStart != nil {
+                tracer.OnTxStart(evm.GetVMContext(), tx, msg.From())
+            }

138-139: Symmetric guard for OnTxEnd.

Avoid nil-call panics and keep symmetry with start.

-            tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
+            if tracer.OnTxEnd != nil {
+                tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
+            }

205-205: Prefer time-aware signer for determinism across fork rules.

Align with the repo-wide change to a time-aware signer.

-    signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
+    signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))

Note: Already correct here; this is just a confirmation. If you reintroduce LatestSigner elsewhere, prefer MakeSigner.


248-249: Use MakeSigner with block/time in TestInternals.

Keeps tests consistent with time-aware signer logic.

-        signer    = types.LatestSigner(config)
+        signer    = types.MakeSigner(config, context.BlockNumber, context.Time.Uint64())
cmd/evm/main.go (2)

206-218: Remove or wire up unused traceFlags.

traceFlags isn’t referenced in this file. Drop it or use it to group flags where needed.


289-291: Avoid os.Exit in helper; bubble up error instead.

Returning an error keeps CLI wiring testable and avoids abrupt exits from callers.

-            fmt.Fprintf(os.Stderr, "unknown trace format: %q\n", format)
-            os.Exit(1)
-            return nil
+            panic(fmt.Errorf("unknown trace format: %q", format))

Or change signature to return (*tracing.Hooks, error) and handle upstream.

core/state/statedb.go (5)

416-423: Remove unreachable nil-check; consider reason handling.

GetOrNewStateObject never returns nil; the guard is dead code. Also, the reason arg is unused here — if hooks aren’t injected elsewhere, this drops tracing semantics.

Apply:

 func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int {
-    stateObject := s.GetOrNewStateObject(addr)
-    if stateObject == nil {
-        return new(big.Int)
-    }
-    return stateObject.AddBalance(amount)
+    stateObject := s.GetOrNewStateObject(addr)
+    return stateObject.AddBalance(amount)
 }

Please confirm whether balance-change hooks are fired in stateObject or in a hooked wrapper (e.g., statedb_hooked.go). If not, we need to wire reason to the hook path.


426-435: Same here: remove unreachable nil-check; early return on zero is fine.

This simplifies the control flow and avoids redundant branches.

 func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int {
-    stateObject := s.GetOrNewStateObject(addr)
-    if stateObject == nil {
-        return new(big.Int)
-    }
+    stateObject := s.GetOrNewStateObject(addr)
     if amount.Sign() == 0 {
         return stateObject.Balance()
     }
     return stateObject.SubBalance(amount)
 }

437-442: Nonce/balance reasons are ignored here.

SetBalance does not propagate reason. If the hook wiring isn’t in a wrapper, the tracer will miss cause attribution.

Would you like me to patch these setters to call the appropriate logger.OnBalanceChange/OnNonceChange hooks if present?


444-449: Nonce change reason is unused.

Same concern as above; please confirm where the reason is consumed.


451-457: Return value OK, but confirm hook path for code changes.

If code-change events should be observable, ensure OnCodeChange is emitted somewhere (either here or in stateObject.SetCode or the hooked wrapper).

core/vm/evm.go (1)

312-317: Remove stale commented debug tracer block.

Dead code comments add noise and can diverge from actual behavior.

-    // if evm.Config.Debug {
-    //  evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
-    //  defer func(startGas uint64) {
-    //      evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
-    //  }(gas)
-    // }
core/state_transition.go (2)

134-185: Charge gas for auth list — add overflow check for symmetry.

Mirror the overflow guards used for data and access lists.

-    if authList != nil {
-        gas += uint64(len(authList)) * params.CallNewAccountGas
-    }
+    if authList != nil {
+        if (math.MaxUint64-gas)/params.CallNewAccountGas < uint64(len(authList)) {
+            return 0, ErrGasUintOverflow
+        }
+        gas += uint64(len(authList)) * params.CallNewAccountGas
+    }

302-306: EOA check: avoid loading full code unless needed.

Use GetCodeSize(from) first; only fetch code to parse delegation if size > 0.

- code := st.state.GetCode(st.msg.From())
- _, delegated := types.ParseDelegation(code)
- if len(code) > 0 && !delegated {
+ if st.state.GetCodeSize(st.msg.From()) > 0 {
+     code := st.state.GetCode(st.msg.From())
+     if _, delegated := types.ParseDelegation(code); !delegated {
         return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, st.msg.From().Hex(), len(code))
- }
+     }
+ }
core/vm/interpreter.go (1)

276-281: Gas handling: propagate dynamic-gas errors as-is.

Wrapping every dynamic-gas error as ErrOutOfGas may mask different error causes. Prefer returning the original error unless it's specifically an OOG condition.

- if err != nil {
-   return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
- }
+ if err != nil {
+   return nil, err
+ }

Also applies to: 299-315

eth/tracers/api.go (2)

771-779: Use modern os helpers; drop deprecated ioutil.

Minor cleanup.

- "io/ioutil"
+ // (no new import needed; already importing "os")
@@
- blob, err := ioutil.ReadFile(file)
+ blob, err := os.ReadFile(file)
@@
- dump, err = ioutil.TempFile(os.TempDir(), prefix)
+ dump, err = os.CreateTemp(os.TempDir(), prefix)

Also applies to: 482-486, 19-32


300-309: TraceChain error flow: continue vs. break semantics.

On L1 fee calc or tracing error you break, skipping remaining txs in the block. If partial results are acceptable, consider continue and record the error at this index.

core/tracing/hooks.go (7)

131-135: Doc mismatch: mentions td parameter that no longer exists

Comment references td but BlockStartHook only takes BlockEvent. Clarify or remove the TD mention.

Apply:

-// `td` is the total difficulty prior to `block`.
+// Provides block and its consensus context via BlockEvent.

161-164: Fix OnSystemCallEndHook wording

It says “about to be executed” for the end hook.

-// this hook is invoked when the EIP-4788 system call is about to be executed to set the
-// beacon block root.
+// this hook is invoked after the EIP-4788 system call finishes executing to set the
+// beacon block root.

324-325: Minor grammar: “can be check”

-// performed can be check by `OnOpcode` handling.
+// performed can be checked via `OnOpcode` handling.

244-245: Minor grammar: BalanceDecreaseGasBuy

-// BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction.
-// Part of this gas will be burnt as per EIP-1559 rules.
+// BalanceDecreaseGasBuy is spent to purchase gas for executing a transaction.
+// Part of this gas will be burned as per EIP-1559 rules.

170-175: Deprecate v1 nonce hook in docs to steer new integrations

Given V2 exists, mark V1 as deprecated to reduce ambiguity.

-// NonceChangeHook is called when the nonce of an account changes.
+// NonceChangeHook is called when the nonce of an account changes.
+// Deprecated: use NonceChangeHookV2 to receive a reason.

146-156: Explicitly deprecate OnSystemCallStartHook in favor of V2

V2 provides VM context; mark V1 deprecated.

-// OnSystemCallStartHook is called when a system call is about to be executed. Today,
+// OnSystemCallStartHook is called when a system call is about to be executed. Today,
 // this hook is invoked when the EIP-4788 system call is about to be executed to set the
 // beacon block root.
+// Deprecated: use OnSystemCallStartHookV2 to access VM context.

Also applies to: 157-160


98-99: Use a strongly-typed call kind instead of byte

Improves clarity and type-safety for EnterHook.

-EnterHook = func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
+// CallKind enumerates the kind of call/create.
+type CallKind byte
+const (
+  Call CallKind = iota
+  CallCode
+  DelegateCall
+  StaticCall
+  Create
+  Create2
+)
+EnterHook = func(depth int, typ CallKind, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)

Comment thread cmd/evm/internal/t8ntool/execution.go
Comment on lines +48 to +57
func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
if l.destination != nil {
return l.destination.Write(p)
}
log.Warn("Tracer wrote to non-existing output")
// It is tempting to return an error here, however, the json encoder
// will no retry writing to an io.Writer once it has returned an error once.
// Therefore, we must squash the error.
return n, nil
}
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.

🛠️ Refactor suggestion

Return len(p) when dropping writes to avoid Writer contract ambiguity.

Returning 0, nil can confuse callers; signal that bytes were “accepted” even if ignored.

-  return n, nil
+  return len(p), nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
if l.destination != nil {
return l.destination.Write(p)
}
log.Warn("Tracer wrote to non-existing output")
// It is tempting to return an error here, however, the json encoder
// will no retry writing to an io.Writer once it has returned an error once.
// Therefore, we must squash the error.
return n, nil
}
func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
if l.destination != nil {
return l.destination.Write(p)
}
log.Warn("Tracer wrote to non-existing output")
// It is tempting to return an error here, however, the json encoder
// will no retry writing to an io.Writer once it has returned an error once.
// Therefore, we must squash the error.
return len(p), nil
}
🤖 Prompt for AI Agents
In cmd/evm/internal/t8ntool/file_tracer.go around lines 48 to 57, the Write
method currently returns the zero-valued n when dropping writes which can
violate io.Writer expectations; change the return so it reports the number of
bytes accepted by returning len(p), nil (or set n = len(p) before returning)
when l.destination is nil to clearly signal the bytes were "accepted" even
though they are ignored.

Comment on lines +85 to +93
// Open a new file, or print a warning log if it's failed
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.%v", l.txIndex, tx.Hash().String(), l.suffix))
traceFile, err := os.Create(fname)
if err != nil {
log.Warn("Failed creating trace-file", "err", err)
} else {
log.Info("Created tracing-file", "path", fname)
l.destination = traceFile
}
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.

🛠️ Refactor suggestion

Create baseDir before file create; minor filename formatting.

Ensure directory exists to avoid silent drops; use %s for hash.

- fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.%v", l.txIndex, tx.Hash().String(), l.suffix))
- traceFile, err := os.Create(fname)
+ fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%s.%s", l.txIndex, tx.Hash(), l.suffix))
+ if mkErr := os.MkdirAll(l.baseDir, 0o755); mkErr != nil {
+   log.Warn("Failed ensuring trace directory", "dir", l.baseDir, "err", mkErr)
+ }
+ traceFile, err := os.Create(fname)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Open a new file, or print a warning log if it's failed
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%v.%v", l.txIndex, tx.Hash().String(), l.suffix))
traceFile, err := os.Create(fname)
if err != nil {
log.Warn("Failed creating trace-file", "err", err)
} else {
log.Info("Created tracing-file", "path", fname)
l.destination = traceFile
}
// Open a new file, or print a warning log if it fails
fname := filepath.Join(l.baseDir, fmt.Sprintf("trace-%d-%s.%s", l.txIndex, tx.Hash(), l.suffix))
if mkErr := os.MkdirAll(l.baseDir, 0o755); mkErr != nil {
log.Warn("Failed ensuring trace directory", "dir", l.baseDir, "err", mkErr)
}
traceFile, err := os.Create(fname)
if err != nil {
log.Warn("Failed creating trace-file", "err", err)
} else {
log.Info("Created tracing-file", "path", fname)
l.destination = traceFile
}
🤖 Prompt for AI Agents
In cmd/evm/internal/t8ntool/file_tracer.go around lines 85 to 93, the code
creates a trace file without ensuring the baseDir exists and formats the
filename using %v for the tx hash; update it to: ensure l.baseDir exists by
calling os.MkdirAll(l.baseDir, 0o755) and handling its error before calling
os.Create, change fmt.Sprintf to use "%s" for tx.Hash().String(), and preserve
logging on both MkdirAll and os.Create failures so the trace isn't silently
dropped.

Comment on lines +101 to +115
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
if l.inner != nil && l.inner.OnTxEnd != nil {
l.inner.OnTxEnd(receipt, err)
}
if l.getResult != nil && l.destination != nil {
if result, err := l.getResult(); result != nil {
json.NewEncoder(l.destination).Encode(result)
} else {
log.Warn("Error obtaining tracer result", "err", err)
}
l.destination.Close()
l.destination = nil
}
l.txIndex++
}
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.

⚠️ Potential issue

Always close the per-tx file; current logic leaks FDs when getResult is nil.

When using newFileWriter (jsonl), getResult is nil, so the file never closes. This leaks descriptors and can truncate subsequent writes.

Apply this diff to always close and to handle result/encoding errors:

 func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
   if l.inner != nil && l.inner.OnTxEnd != nil {
     l.inner.OnTxEnd(receipt, err)
   }
-  if l.getResult != nil && l.destination != nil {
-    if result, err := l.getResult(); result != nil {
-      json.NewEncoder(l.destination).Encode(result)
-    } else {
-      log.Warn("Error obtaining tracer result", "err", err)
-    }
-    l.destination.Close()
-    l.destination = nil
-  }
+  if l.destination != nil {
+    if l.getResult != nil {
+      if result, rerr := l.getResult(); rerr != nil {
+        log.Warn("Error obtaining tracer result", "err", rerr)
+      } else if result != nil {
+        if encErr := json.NewEncoder(l.destination).Encode(result); encErr != nil {
+          log.Warn("Error writing tracer result", "err", encErr)
+        }
+      }
+    }
+    if cerr := l.destination.Close(); cerr != nil {
+      log.Warn("Failed closing trace-file", "err", cerr)
+    }
+    l.destination = nil
+  }
   l.txIndex++
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
if l.inner != nil && l.inner.OnTxEnd != nil {
l.inner.OnTxEnd(receipt, err)
}
if l.getResult != nil && l.destination != nil {
if result, err := l.getResult(); result != nil {
json.NewEncoder(l.destination).Encode(result)
} else {
log.Warn("Error obtaining tracer result", "err", err)
}
l.destination.Close()
l.destination = nil
}
l.txIndex++
}
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
if l.inner != nil && l.inner.OnTxEnd != nil {
l.inner.OnTxEnd(receipt, err)
}
if l.destination != nil {
if l.getResult != nil {
if result, rerr := l.getResult(); rerr != nil {
log.Warn("Error obtaining tracer result", "err", rerr)
} else if result != nil {
if encErr := json.NewEncoder(l.destination).Encode(result); encErr != nil {
log.Warn("Error writing tracer result", "err", encErr)
}
}
}
if cerr := l.destination.Close(); cerr != nil {
log.Warn("Failed closing trace-file", "err", cerr)
}
l.destination = nil
}
l.txIndex++
}
🤖 Prompt for AI Agents
In cmd/evm/internal/t8ntool/file_tracer.go around lines 101 to 115, the current
OnTxEnd logic skips closing the per-transaction file when getResult is nil
causing FD leaks; always close and nil out l.destination regardless of
getResult, and avoid shadowing the outer err variable. Modify the block so it:
1) checks l.destination != nil and defers or calls l.destination.Close() and
sets l.destination = nil immediately; 2) calls l.getResult() if non-nil and
captures its error into a new local variable name (not err) so it doesn’t shadow
the method parameter; 3) if a result is returned, attempt json.Encode and log
any encoding error; 4) if getResult returns nil or an error, log the error with
context. Ensure closing happens even when encoding or getResult fails.

Comment on lines +117 to +151
func (l *fileWritingTracer) hooks() *tracing.Hooks {
return &tracing.Hooks{
OnTxStart: l.OnTxStart,
OnTxEnd: l.OnTxEnd,
OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if l.inner != nil && l.inner.OnEnter != nil {
l.inner.OnEnter(depth, typ, from, to, input, gas, value)
}
},
OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if l.inner != nil && l.inner.OnExit != nil {
l.inner.OnExit(depth, output, gasUsed, err, reverted)
}
},
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
if l.inner != nil && l.inner.OnOpcode != nil {
l.inner.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
}
},
OnFault: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
if l.inner != nil && l.inner.OnFault != nil {
l.inner.OnFault(pc, op, gas, cost, scope, depth, err)
}
},
OnSystemCallStart: func() {
if l.inner != nil && l.inner.OnSystemCallStart != nil {
l.inner.OnSystemCallStart()
}
},
OnSystemCallEnd: func() {
if l.inner != nil && l.inner.OnSystemCallEnd != nil {
l.inner.OnSystemCallEnd()
}
},
}
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.

🛠️ Refactor suggestion

Pass through all inner hooks; current wrapper drops many (GasChange, Block, State, etc.).**

Only a subset is forwarded, causing lost events when using this wrapper.

Would you like me to add passthroughs for the remaining hooks (OnGasChange, OnBlockchainInit, OnClose, OnBlockStart/End/Skipped/Genesis, OnSystemCallStartV2, OnBalanceChange, OnNonceChange/OnNonceChangeV2, OnCodeChange, OnStorageChange, OnLog, OnBlockHashRead)? I can provide a patch once you confirm desired coverage.

🤖 Prompt for AI Agents
In cmd/evm/internal/t8ntool/file_tracer.go around lines 117 to 151, the hooks
wrapper only forwards a subset of tracing.Hooks and drops many events
(GasChange, Blockchain/Block lifecycle hooks, State changes, SystemCallStartV2,
Balance/Nonce/Code/Storage changes, Log, BlockHashRead, OnClose, etc.); update
the hooks() function to passthrough every hook field from l.inner when non-nil
by adding forwarding lambdas for OnGasChange, OnBlockchainInit, OnClose,
OnBlockStart, OnBlockEnd, OnBlockSkipped, OnBlockGenesis, OnSystemCallStartV2,
OnBalanceChange, OnNonceChange, OnNonceChangeV2, OnCodeChange, OnStorageChange,
OnLog, OnBlockHashRead (and any other missing fields present on tracing.Hooks)
following the same pattern used for existing hooks so no events are dropped.

"stateRoot": "0xff403612573d76dfdaf4fea2429b77dbe9764021ae0e38dc8ac79a3cf551179e",
"timestamp": "1513681246",
"totalDifficulty": "7162347056825919"
"timestamp": "1513681246"
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.

💡 Verification agent

🧩 Analysis chain

Genesis timestamp changed — confirm intent and cross-fixture consistency.

Parent block timestamp is now "1513681246". Verify this was intentional and remains consistent with any precomputed header fields and sibling fixtures. Unintended timestamp drift can invalidate deterministic trace expectations.

Run to sanity-check timestamps across all tracer fixtures and ensure no ordering violations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) No lingering totalDifficulty fields after cleanup
if rg -n '"totalDifficulty"' eth/tracers/internal/tracetest/testdata/**/*.json; then
  echo "Found totalDifficulty occurrences above"; exit 1
else
  echo "OK: no totalDifficulty in fixtures"
fi

# 2) Parent (genesis) timestamp must be <= context timestamp
fd -e json eth/tracers/internal/tracetest/testdata | while read -r f; do
  gt=$(jq -r '.genesis.timestamp // empty' "$f")
  ct=$(jq -r '.context.timestamp // empty' "$f")
  if [[ -n "${gt}" && -n "${ct}" ]]; then
    if (( gt > ct )); then
      echo "Timestamp ordering violated in $f: genesis=$gt > context=$ct"
      exit 1
    fi
  fi
done
echo "OK: timestamp ordering valid"

Length of output: 8755


Remove lingering totalDifficulty fields from tracer fixtures
The following fixtures still contain "totalDifficulty" and must be cleaned up before validating timestamps:

  • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple_disable_code_and_storage.json:54
  • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create_disable_code_and_storage.json:13
  • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_code.json:13
  • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_storage.json:13
  • eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_storage.json:54
  • eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code_and_storage.json:54
  • eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code.json:54
  • eth/tracers/internal/tracetest/testdata/call_tracer_flat/create_oog_parity.json:13

After removing these fields, re-run the sanity-check script and confirm that each fixture’s genesis timestamp ≤ context timestamp.

Comment on lines +44 to 45
"timestamp": "1513676127"
},
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.

💡 Verification agent

🧩 Analysis chain

Genesis timestamp changed but genesis hash not updated — inconsistency risk

You modified "genesis.timestamp" (Line 44) without updating "genesis.hash" (Line 38). For genesis files, the hash is derived from header fields including the timestamp; changing the timestamp requires recomputing and updating the hash. Otherwise the fixture can become non-deterministic or rejected by tools that validate the genesis hash.

If the timestamp change is intentional, please recompute and replace "genesis.hash" accordingly; otherwise revert the timestamp to match the existing hash.


🏁 Script executed:

#!/bin/bash
# Display the genesis.hash and genesis.timestamp fields to verify consistency
grep -n '"genesis"' -A6 eth/tracers/internal/tracetest/testdata/call_tracer/create.json

Length of output: 299


Recompute genesis.hash to match updated timestamp

File eth/tracers/internal/tracetest/testdata/call_tracer/create.json (lines 38 & 44): you modified genesis.timestamp without updating the derived genesis.hash; regenerate the hash to reflect the new timestamp or revert the timestamp to maintain fixture consistency.

🤖 Prompt for AI Agents
In eth/tracers/internal/tracetest/testdata/call_tracer/create.json around lines
38 and 44, the genesis.timestamp was changed but genesis.hash was not updated;
regenerate the genesis.hash to reflect the modified timestamp (or revert the
timestamp to its previous value) so the fixture remains consistent—compute the
new genesis hash using the same algorithm/serialization used for other fixtures
and replace the existing genesis.hash value in the file.

```solidity
contract Revertor {
function run() public pure {
require(2 > 3, "This called failed");
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.

⚠️ Potential issue

Fix typo in revert message.

“This called failed” → “This call failed”.

Apply:

-        require(2 > 3, "This called failed");
+        require(2 > 3, "This call failed");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
require(2 > 3, "This called failed");
require(2 > 3, "This call failed");
🤖 Prompt for AI Agents
In
eth/tracers/internal/tracetest/testdata/call_tracer/inner_throw_outer_revert.md
around line 6, the revert message contains a typo "This called failed"; update
the string to "This call failed" so the require message is grammatically correct
and consistent.

Comment on lines +52 to +53
"gas": "0xca1d",
"gasUsed": "0xca1d",
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.

💡 Verification agent

🧩 Analysis chain

LGTM: gas/gasUsed now match the tx gas (0xca1d).

The updated expectations align with the encoded tx field (…82ca1d…). This should stabilize OOG tracing with the revised intrinsic gas rules.

Recommend running the tracer tests to ensure no other fixtures need updates:


🏁 Script executed:

#!/bin/bash
go test ./eth/tracers/internal/tracetest -run CallTracer

Length of output: 16566


Restore tracer tests by aligning gas serialization and fixtures

  • CallTracerNativeWithLog tests are failing: actual gasUsed remains 0x7160 but fixtures expect 0x1a9e5—update the OOG and related JSON fixtures under eth/tracers/internal/tracetest to the correct values.
  • FlatCallTracerNative tests are failing with unmarshal errors (non-string into hexutil.Uint64): adjust the flat tracer output to emit numeric gas fields or update the Go struct to accept hex strings.

Re-run go test ./eth/tracers/internal/tracetest until all tracer tests pass.

🤖 Prompt for AI Agents
In eth/tracers/internal/tracetest/testdata/call_tracer/oog.json around lines
52-53, update the fixture gas and gasUsed fields from the expected 0x1a9e5 to
the actual 0x7160 so the JSON matches runtime output; additionally, fix the
FlatCallTracer output to emit gas fields as JSON numeric values (not hex
strings) so Go unmarshalling into hexutil.Uint64 succeeds (alternatively, modify
the Go struct to accept hex strings), then re-run go test
./eth/tracers/internal/tracetest until all tracer tests pass.

"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
"timestamp": "1513601261",
"totalDifficulty": "7143276353481064"
"timestamp": "1513601261"
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.

💡 Verification agent

🧩 Analysis chain

Confirm removal of genesis.totalDifficulty across fixtures and loader tolerance.

Only genesis.timestamp is shown as changed here, but the file no longer contains genesis.totalDifficulty. Ensure all fixtures are updated consistently and that any loader/schema logic tolerates the missing field.

Run to sanity-check all tracer fixtures:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find any remaining uses of genesis.totalDifficulty
fd -t f -e json 'eth/tracers/internal/tracetest/testdata' \
  | xargs -I{} sh -c 'jq -e ".genesis.totalDifficulty" "{}" >/dev/null 2>&1 && echo "HAS totalDifficulty: {}" || true'

# 2) Ensure context.timestamp >= genesis.timestamp everywhere (numeric compare)
fd -t f -e json 'eth/tracers/internal/tracetest/testdata' \
  | xargs -I{} jq -r '"{} "+((.context.timestamp|tonumber) >= (.genesis.timestamp|tonumber) | tostring)' \
  | awk '$2!="true"{print "Timestamp order check failed for", $1}'

Length of output: 658


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find any remaining uses of genesis.totalDifficulty
find eth/tracers/internal/tracetest/testdata -type f -name '*.json' -print0 \
  | xargs -0 -I{} sh -c 'jq -e ".genesis.totalDifficulty" "{}" >/dev/null 2>&1 && echo "HAS totalDifficulty: {}"'

# 2) Ensure context.timestamp >= genesis.timestamp everywhere
find eth/tracers/internal/tracetest/testdata -type f -name '*.json' -print0 \
  | xargs -0 -I{} sh -c 'jq -e "(.context.timestamp|tonumber) >= (.genesis.timestamp|tonumber)" "{}" >/dev/null || echo "Timestamp order check failed for {}"'

Length of output: 1113


Remove genesis.totalDifficulty from tracer fixtures and update loader tolerance

  • The following fixtures still include genesis.totalDifficulty and need the field removed:
    • eth/tracers/internal/tracetest/testdata/call_tracer_flat/create_oog_parity.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_code_and_storage.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer/disable_storage.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_code.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_disable_storage.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/inner_create_disable_code_and_storage.json
    • eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/simple_disable_code_and_storage.json
  • Ensure the fixture loader/schema logic tolerates a missing genesis.totalDifficulty (e.g., default to zero or treat as optional).
  • Verified that every fixture’s context.timestampgenesis.timestamp. [run_scripts verification]
🤖 Prompt for AI Agents
In eth/tracers/internal/tracetest/testdata/call_tracer/selfdestruct.json around
line 50, the fixture still includes a genesis.totalDifficulty field that must be
removed and the test fixture loader/schema must be made tolerant of its absence:
delete genesis.totalDifficulty from this and the other listed fixture files, and
update the fixture loader/schema to treat genesis.totalDifficulty as optional
(default to zero or equivalent) so parsing succeeds when the field is missing;
no changes to timestamps are needed since context.timestamp ≥ genesis.timestamp
has been verified.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
eth/ethconfig/config.go (1)

187-190: Regenerate TOML codec and wire flags for VM tracing

VMTrace and VMTraceConfig were added here, but they’re absent from gen_config.go. Regenerate the TOML encoder/decoder so these fields persist in config files; also ensure CLI flags and tracer wiring are in place (per earlier feedback).

🧹 Nitpick comments (1)
eth/gasprice/gasprice_test.go (1)

101-111: Create the signer after mutating ChainConfig to avoid subtle fork-gate mismatches

Minor ordering nit: signer is constructed before you mutate config (London/Shanghai/etc. and Morph gates). Constructing it after the config edits is safer and more future-proof for time-gated forks.

Apply this minimal diff:

@@
-   gspec  = &core.Genesis{
+   gspec  = &core.Genesis{
      Config: &config,
      Alloc:  core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
    }
-   signer = types.LatestSigner(gspec.Config)
+   signer types.Signer
   )
@@
   config.Morph203Time = nil
   config.Morph300Time = nil
+  signer = types.LatestSigner(gspec.Config)

Also applies to: 118-119

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 26cb8a5 and fc46268.

📒 Files selected for processing (11)
  • cmd/geth/chaincmd.go (2 hunks)
  • cmd/geth/config.go (1 hunks)
  • cmd/geth/main.go (1 hunks)
  • cmd/utils/flags.go (1 hunks)
  • core/genesis.go (4 hunks)
  • core/state_processor_test.go (1 hunks)
  • core/vm/runtime/runtime.go (5 hunks)
  • eth/backend.go (1 hunks)
  • eth/ethconfig/config.go (2 hunks)
  • eth/ethconfig/gen_config.go (2 hunks)
  • eth/gasprice/gasprice_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/vm/runtime/runtime.go
🧰 Additional context used
🧬 Code graph analysis (4)
cmd/geth/main.go (1)
cmd/utils/flags.go (1)
  • OverrideMorph300TimeFlag (274-277)
cmd/geth/chaincmd.go (1)
cmd/utils/flags.go (1)
  • OverrideMorph300TimeFlag (274-277)
cmd/geth/config.go (1)
cmd/utils/flags.go (1)
  • OverrideMorph300TimeFlag (274-277)
core/genesis.go (1)
core/tracing/hooks.go (2)
  • BalanceIncreaseGenesisBalance (239-239)
  • NonceChangeGenesis (359-359)
🔇 Additional comments (11)
eth/gasprice/gasprice_test.go (1)

117-119: Explicitly disabling Morph300 in this test — LGTM

Setting config.Morph300Time = nil (alongside Morph203Time = nil) makes the pre-Morph300 behavior explicit and avoids future defaults affecting these gasprice tests.

eth/backend.go (1)

137-139: Genesis override wiring for Morph300 looks correct.

Mirrors Morph203 handling and cleanly feeds core.SetupGenesisBlockWithOverride.

core/state_processor_test.go (1)

62-63: Verify TestStateProcessorErrors under Morph300 activation
Setting Morph300Time = new(uint64) enables Morph300 from genesis, causing MakeSigner to select Morph300Signer. Confirm the test’s intent and update the want error strings in core/state_processor_test.go (around the dynamic‐fee cases) to reflect any behavior changes.

cmd/geth/main.go (1)

75-75: Flag exposure LGTM.

Override.morph300time is properly added next to Morph203 for discoverability.

cmd/utils/flags.go (1)

274-277: New flag definition is consistent with existing overrides.

Name/usage align with Morph203 counterpart; pointer type matches repo conventions.

cmd/geth/config.go (1)

165-168: Morph300 override plumbing is correctly wired end-to-end.
Confirmed OverrideMorph300Time flows through cmd/geth/config, ChainOverrides.apply, SetupGenesisBlockWithOverride, and client/backend initialization.

eth/ethconfig/config.go (1)

223-224: LGTM: Morph300Time override field mirrors Morph203Time

Field shape and tag look correct; just ensure gen_config.go is regenerated so both marshal and unmarshal paths include it. The init CLI flag is already wired in cmd/geth/chaincmd.go.

cmd/geth/chaincmd.go (2)

54-55: LGTM: Exposes --override.morph300time on init

Flag addition is consistent with Morph203Time and follows existing utils flag patterns.


218-221: LGTM: Applies Morph300Time override during genesis init

Override assignment mirrors Morph203Time and correctly passes through to SetupGenesisBlockWithOverride.

core/genesis.go (2)

149-151: LGTM: ChainOverrides extended with Morph300Time and applied

Override propagation and fork-order validation via CheckConfigForkOrder look correct.

Also applies to: 161-163


35-35: All AddBalance/SetNonce invocations now include tracing reasons — no two-argument calls remain across the codebase.

Comment on lines +67 to 68
OverrideMorph300Time *uint64 `toml:",omitempty"`
}
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.

💡 Verification agent

🧩 Analysis chain

Fix TOML round-trip: OverrideMorph300Time is not decoded in UnmarshalTOML

You added OverrideMorph300Time to the marshaler and assigned enc.OverrideMorph300Time, but the UnmarshalTOML path doesn’t populate it. Users setting this in TOML won’t get the value applied.

Action:

  • Add the field to the UnmarshalTOML dec struct and copy it into c in the decode path.
  • Since this is a generated file, prefer re-running codegen rather than hand-editing.

Example (non-diff, since the additions are outside the shown range):

// In UnmarshalTOML's inner Config struct:
OverrideMorph300Time *uint64 `toml:",omitempty"`

// After decoding:
if dec.OverrideMorph300Time != nil {
    c.OverrideMorph300Time = dec.OverrideMorph300Time
}

Also, VMTrace and VMTraceConfig recently added to eth/ethconfig/config.go are still missing in this generated file. Please run:

  • go generate ./eth/ethconfig

Also applies to: 116-116


🏁 Script executed:

#!/bin/bash
set -e
# Locate the generated config file
fd gen_config.go
# Show UnmarshalTOML definition
grep -R "func UnmarshalTOML" -n eth/ethconfig/gen_config.go || true
# Check if OverrideMorph300Time is decoded
grep -R "OverrideMorph300Time" -n eth/ethconfig/gen_config.go || true
# Check VMTrace and VMTraceConfig presence
grep -R "VMTrace" -n eth/ethconfig/gen_config.go || true

Length of output: 354


🏁 Script executed:

#!/bin/bash
set -e
# Check for UnmarshalTOML method
grep -R "UnmarshalTOML" -n eth/ethconfig/gen_config.go || echo "no UnmarshalTOML"
# Any unmarshal or decode references
grep -R "Unmarshal" -n eth/ethconfig/gen_config.go | head -n 20
# Inspect top of file to locate decoding logic
sed -n '1,200p' eth/ethconfig/gen_config.go

Length of output: 8207


🏁 Script executed:

#!/bin/bash
set -e
# Locate config.go
fd -e go eth/ethconfig/config.go
# Check for VMTrace in config.go
grep -R "VMTrace" -n eth/ethconfig/config.go || echo "no VMTrace"
# Check for VMTraceConfig in config.go
grep -R "VMTraceConfig" -n eth/ethconfig/config.go || echo "no VMTraceConfig"

Length of output: 217


Regenerate eth/ethconfig/gen_config.go

  • UnmarshalTOML’s dec struct omits OverrideMorph300Time, so it isn’t applied
  • VMTrace and VMTraceConfig fields are absent from the generated file
    Rerun go generate ./eth/ethconfig to include these mappings.
🤖 Prompt for AI Agents
In eth/ethconfig/gen_config.go around lines 67-68, the generated dec/unmarshal
struct is missing OverrideMorph300Time and the VMTrace and VMTraceConfig fields
so TOML/unmarshal ignores them; re-run the generator to regenerate mappings by
executing `go generate ./eth/ethconfig` (or the package-specific generator
command), verify the generated file now includes OverrideMorph300Time *uint64
and fields for VMTrace and VMTraceConfig in the dec struct and unmarshal logic,
run `go vet`/`go test` to ensure no build breaks, and commit the updated
gen_config.go.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
cmd/evm/internal/t8ntool/execution.go (2)

209-209: Resolved: receipt now carries L1 data fee.
Addresses prior feedback to persist L1 fee into receipt.


225-227: Resolved: paired OnTxEnd after receipt append.
Start/End parity achieved for successful txs.

core/state/state_object.go (2)

427-437: Fix inconsistent AddBalance return (prev vs current). Always return the updated balance.

For amount > 0, you currently return the previous balance via SetBalance, while for amount == 0 you return the current balance. This inconsistency will cause subtle bugs in callers.

Apply:

 func (s *stateObject) AddBalance(amount *big.Int) *big.Int {
   // EIP161: We must check emptiness for the objects such that the account
   // clearing (0,0,0 objects) can take effect.
   if amount.Sign() == 0 {
     if s.empty() {
       s.touch()
     }
-    return new(big.Int).Set(s.Balance())
+    return new(big.Int).Set(s.Balance())
   }
-  return s.SetBalance(new(big.Int).Add(s.Balance(), amount))
+  newBal := new(big.Int).Add(s.Balance(), amount)
+  _ = s.SetBalance(newBal) // journal prev, update balance
+  return new(big.Int).Set(newBal)
 }

Run to find callsites that might assume the old semantics:

#!/bin/bash
rg -nP -C2 '\.AddBalance\(' --type go

441-446: Make SubBalance return semantics consistent and copy on zero.

For amount == 0 you return a pointer to the internal balance; for > 0 you return the previous balance via SetBalance. Always return the updated balance and avoid exposing the internal pointer.

 func (s *stateObject) SubBalance(amount *big.Int) *big.Int {
   if amount.Sign() == 0 {
-    return s.Balance()
+    return new(big.Int).Set(s.Balance())
   }
-  return s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
+  newBal := new(big.Int).Sub(s.Balance(), amount)
+  _ = s.SetBalance(newBal)
+  return new(big.Int).Set(newBal)
 }
🧹 Nitpick comments (5)
cmd/evm/internal/t8ntool/execution.go (1)

167-171: Optional: fire OnTxEnd on error paths for start/end parity.
Today OnTxEnd runs only on success; consider invoking it with err and nil receipt on early rejections.

Apply:

       l1DataFee, err := fees.CalculateL1DataFee(tx, statedb, chainConfig, new(big.Int).SetUint64(pre.Env.Number))
       if err != nil {
         log.Info("rejected tx due to fees.CalculateL1DataFee", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)
         rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
+        if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil {
+          evm.Config.Tracer.OnTxEnd(nil, err)
+        }
         continue
       }
@@
       msgResult, err := core.ApplyMessage(evm, msg, gaspool, l1DataFee)
       if err != nil {
         statedb.RevertToSnapshot(snapshot)
         log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)
         rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
+        if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil {
+          evm.Config.Tracer.OnTxEnd(nil, err)
+        }
         continue
       }

Confirm OnTxEnd(receipt, err) signature matches Hooks.

Also applies to: 178-183

core/state/state_object.go (1)

448-457: SetBalance returns previous balance — verify all callers expect this.

Returning the previous value is fine for journaling, but easy to misuse. Given Add/Sub will now ignore this return, confirm no other callers expect “new/current”.

If confusion persists, consider renaming to SetBalancePrev (internal) and introducing a helper that returns the new/current value.

core/state/statedb.go (3)

407-414: Remove unreachable nil-guard after GetOrNewStateObject in AddBalance.

GetOrNewStateObject never returns nil. The guard adds noise.

 func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int {
-  stateObject := s.GetOrNewStateObject(addr)
-  if stateObject == nil {
-    return new(big.Int)
-  }
-  return stateObject.AddBalance(amount)
+  return s.GetOrNewStateObject(addr).AddBalance(amount)
 }

417-426: Same here: drop unreachable nil-guard in SubBalance.

Also, once stateObject.SubBalance is fixed to return the updated balance, this API will be consistent across zero/non-zero amounts.

 func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) *big.Int {
-  stateObject := s.GetOrNewStateObject(addr)
-  if stateObject == nil {
-    return new(big.Int)
-  }
-  if amount.Sign() == 0 {
-    return stateObject.Balance()
-  }
-  return stateObject.SubBalance(amount)
+  stateObject := s.GetOrNewStateObject(addr)
+  if amount.Sign() == 0 {
+    return stateObject.Balance()
+  }
+  return stateObject.SubBalance(amount)
 }

442-448: Simplify SetCode: nil-guard is unreachable after GetOrNewStateObject.

 func (s *StateDB) SetCode(addr common.Address, code []byte) []byte {
-  stateObject := s.GetOrNewStateObject(addr)
-  if stateObject != nil {
-    return stateObject.SetCode(code)
-  }
-  return nil
+  return s.GetOrNewStateObject(addr).SetCode(code)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fc46268 and 2bccdae.

📒 Files selected for processing (4)
  • cmd/evm/internal/t8ntool/execution.go (8 hunks)
  • core/state/state_object.go (2 hunks)
  • core/state/statedb.go (2 hunks)
  • core/tracing/hooks.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/tracing/hooks.go
🧰 Additional context used
🧬 Code graph analysis (3)
core/state/statedb.go (2)
core/tracing/hooks.go (3)
  • StateDB (49-60)
  • BalanceChangeReason (224-224)
  • NonceChangeReason (351-351)
core/vm/interface.go (1)
  • StateDB (29-89)
core/state/state_object.go (1)
crypto/codehash/codehash.go (1)
  • KeccakCodeHash (16-18)
cmd/evm/internal/t8ntool/execution.go (6)
core/vm/interpreter.go (1)
  • Config (33-42)
core/state/statedb.go (1)
  • StateDB (63-127)
core/tracing/hooks.go (5)
  • StateDB (49-60)
  • BalanceIncreaseRewardMineUncle (233-233)
  • BalanceIncreaseRewardMineBlock (235-235)
  • NonceChangeGenesis (359-359)
  • BalanceIncreaseGenesisBalance (239-239)
core/vm/interface.go (1)
  • StateDB (29-89)
core/state_transition.go (1)
  • ExecutionResult (99-104)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
🔇 Additional comments (10)
cmd/evm/internal/t8ntool/execution.go (6)

33-33: Good: tracing import is warranted by new reasoned state mutations.


118-118: Signer now time-aware: correct param used.
Passing pre.Env.Timestamp matches MakeSigner(config, blockNumber, blockTime).


173-175: Good: OnTxStart hook before execution.
Ensures trace context has VM, tx, sender.


254-256: Good: reward attributions annotated with tracing reasons.
Improves post-hoc analysis for miner and ommer rewards.


282-283: Good: genesis nonce/balance set with explicit reasons.
Consistent with reasoned StateDB API.


101-101: All Prestate.Apply call sites have been updated to match the new signature; no remaining callers pass the old tracer/factory arguments.

core/state/state_object.go (2)

510-518: SetCode journaling and return of prevCode look correct.


520-527: Hash/size updates in setCode are correct and ordered.

core/state/statedb.go (2)

428-433: Reason parameter currently unused here — ensure hooks consume it (wrapper or tracer).

If statedb_hooked.go or a wrapper wires reason to tracing hooks, all good; otherwise you lose valuable context.


435-440: Ditto for SetNonce: verify reason is propagated to tracing.

Comment thread core/state/statedb.go
Comment on lines +450 to 456
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash {
stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(s.db, key, value)
}
return common.Hash{}
}
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.

💡 Verification agent

🧩 Analysis chain

SetState discards the required return value; return the previous slot value.

The interface returns common.Hash. Currently always zero. Return the previous value for callers/tracers.

 func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash {
-  stateObject := s.GetOrNewStateObject(addr)
-  if stateObject != nil {
-    stateObject.SetState(s.db, key, value)
-  }
-  return common.Hash{}
+  stateObject := s.GetOrNewStateObject(addr)
+  prev := stateObject.GetState(s.db, key)
+  stateObject.SetState(s.db, key, value)
+  return prev
 }

🏁 Script executed:

#!/bin/bash
# Grep potential callsites relying on SetState's return
rg -nP -C2 '\.SetState\(' --type go

Length of output: 21396


🏁 Script executed:

#!/bin/bash
# Locate hookedStateDB SetState definition and context
rg -nP 'func\s+\(s \*hookedStateDB\)\s+SetState' -n --type go
rg -nP -C3 'func\s+\(s \*hookedStateDB\)\s+SetState' -n --type go

Length of output: 779


Return previous storage slot value in StateDB.SetState
Modify core/state/statedb.go so that SetState fetches and returns the old value (with a nil-check) instead of always returning a zero hash:

 func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash {
-   stateObject := s.GetOrNewStateObject(addr)
-   if stateObject != nil {
-       stateObject.SetState(s.db, key, value)
-   }
-   return common.Hash{}
+   stateObject := s.GetOrNewStateObject(addr)
+   if stateObject == nil {
+       return common.Hash{}
+   }
+   prev := stateObject.GetState(s.db, key)
+   stateObject.SetState(s.db, key, value)
+   return prev
 }

HookedStateDB already propagates this value to OnStorageChange (core/state/statedb_hooked.go:183–185), and existing call sites ignore the return so this change is backwards-compatible. (blog.csdn.net)

🤖 Prompt for AI Agents
In core/state/statedb.go around lines 450–456, change SetState to fetch and
return the previous storage slot value instead of always returning a zero hash:
if stateObject is nil return common.Hash{}; otherwise call
stateObject.GetState(s.db, key) (or the appropriate getter on StateObject with
the db and key) to capture the old value, then call stateObject.SetState(s.db,
key, value) and return the captured old value (preserving nil-checks and
existing behavior).

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
core/types/l2trace.go (3)

129-147: Document the new EIP‑7702 field; consider JSON omission semantics.

  • Add a short comment tying AuthorizationList to EIP‑7702 for clarity.
  • Note: with omitempty, non-nil empty slices still marshal as []. If you want the field omitted when empty, ensure it is nil at construction time (see suggestion below on Lines 161-180).
 type TransactionData struct {
@@
-    AuthorizationList []SetCodeAuthorization `json:"authorizationList,omitempty"`
+    // EIP-7702: per-tx SetCode authorizations (present on typed tx that carry them).
+    AuthorizationList []SetCodeAuthorization `json:"authorizationList,omitempty"`

153-155: Don’t silently discard Sender() errors; make intent explicit.

If signature recovery fails, traces will show zero-address. Keep behavior, but document it to avoid confusion during debugging.

-from, _ := Sender(signer, tx)
+from, err := Sender(signer, tx)
+if err != nil {
+    // Intentionally ignore to keep trace emission resilient; zero address denotes
+    // an unrecoverable sender for this tx in trace outputs.
+}

161-180: Ensure authorizationList truly omits on empty.

Normalize to nil when there are no authorizations so omitempty suppresses the field rather than emitting [].

 result := &TransactionData{
@@
-    AccessList:        tx.AccessList(),
-    AuthorizationList: tx.SetCodeAuthorizations(),
+    AccessList:        tx.AccessList(),
+    // Normalize empty -> nil so `omitempty` suppresses the field.
+    AuthorizationList: func() []SetCodeAuthorization {
+        al := tx.SetCodeAuthorizations()
+        if len(al) == 0 {
+            return nil
+        }
+        return al
+    }(),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2bccdae and 6e5f726.

📒 Files selected for processing (1)
  • core/types/l2trace.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
core/types/l2trace.go (2)
core/types/tx_setcode.go (1)
  • SetCodeAuthorization (72-79)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
🔇 Additional comments (1)
core/types/l2trace.go (1)

151-153: All call sites updated. Verified there are no remaining 3-arg NewTransactionData calls.
(Optional) Clarify in the doc comment that blockTime is the block header timestamp (seconds since Unix epoch):

 // NewTransactionData returns a transaction that will serialize to the trace
 // representation, with the given location metadata set (if available).
+// blockTime must be the block header timestamp (seconds since Unix epoch).

* add logs for debug

* fix sender

* convert auth data

* revert log

---------

Co-authored-by: fletcher.fan <fletcher.fan@bitget.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
core/types/transaction_signing.go (1)

41-61: MakeSigner may panic when ChainID is nil on Berlin+ forks

newModernSigner panics for nil/≤0 chainIDs. If a config with Berlin/London/Curie/Morph300 is used without ChainID set (tests, tools), this path will panic. Guard and fall back to Homestead/Frontier when ChainID is nil.

 func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
-	var signer Signer
-	switch {
+	// If ChainID is nil, avoid modern signers that require it.
+	if config.ChainID == nil {
+		if config.IsHomestead(blockNumber) {
+			return HomesteadSigner{}
+		}
+		return FrontierSigner{}
+	}
+	var signer Signer
+	switch {
 	case config.IsMorph300(blockNumber, blockTime):
 		signer = NewMorph300Signer(config.ChainID)
 	case config.IsCurie(blockNumber):
 		signer = NewCurieSigner(config.ChainID)
 	case config.IsLondon(blockNumber):
 		signer = NewLondonSigner(config.ChainID)
 	case config.IsBerlin(blockNumber):
 		signer = NewEIP2930Signer(config.ChainID)
core/types/l2trace.go (1)

7-11: Use hexutil.U256 for JSON hex encoding; drop unused uint256 import

uint256.Int has no JSON marshaler; it will serialize as {}. Use hexutil.U256 to get 0x-prefixed hex. Then remove the uint256 import to avoid compile errors.

-import (
-	"encoding/json"
-	"math/big"
-
-	"github.com/holiman/uint256"
+import (
+	"encoding/json"
+	"math/big"
🧹 Nitpick comments (4)
core/types/transaction_signing.go (3)

234-237: maps.Equal OK but see toolchain note

Equality logic is sound; dependency caveat covered above.

If you prefer avoiding maps.Equal, a tiny length+key loop would decouple from Go version.


248-267: Sender() chainID check stricter than SignatureValues() — align semantics

Sender rejects typed txs when tx.ChainId()==0, but SignatureValues allows zero. This inconsistency can surprise signing flows (sign ok, recover fails). Consider accepting zero here too (as “unspecified”) or tightening SignatureValues.

-	if tx.ChainId().Cmp(s.chainID) != 0 {
+	if tx.ChainId().Sign() != 0 && tx.ChainId().Cmp(s.chainID) != 0 {
 		return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, tx.ChainId(), s.chainID)
 	}

269-285: SignatureValues: OK; consider explicit length check

decodeSignature panics on wrong length; if you prefer error returns over panic at this layer, validate sig len first.

-	R, S, _ = decodeSignature(sig)
+	if len(sig) != crypto.SignatureLength {
+		return nil, nil, nil, ErrInvalidSig
+	}
+	R, S, _ = decodeSignature(sig)
core/types/l2trace.go (1)

161-192: NewTransactionData: signer construction looks correct

Passing blockNumber and blockTime into MakeSigner aligns with the new API. The rest of the mapping is consistent, and AuthorizationList uses omitempty (empty slice omitted).

Minor: avoid allocating an empty slice by returning nil when len(auths)==0 inside convertToAuthorizationData to reduce JSON clutter/allocs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e5f726 and 8464a96.

📒 Files selected for processing (2)
  • core/types/l2trace.go (3 hunks)
  • core/types/transaction_signing.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
core/types/l2trace.go (3)
internal/ethapi/api.go (1)
  • AccessList (1536-1632)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/types/tx_setcode.go (1)
  • SetCodeAuthorization (72-79)
core/types/transaction_signing.go (4)
params/config.go (1)
  • ChainConfig (519-560)
params/forks/forks.go (7)
  • Fork (20-20)
  • SpuriousDragon (28-28)
  • Homestead (25-25)
  • Berlin (34-34)
  • London (35-35)
  • Curie (40-40)
  • Morph300 (42-42)
core/types/transaction.go (8)
  • LegacyTxType (50-50)
  • L1MessageTxType (56-56)
  • AccessListTxType (51-51)
  • DynamicFeeTxType (52-52)
  • BlobTxType (53-53)
  • SetCodeTxType (54-54)
  • Transaction (60-68)
  • ErrTxTypeNotSupported (39-39)
cmd/evm/internal/t8ntool/transaction.go (1)
  • Transaction (68-184)
🔇 Additional comments (4)
core/types/transaction_signing.go (4)

99-106: Guard against zero/negative chainIDs

LatestSignerForChainID forwards chainID to newModernSigner which panics on ≤0. Either validate here or document the precondition.

 func LatestSignerForChainID(chainID *big.Int) Signer {
 	var signer Signer
-	if chainID != nil {
+	if chainID != nil && chainID.Sign() > 0 {
 		signer = NewMorph300Signer(chainID)
 	} else {
 		signer = HomesteadSigner{}
 	}
 	return signer
 }

195-213: Legacy signer selection looks good

Fork-based legacy fallback (Frontier/Homestead/EIP155) is correct and makes modernSigner safe for LegacyTxType.


287-321: Per-fork constructors are consistent

The new constructors correctly parameterize modernSigner by fork thresholds.


19-30: Go 1.21+ dependency via maps.Equal — go.mod OK; verify CI images

  • go.mod declares "go 1.21" (line 3) — stdlib maps (maps.Equal) is supported.
  • CI runner images / Dockerfiles referencing Go were not found in the repo; ensure CI workflows or Dockerfiles are pinned to Go ≥1.21 or switch to golang.org/x/exp/maps / a manual equality loop.

Comment thread core/types/l2trace.go
Comment on lines +150 to 157
type SetCodeAuthorizationData struct {
ChainID uint256.Int `json:"chainId" gencodec:"required"`
Address common.Address `json:"address" gencodec:"required"`
Nonce uint64 `json:"nonce" gencodec:"required"`
V hexutil.Uint64 `json:"yParity" gencodec:"required"`
R hexutil.U256 `json:"r" gencodec:"required"`
S hexutil.U256 `json:"s" gencodec:"required"`
}
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.

⚠️ Potential issue

Fix ChainID JSON encoding in SetCodeAuthorizationData

Switch ChainID to hexutil.U256 for correct wire format.

 type SetCodeAuthorizationData struct {
-	ChainID uint256.Int    `json:"chainId" gencodec:"required"`
+	ChainID hexutil.U256   `json:"chainId" gencodec:"required"`
 	Address common.Address `json:"address" gencodec:"required"`
 	Nonce   uint64         `json:"nonce" gencodec:"required"`
 	V       hexutil.Uint64 `json:"yParity" gencodec:"required"`
 	R       hexutil.U256   `json:"r" gencodec:"required"`
 	S       hexutil.U256   `json:"s" gencodec:"required"`
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type SetCodeAuthorizationData struct {
ChainID uint256.Int `json:"chainId" gencodec:"required"`
Address common.Address `json:"address" gencodec:"required"`
Nonce uint64 `json:"nonce" gencodec:"required"`
V hexutil.Uint64 `json:"yParity" gencodec:"required"`
R hexutil.U256 `json:"r" gencodec:"required"`
S hexutil.U256 `json:"s" gencodec:"required"`
}
type SetCodeAuthorizationData struct {
ChainID hexutil.U256 `json:"chainId" gencodec:"required"`
Address common.Address `json:"address" gencodec:"required"`
Nonce uint64 `json:"nonce" gencodec:"required"`
V hexutil.Uint64 `json:"yParity" gencodec:"required"`
R hexutil.U256 `json:"r" gencodec:"required"`
S hexutil.U256 `json:"s" gencodec:"required"`
}
🤖 Prompt for AI Agents
In core/types/l2trace.go around lines 150 to 157, the ChainID field in
SetCodeAuthorizationData uses uint256.Int which produces the wrong wire JSON
format; change the ChainID field type to hexutil.U256 (maintain the
`json:"chainId" gencodec:"required"` tag) so it serializes as the expected
hex-encoded unsigned 256-bit value, and update imports accordingly (remove or
replace uint256 import if no longer used and ensure hexutil is imported).

Comment thread core/types/l2trace.go
Comment on lines +194 to 207
func convertToAuthorizationData(auths []SetCodeAuthorization) []SetCodeAuthorizationData {
result := make([]SetCodeAuthorizationData, len(auths))
for i, auth := range auths {
result[i] = SetCodeAuthorizationData{
ChainID: auth.ChainID,
Address: auth.Address,
Nonce: auth.Nonce,
V: hexutil.Uint64(auth.V),
R: hexutil.U256(auth.R),
S: hexutil.U256(auth.S),
}
}
return result
}
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.

🛠️ Refactor suggestion

Map auth.ChainID to hexutil.U256

Update the converter to match the struct change above.

 func convertToAuthorizationData(auths []SetCodeAuthorization) []SetCodeAuthorizationData {
 	result := make([]SetCodeAuthorizationData, len(auths))
 	for i, auth := range auths {
 		result[i] = SetCodeAuthorizationData{
-			ChainID: auth.ChainID,
+			ChainID: hexutil.U256(auth.ChainID),
 			Address: auth.Address,
 			Nonce:   auth.Nonce,
 			V:       hexutil.Uint64(auth.V),
 			R:       hexutil.U256(auth.R),
 			S:       hexutil.U256(auth.S),
 		}
 	}
 	return result
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func convertToAuthorizationData(auths []SetCodeAuthorization) []SetCodeAuthorizationData {
result := make([]SetCodeAuthorizationData, len(auths))
for i, auth := range auths {
result[i] = SetCodeAuthorizationData{
ChainID: auth.ChainID,
Address: auth.Address,
Nonce: auth.Nonce,
V: hexutil.Uint64(auth.V),
R: hexutil.U256(auth.R),
S: hexutil.U256(auth.S),
}
}
return result
}
func convertToAuthorizationData(auths []SetCodeAuthorization) []SetCodeAuthorizationData {
result := make([]SetCodeAuthorizationData, len(auths))
for i, auth := range auths {
result[i] = SetCodeAuthorizationData{
ChainID: hexutil.U256(auth.ChainID),
Address: auth.Address,
Nonce: auth.Nonce,
V: hexutil.Uint64(auth.V),
R: hexutil.U256(auth.R),
S: hexutil.U256(auth.S),
}
}
return result
}
🤖 Prompt for AI Agents
In core/types/l2trace.go around lines 194 to 207, the converter currently
assigns ChainID directly but the SetCodeAuthorizationData struct now expects
hexutil.U256; update the assignment to convert auth.ChainID to hexutil.U256
(e.g., ChainID: hexutil.U256(auth.ChainID)) while keeping the existing
conversions for V, R, S unchanged so the result slice uses the correct hexutil
types.

Comment on lines 70 to 89
func LatestSigner(config *params.ChainConfig) Signer {
var signer Signer
if config.ChainID != nil {
if config.LondonBlock != nil {
return NewLondonSignerWithEIP4844(config.ChainID)
}
if config.BerlinBlock != nil {
return NewEIP2930Signer(config.ChainID)
}
if config.EIP155Block != nil {
return NewEIP155Signer(config.ChainID)
switch {
case config.Morph300Time != nil:
signer = NewMorph300Signer(config.ChainID)
case config.CurieBlock != nil:
signer = NewCurieSigner(config.ChainID)
case config.LondonBlock != nil:
signer = NewLondonSigner(config.ChainID)
case config.BerlinBlock != nil:
signer = NewEIP2930Signer(config.ChainID)
case config.EIP155Block != nil:
signer = NewEIP155Signer(config.ChainID)
default:
signer = HomesteadSigner{}
}
}
return HomesteadSigner{}
return signer
}
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.

⚠️ Potential issue

LatestSigner returns nil when ChainID is nil

Current logic can return a nil Signer, causing NPEs at call sites. Default to Homestead when ChainID is nil.

 func LatestSigner(config *params.ChainConfig) Signer {
-	var signer Signer
-	if config.ChainID != nil {
+	var signer Signer
+	if config.ChainID != nil {
 		switch {
 		case config.Morph300Time != nil:
 			signer = NewMorph300Signer(config.ChainID)
 		case config.CurieBlock != nil:
 			signer = NewCurieSigner(config.ChainID)
 		case config.LondonBlock != nil:
 			signer = NewLondonSigner(config.ChainID)
 		case config.BerlinBlock != nil:
 			signer = NewEIP2930Signer(config.ChainID)
 		case config.EIP155Block != nil:
 			signer = NewEIP155Signer(config.ChainID)
 		default:
 			signer = HomesteadSigner{}
 		}
-	}
-	return signer
+	} else {
+		signer = HomesteadSigner{}
+	}
+	return signer
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func LatestSigner(config *params.ChainConfig) Signer {
var signer Signer
if config.ChainID != nil {
if config.LondonBlock != nil {
return NewLondonSignerWithEIP4844(config.ChainID)
}
if config.BerlinBlock != nil {
return NewEIP2930Signer(config.ChainID)
}
if config.EIP155Block != nil {
return NewEIP155Signer(config.ChainID)
switch {
case config.Morph300Time != nil:
signer = NewMorph300Signer(config.ChainID)
case config.CurieBlock != nil:
signer = NewCurieSigner(config.ChainID)
case config.LondonBlock != nil:
signer = NewLondonSigner(config.ChainID)
case config.BerlinBlock != nil:
signer = NewEIP2930Signer(config.ChainID)
case config.EIP155Block != nil:
signer = NewEIP155Signer(config.ChainID)
default:
signer = HomesteadSigner{}
}
}
return HomesteadSigner{}
return signer
}
func LatestSigner(config *params.ChainConfig) Signer {
var signer Signer
if config.ChainID != nil {
switch {
case config.Morph300Time != nil:
signer = NewMorph300Signer(config.ChainID)
case config.CurieBlock != nil:
signer = NewCurieSigner(config.ChainID)
case config.LondonBlock != nil:
signer = NewLondonSigner(config.ChainID)
case config.BerlinBlock != nil:
signer = NewEIP2930Signer(config.ChainID)
case config.EIP155Block != nil:
signer = NewEIP155Signer(config.ChainID)
default:
signer = HomesteadSigner{}
}
} else {
signer = HomesteadSigner{}
}
return signer
}
🤖 Prompt for AI Agents
In core/types/transaction_signing.go around lines 70 to 89, LatestSigner can
return a nil Signer when config.ChainID is nil; change the function to default
to returning HomesteadSigner instead of nil. Specifically, initialize signer to
HomesteadSigner (or set signer = HomesteadSigner{} in the outer else branch when
ChainID is nil) so that callers never receive a nil Signer while preserving the
existing switch logic when ChainID is non-nil.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
core/genesis.go (1)

149-151: Doc nit: update ChainOverrides comment and new field naming context.

Comment mentions BSC; this repo is Morph. Also, please add a brief note that ViridianTime gates the Morph300/Viridian instruction set and tracing behavior.

-// Typically, these modifications involve hardforks that are not enabled on the BSC mainnet, intended for testing purposes.
+// Typically, these modifications involve hardforks that may not be enabled on Morph mainnet, intended for testing purposes.
+// ViridianTime gates the Viridian/Morph300 fork (EIP‑7702 enablement and tracing changes).
core/vm/runtime/runtime.go (1)

129-131: Tx-start tracing added; consider also emitting OnTxEnd.

OnTxStart is now invoked in Execute/Create/Call. If tracers rely on paired lifecycle hooks, add a matching OnTxEnd call after execution completes.

If OnTxEnd’s signature differs (e.g., needs status/gasUsed), I can adapt the call sites once you confirm the hook shape.

Also applies to: 167-168, 198-200

core/vm/evm.go (1)

312-316: Remove stale commented-out tracer code.

These commented debug hooks add noise; they’re superseded by captureBegin/captureEnd.

-	// if evm.Config.Debug {
-	// 	evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
-	// 	defer func(startGas uint64) {
-	// 		evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
-	// 	}(gas)
-	// }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8464a96 and 21f6f6c.

📒 Files selected for processing (16)
  • cmd/geth/chaincmd.go (2 hunks)
  • cmd/geth/config.go (1 hunks)
  • cmd/geth/main.go (1 hunks)
  • cmd/utils/flags.go (1 hunks)
  • core/genesis.go (4 hunks)
  • core/state_processor_test.go (1 hunks)
  • core/tx_pool.go (10 hunks)
  • core/types/transaction_signing.go (5 hunks)
  • core/vm/evm.go (18 hunks)
  • core/vm/interpreter.go (7 hunks)
  • core/vm/jump_table.go (2 hunks)
  • core/vm/runtime/runtime.go (5 hunks)
  • eth/backend.go (1 hunks)
  • eth/ethconfig/config.go (2 hunks)
  • eth/ethconfig/gen_config.go (2 hunks)
  • eth/gasprice/gasprice_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • cmd/geth/main.go
  • cmd/utils/flags.go
  • eth/backend.go
  • eth/ethconfig/gen_config.go
🧰 Additional context used
🧬 Code graph analysis (8)
cmd/geth/chaincmd.go (1)
cmd/utils/flags.go (1)
  • OverrideViridianTimeFlag (274-277)
cmd/geth/config.go (1)
cmd/utils/flags.go (1)
  • OverrideViridianTimeFlag (274-277)
core/types/transaction_signing.go (4)
params/config.go (1)
  • ChainConfig (519-560)
params/forks/forks.go (7)
  • Fork (20-20)
  • SpuriousDragon (28-28)
  • Homestead (25-25)
  • Berlin (34-34)
  • London (35-35)
  • Curie (40-40)
  • Viridian (42-42)
core/types/transaction.go (8)
  • LegacyTxType (50-50)
  • L1MessageTxType (56-56)
  • AccessListTxType (51-51)
  • DynamicFeeTxType (52-52)
  • BlobTxType (53-53)
  • SetCodeTxType (54-54)
  • Transaction (60-68)
  • ErrTxTypeNotSupported (39-39)
cmd/evm/internal/t8ntool/transaction.go (1)
  • Transaction (68-184)
core/vm/runtime/runtime.go (2)
core/types/transaction.go (1)
  • NewTx (71-75)
core/types/legacy_tx.go (1)
  • LegacyTx (27-35)
core/tx_pool.go (5)
core/types/transaction.go (3)
  • SetCodeTxType (54-54)
  • ErrTxTypeNotSupported (39-39)
  • Transaction (60-68)
core/error.go (5)
  • ErrTxTypeNotSupported (89-89)
  • ErrIntrinsicGas (85-85)
  • ErrOutOfOrderTxFromDelegated (122-122)
  • ErrInflightTxLimitReached (118-118)
  • ErrAuthorityReserved (127-127)
core/state_transition.go (1)
  • IntrinsicGas (134-184)
core/types/transaction_signing.go (1)
  • Sender (146-162)
crypto/codehash/codehash.go (1)
  • EmptyKeccakCodeHash (10-10)
core/vm/evm.go (6)
core/vm/interpreter.go (1)
  • Config (33-42)
core/vm/interface.go (1)
  • StateDB (29-89)
core/vm/contracts.go (1)
  • RunPrecompiledContract (216-227)
core/vm/contract.go (4)
  • NewContract (68-85)
  • AccountRef (40-40)
  • Contract (47-65)
  • ContractRef (29-31)
core/tracing/hooks.go (7)
  • GasChangeCallFailedExecution (332-332)
  • BalanceChangeTouchAccount (261-261)
  • NonceChangeNewContract (368-368)
  • GasChangeCallCodeStorage (323-323)
  • GasChangeCallInitialBalance (309-309)
  • GasChangeCallLeftOverReturned (313-313)
  • VMContext (63-70)
core/types/tx_setcode.go (1)
  • ParseDelegation (37-42)
core/vm/interpreter.go (6)
eth/tracers/dir.go (1)
  • Tracer (40-45)
core/tracing/hooks.go (2)
  • Hooks (192-220)
  • GasChangeCallOpCode (326-326)
core/vm/memory.go (1)
  • Memory (26-29)
core/vm/stack.go (1)
  • Stack (35-37)
core/vm/contract.go (1)
  • Contract (47-65)
core/vm/errors.go (2)
  • VMErrorFromErr (98-107)
  • ErrOutOfGas (27-27)
core/genesis.go (1)
core/tracing/hooks.go (2)
  • BalanceIncreaseGenesisBalance (239-239)
  • NonceChangeGenesis (359-359)
🔇 Additional comments (19)
core/state_processor_test.go (1)

62-63: Include Viridian fork in test config.

Setting ViridianTime alongside the other zero-time forks keeps these “recent chain” tests aligned with the updated configuration shape, ensuring LatestSigner and fork gating see the new activation.

core/types/transaction_signing.go (1)

70-89: Ensure LatestSigner never returns nil

Returning nil when ChainID is unset leaves callers like the txpool with a nil signer, and the first call to types.Sender will panic. This regression shows up immediately when bootstrapping with a chain config that omits ChainID (common in tests/private devnets).

 func LatestSigner(config *params.ChainConfig) Signer {
-	var signer Signer
-	if config.ChainID != nil {
+	signer := Signer(HomesteadSigner{})
+	if config.ChainID != nil {
 		switch {
 		case config.ViridianTime != nil:
 			signer = NewViridianSigner(config.ChainID)
@@
-		default:
-			signer = HomesteadSigner{}
 		}
 	}
 	return signer
 }
core/genesis.go (2)

161-163: Override wiring for ViridianTime looks correct.

Nil-safe receiver on apply avoids panics when overrides is nil; fork-order check is preserved.

Please confirm ChainConfig has ViridianTime in CheckConfigForkOrder() ordering.


346-349: Good: genesis mutations emit tracing reasons.

AddBalance(..., BalanceIncreaseGenesisBalance) and SetNonce(..., NonceChangeGenesis) align with the new Hooks plumbing.

Do you want a small test ensuring OnBalanceChange/OnNonceChange fire during ToBlock() for alloc entries? I can draft it.

core/vm/runtime/runtime.go (1)

77-78: Defaulting ViridianTime to 0 enables the fork by default in runtime.

This mirrors other forks in setDefaults, but it activates Viridian for runtime executions at time 0. Confirm this is intentional for t8n/evm usage.

core/vm/jump_table.go (2)

59-60: Viridian instruction set registration matches interpreter selection.

Interpreter prefers viridianInstructionSet when IsViridian; good cohesion.


65-69: Initialize Viridian from Curie and enable 7702.

Looks right. Please ensure enable7702 is idempotent and only toggles the intended opcodes.

core/vm/evm.go (6)

182-188: Call tracing begin/end wrapper is correct and defers will see final gas.

Using the named result (leftOverGas) in the deferred captureEnd is fine because named returns are assigned before defers run.

Confirm that tracers expect GasChangeCallInitialBalance at begin and GasChangeCallLeftOverReturned when leftOverGas > 0.


535-563: Delegation resolution: update fork name in comments and guard self-delegation.

Prevent a redundant self lookup and align comments with the Viridian fork name.

-// resolveCode returns the code associated with the provided account. After
-// Prague, it can also resolve code pointed to by a delegation designator.
+// resolveCode returns the code associated with the provided account.
+// On Viridian, it can also resolve code pointed to by a delegation designator.
 func (evm *EVM) resolveCode(addr common.Address) []byte {
 	code := evm.StateDB.GetCode(addr)
 	if !evm.chainRules.IsViridian {
 		return code
 	}
 	if target, ok := types.ParseDelegation(code); ok {
-		// Note we only follow one level of delegation.
-		return evm.StateDB.GetCode(target)
+		if target == addr { // avoid self-delegation loop
+			return code
+		}
+		// Note we only follow one level of delegation.
+		return evm.StateDB.GetCode(target)
 	}
 	return code
 }
 
-// resolveCodeHash returns the code hash associated with the provided address.
-// After Prague, it can also resolve code hash of the account pointed to by a
+// resolveCodeHash returns the code hash associated with the provided address.
+// On Viridian, it can also resolve code hash of the account pointed to by a
 // delegation designator. Although this is not accessible in the EVM it is used
 // internally to associate jumpdest analysis to code.
 func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
 	if evm.chainRules.IsViridian {
 		code := evm.StateDB.GetCode(addr)
 		if target, ok := types.ParseDelegation(code); ok {
-			// Note we only follow one level of delegation.
-			return evm.StateDB.GetKeccakCodeHash(target)
+			if target == addr { // avoid self-delegation loop
+				return evm.StateDB.GetKeccakCodeHash(addr)
+			}
+			// Note we only follow one level of delegation.
+			return evm.StateDB.GetKeccakCodeHash(target)
 		}
 	}
 	return evm.StateDB.GetKeccakCodeHash(addr)
 }

572-610: Tracer helpers and VM context exposure: LGTM.

captureBegin/captureEnd and GetVMContext provide the needed signals and context.


389-399: Gas-change on failed execution looks consistent.

On failure (non-revert), GasChangeCallFailedExecution is emitted and gas zeroed.


460-470: Nonce change reasons during create are tagged correctly.

Incrementing caller nonce and setting created-account nonce with NonceChangeNewContract is consistent with tracing expectations.


210-211: Precompile execution now traces gas deltas.

Passing tracer through RunPrecompiledContract enables GasChangeCallPrecompiledContract emissions.

Also applies to: 278-279, 336-337, 390-391, 500-501, 513-514

core/vm/interpreter.go (6)

34-42: Tracer API switch to tracing.Hooks is good; Debug flag removal aligns with hook presence.

This reduces legacy logger surface area; jump table remains configurable via Config.


52-95: ScopeContext accessors: useful for tracers; ensure read-only semantics.

Returning underlying slices without copy is fine for internal tracers; callers must not mutate.

Confirm external tracers never mutate MemoryData/StackData.


124-126: Viridian jump table selection precedence.

Viridian first, then Curie et al. Matches the new fork. Ensure Rules(..).IsViridian toggles with ViridianTime.


299-307: Dynamic gas error wrapping.

Returning fmt.Errorf("%w: %v", ErrOutOfGas, err) keeps error class while preserving detail; good for VMErrorFromErr classification.


316-325: Opcode-level tracing order and gas-change emission look correct.

OnGasChange before OnOpcode with (prevGas, prevGas-cost) and pre-expansion memory snapshot is appropriate.


333-342: STOP token normalization.

Mapping errStopToken to nil maintains legacy behavior.

Comment thread core/tx_pool.go
Comment on lines +758 to 779
// Allow at most one in-flight tx for delegated accounts or those with a
// pending authorization.
if err := pool.checkDelegationLimit(tx); err != nil {
return err
}
// For symmetry, allow at most one in-flight tx for any authority with a
// pending transaction.
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
for _, auth := range auths {
var count int
if pending := pool.pending[auth]; pending != nil {
count += pending.Len()
}
if queue := pool.queue[auth]; queue != nil {
count += queue.Len()
}
if count > 1 {
return ErrAuthorityReserved
}
}
}
return nil
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.

⚠️ Potential issue | 🟠 Major

Authority reservation check misses cross-sender conflicts

pool.pending/pool.queue are keyed by sender, so this path only notices transactions whose sender equals the authority. It lets multiple SetCode txs from different senders reserve the same authority concurrently, defeating the single-in-flight guarantee the EIP requires. Please drive the check off the authority index you already maintain in txLookup so we examine the transactions that actually reference the authority and only allow same-sender/same-nonce replacements.

 func (pool *TxPool) validateAuth(tx *types.Transaction) error {
@@
-	if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
-		for _, auth := range auths {
-			var count int
-			if pending := pool.pending[auth]; pending != nil {
-				count += pending.Len()
-			}
-			if queue := pool.queue[auth]; queue != nil {
-				count += queue.Len()
-			}
-			if count > 1 {
-				return ErrAuthorityReserved
-			}
-		}
+	if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
+		from, _ := types.Sender(pool.signer, tx)
+		for _, auth := range auths {
+			for _, hash := range pool.all.AuthTxHashes(auth) {
+				if other := pool.all.Get(hash); other != nil {
+					ofrom, _ := types.Sender(pool.signer, other)
+					if ofrom != from || other.Nonce() != tx.Nonce() {
+						return ErrAuthorityReserved
+					}
+				}
+			}
+		}
 	}
 	return nil
 }

Drop this helper next to hasAuth so the validation can query the authority index:

func (t *txLookup) AuthTxHashes(addr common.Address) []common.Hash {
	t.lock.RLock()
	defer t.lock.RUnlock()
	hashes := t.auths[addr]
	if len(hashes) == 0 {
		return nil
	}
	out := make([]common.Hash, len(hashes))
	copy(out, hashes)
	return out
}

Comment thread core/tx_pool.go
Comment on lines 1452 to 1455
pool.eip1559 = pool.chainconfig.IsCurie(next)
pool.shanghai = pool.chainconfig.IsShanghai(next)
pool.eip7702 = pool.chainconfig.IsViridian(next, newHead.Time)

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.

⚠️ Potential issue | 🔴 Critical

Enable SetCode as soon as the fork time is reachable

Basing pool.eip7702 on newHead.Time delays the switch until after the first Viridian block lands, so the txpool rejects every SetCode tx during the fork block and miners cannot include them. Compute the flag from the earliest legal timestamp for the next block (parent time + 1, clamped to time.Now()), so we accept SetCode txs as soon as the fork timestamp is attainable.

-	pool.eip7702 = pool.chainconfig.IsViridian(next, newHead.Time)
+	nextTime := newHead.Time + 1
+	if unix := uint64(time.Now().Unix()); unix > nextTime {
+		nextTime = unix
+	}
+	pool.eip7702 = pool.chainconfig.IsViridian(next, nextTime)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pool.eip1559 = pool.chainconfig.IsCurie(next)
pool.shanghai = pool.chainconfig.IsShanghai(next)
pool.eip7702 = pool.chainconfig.IsViridian(next, newHead.Time)
pool.eip1559 = pool.chainconfig.IsCurie(next)
pool.shanghai = pool.chainconfig.IsShanghai(next)
nextTime := newHead.Time + 1
if unix := uint64(time.Now().Unix()); unix > nextTime {
nextTime = unix
}
pool.eip7702 = pool.chainconfig.IsViridian(next, nextTime)
🤖 Prompt for AI Agents
In core/tx_pool.go around lines 1452-1455, pool.eip7702 is computed using
newHead.Time which delays enabling SetCode until after the fork block; instead
compute the earliest legal timestamp for the next block as parent.Time + 1 and
clamp it to now (i.e. use max(parent.Time+1, time.Now())), then pass that
earliest timestamp into chainconfig.IsViridian(next, earliestTimestamp) so
pool.eip7702 becomes true as soon as the fork timestamp is attainable.

@FletcherMan FletcherMan changed the title WIP: implement eip-7702 feat: implement eip-7702 Oct 17, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
core/state_transition.go (1)

530-535: Refund path for existing authority accounts.

Note: This refund also counts towards the global refund cap. If the intent is to guarantee net TxAuthTupleGas for existing accounts irrespective of cap, this will not hold. See earlier gas comment.

🧹 Nitpick comments (3)
core/state_transition.go (3)

133-135: Auth tuple gas via refund is capped by EIP‑3529; confirm economics.

Current model:

  • Intrinsic: len(authList) * CallNewAccountGas.
  • Runtime: if account exists, refund (CallNewAccountGas - TxAuthTupleGas).

This makes net cost for existing accounts subject to the refund cap (gasUsed/5 post‑3529), potentially overcharging txs with many authorizations. Is this intentional per EIP‑7702? If not, consider a non‑refund approach (e.g., charge TxAuthTupleGas intrinsically and debit the delta only when creating a new authority account).

Also applies to: 180-183


429-434: Invalid authorizations are skipped silently; confirm spec/UX.

You ignore errors from applyAuthorization. If this is spec‑intended (invalid tuples are no‑ops), fine. Otherwise consider surfacing a reason via tracer/logs to aid debugging.


508-510: Typo in comment.

“exisiting” → “existing”.

-   //  1) doesn't have code or has exisiting delegation
+   //  1) doesn't have code or has existing delegation
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21f6f6c and 1f528e9.

📒 Files selected for processing (2)
  • core/state_processor.go (4 hunks)
  • core/state_transition.go (12 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
core/state_transition.go (6)
core/types/tx_setcode.go (3)
  • SetCodeAuthorization (72-79)
  • ParseDelegation (37-42)
  • AddressToDelegation (45-47)
internal/ethapi/api.go (1)
  • AccessList (1536-1632)
params/protocol_params.go (2)
  • CallNewAccountGas (31-31)
  • TxAuthTupleGas (93-93)
core/vm/interpreter.go (1)
  • Config (33-42)
core/tracing/hooks.go (9)
  • GasChangeTxInitialBalance (294-294)
  • BalanceDecreaseGasBuy (246-246)
  • GasChangeTxIntrinsicGas (297-297)
  • NonceChangeEoACall (362-362)
  • BalanceIncreaseRewardTransactionFee (243-243)
  • NonceChangeAuthorization (371-371)
  • GasChangeTxRefunds (300-300)
  • BalanceIncreaseGasReturn (248-248)
  • GasChangeTxLeftOverReturned (305-305)
core/error.go (8)
  • ErrSenderNoEOA (108-108)
  • ErrSetCodeTxCreate (114-114)
  • ErrEmptyAuthList (113-113)
  • ErrAuthorizationWrongChainID (133-133)
  • ErrAuthorizationNonceOverflow (134-134)
  • ErrAuthorizationInvalidSignature (135-135)
  • ErrAuthorizationDestinationHasCode (136-136)
  • ErrAuthorizationNonceMismatch (137-137)
core/state_processor.go (8)
core/vm/interface.go (1)
  • StateDB (29-89)
eth/tracers/dir.go (1)
  • Tracer (40-45)
core/state/statedb_hooked.go (1)
  • NewHookedState (38-44)
core/evm.go (2)
  • NewEVMBlockContext (41-69)
  • NewEVMTxContext (72-78)
core/vm/evm.go (3)
  • NewEVM (138-149)
  • TxContext (92-97)
  • EVM (108-134)
core/types/transaction.go (1)
  • Message (738-752)
core/types/transaction_signing.go (1)
  • MakeSigner (42-61)
core/vm/interpreter.go (1)
  • Config (33-42)
🔇 Additional comments (7)
core/state_transition.go (3)

302-306: EOA check correctly permits delegated code.

Allowing EOAs that have delegation code installed is aligned with EIP‑7702 semantics. LGTM.


337-345: Good shape checks for 7702.

Rejecting contract creation and empty auth lists when authorizations are present is correct.


94-95: No issues found—all Message interface implementers define SetCodeAuthorizations().

The verification confirms that all three implementations of core.Message properly define the SetCodeAuthorizations() method:

  1. Transaction.SetCodeAuthorizations() at core/types/transaction.go:484
  2. Message.SetCodeAuthorizations() at core/types/transaction.go:809
  3. callMsg.SetCodeAuthorizations() at accounts/abi/bind/backends/simulated.go:918

All call sites throughout the codebase (tests, tx pool, API, tracers, state transition) successfully resolve to these implementations with consistent signatures. No missing implementations detected.

core/state_processor.go (4)

93-100: Tracing StateDB wrapping looks good.

Hooking the statedb only when a tracer exists is clean; passing the hooked StateDB into the EVM is correct.


99-100: Single EVM reused across tx in a block; verify no per‑tx state leakage.

EVM holds mutable fields (e.g., depth, callGasTemp) and caches. You reset TxContext per tx, but please confirm interpreter/EVM state is fully per‑call and safe to reuse across transactions.

Also applies to: 123-131


103-104: Time‑aware signer propagation.

Switching to MakeSigner(config, header.Number, header.Time) at both call sites is consistent with the new API.

Also applies to: 203-211


131-138: Tx lifecycle tracing and receipt construction LGTM.

OnTxStart/OnTxEnd hooks, L1 fee calculation and wiring into receipts, and Byzantium‑aware state root handling look correct.

Also applies to: 148-156, 172-196

Comment thread core/state_transition.go
Comment on lines +436 to +444
// Perform convenience warming of sender's delegation target. Although the
// sender is already warmed in Prepare(..), it's possible a delegation to
// the account was deployed during this transaction. To handle correctly,
// simply wait until the final state of delegations is determined before
// performing the resolution and warming.
if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To())); ok {
st.state.AddAddressToAccessList(addr)
}

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.

⚠️ Potential issue | 🟡 Minor

Comment/code mismatch (“sender” vs “recipient”) in warming block.

Code warms delegation target of msg.To(), but comment says “sender’s delegation target”. Update the comment (or change the code if warming the sender was intended).

Apply this diff to fix the comment:

-        // Perform convenience warming of sender's delegation target. Although the
-        // sender is already warmed in Prepare(..), it's possible a delegation to
+        // Perform convenience warming of the recipient's delegation target. Although the
+        // recipient is already warmed in Prepare(..), it's possible a delegation to
-        // the account was deployed during this transaction. To handle correctly,
+        // the account was deployed during this transaction. To handle correctly,
🤖 Prompt for AI Agents
In core/state_transition.go around lines 436 to 444, the comment refers to
"sender's delegation target" but the code actually warms the delegation target
of msg.To() (the recipient); update the comment to say it warms the recipient's
(msg.To()) delegation target or otherwise change wording to "recipient" /
"msg.To()" so the comment matches the code, keeping the existing logic that
parses delegation from st.state.GetCode(*msg.To()) and adds the address to the
access list.

curryxbo
curryxbo previously approved these changes Oct 17, 2025
curryxbo
curryxbo previously approved these changes Oct 17, 2025
@FletcherMan FletcherMan merged commit 43cab94 into main Oct 17, 2025
1 check was pending
@FletcherMan FletcherMan deleted the feat/eip-7702 branch October 17, 2025 02:24
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f528e9 and e297e46.

📒 Files selected for processing (4)
  • cmd/geth/chaincmd.go (2 hunks)
  • cmd/geth/main.go (1 hunks)
  • cmd/utils/flags.go (1 hunks)
  • core/genesis.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/utils/flags.go
  • cmd/geth/chaincmd.go
🧰 Additional context used
🧬 Code graph analysis (2)
cmd/geth/main.go (1)
cmd/utils/flags.go (1)
  • OverrideViridianTimeFlag (278-281)
core/genesis.go (1)
core/tracing/hooks.go (2)
  • BalanceIncreaseGenesisBalance (239-239)
  • NonceChangeGenesis (359-359)
🔇 Additional comments (4)
core/genesis.go (4)

34-34: LGTM!

The tracing import is correctly added to support the genesis-level tracing constants used in lines 348 and 350.


161-163: LGTM!

The override application logic correctly follows the established pattern for fork time configuration, with appropriate nil checks and config validation.


348-350: LGTM!

The tracing parameters are correctly added to enable genesis-level state mutations to be recorded with specific reasons. The constants BalanceIncreaseGenesisBalance and NonceChangeGenesis are properly defined in core/tracing/hooks.go.


150-150: No issues found. The ViridianTime field is properly defined in params/config.go (line 579) with the correct type *uint64, matching the pattern of Morph203Time. The code change at core/genesis.go:150 will not produce compilation errors.

Comment thread cmd/geth/main.go
utils.SmartCardDaemonPathFlag,
utils.OverrideArrowGlacierFlag,
utils.OverrideMorph203TimeFlag,
utils.OverrideViridianTimeFlag,
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

LGTM! Flag addition follows the established pattern.

The addition of utils.OverrideViridianTimeFlag to the nodeFlags slice is correct and consistent with the existing OverrideMorph203TimeFlag on line 74. The flag enables CLI-level control of the Viridian fork timestamp, which aligns with the EIP-7702 implementation's time-aware fork handling.

Run the following script to verify the flag is properly consumed by the configuration logic:


🏁 Script executed:

#!/bin/bash
# Description: Verify OverrideViridianTimeFlag usage across the codebase

# Search for references to OverrideViridianTimeFlag
echo "=== Searching for OverrideViridianTimeFlag references ==="
rg -n -C3 'OverrideViridianTimeFlag' --type=go

# Search for OverrideViridianTime (the config field that should consume this flag)
echo -e "\n=== Searching for OverrideViridianTime config field ==="
rg -n -C3 'OverrideViridianTime' --type=go -g '!*_test.go'

# Search for Viridian fork/time handling
echo -e "\n=== Searching for Viridian fork references ==="
rg -n -C2 '\bViridian\b' --type=go -g '!*_test.go' | head -50

Length of output: 7881


[skip_cloning: false]


🏁 Script executed:

#!/bin/bash
# Verify OverrideMorph300Time vs OverrideViridianTime discrepancy

echo "=== Searching for OverrideMorph300Time ==="
rg -n 'OverrideMorph300Time|Morph300' --type=go -C2

echo -e "\n=== Checking gen_config.go context ==="
rg -n 'enc\.' eth/ethconfig/gen_config.go -A1 -B1 | grep -E '(OverrideViridian|OverrideMorph|Morph300)' -C1

Length of output: 891



🏁 Script executed:

#!/bin/bash
# Verify the config struct definition to understand the naming scheme

echo "=== Checking eth/ethconfig/config.go struct definition ==="
rg -n 'type Config struct' eth/ethconfig/config.go -A 100 | head -150

echo -e "\n=== Confirming no OverrideMorph300Time in main Config ==="
rg -n 'OverrideMorph300Time' eth/ethconfig/config.go

echo -e "\n=== Checking UnmarshalTOML to see inverse mapping ==="
rg -n 'func.*UnmarshalTOML' eth/ethconfig/gen_config.go -A 50 | grep -E '(OverrideViridian|OverrideMorph300)' -B2 -A2

Length of output: 4351


🏁 Script executed:

#!/bin/bash
# Verify the config struct definition and understand the naming scheme

echo "=== Checking eth/ethconfig/config.go struct definition ==="
sed -n '/^type Config struct/,/^}/p' eth/ethconfig/config.go | tail -30

echo -e "\n=== Confirming OverrideMorph300Time location ==="
rg -n 'OverrideMorph300Time' eth/ethconfig/

echo -e "\n=== Check UnmarshalTOML for reverse mapping ==="
rg -n 'func.*UnmarshalTOML' eth/ethconfig/gen_config.go -A 60 | grep -E '(Viridian|Morph300)' -B1 -A1

Length of output: 1237


🏁 Script executed:

#!/bin/bash
# Verify the config struct definition and understand the naming scheme

echo "=== Checking eth/ethconfig/config.go struct definition ==="
rg -n 'OverrideViridianTime|OverrideMorph203Time' eth/ethconfig/config.go -B2 -A2

echo -e "\n=== Confirming OverrideMorph300Time only in gen_config.go ==="
rg -l 'OverrideMorph300Time' --type=go

echo -e "\n=== Check UnmarshalTOML for reverse mapping ==="
ast-grep --pattern $'func ($_ *Config) UnmarshalTOML($_) $_ {
  $$$
}'

Length of output: 12435


🏁 Script executed:

#!/bin/bash
# Verify the config struct definition and understand the naming scheme

echo "=== Config struct fields related to overrides ==="
rg -n 'Override.*Time' eth/ethconfig/config.go

echo -e "\n=== Files containing OverrideMorph300Time ==="
rg -l 'OverrideMorph300Time' --type=go

echo -e "\n=== UnmarshalTOML function mapping ==="
rg -n 'UnmarshalTOML' eth/ethconfig/gen_config.go -A 80 | tail -50

Length of output: 1940


🏁 Script executed:

#!/bin/bash
# Verify config structure and naming

echo "=== Config.go Override fields ==="
grep -n 'Override.*Time' eth/ethconfig/config.go

echo -e "\n=== Files with OverrideMorph300Time ==="
grep -r 'OverrideMorph300Time' --include='*.go' .

echo -e "\n=== UnmarshalTOML mapping logic ==="
sed -n '/func (c \*Config) UnmarshalTOML/,/^}/p' eth/ethconfig/gen_config.go | grep -E '(Viridian|Morph300|Marshal)' -B1 -A1

Length of output: 384


🏁 Script executed:

#!/bin/bash
# Direct check on UnmarshalTOML logic

echo "=== UnmarshalTOML full function from gen_config.go ==="
awk '/func.*\(c \*Config\) UnmarshalTOML/,/^}$/{print NR": "$0}' eth/ethconfig/gen_config.go | head -100

Length of output: 4125


🏁 Script executed:

#!/bin/bash
# Examine the full UnmarshalTOML to understand the bidirectional mapping

cat eth/ethconfig/gen_config.go | sed -n '/func.*UnmarshalTOML/,/^}/p' | grep -n '.'

Length of output: 6535


🏁 Script executed:

#!/bin/bash
# Check file header and full UnmarshalTOML function

echo "=== File header (check for code generation marker) ==="
head -10 eth/ethconfig/gen_config.go

echo -e "\n=== Full UnmarshalTOML function ==="
grep -n 'func.*UnmarshalTOML' eth/ethconfig/gen_config.go

# Get line number and extract that section
START_LINE=$(grep -n 'func.*UnmarshalTOML' eth/ethconfig/gen_config.go | cut -d: -f1)
if [ ! -z "$START_LINE" ]; then
  echo -e "\n=== UnmarshalTOML body starting at line $START_LINE ==="
  sed -n "${START_LINE},$((START_LINE+100))p" eth/ethconfig/gen_config.go
fi

Length of output: 3995


🏁 Script executed:

#!/bin/bash
# Check file header and get UnmarshalTOML function with proper line context

echo "=== File header (first 15 lines) ==="
head -15 eth/ethconfig/gen_config.go

echo -e "\n=== Locate and extract UnmarshalTOML ==="
awk '/func \(c \*Config\) UnmarshalTOML/,/^}/ {print NR": "$0}' eth/ethconfig/gen_config.go | head -120

Length of output: 5221


🏁 Script executed:

#!/bin/bash
cat eth/ethconfig/gen_config.go | head -20
echo "---"
python3 << 'PYEOF'
with open('eth/ethconfig/gen_config.go', 'r') as f:
    lines = f.readlines()
    in_func = False
    for i, line in enumerate(lines, 1):
        if 'UnmarshalTOML' in line:
            in_func = True
        if in_func:
            print(f"{i}: {line}", end='')
            if line.strip() == '}' and i > 100:
                break
PYEOF

Length of output: 3230


🏁 Script executed:

#!/bin/bash
python3 << 'PYEOF'
import re

# Read the file
with open('eth/ethconfig/gen_config.go', 'r') as f:
    content = f.read()

# Check file header
lines = content.split('\n')
print("=== File header ===")
for i, line in enumerate(lines[:15]):
    print(f"{i+1}: {line}")

# Find UnmarshalTOML function
match = re.search(r'(func \(c \*Config\) UnmarshalTOML.*?\n})', content, re.DOTALL)
if match:
    func_lines = match.group(1).split('\n')
    print("\n=== UnmarshalTOML function ===")
    start = next(i for i, l in enumerate(lines) if 'UnmarshalTOML' in l)
    for i, line in enumerate(func_lines[:80]):
        print(f"{start+i+1}: {line}")
PYEOF

Length of output: 3998


🏁 Script executed:

#!/bin/bash
python3 << 'PYEOF'
with open('eth/ethconfig/gen_config.go', 'r') as f:
    lines = f.readlines()

# Show first 20 lines
print("=== File Header ===")
print(''.join(lines[:20]))

# Find and show UnmarshalTOML
print("\n=== UnmarshalTOML Function ===")
in_func = False
for i, line in enumerate(lines):
    if 'func (c *Config) UnmarshalTOML' in line:
        in_func = True
        start_line = i
    if in_func:
        print(f"{i+1}: {line.rstrip()}")
        if line.strip() == '}' and i > start_line:
            break
PYEOF

Length of output: 3235


🏁 Script executed:

#!/bin/bash
python3 << 'PYEOF'
with open('eth/ethconfig/gen_config.go', 'r') as f:
    lines = f.readlines()

print("=== First 20 lines ===")
for i in range(20):
    print(f"{i+1}: {lines[i].rstrip()}")

print("\n=== UnmarshalTOML function ===")
for i in range(len(lines)):
    if 'UnmarshalTOML' in lines[i]:
        for j in range(i, min(i+120, len(lines))):
            print(f"{j+1}: {lines[j].rstrip()}")
            if j > i and lines[j].strip() == '}':
                break
        break
PYEOF

Length of output: 3352


🏁 Script executed:

#!/bin/bash
python3 << 'EOF'
try:
    with open('eth/ethconfig/gen_config.go', 'r') as f:
        content = f.read()
    
    # Find UnmarshalTOML
    idx = content.find('UnmarshalTOML')
    if idx > 0:
        # Get lines around it
        start = content.rfind('\n', max(0, idx-200), idx)
        end = content.find('\n}\n', idx) + 3
        section = content[start:end]
        print(section)
    else:
        print("UnmarshalTOML not found")
except Exception as e:
    print(f"Error: {e}")
EOF

Length of output: 5912


🏁 Script executed:

#!/bin/bash
# Simple approach: show lines 130-200 which should contain UnmarshalTOML
sed -n '125,200p' eth/ethconfig/gen_config.go

Length of output: 2881


🏁 Script executed:

#!/bin/bash
# Direct file view using sed - get context around UnmarshalTOML
echo "=== MarshalTOML (lines 105-120) ==="
sed -n '105,120p' eth/ethconfig/gen_config.go

echo -e "\n=== UnmarshalTOML location ==="
grep -n 'func.*UnmarshalTOML' eth/ethconfig/gen_config.go

echo -e "\n=== UnmarshalTOML function (lines 120-200) ==="
sed -n '120,200p' eth/ethconfig/gen_config.go

Length of output: 3860


🏁 Script executed:

#!/bin/bash
sed -n '1,250p' eth/ethconfig/gen_config.go | tail -120

Length of output: 3997


🏁 Script executed:

#!/bin/bash
cat eth/ethconfig/gen_config.go | tail -60

Length of output: 1357


🏁 Script executed:

#!/bin/bash
wc -l eth/ethconfig/gen_config.go
tail -100 eth/ethconfig/gen_config.go

Length of output: 2539


🏁 Script executed:

#!/bin/bash
# Get file line count and show full MarshalTOML and UnmarshalTOML sections
echo "=== File stats ==="
wc -l eth/ethconfig/gen_config.go

echo -e "\n=== Lines 60-120 (covering both functions) ==="
sed -n '60,120p' eth/ethconfig/gen_config.go

echo -e "\n=== Lines 120-180 (UnmarshalTOML) ==="
sed -n '120,180p' eth/ethconfig/gen_config.go

Length of output: 5083


🏁 Script executed:

#!/bin/bash
# Show the entire file content
cat eth/ethconfig/gen_config.go

Length of output: 10593


Critical serialization bug in generated code must be fixed.

The addition of utils.OverrideViridianTimeFlag in main.go is correct, but verification reveals a one-way serialization bug in eth/ethconfig/gen_config.go:

MarshalTOML (line 116) serializes OverrideViridianTimeOverrideMorph300Time for TOML output, but UnmarshalTOML (lines 120–312) never deserializes OverrideMorph300Time back. The internal Config struct in UnmarshalTOML only includes OverrideMorph203Time and terminates at line 312 without handling OverrideMorph300Time. This causes configuration files to lose the Viridian override value when read.

The generated codec file needs to be regenerated or manually patched to include bidirectional mapping for OverrideMorph300TimeOverrideViridianTime in UnmarshalTOML.

🤖 Prompt for AI Agents
In cmd/geth/main.go around line 75 the new utils.OverrideViridianTimeFlag was
added but the generated codec eth/ethconfig/gen_config.go contains a one-way
serialization bug: MarshalTOML writes OverrideViridianTime as
OverrideMorph300Time but UnmarshalTOML never reads OverrideMorph300Time back.
Fix by regenerating the codec or manually patching eth/ethconfig/gen_config.go
UnmarshalTOML to include OverrideMorph300Time in the internal Config struct and
map it back into the exported OverrideViridianTime field (i.e., add the
OverrideMorph300Time field to the temporary struct used for TOML unmarshalling
and assign its value to cfg.OverrideViridianTime), ensuring symmetric TOML
marshal/unmarshal behavior.

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.

3 participants