mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
core/types: add optional bal construction in statedb when triggered by a flag
This commit is contained in:
parent
b992b105ef
commit
b5948eeb38
33 changed files with 363 additions and 105 deletions
|
|
@ -89,7 +89,7 @@ func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result := &testResult{Name: name, Pass: true}
|
result := &testResult{Name: name, Pass: true}
|
||||||
if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) {
|
if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), false, tracer, func(res error, chain *core.BlockChain) {
|
||||||
if ctx.Bool(DumpFlag.Name) {
|
if ctx.Bool(DumpFlag.Name) {
|
||||||
if s, _ := chain.State(); s != nil {
|
if s, _ := chain.State(); s != nil {
|
||||||
result.State = dump(s)
|
result.State = dump(s)
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ var (
|
||||||
utils.BeaconGenesisTimeFlag,
|
utils.BeaconGenesisTimeFlag,
|
||||||
utils.BeaconCheckpointFlag,
|
utils.BeaconCheckpointFlag,
|
||||||
utils.BeaconCheckpointFileFlag,
|
utils.BeaconCheckpointFileFlag,
|
||||||
|
utils.BuildBALFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
|
|
@ -338,6 +339,10 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
|
||||||
log.Warn(`The "unlock" flag has been deprecated and has no effect`)
|
log.Warn(`The "unlock" flag has been deprecated and has no effect`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet(utils.BuildBALFlag.Name) {
|
||||||
|
log.Warn(`block-access-list construction enabled. This is an experimental feature that shouldn't be enabled outside of a Geth development context.'`)
|
||||||
|
}
|
||||||
|
|
||||||
// Register wallet event handlers to open and auto-derive wallets
|
// Register wallet event handlers to open and auto-derive wallets
|
||||||
events := make(chan accounts.WalletEvent, 16)
|
events := make(chan accounts.WalletEvent, 16)
|
||||||
stack.AccountManager().Subscribe(events)
|
stack.AccountManager().Subscribe(events)
|
||||||
|
|
|
||||||
|
|
@ -966,6 +966,13 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||||
Value: metrics.DefaultConfig.InfluxDBOrganization,
|
Value: metrics.DefaultConfig.InfluxDBOrganization,
|
||||||
Category: flags.MetricsCategory,
|
Category: flags.MetricsCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block Access List flags
|
||||||
|
BuildBALFlag = &cli.BoolFlag{
|
||||||
|
Name: "buildbal",
|
||||||
|
Usage: "Enable block-access-list building when executing blocks (experimental)",
|
||||||
|
Category: flags.MiscCategory,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -1852,6 +1859,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
|
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet(BuildBALFlag.Name) {
|
||||||
|
enabled := ctx.Bool(BuildBALFlag.Name)
|
||||||
|
cfg.BuildBAL = &enabled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBeaconLightConfig constructs a beacon light client config based on the
|
// MakeBeaconLightConfig constructs a beacon light client config based on the
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||||
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
|
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
|
||||||
case <-time.After(25 * time.Millisecond):
|
case <-time.After(25 * time.Millisecond):
|
||||||
}
|
}
|
||||||
chain.InsertBlockWithoutSetHead(postBlocks[i], false)
|
chain.InsertBlockWithoutSetHead(postBlocks[i], false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the blocks with pre-merge blocks and post-merge blocks
|
// Verify the blocks with pre-merge blocks and post-merge blocks
|
||||||
|
|
|
||||||
|
|
@ -1709,7 +1709,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
|
_, n, err := bc.insertChain(chain, true, false, true) // No witness collection for mass inserts (would get super large)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1721,7 +1721,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
// racey behaviour. If a sidechain import is in progress, and the historic state
|
// racey behaviour. If a sidechain import is in progress, and the historic state
|
||||||
// is imported, but then new canon-head is added before the actual sidechain
|
// is imported, but then new canon-head is added before the actual sidechain
|
||||||
// completes, then the historic state could be pruned again
|
// completes, then the historic state could be pruned again
|
||||||
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool, makeBAL bool) (*stateless.Witness, int, error) {
|
||||||
// If the chain is terminating, don't even bother starting up.
|
// If the chain is terminating, don't even bother starting up.
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
return nil, 0, nil
|
return nil, 0, nil
|
||||||
|
|
@ -1879,7 +1879,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
}
|
}
|
||||||
// The traced section of block import.
|
// The traced section of block import.
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
|
res, err := bc.processBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1, makeBAL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
@ -1947,7 +1947,7 @@ type blockProcessingResult struct {
|
||||||
|
|
||||||
// processBlock executes and validates the given block. If there was no error
|
// processBlock executes and validates the given block. If there was no error
|
||||||
// it writes the block and associated state to database.
|
// it writes the block and associated state to database.
|
||||||
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) {
|
func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool, makeBAL bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
|
|
@ -2006,6 +2006,11 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
}(time.Now(), throwaway, block)
|
}(time.Now(), throwaway, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructBAL := bc.chainConfig.IsByzantium(block.Number()) && makeBAL && bc.cfg.VmConfig.BALConstruction
|
||||||
|
if constructBAL {
|
||||||
|
statedb.EnableBALConstruction()
|
||||||
|
}
|
||||||
|
|
||||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
// 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
|
||||||
}
|
}
|
||||||
vtime := time.Since(vstart)
|
vtime := time.Since(vstart)
|
||||||
|
|
||||||
|
if constructBAL {
|
||||||
|
// very ugly... deep-copy the block body before setting the block access
|
||||||
|
// list on it to prevent mutating the block instance passed by the caller.
|
||||||
|
existingBody := block.Body()
|
||||||
|
block = block.WithBody(*existingBody)
|
||||||
|
existingBody = block.Body()
|
||||||
|
existingBody.AccessList = statedb.BlockAccessList().ToEncodingObj()
|
||||||
|
block = block.WithBody(*existingBody)
|
||||||
|
}
|
||||||
|
|
||||||
// If witnesses was generated and stateless self-validation requested, do
|
// If witnesses was generated and stateless self-validation requested, do
|
||||||
// that now. Self validation should *never* run in production, it's more of
|
// that now. Self validation should *never* run in production, it's more of
|
||||||
// a tight integration to enable running *all* consensus tests through the
|
// 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
|
||||||
// memory here.
|
// memory here.
|
||||||
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
||||||
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
||||||
if _, _, err := bc.insertChain(blocks, true, false); err != nil {
|
if _, _, err := bc.insertChain(blocks, true, false, false); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
blocks, memory = blocks[:0], 0
|
blocks, memory = blocks[:0], 0
|
||||||
|
|
@ -2240,7 +2255,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||||
}
|
}
|
||||||
if len(blocks) > 0 {
|
if len(blocks) > 0 {
|
||||||
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
||||||
return bc.insertChain(blocks, true, makeWitness)
|
return bc.insertChain(blocks, true, makeWitness, false)
|
||||||
}
|
}
|
||||||
return nil, 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
@ -2289,7 +2304,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co
|
||||||
} else {
|
} else {
|
||||||
b = bc.GetBlock(hashes[i], numbers[i])
|
b = bc.GetBlock(hashes[i], numbers[i])
|
||||||
}
|
}
|
||||||
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil {
|
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0, false); err != nil {
|
||||||
return b.ParentHash(), err
|
return b.ParentHash(), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2509,13 +2524,13 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
||||||
// The key difference between the InsertChain is it won't do the canonical chain
|
// The key difference between the InsertChain is it won't do the canonical chain
|
||||||
// updating. It relies on the additional SetCanonical call to finalize the entire
|
// updating. It relies on the additional SetCanonical call to finalize the entire
|
||||||
// procedure.
|
// procedure.
|
||||||
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) {
|
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool, makeBAL bool) (*stateless.Witness, error) {
|
||||||
if !bc.chainmu.TryLock() {
|
if !bc.chainmu.TryLock() {
|
||||||
return nil, errChainStopped
|
return nil, errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
|
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness, makeBAL)
|
||||||
return witness, err
|
return witness, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3456,7 +3456,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
})
|
})
|
||||||
for _, block := range side {
|
for _, block := range side {
|
||||||
_, err := chain.InsertBlockWithoutSetHead(block, false)
|
_, err := chain.InsertBlockWithoutSetHead(block, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert into chain: %v", err)
|
t.Fatalf("Failed to insert into chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -681,7 +681,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if faucet != nil {
|
if faucet != nil {
|
||||||
genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
|
genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Lsh(big.NewInt(1), 95)}
|
||||||
}
|
}
|
||||||
return genesis
|
return genesis
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,10 @@ type stateObject struct {
|
||||||
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
||||||
data types.StateAccount // Account data with all mutations applied in the scope of block
|
data types.StateAccount // Account data with all mutations applied in the scope of block
|
||||||
|
|
||||||
|
// TODO: figure out whether or not this is first set after system contracts (withdrawals) are applied
|
||||||
|
txPreBalance *uint256.Int // the account balance at the start of the current transaction
|
||||||
|
txPreNonce uint64
|
||||||
|
|
||||||
// Write caches.
|
// Write caches.
|
||||||
trie Trie // storage trie, which becomes non-nil on first access
|
trie Trie // storage trie, which becomes non-nil on first access
|
||||||
code []byte // contract bytecode, which gets set when code is loaded
|
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
|
||||||
addrHash: crypto.Keccak256Hash(address[:]),
|
addrHash: crypto.Keccak256Hash(address[:]),
|
||||||
origin: origin,
|
origin: origin,
|
||||||
data: *acct,
|
data: *acct,
|
||||||
|
txPreBalance: acct.Balance.Clone(),
|
||||||
|
txPreNonce: acct.Nonce,
|
||||||
originStorage: make(Storage),
|
originStorage: make(Storage),
|
||||||
dirtyStorage: make(Storage),
|
dirtyStorage: make(Storage),
|
||||||
pendingStorage: make(Storage),
|
pendingStorage: make(Storage),
|
||||||
|
|
@ -272,6 +278,9 @@ func (s *stateObject) finalise() {
|
||||||
// of the newly-created object as it's no longer eligible for self-destruct
|
// of the newly-created object as it's no longer eligible for self-destruct
|
||||||
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
||||||
s.newContract = false
|
s.newContract = false
|
||||||
|
|
||||||
|
s.txPreBalance = s.data.Balance.Clone()
|
||||||
|
s.txPreNonce = s.data.Nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateTrie is responsible for persisting cached storage changes into the
|
// updateTrie is responsible for persisting cached storage changes into the
|
||||||
|
|
@ -493,6 +502,8 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||||
dirtyCode: s.dirtyCode,
|
dirtyCode: s.dirtyCode,
|
||||||
selfDestructed: s.selfDestructed,
|
selfDestructed: s.selfDestructed,
|
||||||
newContract: s.newContract,
|
newContract: s.newContract,
|
||||||
|
txPreNonce: s.txPreNonce,
|
||||||
|
txPreBalance: s.txPreBalance.Clone(),
|
||||||
}
|
}
|
||||||
if s.trie != nil {
|
if s.trie != nil {
|
||||||
obj.trie = mustCopyTrie(s.trie)
|
obj.trie = mustCopyTrie(s.trie)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
|
@ -138,6 +140,9 @@ type StateDB struct {
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
|
||||||
|
// block access list, if bal construction is specified
|
||||||
|
b *bal.ConstructionBlockAccessList
|
||||||
|
|
||||||
// Measurements gathered during execution for debugging purposes
|
// Measurements gathered during execution for debugging purposes
|
||||||
AccountReads time.Duration
|
AccountReads time.Duration
|
||||||
AccountHashes time.Duration
|
AccountHashes time.Duration
|
||||||
|
|
@ -157,6 +162,19 @@ type StateDB struct {
|
||||||
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
|
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableBALConstruction configures the StateDB instance to construct block
|
||||||
|
// access lists from state reads/writes recorded during block execution
|
||||||
|
func (s *StateDB) EnableBALConstruction() {
|
||||||
|
bal := bal.NewConstructionBlockAccessList()
|
||||||
|
s.b = &bal
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockAccessList retrieves the access list that has been constructed
|
||||||
|
// by the StateDB instance, or nil if BAL construction was not enabled.
|
||||||
|
func (s *StateDB) BlockAccessList() *bal.ConstructionBlockAccessList {
|
||||||
|
return s.b
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
func New(root common.Hash, db Database) (*StateDB, error) {
|
func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
reader, err := db.Reader(root)
|
reader, err := db.Reader(root)
|
||||||
|
|
@ -373,6 +391,9 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||||
// GetState retrieves the value associated with the specific key.
|
// GetState retrieves the value associated with the specific key.
|
||||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||||
stateObject := s.getStateObject(addr)
|
stateObject := s.getStateObject(addr)
|
||||||
|
if s.b != nil {
|
||||||
|
s.b.StorageRead(addr, hash)
|
||||||
|
}
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetState(hash)
|
return stateObject.GetState(hash)
|
||||||
}
|
}
|
||||||
|
|
@ -629,6 +650,12 @@ func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
obj = s.createObject(addr)
|
obj = s.createObject(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.b != nil {
|
||||||
|
// note: when sending a transfer with no value, the target account is loaded here
|
||||||
|
// we probably want to specify whether this is proper behavior in the EIP
|
||||||
|
s.b.AccountRead(addr)
|
||||||
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,6 +792,29 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
s.stateObjectsDestruct[obj.address] = obj
|
s.stateObjectsDestruct[obj.address] = obj
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if s.b != nil {
|
||||||
|
// add written storage keys/values
|
||||||
|
for key, val := range obj.dirtyStorage {
|
||||||
|
s.b.StorageWrite(uint16(s.txIndex), obj.address, key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for addresses that changed balance, add the post-change value
|
||||||
|
if obj.Balance().Cmp(obj.txPreBalance) != 0 {
|
||||||
|
s.b.BalanceChange(uint16(s.txIndex), obj.address, obj.Balance())
|
||||||
|
}
|
||||||
|
|
||||||
|
// include nonces for any contract-like accounts which incremented them
|
||||||
|
if common.BytesToHash(obj.CodeHash()) != types.EmptyCodeHash && obj.Nonce() != obj.txPreNonce {
|
||||||
|
s.b.NonceChange(obj.address, uint16(s.txIndex), obj.Nonce())
|
||||||
|
}
|
||||||
|
|
||||||
|
// include code of created contracts
|
||||||
|
// TODO: validate that this doesn't trigger on delegated EOAs
|
||||||
|
if obj.newContract {
|
||||||
|
s.b.CodeChange(obj.address, uint16(s.txIndex), obj.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
obj.finalise()
|
obj.finalise()
|
||||||
s.markUpdate(addr)
|
s.markUpdate(addr)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package state
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
|
@ -157,6 +159,10 @@ func (s *hookedStateDB) Witness() *stateless.Witness {
|
||||||
return s.inner.Witness()
|
return s.inner.Witness()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) BlockAccessList() *bal.ConstructionBlockAccessList {
|
||||||
|
return s.inner.BlockAccessList()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) AccessEvents() *AccessEvents {
|
func (s *hookedStateDB) AccessEvents() *AccessEvents {
|
||||||
return s.inner.AccessEvents()
|
return s.inner.AccessEvents()
|
||||||
}
|
}
|
||||||
|
|
@ -261,6 +267,10 @@ func (s *hookedStateDB) AddLog(log *types.Log) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *hookedStateDB) TxIndex() int {
|
||||||
|
return s.inner.TxIndex()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
|
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
defer s.inner.Finalise(deleteEmptyObjects)
|
defer s.inner.Finalise(deleteEmptyObjects)
|
||||||
if s.hooks.OnBalanceChange == nil {
|
if s.hooks.OnBalanceChange == nil {
|
||||||
|
|
|
||||||
|
|
@ -82,12 +82,23 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
context = NewEVMBlockContext(header, p.chain, nil)
|
context = NewEVMBlockContext(header, p.chain, nil)
|
||||||
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
|
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
|
||||||
|
|
||||||
|
// process beacon-root and parent block system contracts.
|
||||||
|
// do not include the storage writes in the BAL:
|
||||||
|
// * beacon root will be provided as a standalone field in the BAL
|
||||||
|
// * parent block hash is already in the header field of the block
|
||||||
|
if statedb.BlockAccessList() != nil {
|
||||||
|
statedb.BlockAccessList().DisableMutations()
|
||||||
|
}
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
}
|
}
|
||||||
|
// TODO: set the beacon root on the BAL
|
||||||
if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) {
|
if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) {
|
||||||
ProcessParentBlockHash(block.ParentHash(), evm)
|
ProcessParentBlockHash(block.ParentHash(), evm)
|
||||||
}
|
}
|
||||||
|
if statedb.BlockAccessList() != nil {
|
||||||
|
statedb.BlockAccessList().EnableMutations()
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
|
|
@ -104,6 +115,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't write post-block state mutations to the BAL to save on size.
|
||||||
|
// these can be easily computed in BAL verification.
|
||||||
|
if statedb.BlockAccessList() != nil {
|
||||||
|
statedb.BlockAccessList().DisableMutations()
|
||||||
|
}
|
||||||
// Read requests if Prague is enabled.
|
// Read requests if Prague is enabled.
|
||||||
var requests [][]byte
|
var requests [][]byte
|
||||||
if p.config.IsPrague(block.Number(), block.Time()) {
|
if p.config.IsPrague(block.Number(), block.Time()) {
|
||||||
|
|
|
||||||
|
|
@ -38,26 +38,26 @@ type ConstructionAccountAccess struct {
|
||||||
// StorageWrites is the post-state values of an account's storage slots
|
// StorageWrites is the post-state values of an account's storage slots
|
||||||
// that were modified in a block, keyed by the slot key and the tx index
|
// that were modified in a block, keyed by the slot key and the tx index
|
||||||
// where the modification occurred.
|
// where the modification occurred.
|
||||||
StorageWrites map[common.Hash]map[uint16]common.Hash `json:"storageWrites,omitempty"`
|
StorageWrites map[common.Hash]map[uint16]common.Hash
|
||||||
|
|
||||||
// StorageReads is the set of slot keys that were accessed during block
|
// StorageReads is the set of slot keys that were accessed during block
|
||||||
// execution.
|
// execution.
|
||||||
//
|
//
|
||||||
// Storage slots which are both read and written (with changed values)
|
// Storage slots which are both read and written (with changed values)
|
||||||
// appear only in StorageWrites.
|
// appear only in StorageWrites.
|
||||||
StorageReads map[common.Hash]struct{} `json:"storageReads,omitempty"`
|
StorageReads map[common.Hash]struct{}
|
||||||
|
|
||||||
// BalanceChanges contains the post-transaction balances of an account,
|
// BalanceChanges contains the post-transaction balances of an account,
|
||||||
// keyed by transaction indices where it was changed.
|
// keyed by transaction indices where it was changed.
|
||||||
BalanceChanges map[uint16]*uint256.Int `json:"balanceChanges,omitempty"`
|
BalanceChanges map[uint16]*uint256.Int
|
||||||
|
|
||||||
// NonceChanges contains the post-state nonce values of an account keyed
|
// NonceChanges contains the post-state nonce values of an account keyed
|
||||||
// by tx index.
|
// by tx index.
|
||||||
NonceChanges map[uint16]uint64 `json:"nonceChanges,omitempty"`
|
NonceChanges map[uint16]uint64
|
||||||
|
|
||||||
// CodeChange is only set for contract accounts which were deployed in
|
// CodeChange is only set for contract accounts which were deployed in
|
||||||
// the block.
|
// the block.
|
||||||
CodeChange *CodeChange `json:"codeChange,omitempty"`
|
CodeChange *CodeChange
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstructionAccountAccess initializes the account access object.
|
// NewConstructionAccountAccess initializes the account access object.
|
||||||
|
|
@ -74,6 +74,7 @@ func NewConstructionAccountAccess() *ConstructionAccountAccess {
|
||||||
// in execution (account addresses and storage keys).
|
// in execution (account addresses and storage keys).
|
||||||
type ConstructionBlockAccessList struct {
|
type ConstructionBlockAccessList struct {
|
||||||
Accounts map[common.Address]*ConstructionAccountAccess
|
Accounts map[common.Address]*ConstructionAccountAccess
|
||||||
|
enabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstructionBlockAccessList instantiates an empty access list.
|
// NewConstructionBlockAccessList instantiates an empty access list.
|
||||||
|
|
@ -83,44 +84,69 @@ func NewConstructionBlockAccessList() ConstructionBlockAccessList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableMutations enables the modification of the access list via subsequent
|
||||||
|
// calls to record access events. BALs are mutable by default.
|
||||||
|
func (c *ConstructionBlockAccessList) EnableMutations() {
|
||||||
|
c.enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableMutations prevents the mutation of the access list by subsequent calls
|
||||||
|
// to record access events.
|
||||||
|
func (c *ConstructionBlockAccessList) DisableMutations() {
|
||||||
|
c.enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
// AccountRead records the address of an account that has been read during execution.
|
// AccountRead records the address of an account that has been read during execution.
|
||||||
func (b *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
func (c *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
||||||
if _, ok := b.Accounts[addr]; !ok {
|
if !c.enabled {
|
||||||
b.Accounts[addr] = NewConstructionAccountAccess()
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := c.Accounts[addr]; !ok {
|
||||||
|
c.Accounts[addr] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageRead records a storage key read during execution.
|
// StorageRead records a storage key read during execution.
|
||||||
func (b *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) {
|
func (c *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) {
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
if !c.enabled {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
|
||||||
}
|
|
||||||
if _, ok := b.Accounts[address].StorageWrites[key]; ok {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.Accounts[address].StorageReads[key] = struct{}{}
|
if _, ok := c.Accounts[address]; !ok {
|
||||||
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
|
}
|
||||||
|
if _, ok := c.Accounts[address].StorageWrites[key]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Accounts[address].StorageReads[key] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageWrite records the post-transaction value of a mutated storage slot.
|
// StorageWrite records the post-transaction value of a mutated storage slot.
|
||||||
// The storage slot is removed from the list of read slots.
|
// The storage slot is removed from the list of read slots.
|
||||||
func (b *ConstructionBlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) {
|
func (c *ConstructionBlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) {
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
if !c.enabled {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
return
|
||||||
}
|
}
|
||||||
if _, ok := b.Accounts[address].StorageWrites[key]; !ok {
|
if _, ok := c.Accounts[address]; !ok {
|
||||||
b.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash)
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
b.Accounts[address].StorageWrites[key][txIdx] = value
|
if _, ok := c.Accounts[address].StorageWrites[key]; !ok {
|
||||||
|
c.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash)
|
||||||
|
}
|
||||||
|
c.Accounts[address].StorageWrites[key][txIdx] = value
|
||||||
|
|
||||||
delete(b.Accounts[address].StorageReads, key)
|
delete(c.Accounts[address].StorageReads, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeChange records the code of a newly-created contract.
|
// CodeChange records the code of a newly-created contract.
|
||||||
func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) {
|
func (c *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) {
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
if !c.enabled {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
return
|
||||||
}
|
}
|
||||||
b.Accounts[address].CodeChange = &CodeChange{
|
if _, ok := c.Accounts[address]; !ok {
|
||||||
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
|
}
|
||||||
|
c.Accounts[address].CodeChange = &CodeChange{
|
||||||
TxIndex: txIndex,
|
TxIndex: txIndex,
|
||||||
Code: bytes.Clone(code),
|
Code: bytes.Clone(code),
|
||||||
}
|
}
|
||||||
|
|
@ -128,32 +154,38 @@ func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex
|
||||||
|
|
||||||
// NonceChange records tx post-state nonce of any contract-like accounts whose
|
// NonceChange records tx post-state nonce of any contract-like accounts whose
|
||||||
// nonce was incremented.
|
// nonce was incremented.
|
||||||
func (b *ConstructionBlockAccessList) NonceChange(address common.Address, txIdx uint16, postNonce uint64) {
|
func (c *ConstructionBlockAccessList) NonceChange(address common.Address, txIdx uint16, postNonce uint64) {
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
if !c.enabled {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
return
|
||||||
}
|
}
|
||||||
b.Accounts[address].NonceChanges[txIdx] = postNonce
|
if _, ok := c.Accounts[address]; !ok {
|
||||||
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
|
}
|
||||||
|
c.Accounts[address].NonceChanges[txIdx] = postNonce
|
||||||
}
|
}
|
||||||
|
|
||||||
// BalanceChange records the post-transaction balance of an account whose
|
// BalanceChange records the post-transaction balance of an account whose
|
||||||
// balance changed.
|
// balance changed.
|
||||||
func (b *ConstructionBlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) {
|
func (c *ConstructionBlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) {
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
if !c.enabled {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
return
|
||||||
}
|
}
|
||||||
b.Accounts[address].BalanceChanges[txIdx] = balance.Clone()
|
if _, ok := c.Accounts[address]; !ok {
|
||||||
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
|
}
|
||||||
|
c.Accounts[address].BalanceChanges[txIdx] = balance.Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrettyPrint returns a human-readable representation of the access list
|
// PrettyPrint returns a human-readable representation of the access list
|
||||||
func (b *ConstructionBlockAccessList) PrettyPrint() string {
|
func (c *ConstructionBlockAccessList) PrettyPrint() string {
|
||||||
enc := b.toEncodingObj()
|
enc := c.ToEncodingObj()
|
||||||
return enc.PrettyPrint()
|
return enc.PrettyPrint()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a deep copy of the access list.
|
// Copy returns a deep copy of the access list.
|
||||||
func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
func (c *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
||||||
res := NewConstructionBlockAccessList()
|
res := NewConstructionBlockAccessList()
|
||||||
for addr, aa := range b.Accounts {
|
for addr, aa := range c.Accounts {
|
||||||
var aaCopy ConstructionAccountAccess
|
var aaCopy ConstructionAccountAccess
|
||||||
|
|
||||||
slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites))
|
slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites))
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ import (
|
||||||
|
|
||||||
// BlockAccessList is the encoding format of ConstructionBlockAccessList.
|
// BlockAccessList is the encoding format of ConstructionBlockAccessList.
|
||||||
type BlockAccessList struct {
|
type BlockAccessList struct {
|
||||||
Accesses []AccountAccess `ssz-max:"300000"`
|
Accesses []AccountAccess `ssz-max:"300000" json:"accesses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns an error if the contents of the access list are not ordered
|
// Validate returns an error if the contents of the access list are not ordered
|
||||||
|
|
@ -86,26 +86,26 @@ func encodeBalance(val *uint256.Int) [16]byte {
|
||||||
|
|
||||||
// encodingBalanceChange is the encoding format of BalanceChange.
|
// encodingBalanceChange is the encoding format of BalanceChange.
|
||||||
type encodingBalanceChange struct {
|
type encodingBalanceChange struct {
|
||||||
TxIdx uint16 `ssz-size:"2"`
|
TxIdx uint16 `ssz-size:"2" json:"txIndex"`
|
||||||
Balance [16]byte `ssz-size:"16"`
|
Balance [16]byte `ssz-size:"16" json:"balance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodingAccountNonce is the encoding format of NonceChange.
|
// encodingAccountNonce is the encoding format of NonceChange.
|
||||||
type encodingAccountNonce struct {
|
type encodingAccountNonce struct {
|
||||||
TxIdx uint16 `ssz-size:"2"`
|
TxIdx uint16 `ssz-size:"2" json:"txIndex"`
|
||||||
Nonce uint64 `ssz-size:"8"`
|
Nonce uint64 `ssz-size:"8" json:"nonce"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodingStorageWrite is the encoding format of StorageWrites.
|
// encodingStorageWrite is the encoding format of StorageWrites.
|
||||||
type encodingStorageWrite struct {
|
type encodingStorageWrite struct {
|
||||||
TxIdx uint16
|
TxIdx uint16 `json:"txIndex"`
|
||||||
ValueAfter [32]byte `ssz-size:"32"`
|
ValueAfter common.Hash `ssz-size:"32" json:"valueAfter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodingStorageWrite is the encoding format of SlotWrites.
|
// encodingStorageWrite is the encoding format of SlotWrites.
|
||||||
type encodingSlotWrites struct {
|
type encodingSlotWrites struct {
|
||||||
Slot [32]byte `ssz-size:"32"`
|
Slot common.Hash `ssz-size:"32" json:"slot"`
|
||||||
Accesses []encodingStorageWrite `ssz-max:"300000"`
|
Accesses []encodingStorageWrite `ssz-max:"300000" json:"accesses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate returns an instance of the encoding-representation slot writes in
|
// validate returns an instance of the encoding-representation slot writes in
|
||||||
|
|
@ -121,12 +121,12 @@ func (e *encodingSlotWrites) validate() error {
|
||||||
|
|
||||||
// AccountAccess is the encoding format of ConstructionAccountAccess.
|
// AccountAccess is the encoding format of ConstructionAccountAccess.
|
||||||
type AccountAccess struct {
|
type AccountAccess struct {
|
||||||
Address [20]byte `ssz-size:"20"` // 20-byte Ethereum address
|
Address common.Address `ssz-size:"20" json:"address,omitempty"` // 20-byte Ethereum address
|
||||||
StorageWrites []encodingSlotWrites `ssz-max:"300000"` // Storage changes (slot -> [tx_index -> new_value])
|
StorageWrites []encodingSlotWrites `ssz-max:"300000" json:"storageWrites,omitempty"` // Storage changes (slot -> [tx_index -> new_value])
|
||||||
StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys
|
StorageReads []common.Hash `ssz-max:"300000" json:"storageReads,omitempty"` // Read-only storage keys
|
||||||
BalanceChanges []encodingBalanceChange `ssz-max:"300000"` // Balance changes ([tx_index -> post_balance])
|
BalanceChanges []encodingBalanceChange `ssz-max:"300000" json:"balanceChanges,omitempty"` // Balance changes ([tx_index -> post_balance])
|
||||||
NonceChanges []encodingAccountNonce `ssz-max:"300000"` // Nonce changes ([tx_index -> new_nonce])
|
NonceChanges []encodingAccountNonce `ssz-max:"300000" json:"nonceChanges,omitempty"` // Nonce changes ([tx_index -> new_nonce])
|
||||||
Code []CodeChange `ssz-max:"1"` // Code changes ([tx_index -> new_code])
|
Code []CodeChange `ssz-max:"1" json:"code,omitempty"` // Code changes ([tx_index -> new_code])
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate converts the account accesses out of encoding format.
|
// validate converts the account accesses out of encoding format.
|
||||||
|
|
@ -146,7 +146,7 @@ func (e *AccountAccess) validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the storage read slots are sorted in order
|
// Check the storage read slots are sorted in order
|
||||||
if !slices.IsSortedFunc(e.StorageReads, func(a, b [32]byte) int {
|
if !slices.IsSortedFunc(e.StorageReads, func(a, b common.Hash) int {
|
||||||
return bytes.Compare(a[:], b[:])
|
return bytes.Compare(a[:], b[:])
|
||||||
}) {
|
}) {
|
||||||
return errors.New("storage read slots not in lexicographic order")
|
return errors.New("storage read slots not in lexicographic order")
|
||||||
|
|
@ -201,8 +201,8 @@ func (e *AccountAccess) Copy() AccountAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP returns the RLP-encoded access list
|
// EncodeRLP returns the RLP-encoded access list
|
||||||
func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error {
|
func (c *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error {
|
||||||
return b.toEncodingObj().EncodeRLP(wr)
|
return c.ToEncodingObj().EncodeRLP(wr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ rlp.Encoder = &ConstructionBlockAccessList{}
|
var _ rlp.Encoder = &ConstructionBlockAccessList{}
|
||||||
|
|
@ -213,7 +213,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
res := AccountAccess{
|
res := AccountAccess{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
StorageWrites: make([]encodingSlotWrites, 0),
|
StorageWrites: make([]encodingSlotWrites, 0),
|
||||||
StorageReads: make([][32]byte, 0),
|
StorageReads: make([]common.Hash, 0),
|
||||||
BalanceChanges: make([]encodingBalanceChange, 0),
|
BalanceChanges: make([]encodingBalanceChange, 0),
|
||||||
NonceChanges: make([]encodingAccountNonce, 0),
|
NonceChanges: make([]encodingAccountNonce, 0),
|
||||||
Code: nil,
|
Code: nil,
|
||||||
|
|
@ -279,18 +279,18 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// toEncodingObj returns an instance of the access list expressed as the type
|
// ToEncodingObj returns an instance of the access list expressed as the type
|
||||||
// which is used as input for the encoding/decoding.
|
// which is used as input for the encoding/decoding.
|
||||||
func (b *ConstructionBlockAccessList) toEncodingObj() *BlockAccessList {
|
func (c *ConstructionBlockAccessList) ToEncodingObj() *BlockAccessList {
|
||||||
var addresses []common.Address
|
var addresses []common.Address
|
||||||
for addr := range b.Accounts {
|
for addr := range c.Accounts {
|
||||||
addresses = append(addresses, addr)
|
addresses = append(addresses, addr)
|
||||||
}
|
}
|
||||||
slices.SortFunc(addresses, common.Address.Cmp)
|
slices.SortFunc(addresses, common.Address.Cmp)
|
||||||
|
|
||||||
var res BlockAccessList
|
var res BlockAccessList
|
||||||
for _, addr := range addresses {
|
for _, addr := range addresses {
|
||||||
res.Accesses = append(res.Accesses, b.Accounts[addr].toEncodingObj(addr))
|
res.Accesses = append(res.Accesses, c.Accounts[addr].toEncodingObj(addr))
|
||||||
}
|
}
|
||||||
return &res
|
return &res
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
package bal
|
package bal
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/common"
|
||||||
import "github.com/ethereum/go-ethereum/rlp"
|
import "github.com/ethereum/go-ethereum/rlp"
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
|
|
@ -81,7 +82,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Address:
|
// Address:
|
||||||
var _tmp3 [20]byte
|
var _tmp3 common.Address
|
||||||
if err := dec.ReadBytes(_tmp3[:]); err != nil {
|
if err := dec.ReadBytes(_tmp3[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +99,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Slot:
|
// Slot:
|
||||||
var _tmp6 [32]byte
|
var _tmp6 common.Hash
|
||||||
if err := dec.ReadBytes(_tmp6[:]); err != nil {
|
if err := dec.ReadBytes(_tmp6[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +122,7 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
_tmp8.TxIdx = _tmp9
|
_tmp8.TxIdx = _tmp9
|
||||||
// ValueAfter:
|
// ValueAfter:
|
||||||
var _tmp10 [32]byte
|
var _tmp10 common.Hash
|
||||||
if err := dec.ReadBytes(_tmp10[:]); err != nil {
|
if err := dec.ReadBytes(_tmp10[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -147,12 +148,12 @@ func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
_tmp2.StorageWrites = _tmp4
|
_tmp2.StorageWrites = _tmp4
|
||||||
// StorageReads:
|
// StorageReads:
|
||||||
var _tmp11 [][32]byte
|
var _tmp11 []common.Hash
|
||||||
if _, err := dec.List(); err != nil {
|
if _, err := dec.List(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for dec.MoreDataInList() {
|
for dec.MoreDataInList() {
|
||||||
var _tmp12 [32]byte
|
var _tmp12 common.Hash
|
||||||
if err := dec.ReadBytes(_tmp12[:]); err != nil {
|
if err := dec.ReadBytes(_tmp12[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,10 +103,10 @@ func TestBALEncoding(t *testing.T) {
|
||||||
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 10000000)); err != nil {
|
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 10000000)); err != nil {
|
||||||
t.Fatalf("decoding failed: %v\n", err)
|
t.Fatalf("decoding failed: %v\n", err)
|
||||||
}
|
}
|
||||||
if dec.Hash() != bal.toEncodingObj().Hash() {
|
if dec.Hash() != bal.ToEncodingObj().Hash() {
|
||||||
t.Fatalf("encoded block hash doesn't match decoded")
|
t.Fatalf("encoded block hash doesn't match decoded")
|
||||||
}
|
}
|
||||||
if !equalBALs(bal.toEncodingObj(), &dec) {
|
if !equalBALs(bal.ToEncodingObj(), &dec) {
|
||||||
t.Fatal("decoded BAL doesn't match")
|
t.Fatal("decoded BAL doesn't match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +114,7 @@ func TestBALEncoding(t *testing.T) {
|
||||||
func makeTestAccountAccess(sort bool) AccountAccess {
|
func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
var (
|
var (
|
||||||
storageWrites []encodingSlotWrites
|
storageWrites []encodingSlotWrites
|
||||||
storageReads [][32]byte
|
storageReads []common.Hash
|
||||||
balances []encodingBalanceChange
|
balances []encodingBalanceChange
|
||||||
nonces []encodingAccountNonce
|
nonces []encodingAccountNonce
|
||||||
)
|
)
|
||||||
|
|
@ -144,7 +145,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
storageReads = append(storageReads, testrand.Hash())
|
storageReads = append(storageReads, testrand.Hash())
|
||||||
}
|
}
|
||||||
if sort {
|
if sort {
|
||||||
slices.SortFunc(storageReads, func(a, b [32]byte) int {
|
slices.SortFunc(storageReads, func(a, b common.Hash) int {
|
||||||
return bytes.Compare(a[:], b[:])
|
return bytes.Compare(a[:], b[:])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +246,7 @@ func TestBlockAccessListValidation(t *testing.T) {
|
||||||
|
|
||||||
// Validate the derived block access list
|
// Validate the derived block access list
|
||||||
cBAL := makeTestConstructionBAL()
|
cBAL := makeTestConstructionBAL()
|
||||||
listB := cBAL.toEncodingObj()
|
listB := cBAL.ToEncodingObj()
|
||||||
if err := listB.Validate(); err != nil {
|
if err := listB.Validate(); err != nil {
|
||||||
t.Fatalf("Unexpected validation error: %v", err)
|
t.Fatalf("Unexpected validation error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -28,6 +29,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
|
@ -106,6 +109,8 @@ type Header struct {
|
||||||
|
|
||||||
// RequestsHash was added by EIP-7685 and is ignored in legacy headers.
|
// RequestsHash was added by EIP-7685 and is ignored in legacy headers.
|
||||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||||
|
|
||||||
|
BALHash *common.Hash `json:"balHash" rlp:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// field type overrides for gencodec
|
// field type overrides for gencodec
|
||||||
|
|
@ -184,6 +189,7 @@ type Body struct {
|
||||||
Transactions []*Transaction
|
Transactions []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
|
AccessList *bal.BlockAccessList `rlp:"optional,nil"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block represents an Ethereum block.
|
// Block represents an Ethereum block.
|
||||||
|
|
@ -214,6 +220,8 @@ type Block struct {
|
||||||
// that process it.
|
// that process it.
|
||||||
witness *ExecutionWitness
|
witness *ExecutionWitness
|
||||||
|
|
||||||
|
accessList *bal.BlockAccessList
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Pointer[common.Hash]
|
hash atomic.Pointer[common.Hash]
|
||||||
size atomic.Uint64
|
size atomic.Uint64
|
||||||
|
|
@ -230,6 +238,7 @@ type extblock struct {
|
||||||
Txs []*Transaction
|
Txs []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
|
BAL []byte `rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlock creates a new block. The input data is copied, changes to header and to the
|
// NewBlock creates a new block. The input data is copied, changes to header and to the
|
||||||
|
|
@ -290,6 +299,12 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher
|
||||||
b.withdrawals = slices.Clone(withdrawals)
|
b.withdrawals = slices.Clone(withdrawals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if body.AccessList != nil {
|
||||||
|
balHash := body.AccessList.Hash()
|
||||||
|
b.header.BALHash = &balHash
|
||||||
|
b.accessList = body.AccessList
|
||||||
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,30 +349,50 @@ func CopyHeader(h *Header) *Header {
|
||||||
|
|
||||||
// DecodeRLP decodes a block from RLP.
|
// DecodeRLP decodes a block from RLP.
|
||||||
func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
||||||
var eb extblock
|
var (
|
||||||
|
eb extblock
|
||||||
|
err error
|
||||||
|
)
|
||||||
_, size, _ := s.Kind()
|
_, size, _ := s.Kind()
|
||||||
if err := s.Decode(&eb); err != nil {
|
if err := s.Decode(&eb); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals
|
b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals
|
||||||
|
if eb.BAL != nil {
|
||||||
|
err = b.accessList.DecodeRLP(rlp.NewStream(bytes.NewBuffer(eb.BAL), 10_000_000))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: ensure that BAL is accounted for in size
|
||||||
b.size.Store(rlp.ListSize(size))
|
b.size.Store(rlp.ListSize(size))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP serializes a block as RLP.
|
// EncodeRLP serializes a block as RLP.
|
||||||
func (b *Block) EncodeRLP(w io.Writer) error {
|
func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
|
var alEnc []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if b.accessList != nil {
|
||||||
|
err = b.accessList.EncodeRLP(bytes.NewBuffer(alEnc))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return rlp.Encode(w, &extblock{
|
return rlp.Encode(w, &extblock{
|
||||||
Header: b.header,
|
Header: b.header,
|
||||||
Txs: b.transactions,
|
Txs: b.transactions,
|
||||||
Uncles: b.uncles,
|
Uncles: b.uncles,
|
||||||
Withdrawals: b.withdrawals,
|
Withdrawals: b.withdrawals,
|
||||||
|
BAL: alEnc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body returns the non-header content of the block.
|
// Body returns the non-header content of the block.
|
||||||
// Note the returned data is not an independent copy.
|
// Note the returned data is not an independent copy.
|
||||||
func (b *Block) Body() *Body {
|
func (b *Block) Body() *Body {
|
||||||
return &Body{b.transactions, b.uncles, b.withdrawals}
|
return &Body{b.transactions, b.uncles, b.withdrawals, b.accessList}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessors for body data. These do not return a copy because the content
|
// Accessors for body data. These do not return a copy because the content
|
||||||
|
|
@ -508,6 +543,10 @@ func (b *Block) WithBody(body Body) *Block {
|
||||||
withdrawals: slices.Clone(body.Withdrawals),
|
withdrawals: slices.Clone(body.Withdrawals),
|
||||||
witness: b.witness,
|
witness: b.witness,
|
||||||
}
|
}
|
||||||
|
if body.AccessList != nil {
|
||||||
|
balCopy := body.AccessList.Copy()
|
||||||
|
block.accessList = &balCopy
|
||||||
|
}
|
||||||
for i := range body.Uncles {
|
for i := range body.Uncles {
|
||||||
block.uncles[i] = CopyHeader(body.Uncles[i])
|
block.uncles[i] = CopyHeader(body.Uncles[i])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||||
|
BALHash *common.Hash `json:"balHash" rlp:"-"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
var enc Header
|
var enc Header
|
||||||
|
|
@ -61,6 +62,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
|
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
|
||||||
enc.ParentBeaconRoot = h.ParentBeaconRoot
|
enc.ParentBeaconRoot = h.ParentBeaconRoot
|
||||||
enc.RequestsHash = h.RequestsHash
|
enc.RequestsHash = h.RequestsHash
|
||||||
|
enc.BALHash = h.BALHash
|
||||||
enc.Hash = h.Hash()
|
enc.Hash = h.Hash()
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +91,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||||
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||||
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
RequestsHash *common.Hash `json:"requestsHash" rlp:"optional"`
|
||||||
|
BALHash *common.Hash `json:"balHash" rlp:"-"`
|
||||||
}
|
}
|
||||||
var dec Header
|
var dec Header
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -169,5 +172,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
if dec.RequestsHash != nil {
|
if dec.RequestsHash != nil {
|
||||||
h.RequestsHash = dec.RequestsHash
|
h.RequestsHash = dec.RequestsHash
|
||||||
}
|
}
|
||||||
|
if dec.BALHash != nil {
|
||||||
|
h.BALHash = dec.BALHash
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
core/types/testdata/22615532_block_access_list_with_reads_eip7928.ssz
vendored
Normal file
BIN
core/types/testdata/22615532_block_access_list_with_reads_eip7928.ssz
vendored
Normal file
Binary file not shown.
|
|
@ -76,6 +76,7 @@ type TxContext struct {
|
||||||
BlobHashes []common.Hash // Provides information for BLOBHASH
|
BlobHashes []common.Hash // Provides information for BLOBHASH
|
||||||
BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
|
BlobFeeCap *big.Int // Is used to zero the blobbasefee if NoBaseFee is set
|
||||||
AccessEvents *state.AccessEvents // Capture all state accesses for this tx
|
AccessEvents *state.AccessEvents // Capture all state accesses for this tx
|
||||||
|
Index uint64 // the index of the transaction within the block being executed (0 if executing a standalone call)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EVM is the Ethereum Virtual Machine base object and provides
|
// EVM is the Ethereum Virtual Machine base object and provides
|
||||||
|
|
@ -461,7 +462,9 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
|
||||||
// - the storage is non-empty
|
// - the storage is non-empty
|
||||||
contractHash := evm.StateDB.GetCodeHash(address)
|
contractHash := evm.StateDB.GetCodeHash(address)
|
||||||
storageRoot := evm.StateDB.GetStorageRoot(address)
|
storageRoot := evm.StateDB.GetStorageRoot(address)
|
||||||
if evm.StateDB.GetNonce(address) != 0 ||
|
targetNonce := evm.StateDB.GetNonce(address)
|
||||||
|
|
||||||
|
if targetNonce != 0 ||
|
||||||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||||
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
|
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
|
|
||||||
|
|
@ -681,7 +681,7 @@ func TestCreate2Addresses(t *testing.T) {
|
||||||
stack.push(big.NewInt(0)) // memstart
|
stack.push(big.NewInt(0)) // memstart
|
||||||
stack.push(big.NewInt(0)) // value
|
stack.push(big.NewInt(0)) // value
|
||||||
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
|
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
|
||||||
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
|
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.PrettyPrint())
|
||||||
*/
|
*/
|
||||||
expected := common.BytesToAddress(common.FromHex(tt.expected))
|
expected := common.BytesToAddress(common.FromHex(tt.expected))
|
||||||
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
|
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,12 @@ type StateDB interface {
|
||||||
|
|
||||||
Witness() *stateless.Witness
|
Witness() *stateless.Witness
|
||||||
|
|
||||||
|
//BlockAccessList() *types.BlockAccessList
|
||||||
|
|
||||||
AccessEvents() *state.AccessEvents
|
AccessEvents() *state.AccessEvents
|
||||||
|
|
||||||
|
TxIndex() int
|
||||||
|
|
||||||
// Finalise must be invoked at the end of a transaction
|
// Finalise must be invoked at the end of a transaction
|
||||||
Finalise(bool)
|
Finalise(bool)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ type Config struct {
|
||||||
ExtraEips []int // Additional EIPS that are to be enabled
|
ExtraEips []int // Additional EIPS that are to be enabled
|
||||||
|
|
||||||
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
|
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
|
||||||
|
BALConstruction bool // Generate BAL for executed blocks (testing purposes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
|
||||||
switch {
|
switch {
|
||||||
case rules.IsVerkle:
|
case rules.IsVerkle:
|
||||||
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
|
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
|
||||||
|
case rules.IsGlamsterdam:
|
||||||
|
return newPragueInstructionSet(), errors.New("glamsterdam-fork not defined yet")
|
||||||
case rules.IsOsaka:
|
case rules.IsOsaka:
|
||||||
return newOsakaInstructionSet(), nil
|
return newOsakaInstructionSet(), nil
|
||||||
case rules.IsPrague:
|
case rules.IsPrague:
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,7 @@ func TestColdAccountAccessCost(t *testing.T) {
|
||||||
Tracer: &tracing.Hooks{
|
Tracer: &tracing.Hooks{
|
||||||
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
// Uncomment to investigate failures:
|
// Uncomment to investigate failures:
|
||||||
//t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)
|
//t.Logf("%d: %v %d", step, vm.OpCode(op).PrettyPrint(), cost)
|
||||||
if step == tc.step {
|
if step == tc.step {
|
||||||
have = cost
|
have = cost
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,14 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
|
@ -443,3 +446,29 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
|
||||||
}
|
}
|
||||||
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
|
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *DebugAPI) GetBlockAccessList(number rpc.BlockNumberOrHash) (*bal.BlockAccessList, error) {
|
||||||
|
var block *types.Block
|
||||||
|
if num := number.BlockNumber; num != nil {
|
||||||
|
block = api.eth.blockchain.GetBlockByNumber(uint64(num.Int64()))
|
||||||
|
} else if hash := number.BlockHash; hash != nil {
|
||||||
|
block = api.eth.blockchain.GetBlockByHash(*hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
return nil, fmt.Errorf("block not found")
|
||||||
|
}
|
||||||
|
return block.Body().AccessList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *DebugAPI) GetEncodedAccessList(number rpc.BlockNumberOrHash) ([]byte, error) {
|
||||||
|
bal, err := api.GetBlockAccessList(number)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var enc bytes.Buffer
|
||||||
|
if err = bal.EncodeRLP(&enc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return enc.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var buildBAL bool
|
||||||
|
if config.BuildBAL != nil {
|
||||||
|
buildBAL = *config.BuildBAL
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
options = &core.BlockChainConfig{
|
options = &core.BlockChainConfig{
|
||||||
TrieCleanLimit: config.TrieCleanCache,
|
TrieCleanLimit: config.TrieCleanCache,
|
||||||
|
|
@ -235,6 +239,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),
|
TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)),
|
||||||
VmConfig: vm.Config{
|
VmConfig: vm.Config{
|
||||||
EnablePreimageRecording: config.EnablePreimageRecording,
|
EnablePreimageRecording: config.EnablePreimageRecording,
|
||||||
|
BALConstruction: buildBAL,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -748,7 +748,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
||||||
return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil
|
return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil
|
||||||
}
|
}
|
||||||
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number())
|
log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number())
|
||||||
proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness)
|
proofs, err := api.eth.BlockChain().InsertBlockWithoutSetHead(block, witness, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("NewPayload: inserting block failed", "error", err)
|
log.Warn("NewPayload: inserting block failed", "error", err)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,8 @@ type Config struct {
|
||||||
|
|
||||||
// OverrideVerkle (TODO: remove after the fork)
|
// OverrideVerkle (TODO: remove after the fork)
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
BuildBAL *bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -468,6 +468,11 @@ web3._extend({
|
||||||
call: 'debug_getTrieFlushInterval',
|
call: 'debug_getTrieFlushInterval',
|
||||||
params: 0
|
params: 0
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'getBlockAccessList',
|
||||||
|
call: 'debug_getBlockAccessList',
|
||||||
|
params: 1
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
properties: []
|
properties: []
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -410,6 +410,7 @@ type ChainConfig struct {
|
||||||
CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun)
|
CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun)
|
||||||
PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
|
PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
|
||||||
OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka)
|
OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka)
|
||||||
|
GlamsterdamTime *uint64 `json:"glamsterdamTime,omitempty"` // Glamsterdam switch time (nil = no fork, 0 = already on glamsterdam)
|
||||||
VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle)
|
VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle)
|
||||||
|
|
||||||
// TerminalTotalDifficulty is the amount of total difficulty reached by
|
// TerminalTotalDifficulty is the amount of total difficulty reached by
|
||||||
|
|
@ -649,6 +650,11 @@ func (c *ChainConfig) IsOsaka(num *big.Int, time uint64) bool {
|
||||||
return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time)
|
return c.IsLondon(num) && isTimestampForked(c.OsakaTime, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsGlamsterdam returns whether time is either equal to the Glamsterdam fork time or greater.
|
||||||
|
func (c *ChainConfig) IsGlamsterdam(num *big.Int, time uint64) bool {
|
||||||
|
return c.IsLondon(num) && isTimestampForked(c.GlamsterdamTime, time)
|
||||||
|
}
|
||||||
|
|
||||||
// IsVerkle returns whether time is either equal to the Verkle fork time or greater.
|
// IsVerkle returns whether time is either equal to the Verkle fork time or greater.
|
||||||
func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
|
func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
|
||||||
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
|
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
|
||||||
|
|
@ -1067,7 +1073,7 @@ type Rules struct {
|
||||||
IsEIP2929, IsEIP4762 bool
|
IsEIP2929, IsEIP4762 bool
|
||||||
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
||||||
IsBerlin, IsLondon bool
|
IsBerlin, IsLondon bool
|
||||||
IsMerge, IsShanghai, IsCancun, IsPrague, IsOsaka bool
|
IsMerge, IsShanghai, IsCancun, IsPrague, IsOsaka, IsGlamsterdam bool
|
||||||
IsVerkle bool
|
IsVerkle bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1098,6 +1104,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
|
||||||
IsCancun: isMerge && c.IsCancun(num, timestamp),
|
IsCancun: isMerge && c.IsCancun(num, timestamp),
|
||||||
IsPrague: isMerge && c.IsPrague(num, timestamp),
|
IsPrague: isMerge && c.IsPrague(num, timestamp),
|
||||||
IsOsaka: isMerge && c.IsOsaka(num, timestamp),
|
IsOsaka: isMerge && c.IsOsaka(num, timestamp),
|
||||||
|
IsGlamsterdam: isMerge && c.IsGlamsterdam(num, timestamp),
|
||||||
IsVerkle: isVerkle,
|
IsVerkle: isVerkle,
|
||||||
IsEIP4762: isVerkle,
|
IsEIP4762: isVerkle,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
|
||||||
}
|
}
|
||||||
for _, snapshot := range snapshotConf {
|
for _, snapshot := range snapshotConf {
|
||||||
for _, dbscheme := range dbschemeConf {
|
for _, dbscheme := range dbschemeConf {
|
||||||
if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil {
|
if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, true, nil, nil)); err != nil {
|
||||||
t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err)
|
t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ type btHeaderMarshaling struct {
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
ExcessBlobGas *math.HexOrDecimal64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) {
|
func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, bal bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) {
|
||||||
config, ok := Forks[t.json.Network]
|
config, ok := Forks[t.json.Network]
|
||||||
if !ok {
|
if !ok {
|
||||||
return UnsupportedForkError{t.json.Network}
|
return UnsupportedForkError{t.json.Network}
|
||||||
|
|
@ -159,6 +159,7 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t
|
||||||
VmConfig: vm.Config{
|
VmConfig: vm.Config{
|
||||||
Tracer: tracer,
|
Tracer: tracer,
|
||||||
StatelessSelfValidation: witness,
|
StatelessSelfValidation: witness,
|
||||||
|
BALConstruction: bal,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if snapshotter {
|
if snapshotter {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue