Skip to content

Commit b377560

Browse files
committed
core/types: add eip-7928 block-level access list structures
1 parent efbba96 commit b377560

13 files changed

+2298
-5
lines changed

core/types/bal/bal.go

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
package bal
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"maps"
7+
"strings"
8+
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/holiman/uint256"
11+
)
12+
13+
// BlockAccessList contains post-block modified state and some state accessed
14+
// in execution (account addresses and storage keys).
15+
type BlockAccessList struct {
16+
accounts map[common.Address]*accountAccess
17+
}
18+
19+
// NewBlockAccessList instantiates an empty access list.
20+
func NewBlockAccessList() BlockAccessList {
21+
return BlockAccessList{
22+
accounts: make(map[common.Address]*accountAccess),
23+
}
24+
}
25+
26+
// AccountRead records the address of an account that has been read during execution.
27+
func (b *BlockAccessList) AccountRead(addr common.Address) {
28+
if _, ok := b.accounts[addr]; !ok {
29+
b.accounts[addr] = newAccountAccess()
30+
}
31+
}
32+
33+
// StorageRead records a storage key read during execution.
34+
func (b *BlockAccessList) StorageRead(address common.Address, key common.Hash) {
35+
if _, ok := b.accounts[address]; !ok {
36+
b.accounts[address] = newAccountAccess()
37+
}
38+
39+
if _, ok := b.accounts[address].StorageWrites[key]; ok {
40+
return
41+
}
42+
43+
b.accounts[address].StorageReads[key] = struct{}{}
44+
}
45+
46+
// StorageWrite records the post-transaction value of a mutated storage slot.
47+
// The storage slot is removed from the list of read slots.
48+
func (b *BlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) {
49+
if _, ok := b.accounts[address]; !ok {
50+
b.accounts[address] = newAccountAccess()
51+
}
52+
53+
if _, ok := b.accounts[address].StorageWrites[key]; !ok {
54+
b.accounts[address].StorageWrites[key] = make(slotWrites)
55+
}
56+
b.accounts[address].StorageWrites[key][txIdx] = value
57+
delete(b.accounts[address].StorageReads, key)
58+
}
59+
60+
// CodeChange records the code of a newly-created contract.
61+
func (b *BlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) {
62+
if _, ok := b.accounts[address]; !ok {
63+
b.accounts[address] = newAccountAccess()
64+
}
65+
66+
b.accounts[address].CodeChange = &codeChange{
67+
TxIndex: txIndex,
68+
Code: bytes.Clone(code),
69+
}
70+
}
71+
72+
// NonceDiff records tx post-state nonce of any contract-like accounts whose nonce was incremented
73+
func (b *BlockAccessList) NonceDiff(address common.Address, txIdx uint16, postNonce uint64) {
74+
if _, ok := b.accounts[address]; !ok {
75+
b.accounts[address] = newAccountAccess()
76+
}
77+
78+
b.accounts[address].NonceChanges[txIdx] = postNonce
79+
}
80+
81+
// BalanceChange records the post-transaction balance of an account whose
82+
// balance changed.
83+
func (b *BlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) {
84+
if _, ok := b.accounts[address]; !ok {
85+
b.accounts[address] = newAccountAccess()
86+
}
87+
88+
b.accounts[address].BalanceChanges[txIdx] = balance.Clone()
89+
}
90+
91+
// contains the post-transaction balances of an account, keyed by transaction indices
92+
// where it was changed.
93+
type balanceDiff map[uint16]*uint256.Int
94+
95+
// copy returns a deep copy of the object
96+
func (b balanceDiff) copy() balanceDiff {
97+
res := make(balanceDiff)
98+
for idx, balance := range b {
99+
res[idx] = balance.Clone()
100+
}
101+
return res
102+
}
103+
104+
// PrettyPrint returns a human-readable representation of the access list
105+
func (b *BlockAccessList) PrettyPrint() string {
106+
enc := b.toEncodingObj()
107+
return enc.prettyPrint()
108+
}
109+
110+
// Hash computes the SSZ hash of the access list
111+
func (b *BlockAccessList) Hash() common.Hash {
112+
hash, err := b.toEncodingObj().HashTreeRoot()
113+
if err != nil {
114+
// errors here are related to BAL values exceeding maximum size defined
115+
// by the spec. Hard-fail because these cases are not expected to be hit
116+
// under reasonable conditions.
117+
panic(err)
118+
}
119+
return hash
120+
}
121+
122+
// codeChange contains the code deployed at an address and the transaction
123+
// index where the deployment took place.
124+
type codeChange struct {
125+
TxIndex uint16
126+
Code []byte `json:"code,omitempty"`
127+
}
128+
129+
// post-state values of an account's storage slots modified in a block, keyed
130+
// by slot key
131+
type storageWrites map[common.Hash]slotWrites
132+
133+
func (s storageWrites) copy() storageWrites {
134+
res := make(storageWrites)
135+
for slot, writes := range s {
136+
res[slot] = maps.Clone(writes)
137+
}
138+
return res
139+
}
140+
141+
// accountAccess contains post-block account state for mutations as well as
142+
// all storage keys that were read during execution.
143+
type accountAccess struct {
144+
StorageWrites storageWrites `json:"storageWrites,omitempty"`
145+
StorageReads map[common.Hash]struct{} `json:"storageReads,omitempty"`
146+
BalanceChanges balanceDiff `json:"balanceChanges,omitempty"`
147+
NonceChanges accountNonceDiffs `json:"nonceChanges,omitempty"`
148+
149+
// only set for contract accounts which were deployed in the block
150+
CodeChange *codeChange `json:"codeChange,omitempty"`
151+
}
152+
153+
func newAccountAccess() *accountAccess {
154+
return &accountAccess{
155+
StorageWrites: make(map[common.Hash]slotWrites),
156+
StorageReads: make(map[common.Hash]struct{}),
157+
BalanceChanges: make(balanceDiff),
158+
NonceChanges: make(accountNonceDiffs),
159+
}
160+
}
161+
162+
// the post-state nonce values of a contract account keyed by tx index
163+
type accountNonceDiffs map[uint16]uint64
164+
165+
// the post-state values of a storage slot, keyed by tx index
166+
type slotWrites map[uint16]common.Hash
167+
168+
// Copy returns a deep copy of the access list.
169+
func (b *BlockAccessList) Copy() *BlockAccessList {
170+
res := new(BlockAccessList)
171+
for addr, aa := range b.accounts {
172+
var aaCopy accountAccess
173+
aaCopy.StorageWrites = aa.StorageWrites.copy()
174+
aaCopy.StorageReads = maps.Clone(aa.StorageReads)
175+
aaCopy.BalanceChanges = aa.BalanceChanges.copy()
176+
aaCopy.NonceChanges = maps.Clone(aa.NonceChanges)
177+
if aa.CodeChange != nil {
178+
aaCopy.CodeChange = &codeChange{
179+
TxIndex: aa.CodeChange.TxIndex,
180+
Code: bytes.Clone(aa.CodeChange.Code),
181+
}
182+
}
183+
res.accounts[addr] = &aaCopy
184+
}
185+
return res
186+
}
187+
188+
func (e *encodingBlockAccessList) prettyPrint() string {
189+
var res bytes.Buffer
190+
printWithIndent := func(indent int, text string) {
191+
fmt.Fprintf(&res, "%s%s\n", strings.Repeat(" ", indent), text)
192+
}
193+
for _, accountDiff := range e.Accesses {
194+
printWithIndent(0, fmt.Sprintf("%x:", accountDiff.Address))
195+
196+
printWithIndent(1, "storage writes:")
197+
for _, sWrite := range accountDiff.StorageWrites {
198+
printWithIndent(2, fmt.Sprintf("%x:", sWrite.Slot))
199+
for _, access := range sWrite.Accesses {
200+
printWithIndent(3, fmt.Sprintf("%d: %x", access.TxIdx, access.ValueAfter))
201+
}
202+
}
203+
204+
printWithIndent(1, "storage reads:")
205+
for _, slot := range accountDiff.StorageReads {
206+
printWithIndent(2, fmt.Sprintf("%x", slot))
207+
}
208+
209+
printWithIndent(1, "balance changes:")
210+
for _, change := range accountDiff.BalanceChanges {
211+
balance := new(uint256.Int).SetBytes(change.Balance[:]).String()
212+
printWithIndent(2, fmt.Sprintf("%d: %s", change.TxIdx, balance))
213+
}
214+
215+
printWithIndent(1, "nonce changes:")
216+
for _, change := range accountDiff.NonceChanges {
217+
printWithIndent(2, fmt.Sprintf("%d: %d", change.TxIdx, change.Nonce))
218+
}
219+
220+
if len(accountDiff.Code) > 0 {
221+
printWithIndent(1, "code:")
222+
printWithIndent(2, fmt.Sprintf("%d: %x", accountDiff.Code[0].TxIndex, accountDiff.Code[0].Code))
223+
}
224+
}
225+
226+
return res.String()
227+
}

0 commit comments

Comments
 (0)