Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 961f517

Browse files
wip: tracer-based BAL creation
1 parent 1cfe624 commit 961f517

File tree

19 files changed

+880
-391
lines changed

19 files changed

+880
-391
lines changed

‎cmd/geth/main.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ var (
155155
utils.BeaconGenesisTimeFlag,
156156
utils.BeaconCheckpointFlag,
157157
utils.BeaconCheckpointFileFlag,
158+
utils.ExperimentalBALFlag,
158159
}, utils.NetworkFlags, utils.DatabaseFlags)
159160

160161
rpcFlags = []cli.Flag{

‎cmd/utils/flags.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,14 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
997997
Value: metrics.DefaultConfig.InfluxDBOrganization,
998998
Category: flags.MetricsCategory,
999999
}
1000+
1001+
// Block Access List flags
1002+
1003+
ExperimentalBALFlag = &cli.BoolFlag{
1004+
Name: "experimental.bal",
1005+
Usage: "Enable block-access-list building when importing post-Cancun blocks, and validation that access lists contained in post-Cancun blocks correctly correspond to the state changes in those blocks. This is used for development purposes only. Do not enable it otherwise.",
1006+
Category: flags.MiscCategory,
1007+
}
10001008
)
10011009

10021010
var (
@@ -1899,6 +1907,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
18991907
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
19001908
}
19011909
}
1910+
1911+
cfg.ExperimentalBAL = ctx.Bool(ExperimentalBALFlag.Name)
19021912
}
19031913

19041914
// MakeBeaconLightConfig constructs a beacon light client config based on the
@@ -2301,6 +2311,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
23012311
}
23022312
options.VmConfig = vmcfg
23032313

