Skip to content

Commit bd27dda

Browse files
authored
execution/tracers: emit empty code in diff mode for EIP-7702 deauthorization (#20601)
This PR fixes prestate tracer diff-mode output for EIP-7702 deauthorization in Erigon. Previously, when a transaction deauthorized an EIP-7702 delegated account (authorization to address(0)), the tracer omitted the `code` field from the `post` object after the delegation code was cleared. That treated the change as if the field had been deleted entirely. In reality, the account still exists and its code is simply updated from delegated code (`0xef0100 || delegate`) to empty code (`0x`). Since this is a normal state transition - not an account deletion - the `post` object should include the updated empty code value. This makes code handling consistent with how other account fields like balance are represented when they transition to zero, and keeps diff-mode semantics clearer: omitted fields should represent actual deletions, while modified fields should appear in `post` even if their new value is empty. After this change, EIP-7702 deauthorization traces correctly return `"code": "0x"` in the `post` state. Refrence: ethereum/go-ethereum#34648
1 parent bad7f58 commit bd27dda

File tree

3 files changed

+134
-22
lines changed

3 files changed

+134
-22
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"genesis": {
3+
"blobGasUsed": "0",
4+
"difficulty": "0",
5+
"excessBlobGas": "0",
6+
"extraData": "0x",
7+
"gasLimit": "11511229",
8+
"hash": "0x455b93a512baa4ed5e117508b184a6bb03904b94d665ce38931728eca9cdd8fe",
9+
"miner": "0x71562b71999873db5b286df957af199ec94617f7",
10+
"mixHash": "0x042877c4fab9f022d29590ae83bad89d6181afb1d6e107619911ea52e5901364",
11+
"nonce": "0x0000000000000000",
12+
"number": "1",
13+
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
14+
"stateRoot": "0xc8688ad6433e6b9f4edeb82360d2b99c8e919f493a01cacbe7c4a97184f5d043",
15+
"timestamp": "1775654796",
16+
"alloc": {
17+
"0x71562b71999873db5b286df957af199ec94617f7": {
18+
"balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffdb64910c3bf7",
19+
"nonce": "1"
20+
},
21+
"0xe85a1c0e9d5b1c9b417c6c1b34c22cd77f623f50": {
22+
"balance": "0x0",
23+
"code": "0xef0100d313d93607c016a85e63e557a11ca5ab0b53ad83",
24+
"codeHash": "0x9eea9f41ed2b35e6234d1e1c14e88c1136f85d56ed1f32a7efc0096d998dad3d",
25+
"nonce": "1"
26+
}
27+
},
28+
"config": {
29+
"chainId": 1337,
30+
"homesteadBlock": 0,
31+
"eip150Block": 0,
32+
"eip155Block": 0,
33+
"eip158Block": 0,
34+
"byzantiumBlock": 0,
35+
"constantinopleBlock": 0,
36+
"petersburgBlock": 0,
37+
"istanbulBlock": 0,
38+
"muirGlacierBlock": 0,
39+
"berlinBlock": 0,
40+
"londonBlock": 0,
41+
"arrowGlacierBlock": 0,
42+
"grayGlacierBlock": 0,
43+
"shanghaiTime": 0,
44+
"cancunTime": 0,
45+
"pragueTime": 0,
46+
"terminalTotalDifficulty": 0,
47+
"blobSchedule": {
48+
"cancun": {
49+
"target": 3,
50+
"max": 6,
51+
"baseFeeUpdateFraction": 3338477
52+
},
53+
"prague": {
54+
"target": 6,
55+
"max": 9,
56+
"baseFeeUpdateFraction": 5007716
57+
}
58+
}
59+
}
60+
},
61+
"context": {
62+
"number": "2",
63+
"difficulty": "0",
64+
"timestamp": "1775654797",
65+
"gasLimit": "11522469",
66+
"miner": "0x71562b71999873db5b286df957af199ec94617f7",
67+
"baseFeePerGas": "766499147"
68+
},
69+
"input": "0x04f8cd82053901843b9aca008477359400830186a09471562b71999873db5b286df957af199ec94617f78080c0f85ef85c8205399400000000000000000000000000000000000000000101a011fc0271f2566e7ebe5ddbff6d48ea97a19afa248452a392781096b7e3b89177a0020107ecefe99c90429b416fe4d1eead5a7fa253761e85cd7cdc7df6e5032d7f80a098495fb16c904f0b67b49afe868b28b0159c8df07522bed99ef6ff2cc2ac2935a048857a9c385d91735a9fdccabc66de7a5ea1897f523a5b9a352e281642a76e6b",
70+
"tracerConfig": {
71+
"diffMode": true
72+
},
73+
"result": {
74+
"post": {
75+
"0x71562b71999873db5b286df957af199ec94617f7": {
76+
"balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffc1bd12c85eb7",
77+
"nonce": 2
78+
},
79+
"0xe85a1c0e9d5b1c9b417c6c1b34c22cd77f623f50": {
80+
"code": "0x",
81+
"codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
82+
"nonce": 2
83+
}
84+
},
85+
"pre": {
86+
"0x71562b71999873db5b286df957af199ec94617f7": {
87+
"balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffdb64910c3bf7",
88+
"nonce": 1
89+
},
90+
"0xe85a1c0e9d5b1c9b417c6c1b34c22cd77f623f50": {
91+
"balance": "0x0",
92+
"code": "0xef0100d313d93607c016a85e63e557a11ca5ab0b53ad83",
93+
"codeHash": "0x9eea9f41ed2b35e6234d1e1c14e88c1136f85d56ed1f32a7efc0096d998dad3d",
94+
"nonce": 1
95+
}
96+
}
97+
}
98+
}

