Skip to content

Commit b5948ee

Browse files
committed
core/types: add optional bal construction in statedb when triggered by a flag
1 parent b992b10 commit b5948ee

33 files changed

+362
-104
lines changed

cmd/evm/blockrunner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) {
8989
continue
9090
}
9191
result := &testResult{Name: name, Pass: true}
92-
if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) {
92+
if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), false, tracer, func(res error, chain *core.BlockChain) {
9393
if ctx.Bool(DumpFlag.Name) {
9494
if s, _ := chain.State(); s != nil {
9595
result.State = dump(s)

cmd/geth/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ var (
151151
utils.BeaconGenesisTimeFlag,
152152
utils.BeaconCheckpointFlag,
153153
utils.BeaconCheckpointFileFlag,
154+
utils.BuildBALFlag,
154155
}, utils.NetworkFlags, utils.DatabaseFlags)
155156

156157
rpcFlags = []cli.Flag{
@@ -338,6 +339,10 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
338339
log.Warn(`The "unlock" flag has been deprecated and has no effect`)
339340
}
340341

342+
if ctx.IsSet(utils.BuildBALFlag.Name) {
343+
log.Warn(`block-access-list construction enabled. This is an experimental feature that shouldn't be enabled outside of a Geth development context.'`)
344+
}
345+
341346
// Register wallet event handlers to open and auto-derive wallets
342347
events := make(chan accounts.WalletEvent, 16)
343348
stack.AccountManager().Subscribe(events)

cmd/utils/flags.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,13 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
966966
Value: metrics.DefaultConfig.InfluxDBOrganization,
967967
Category: flags.MetricsCategory,
968968
}
969+
970+
// Block Access List flags
971+
BuildBALFlag = &cli.BoolFlag{
972+
Name: "buildbal",
973+
Usage: "Enable block-access-list building when executing blocks (experimental)",
974+
Category: flags.MiscCategory,
975+
}
969976
)
970977

971978
var (
@@ -1852,6 +1859,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
18521859
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
18531860
}
18541861
}
1862+
1863+
if ctx.IsSet(BuildBALFlag.Name) {
1864+
enabled := ctx.Bool(BuildBALFlag.Name)
1865+
cfg.BuildBAL = &enabled
1866+
}
18551867
}
18561868

18571869
// MakeBeaconLightConfig constructs a beacon light client config based on the

core/block_validator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
210210
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
211211
case <-time.After(25 * time.Millisecond):
212212
}
213-
chain.InsertBlockWithoutSetHead(postBlocks[i], false)
213+
chain.InsertBlockWithoutSetHead(postBlocks[i], false, false)
214214
}
215215

216216
// Verify the blocks with pre-merge blocks and post-merge blocks

core/blockchain.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,7 +1709,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
17091709
}
17101710
defer bc.chainmu.Unlock()
17111711

1712-
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
1712+
_, n, err := bc.insertChain(chain, true, false, true) // No witness collection for mass inserts (would get super large)
17131713
return n, err
17141714
}
17151715

@@ -1721,7 +1721,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
17211721
// racey behaviour. If a sidechain import is in progress, and the historic state
17221722
// is imported, but then new canon-head is added before the actual sidechain
17231723
// completes, then the historic state could be pruned again
1724-
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
1724+
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool, makeBAL bool) (*stateless.Witness, int, error) {
17251725
// If the chain is terminating, don't even bother starting up.
17261726
if bc.insertStopped() {
17271727
return nil, 0, nil
@@ -1879,7 +1879,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
18791879
}
18801880
// The traced section of block import.
18811881
start := time.Now()
1882-
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
1882+
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1, makeBAL)
18831883
if err != nil {
18841884
return nil, it.index, err
18851885
}
@@ -1947,7 +1947,7 @@ type blockProcessingResult struct {
19471947

19481948
// processBlock executes and validates the given block. If there was no error
19491949
// it writes the block and associated state to database.
1950-
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
1950+
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool, makeBAL bool) (_ *blockProcessingResult, blockEndErr error) {
19511951
var (
19521952
err error
19531953
startTime = time.Now()
@@ -2006,6 +2006,11 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
20062006
}(time.Now(), throwaway, block)
20072007
}
20082008

2009+
constructBAL := bc.chainConfig.IsByzantium(block.Number()) && makeBAL && bc.cfg.VmConfig.BALConstruction
2010+
if constructBAL {
2011+
statedb.EnableBALConstruction()
2012+
}
2013+
20092014
// If we are past Byzantium, enable prefetching to pull in trie node paths
20102015
// while processing transactions. Before Byzantium the prefetcher is mostly
20112016
// useless due to the intermediate root hashing after each transaction.
@@ -2053,6 +2058,16 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
20532058
}
20542059
vtime := time.Since(vstart)
20552060