2314+
options.EnableBALForTesting = ctx.Bool(ExperimentalBALFlag.Name)
23042315
chain, err := core.NewBlockChain(chainDb, gspec, engine, options)
23052316
if err != nil {
23062317
Fatalf("Can't create BlockChain: %v", err)

‎core/block_access_list_creation.go‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package core
2+
3+
import (
4+
"github.com/ethereum/go-ethereum/common"
5+
"github.com/ethereum/go-ethereum/core/tracing"
6+
"github.com/ethereum/go-ethereum/core/types"
7+
"github.com/ethereum/go-ethereum/core/types/bal"
8+
"github.com/holiman/uint256"
9+
"math/big"
10+
)
11+
12+
// BlockAccessListTracer constructs an EIP-7928 block access list from the
13+
// execution of a block
14+
type BlockAccessListTracer struct {
15+
// this is a set of access lists for each call scope. the overall block access lists
16+
// is accrued at index 0, while the access lists of various nested execution
17+
// scopes are in the proceeding indices.
18+
// When an execution scope terminates in a non-reverting fashion, the changes are
19+
// merged into the access list of the parent scope.
20+
callAccessLists []*bal.ConstructionBlockAccessList
21+
txIdx uint16
22+
23+
// if non-nil, it's the address of the account which just self-destructed.
24+
// reset at the end of the call-scope which self-destructed.
25+
selfdestructedAccount *common.Address
26+
}
27+
28+
// NewBlockAccessListTracer returns an BlockAccessListTracer and a set of hooks
29+
func NewBlockAccessListTracer() (*BlockAccessListTracer, *tracing.Hooks) {
30+
balTracer := &BlockAccessListTracer{
31+
callAccessLists: []*bal.ConstructionBlockAccessList{bal.NewConstructionBlockAccessList()},
32+
txIdx: 0,
33+
}
34+
hooks := &tracing.Hooks{
35+
OnTxEnd: balTracer.TxEndHook,
36+
OnTxStart: balTracer.TxStartHook,
37+
OnEnter: balTracer.OnEnter,
38+
OnExit: balTracer.OnExit,
39+
OnCodeChangeV2: balTracer.OnCodeChange,
40+
OnBalanceChange: balTracer.OnBalanceChange,
41+
OnNonceChange: balTracer.OnNonceChange,
42+
OnStorageChange: balTracer.OnStorageChange,
43+
OnColdAccountRead: balTracer.OnColdAccountRead,
44+
OnColdStorageRead: balTracer.OnColdStorageRead,
45+
}
46+
return balTracer, hooks
47+
}
48+
49+
// AccessList returns the constructed access list
50+
func (a *BlockAccessListTracer) AccessList() *bal.ConstructionBlockAccessList {
51+
return a.callAccessLists[0]
52+
}
53+
54+
func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) {
55+
a.txIdx++
56+
}
57+
58+
func (a *BlockAccessListTracer) TxStartHook(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {
59+
if a.txIdx == 0 {
60+
a.txIdx++
61+
}
62+
}
63+
64+
func (a *BlockAccessListTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
65+
a.callAccessLists = append(a.callAccessLists, bal.NewConstructionBlockAccessList())
66+
}
67+
68+
func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
69+
// any self-destructed accounts must have been created in the same transaction
70+
// so there is no difference between the pre/post tx state of those accounts
71+
if a.selfdestructedAccount != nil {
72+
delete(a.callAccessLists[len(a.callAccessLists)-1].Accounts, *a.selfdestructedAccount)
73+
}
74+
if !reverted {
75+
parentAccessList := a.callAccessLists[len(a.callAccessLists)-2]
76+
scopeAccessList := a.callAccessLists[len(a.callAccessLists)-1]
77+
parentAccessList.Merge(scopeAccessList)
78+
}
79+
80+
a.callAccessLists = a.callAccessLists[:len(a.callAccessLists)-1]
81+
}
82+
83+
func (a *BlockAccessListTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason tracing.CodeChangeReason) {
84+
if reason == tracing.CodeChangeSelfDestruct {
85+
a.selfdestructedAccount = &addr
86+
return
87+
}
88+
a.callAccessLists[len(a.callAccessLists)-1].CodeChange(addr, uint16(a.txIdx), code)
89+
}
90+
91+
func (a *BlockAccessListTracer) OnBalanceChange(addr common.Address, prevBalance, newBalance *big.Int, _ tracing.BalanceChangeReason) {
92+
a.callAccessLists[len(a.callAccessLists)-1].BalanceChange(a.txIdx, addr, new(uint256.Int).SetBytes(newBalance.Bytes()))
93+
}
94+
95+
func (a *BlockAccessListTracer) OnNonceChange(addr common.Address, prev uint64, new uint64) {
96+
a.callAccessLists[len(a.callAccessLists)-1].NonceChange(addr, a.txIdx, new)
97+
}
98+
99+
func (a *BlockAccessListTracer) OnColdStorageRead(addr common.Address, key common.Hash) {
100+
a.callAccessLists[len(a.callAccessLists)-1].StorageRead(addr, key)
101+
}
102+
103+
func (a *BlockAccessListTracer) OnColdAccountRead(addr common.Address) {
104+
a.callAccessLists[len(a.callAccessLists)-1].AccountRead(addr)
105+
}
106+
107+
func (a *BlockAccessListTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) {
108+
a.callAccessLists[len(a.callAccessLists)-1].StorageWrite(a.txIdx, addr, slot, new)
109+
}

‎core/block_validator.go‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,31 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
111111
}
112112
}
113113