execution/tracing/tracers/native/gen_account_json.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

execution/tracing/tracers/native/prestate.go

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030

3131
"github.com/erigontech/erigon/common"
3232
"github.com/erigontech/erigon/common/crypto"
33+
"github.com/erigontech/erigon/common/empty"
3334
"github.com/erigontech/erigon/common/hexutil"
3435
"github.com/erigontech/erigon/execution/tracing"
3536
"github.com/erigontech/erigon/execution/tracing/tracers"
@@ -47,20 +48,22 @@ func init() {
4748
type state = map[accounts.Address]*account
4849

4950
type account struct {
50-
Balance *big.Int `json:"balance,omitempty"`
51-
Code []byte `json:"code,omitempty"`
51+
Balance *big.Int `json:"balance,omitempty"`
52+
// Code is a pointer so omitempty can omit unchanged code (nil) while
53+
// still emitting "0x" when code is cleared (e.g. EIP-7702 deauth).
54+
Code *[]byte `json:"code,omitempty"`
5255
CodeHash *common.Hash `json:"codeHash,omitempty"`
5356
Nonce uint64 `json:"nonce,omitempty"`
5457
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
5558
}
5659

5760
func (a *account) exists() bool {
58-
return a.Nonce > 0 || len(a.Code) > 0 || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0)
61+
return a.Nonce > 0 || a.CodeHash != nil || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0)
5962
}
6063

6164
type accountMarshaling struct {
6265
Balance *hexutil.Big
63-
Code hexutil.Bytes
66+
Code *hexutil.Bytes
6467
}
6568

6669
type prestateTracer struct {
@@ -274,11 +277,14 @@ func (t *prestateTracer) processDiffState() {
274277
continue
275278
}
276279
modified := false
277-
postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
280+
postAccount := &account{
281+
Storage: make(map[common.Hash]common.Hash),
282+
}
283+
278284
newBalance, _ := t.env.IntraBlockState.GetBalance(addr)
279285
newNonce, _ := t.env.IntraBlockState.GetNonce(addr)
280286
newCode, _ := t.env.IntraBlockState.GetCode(addr)
281-
newCodeHash := common.Hash{}
287+
newCodeHash := empty.CodeHash
282288
if len(newCode) > 0 {
283289
newCodeHash = crypto.HashData(newCode)
284290
}
@@ -292,7 +298,9 @@ func (t *prestateTracer) processDiffState() {
292298
postAccount.Nonce = newNonce
293299
}
294300

295-
prevCodeHash := common.Hash{}
301+
// Empty code hashes are excluded from the prestate, so default
302+
// to EmptyCodeHash to match what GetCodeHash returns for codeless accounts.
303+
prevCodeHash := empty.CodeHash
296304
if t.pre[addr].CodeHash != nil {
297305
prevCodeHash = *t.pre[addr].CodeHash
298306
}
@@ -303,10 +311,13 @@ func (t *prestateTracer) processDiffState() {
303311
}
304312

305313
if !t.config.DisableCode {
306-
newCode, _ := t.env.IntraBlockState.GetCode(addr)
307-
if !bytes.Equal(newCode, t.pre[addr].Code) {
314+
var prevCode []byte
315+
if t.pre[addr].Code != nil {
316+
prevCode = *t.pre[addr].Code
317+
}
318+
if !bytes.Equal(newCode, prevCode) {
308319
modified = true
309-
postAccount.Code = newCode
320+
postAccount.Code = &newCode
310321
}
311322
}
312323

@@ -317,7 +328,7 @@ func (t *prestateTracer) processDiffState() {
317328
delete(t.pre[addr].Storage, key)
318329
}
319330

320-
var newVal, _ = t.env.IntraBlockState.GetState(addr, accounts.InternKey(key))
331+
newVal, _ := t.env.IntraBlockState.GetState(addr, accounts.InternKey(key))
321332
if new(uint256.Int).SetBytes(val[:]).Eq(&newVal) {
322333
// Omit unchanged slots
323334
delete(t.pre[addr].Storage, key)
@@ -375,23 +386,26 @@ func (t *prestateTracer) lookupAccount(addr accounts.Address) {
375386
nonce, _ := t.env.IntraBlockState.GetNonce(addr)
376387
code, _ := t.env.IntraBlockState.GetCode(addr)
377388

378-
t.pre[addr] = &account{
389+
acc := &account{
379390
Balance: balance.ToBig(),
380391
Nonce: nonce,
381392
}
393+
382394
if len(code) > 0 {
395+
acc.Code = &code
383396
codeHash := crypto.HashData(code)
384-
t.pre[addr].CodeHash = &codeHash
385-
} else {
386-
t.pre[addr].CodeHash = nil
397+
acc.CodeHash = &codeHash
387398
}
388-
389-
if !t.config.DisableCode {
390-
t.pre[addr].Code = code
399+
// The code must be fetched first for the emptiness check.
400+
if t.config.DisableCode {
401+
acc.Code = nil
391402
}
403+
392404
if !t.config.DisableStorage {
393-
t.pre[addr].Storage = make(map[common.Hash]common.Hash)
405+
acc.Storage = make(map[common.Hash]common.Hash)
394406
}
407+
408+
t.pre[addr] = acc
395409
}
396410

397411
// lookupStorage fetches the requested storage slot and adds

0 commit comments

Comments
 (0)