2061+
if constructBAL {
2062+
// very ugly... deep-copy the block body before setting the block access
2063+
// list on it to prevent mutating the block instance passed by the caller.
2064+
existingBody := block.Body()
2065+
block = block.WithBody(*existingBody)
2066+
existingBody = block.Body()
2067+
existingBody.AccessList = statedb.BlockAccessList().ToEncodingObj()
2068+
block = block.WithBody(*existingBody)
2069+
}
2070+
20562071
// If witnesses was generated and stateless self-validation requested, do
20572072
// that now. Self validation should *never* run in production, it's more of
20582073
// a tight integration to enable running *all* consensus tests through the
@@ -2226,7 +2241,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
22262241
// memory here.
22272242
if len(blocks) >= 2048 || memory > 64*1024*1024 {
22282243
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
2229-
if _, _, err := bc.insertChain(blocks, true, false); err != nil {
2244+
if _, _, err := bc.insertChain(blocks, true, false, false); err != nil {
22302245
return nil, 0, err
22312246
}
22322247
blocks, memory = blocks[:0], 0
@@ -2240,7 +2255,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
22402255
}
22412256
if len(blocks) > 0 {
22422257
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
2243-
return bc.insertChain(blocks, true, makeWitness)
2258+
return bc.insertChain(blocks, true, makeWitness, false)
22442259
}
22452260
return nil, 0, nil
22462261
}
@@ -2289,7 +2304,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co
22892304
} else {
22902305
b = bc.GetBlock(hashes[i], numbers[i])
22912306
}
2292-
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil {
2307+
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0, false); err != nil {
22932308
return b.ParentHash(), err
22942309
}
22952310
}
@@ -2509,13 +2524,13 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
25092524
// The key difference between the InsertChain is it won't do the canonical chain
25102525
// updating. It relies on the additional SetCanonical call to finalize the entire
25112526
// procedure.
2512-
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) {
2527+
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool, makeBAL bool) (*stateless.Witness, error) {
25132528
if !bc.chainmu.TryLock() {
25142529
return nil, errChainStopped
25152530
}
25162531
defer bc.chainmu.Unlock()
25172532

2518-
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
2533+
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness, makeBAL)
25192534
return witness, err
25202535
}
25212536

core/blockchain_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3456,7 +3456,7 @@ func testSetCanonical(t *testing.T, scheme string) {
34563456
gen.AddTx(tx)
34573457
})
34583458
for _, block := range side {
3459-
_, err := chain.InsertBlockWithoutSetHead(block, false)
3459+
_, err := chain.InsertBlockWithoutSetHead(block, false, false)
34603460
if err != nil {
34613461
t.Fatalf("Failed to insert into chain: %v", err)
34623462
}

core/genesis.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
681681
},
682682
}
683683
if faucet != nil {
684-
genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
684+
genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Lsh(big.NewInt(1), 95)}
685685
}
686686
return genesis
687687
}

core/state/state_object.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ type stateObject struct {
5151
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
5252
data types.StateAccount // Account data with all mutations applied in the scope of block
5353

54+
// TODO: figure out whether or not this is first set after system contracts (withdrawals) are applied
55+
txPreBalance *uint256.Int // the account balance at the start of the current transaction
56+
txPreNonce uint64
57+
5458
// Write caches.
5559
trie Trie // storage trie, which becomes non-nil on first access
5660
code []byte // contract bytecode, which gets set when code is loaded
@@ -102,6 +106,8 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
102106
addrHash: crypto.Keccak256Hash(address[:]),
103107
origin: origin,
104108
data: *acct,
109+
txPreBalance: acct.Balance.Clone(),
110+
txPreNonce: acct.Nonce,
105111
originStorage: make(Storage),
106112
dirtyStorage: make(Storage),
107113
pendingStorage: make(Storage),
@@ -272,6 +278,9 @@ func (s *stateObject) finalise() {
272278
// of the newly-created object as it's no longer eligible for self-destruct
273279
// by EIP-6780. For non-newly-created objects, it's a no-op.
274280
s.newContract = false
281+
282+
s.txPreBalance = s.data.Balance.Clone()
283+
s.txPreNonce = s.data.Nonce
275284
}
276285

277286
// updateTrie is responsible for persisting cached storage changes into the
@@ -493,6 +502,8 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
493502
dirtyCode: s.dirtyCode,
494503
selfDestructed: s.selfDestructed,
495504
newContract: s.newContract,
505+
txPreNonce: s.txPreNonce,
506+
txPreBalance: s.txPreBalance.Clone(),
496507
}
497508
if s.trie != nil {
498509
obj.trie = mustCopyTrie(s.trie)

core/state/statedb.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"sync/atomic"
2727
"time"
2828

29+
"github.com/ethereum/go-ethereum/core/types/bal"
30+
2931
"github.com/ethereum/go-ethereum/common"
3032
"github.com/ethereum/go-ethereum/core/rawdb"
3133
"github.com/ethereum/go-ethereum/core/state/snapshot"
@@ -138,6 +140,9 @@ type StateDB struct {
138140
// State witness if cross validation is needed
139141
witness *stateless.Witness
140142

143+
// block access list, if bal construction is specified
144+
b *bal.ConstructionBlockAccessList
145+
141146
// Measurements gathered during execution for debugging purposes
142147
AccountReads time.Duration
143148
AccountHashes time.Duration
@@ -157,6 +162,19 @@ type StateDB struct {
157162
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
158163
}
159164

165+
// EnableBALConstruction configures the StateDB instance to construct block
166+
// access lists from state reads/writes recorded during block execution
167+
func (s *StateDB) EnableBALConstruction() {
168+
bal := bal.NewConstructionBlockAccessList()
169+
s.b = &bal
170+
}
171+
172+
// BlockAccessList retrieves the access list that has been constructed
173+
// by the StateDB instance, or nil if BAL construction was not enabled.
174+
func (s *StateDB) BlockAccessList() *bal.ConstructionBlockAccessList {
175+
return s.b
176+
}
177+
160178
// New creates a new state from a given trie.
161179
func New(root common.Hash, db Database) (*StateDB, error) {
162180
reader, err := db.Reader(root)
@@ -373,6 +391,9 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
373391
// GetState retrieves the value associated with the specific key.
374392
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
375393
stateObject := s.getStateObject(addr)
394+
if s.b != nil {
395+
s.b.StorageRead(addr, hash)
396+
}
376397
if stateObject != nil {
377398
return stateObject.GetState(hash)
378399
}
@@ -629,6 +650,12 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
629650
if obj == nil {
630651
obj = s.createObject(addr)
631652
}
653+
654+
if s.b != nil {
655+
// note: when sending a transfer with no value, the target account is loaded here
656+
// we probably want to specify whether this is proper behavior in the EIP
657+
s.b.AccountRead(addr)
658+
}
632659
return obj
633660
}
634661

@@ -765,6 +792,29 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
765792
s.stateObjectsDestruct[obj.address] = obj
766793
}
767794
} else {
795+
if s.b != nil {
796+
// add written storage keys/values
797+
for key, val := range obj.dirtyStorage {
798+
s.b.StorageWrite(uint16(s.txIndex), obj.address, key, val)
799+
}
800+
801+
// for addresses that changed balance, add the post-change value
802+
if obj.Balance().Cmp(obj.txPreBalance) != 0 {
803+
s.b.BalanceChange(uint16(s.txIndex), obj.address, obj.Balance())
804+
}
805+
806+
// include nonces for any contract-like accounts which incremented them
807+
if common.BytesToHash(obj.CodeHash()) != types.EmptyCodeHash && obj.Nonce() != obj.txPreNonce {
808+
s.b.NonceChange(obj.address, uint16(s.txIndex), obj.Nonce())
809+
}
810+
811+
// include code of created contracts
812+
// TODO: validate that this doesn't trigger on delegated EOAs
813+
if obj.newContract {
814+
s.b.CodeChange(obj.address, uint16(s.txIndex), obj.code)
815+
}
816+
}
817+
768818
obj.finalise()
769819
s.markUpdate(addr)
770820
}

core/state/statedb_hooked.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package state
1919
import (
2020
"math/big"
2121

22+
"github.com/ethereum/go-ethereum/core/types/bal"
23+
2224
"github.com/ethereum/go-ethereum/common"
2325
"github.com/ethereum/go-ethereum/core/stateless"
2426
"github.com/ethereum/go-ethereum/core/tracing"
@@ -157,6 +159,10 @@ func (s *hookedStateDB) Witness() *stateless.Witness {
157159
return s.inner.Witness()
158160
}
159161

162+
func (s *hookedStateDB) BlockAccessList() *bal.ConstructionBlockAccessList {
163+
return s.inner.BlockAccessList()
164+
}
165+
160166
func (s *hookedStateDB) AccessEvents() *AccessEvents {
161167
return s.inner.AccessEvents()
162168
}
@@ -261,6 +267,10 @@ func (s *hookedStateDB) AddLog(log *types.Log) {
261267
}
262268
}
263269

270+
func (s *hookedStateDB) TxIndex() int {
271+
return s.inner.TxIndex()
272+
}
273+
264274
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
265275
defer s.inner.Finalise(deleteEmptyObjects)
266276
if s.hooks.OnBalanceChange == nil {

0 commit comments

Comments
 (0)