114+
// block access lists must be present after the Amsterdam hard fork
115+
if v.config.IsAmsterdam(block.Number(), block.Time()) {
116+
if block.Body().AccessList == nil {
117+
return fmt.Errorf("access list not present in block body")
118+
} else if block.Header().BlockAccessListHash == nil {
119+
return fmt.Errorf("access list hash not present in block header")
120+
} else if *block.Header().BlockAccessListHash != block.Body().AccessList.Hash() {
121+
return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.Body().AccessList.Hash(), *block.Header().BlockAccessListHash)
122+
}
123+
} else if !v.bc.cfg.EnableBALForTesting {
124+
// if --experimental.bal is not enabled, block headers cannot have access list hash and bodies cannot have access lists.
125+
if block.Body().AccessList != nil {
126+
return fmt.Errorf("access list not allowed in block body if not in amsterdam or --experimental.bal is set")
127+
} else if block.Header().BlockAccessListHash != nil {
128+
return fmt.Errorf("access list hash in block header not allowed when --experimental.bal is set")
129+
}
130+
} else {
131+
// if --experimental.bal is enabled, the BAL hash is not allowed in the header.
132+
// this is in order that Geth can import pre-existing chains augmented with BALs
133+
// and not have a hash mismatch.
134+
if block.Header().BlockAccessListHash != nil {
135+
return fmt.Errorf("access list hash in block header not allowed pre-amsterdam")
136+
}
137+
}
138+
114139
// Ancestor block must be known.
115140
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
116141
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {

‎core/blockchain.go‎

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ type BlockChainConfig struct {
196196
// If the value is -1, indexing is disabled.
197197
TxLookupLimit int64
198198

199+
// If EnableBALForTesting is enabled, block access lists will be created as part of
200+
// block processing and embedded in the block body. The block access list hash will
201+
// not be set in the header.
202+
EnableBALForTesting bool
203+
199204
// StateSizeTracking indicates whether the state size tracking is enabled.
200205
StateSizeTracking bool
201206
}
@@ -1904,9 +1909,17 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
19041909
if parent == nil {
19051910
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
19061911
}
1912+
1913+
// construct or verify block access lists if BALs are enabled and
1914+
// we are post-selfdestruct removal fork.
1915+
enableBAL := (bc.cfg.EnableBALForTesting && bc.chainConfig.IsCancun(block.Number(), block.Time())) || bc.chainConfig.IsAmsterdam(block.Number(), block.Time())
1916+
blockHasAccessList := block.Body().AccessList != nil
1917+
makeBAL := enableBAL && !blockHasAccessList
1918+
validateBAL := enableBAL && blockHasAccessList
1919+
19071920
// The traced section of block import.
19081921
start := time.Now()
1909-
res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
1922+
res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1, makeBAL, validateBAL)
19101923
if err != nil {
19111924
return nil, it.index, err
19121925
}
@@ -1978,7 +1991,7 @@ func (bpr *blockProcessingResult) Witness() *stateless.Witness {
19781991

19791992
// ProcessBlock executes and validates the given block. If there was no error
19801993
// it writes the block and associated state to database.
1981-
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
1994+
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool, constructBALForTestingbool, validateBALbool) (_ *blockProcessingResult, blockEndErr error) {
19821995
var (
19831996
err error
19841997
startTime = time.Now()
@@ -2074,6 +2087,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20742087
}()
20752088
}
20762089

2090+
var balTracer *BlockAccessListTracer
2091+
// Process block using the parent state as reference point
2092+
if constructBALForTesting {
2093+
balTracer, bc.cfg.VmConfig.Tracer = NewBlockAccessListTracer()
2094+
}
2095+
20772096
// Process block using the parent state as reference point
20782097
pstart := time.Now()
20792098
res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig)
@@ -2090,6 +2109,16 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
20902109
}
20912110
vtime := time.Since(vstart)
20922111

2112+
if constructBALForTesting {
2113+
// very ugly... deep-copy the block body before setting the block access
2114+
// list on it to prevent mutating the block instance passed by the caller.
2115+
existingBody := block.Body()
2116+
block = block.WithBody(*existingBody)
2117+
existingBody = block.Body()
2118+
existingBody.AccessList = balTracer.AccessList().ToEncodingObj()
2119+
block = block.WithBody(*existingBody)
2120+
}
2121+
20932122
// If witnesses was generated and stateless self-validation requested, do
20942123
// that now. Self validation should *never* run in production, it's more of
20952124
// a tight integration to enable running *all* consensus tests through the

‎core/genesis.go‎

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ type Genesis struct {
6767

6868
// These fields are used for consensus tests. Please don't use them
6969
// in actual genesis blocks.
70-
Number uint64 `json:"number"`
71-
GasUsed uint64 `json:"gasUsed"`
72-
ParentHash common.Hash `json:"parentHash"`
73-
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
74-
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
75-
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
70+
Number uint64 `json:"number"`
71+
GasUsed uint64 `json:"gasUsed"`
72+
ParentHash common.Hash `json:"parentHash"`
73+
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
74+
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
75+
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
76+
BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` // EIP-7928
7677
}
7778

7879
// copy copies the genesis.
@@ -122,6 +123,7 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
122123
genesis.BaseFee = genesisHeader.BaseFee
123124
genesis.ExcessBlobGas = genesisHeader.ExcessBlobGas
124125
genesis.BlobGasUsed = genesisHeader.BlobGasUsed
126+
genesis.BlockAccessListHash = genesisHeader.BlockAccessListHash
125127

126128
return &genesis, nil
127129
}
@@ -469,18 +471,19 @@ func (g *Genesis) ToBlock() *types.Block {
469471
// toBlockWithRoot constructs the genesis block with the given genesis state root.
470472
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
471473
head := &types.Header{
472-
Number: new(big.Int).SetUint64(g.Number),
473-
Nonce: types.EncodeNonce(g.Nonce),
474-
Time: g.Timestamp,
475-
ParentHash: g.ParentHash,
476-
Extra: g.ExtraData,
477-
GasLimit: g.GasLimit,
478-
GasUsed: g.GasUsed,
479-
BaseFee: g.BaseFee,
480-
Difficulty: g.Difficulty,
481-
MixDigest: g.Mixhash,
482-
Coinbase: g.Coinbase,
483-
Root: root,
474+
Number: new(big.Int).SetUint64(g.Number),
475+
Nonce: types.EncodeNonce(g.Nonce),
476+
Time: g.Timestamp,
477+
ParentHash: g.ParentHash,
478+
Extra: g.ExtraData,
479+
GasLimit: g.GasLimit,
480+
GasUsed: g.GasUsed,
481+
BaseFee: g.BaseFee,
482+
Difficulty: g.Difficulty,
483+
MixDigest: g.Mixhash,
484+
Coinbase: g.Coinbase,
485+
BlockAccessListHash: g.BlockAccessListHash,
486+
Root: root,
484487
}
485488
if g.GasLimit == 0 {
486489
head.GasLimit = params.GenesisGasLimit

‎core/tracing/hooks.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ type (
183183
// StorageChangeHook is called when the storage of an account changes.
184184
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
185185

186+
// ColdStorageReadHook is called before a previously-unread storage slot is read.
187+
ColdStorageReadHook = func(common.Address, common.Hash)
188+
189+
// ColdAccountReadHook is called before an previously-unread account is read.
190+
ColdAccountReadHook = func(address common.Address)
191+
186192
// LogHook is called when a log is emitted.
187193
LogHook = func(log *types.Log)
188194

@@ -209,6 +215,7 @@ type Hooks struct {
209215
OnSystemCallStart OnSystemCallStartHook
210216
OnSystemCallStartV2 OnSystemCallStartHookV2
211217
OnSystemCallEnd OnSystemCallEndHook
218+
212219
// State events
213220
OnBalanceChange BalanceChangeHook
214221
OnNonceChange NonceChangeHook
@@ -217,6 +224,9 @@ type Hooks struct {
217224
OnCodeChangeV2 CodeChangeHookV2
218225
OnStorageChange StorageChangeHook
219226
OnLog LogHook
227+
//State read events
228+
OnColdStorageRead ColdStorageReadHook
229+
OnColdAccountRead ColdAccountReadHook
220230
// Block hash read
221231
OnBlockHashRead BlockHashReadHook
222232
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /