mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 05:06:43 +00:00
wip: tracer-based BAL creation
This commit is contained in:
parent
1cfe624d03
commit
961f517a26
19 changed files with 901 additions and 412 deletions
|
|
@ -155,6 +155,7 @@ var (
|
||||||
utils.BeaconGenesisTimeFlag,
|
utils.BeaconGenesisTimeFlag,
|
||||||
utils.BeaconCheckpointFlag,
|
utils.BeaconCheckpointFlag,
|
||||||
utils.BeaconCheckpointFileFlag,
|
utils.BeaconCheckpointFileFlag,
|
||||||
|
utils.ExperimentalBALFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
|
|
|
||||||
|
|
@ -997,6 +997,14 @@ 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
|
||||||
|
|
||||||
|
ExperimentalBALFlag = &cli.BoolFlag{
|
||||||
|
Name: "experimental.bal",
|
||||||
|
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.",
|
||||||
|
Category: flags.MiscCategory,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -1899,6 +1907,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
|
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg.ExperimentalBAL = ctx.Bool(ExperimentalBALFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBeaconLightConfig constructs a beacon light client config based on the
|
// 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
|
||||||
}
|
}
|
||||||
options.VmConfig = vmcfg
|
options.VmConfig = vmcfg
|
||||||
|
|
||||||
|
options.EnableBALForTesting = ctx.Bool(ExperimentalBALFlag.Name)
|
||||||
chain, err := core.NewBlockChain(chainDb, gspec, engine, options)
|
chain, err := core.NewBlockChain(chainDb, gspec, engine, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Can't create BlockChain: %v", err)
|
Fatalf("Can't create BlockChain: %v", err)
|
||||||
|
|
|
||||||
109
core/block_access_list_creation.go
Normal file
109
core/block_access_list_creation.go
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockAccessListTracer constructs an EIP-7928 block access list from the
|
||||||
|
// execution of a block
|
||||||
|
type BlockAccessListTracer struct {
|
||||||
|
// this is a set of access lists for each call scope. the overall block access lists
|
||||||
|
// is accrued at index 0, while the access lists of various nested execution
|
||||||
|
// scopes are in the proceeding indices.
|
||||||
|
// When an execution scope terminates in a non-reverting fashion, the changes are
|
||||||
|
// merged into the access list of the parent scope.
|
||||||
|
callAccessLists []*bal.ConstructionBlockAccessList
|
||||||
|
txIdx uint16
|
||||||
|
|
||||||
|
// if non-nil, it's the address of the account which just self-destructed.
|
||||||
|
// reset at the end of the call-scope which self-destructed.
|
||||||
|
selfdestructedAccount *common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockAccessListTracer returns an BlockAccessListTracer and a set of hooks
|
||||||
|
func NewBlockAccessListTracer() (*BlockAccessListTracer, *tracing.Hooks) {
|
||||||
|
balTracer := &BlockAccessListTracer{
|
||||||
|
callAccessLists: []*bal.ConstructionBlockAccessList{bal.NewConstructionBlockAccessList()},
|
||||||
|
txIdx: 0,
|
||||||
|
}
|
||||||
|
hooks := &tracing.Hooks{
|
||||||
|
OnTxEnd: balTracer.TxEndHook,
|
||||||
|
OnTxStart: balTracer.TxStartHook,
|
||||||
|
OnEnter: balTracer.OnEnter,
|
||||||
|
OnExit: balTracer.OnExit,
|
||||||
|
OnCodeChangeV2: balTracer.OnCodeChange,
|
||||||
|
OnBalanceChange: balTracer.OnBalanceChange,
|
||||||
|
OnNonceChange: balTracer.OnNonceChange,
|
||||||
|
OnStorageChange: balTracer.OnStorageChange,
|
||||||
|
OnColdAccountRead: balTracer.OnColdAccountRead,
|
||||||
|
OnColdStorageRead: balTracer.OnColdStorageRead,
|
||||||
|
}
|
||||||
|
return balTracer, hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessList returns the constructed access list
|
||||||
|
func (a *BlockAccessListTracer) AccessList() *bal.ConstructionBlockAccessList {
|
||||||
|
return a.callAccessLists[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) {
|
||||||
|
a.txIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) TxStartHook(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
|
if a.txIdx == 0 {
|
||||||
|
a.txIdx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
a.callAccessLists = append(a.callAccessLists, bal.NewConstructionBlockAccessList())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
|
// any self-destructed accounts must have been created in the same transaction
|
||||||
|
// so there is no difference between the pre/post tx state of those accounts
|
||||||
|
if a.selfdestructedAccount != nil {
|
||||||
|
delete(a.callAccessLists[len(a.callAccessLists)-1].Accounts, *a.selfdestructedAccount)
|
||||||
|
}
|
||||||
|
if !reverted {
|
||||||
|
parentAccessList := a.callAccessLists[len(a.callAccessLists)-2]
|
||||||
|
scopeAccessList := a.callAccessLists[len(a.callAccessLists)-1]
|
||||||
|
parentAccessList.Merge(scopeAccessList)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.callAccessLists = a.callAccessLists[:len(a.callAccessLists)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason tracing.CodeChangeReason) {
|
||||||
|
if reason == tracing.CodeChangeSelfDestruct {
|
||||||
|
a.selfdestructedAccount = &addr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.callAccessLists[len(a.callAccessLists)-1].CodeChange(addr, uint16(a.txIdx), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnBalanceChange(addr common.Address, prevBalance, newBalance *big.Int, _ tracing.BalanceChangeReason) {
|
||||||
|
a.callAccessLists[len(a.callAccessLists)-1].BalanceChange(a.txIdx, addr, new(uint256.Int).SetBytes(newBalance.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnNonceChange(addr common.Address, prev uint64, new uint64) {
|
||||||
|
a.callAccessLists[len(a.callAccessLists)-1].NonceChange(addr, a.txIdx, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnColdStorageRead(addr common.Address, key common.Hash) {
|
||||||
|
a.callAccessLists[len(a.callAccessLists)-1].StorageRead(addr, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnColdAccountRead(addr common.Address) {
|
||||||
|
a.callAccessLists[len(a.callAccessLists)-1].AccountRead(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BlockAccessListTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) {
|
||||||
|
a.callAccessLists[len(a.callAccessLists)-1].StorageWrite(a.txIdx, addr, slot, new)
|
||||||
|
}
|
||||||
|
|
@ -111,6 +111,31 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// block access lists must be present after the Amsterdam hard fork
|
||||||
|
if v.config.IsAmsterdam(block.Number(), block.Time()) {
|
||||||
|
if block.Body().AccessList == nil {
|
||||||
|
return fmt.Errorf("access list not present in block body")
|
||||||
|
} else if block.Header().BlockAccessListHash == nil {
|
||||||
|
return fmt.Errorf("access list hash not present in block header")
|
||||||
|
} else if *block.Header().BlockAccessListHash != block.Body().AccessList.Hash() {
|
||||||
|
return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.Body().AccessList.Hash(), *block.Header().BlockAccessListHash)
|
||||||
|
}
|
||||||
|
} else if !v.bc.cfg.EnableBALForTesting {
|
||||||
|
// if --experimental.bal is not enabled, block headers cannot have access list hash and bodies cannot have access lists.
|
||||||
|
if block.Body().AccessList != nil {
|
||||||
|
return fmt.Errorf("access list not allowed in block body if not in amsterdam or --experimental.bal is set")
|
||||||
|
} else if block.Header().BlockAccessListHash != nil {
|
||||||
|
return fmt.Errorf("access list hash in block header not allowed when --experimental.bal is set")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if --experimental.bal is enabled, the BAL hash is not allowed in the header.
|
||||||
|
// this is in order that Geth can import pre-existing chains augmented with BALs
|
||||||
|
// and not have a hash mismatch.
|
||||||
|
if block.Header().BlockAccessListHash != nil {
|
||||||
|
return fmt.Errorf("access list hash in block header not allowed pre-amsterdam")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ancestor block must be known.
|
// Ancestor block must be known.
|
||||||
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
|
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
|
||||||
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,11 @@ type BlockChainConfig struct {
|
||||||
// If the value is -1, indexing is disabled.
|
// If the value is -1, indexing is disabled.
|
||||||
TxLookupLimit int64
|
TxLookupLimit int64
|
||||||
|
|
||||||
|
// If EnableBALForTesting is enabled, block access lists will be created as part of
|
||||||
|
// block processing and embedded in the block body. The block access list hash will
|
||||||
|
// not be set in the header.
|
||||||
|
EnableBALForTesting bool
|
||||||
|
|
||||||
// StateSizeTracking indicates whether the state size tracking is enabled.
|
// StateSizeTracking indicates whether the state size tracking is enabled.
|
||||||
StateSizeTracking bool
|
StateSizeTracking bool
|
||||||
}
|
}
|
||||||
|
|
@ -1904,9 +1909,17 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// construct or verify block access lists if BALs are enabled and
|
||||||
|
// we are post-selfdestruct removal fork.
|
||||||
|
enableBAL := (bc.cfg.EnableBALForTesting && bc.chainConfig.IsCancun(block.Number(), block.Time())) || bc.chainConfig.IsAmsterdam(block.Number(), block.Time())
|
||||||
|
blockHasAccessList := block.Body().AccessList != nil
|
||||||
|
makeBAL := enableBAL && !blockHasAccessList
|
||||||
|
validateBAL := enableBAL && blockHasAccessList
|
||||||
|
|
||||||
// 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, validateBAL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
@ -1978,7 +1991,7 @@ func (bpr *blockProcessingResult) Witness() *stateless.Witness {
|
||||||
|
|
||||||
// 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, constructBALForTesting bool, validateBAL bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
|
|
@ -2074,6 +2087,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var balTracer *BlockAccessListTracer
|
||||||
|
// Process block using the parent state as reference point
|
||||||
|
if constructBALForTesting {
|
||||||
|
balTracer, bc.cfg.VmConfig.Tracer = NewBlockAccessListTracer()
|
||||||
|
}
|
||||||
|
|
||||||
// Process block using the parent state as reference point
|
// Process block using the parent state as reference point
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig)
|
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
|
||||||
}
|
}
|
||||||
vtime := time.Since(vstart)
|
vtime := time.Since(vstart)
|
||||||
|
|
||||||
|
if constructBALForTesting {
|
||||||
|
// 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 = balTracer.AccessList().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
|
||||||
|
|
|
||||||
|
|
@ -67,12 +67,13 @@ type Genesis struct {
|
||||||
|
|
||||||
// These fields are used for consensus tests. Please don't use them
|
// These fields are used for consensus tests. Please don't use them
|
||||||
// in actual genesis blocks.
|
// in actual genesis blocks.
|
||||||
Number uint64 `json:"number"`
|
Number uint64 `json:"number"`
|
||||||
GasUsed uint64 `json:"gasUsed"`
|
GasUsed uint64 `json:"gasUsed"`
|
||||||
ParentHash common.Hash `json:"parentHash"`
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
|
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
|
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
|
||||||
|
BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` // EIP-7928
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy copies the genesis.
|
// copy copies the genesis.
|
||||||
|
|
@ -122,6 +123,7 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
||||||
genesis.BaseFee = genesisHeader.BaseFee
|
genesis.BaseFee = genesisHeader.BaseFee
|
||||||
genesis.ExcessBlobGas = genesisHeader.ExcessBlobGas
|
genesis.ExcessBlobGas = genesisHeader.ExcessBlobGas
|
||||||
genesis.BlobGasUsed = genesisHeader.BlobGasUsed
|
genesis.BlobGasUsed = genesisHeader.BlobGasUsed
|
||||||
|
genesis.BlockAccessListHash = genesisHeader.BlockAccessListHash
|
||||||
|
|
||||||
return &genesis, nil
|
return &genesis, nil
|
||||||
}
|
}
|
||||||
|
|
@ -469,18 +471,19 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||||
// toBlockWithRoot constructs the genesis block with the given genesis state root.
|
// toBlockWithRoot constructs the genesis block with the given genesis state root.
|
||||||
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||||
head := &types.Header{
|
head := &types.Header{
|
||||||
Number: new(big.Int).SetUint64(g.Number),
|
Number: new(big.Int).SetUint64(g.Number),
|
||||||
Nonce: types.EncodeNonce(g.Nonce),
|
Nonce: types.EncodeNonce(g.Nonce),
|
||||||
Time: g.Timestamp,
|
Time: g.Timestamp,
|
||||||
ParentHash: g.ParentHash,
|
ParentHash: g.ParentHash,
|
||||||
Extra: g.ExtraData,
|
Extra: g.ExtraData,
|
||||||
GasLimit: g.GasLimit,
|
GasLimit: g.GasLimit,
|
||||||
GasUsed: g.GasUsed,
|
GasUsed: g.GasUsed,
|
||||||
BaseFee: g.BaseFee,
|
BaseFee: g.BaseFee,
|
||||||
Difficulty: g.Difficulty,
|
Difficulty: g.Difficulty,
|
||||||
MixDigest: g.Mixhash,
|
MixDigest: g.Mixhash,
|
||||||
Coinbase: g.Coinbase,
|
Coinbase: g.Coinbase,
|
||||||
Root: root,
|
BlockAccessListHash: g.BlockAccessListHash,
|
||||||
|
Root: root,
|
||||||
}
|
}
|
||||||
if g.GasLimit == 0 {
|
if g.GasLimit == 0 {
|
||||||
head.GasLimit = params.GenesisGasLimit
|
head.GasLimit = params.GenesisGasLimit
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,12 @@ type (
|
||||||
// StorageChangeHook is called when the storage of an account changes.
|
// StorageChangeHook is called when the storage of an account changes.
|
||||||
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
||||||
|
|
||||||
|
// ColdStorageReadHook is called before a previously-unread storage slot is read.
|
||||||
|
ColdStorageReadHook = func(common.Address, common.Hash)
|
||||||
|
|
||||||
|
// ColdAccountReadHook is called before an previously-unread account is read.
|
||||||
|
ColdAccountReadHook = func(address common.Address)
|
||||||
|
|
||||||
// LogHook is called when a log is emitted.
|
// LogHook is called when a log is emitted.
|
||||||
LogHook = func(log *types.Log)
|
LogHook = func(log *types.Log)
|
||||||
|
|
||||||
|
|
@ -209,6 +215,7 @@ type Hooks struct {
|
||||||
OnSystemCallStart OnSystemCallStartHook
|
OnSystemCallStart OnSystemCallStartHook
|
||||||
OnSystemCallStartV2 OnSystemCallStartHookV2
|
OnSystemCallStartV2 OnSystemCallStartHookV2
|
||||||
OnSystemCallEnd OnSystemCallEndHook
|
OnSystemCallEnd OnSystemCallEndHook
|
||||||
|
|
||||||
// State events
|
// State events
|
||||||
OnBalanceChange BalanceChangeHook
|
OnBalanceChange BalanceChangeHook
|
||||||
OnNonceChange NonceChangeHook
|
OnNonceChange NonceChangeHook
|
||||||
|
|
@ -217,6 +224,9 @@ type Hooks struct {
|
||||||
OnCodeChangeV2 CodeChangeHookV2
|
OnCodeChangeV2 CodeChangeHookV2
|
||||||
OnStorageChange StorageChangeHook
|
OnStorageChange StorageChangeHook
|
||||||
OnLog LogHook
|
OnLog LogHook
|
||||||
|
//State read events
|
||||||
|
OnColdStorageRead ColdStorageReadHook
|
||||||
|
OnColdAccountRead ColdAccountReadHook
|
||||||
// Block hash read
|
// Block hash read
|
||||||
OnBlockHashRead BlockHashReadHook
|
OnBlockHashRead BlockHashReadHook
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package bal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"maps"
|
"maps"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -27,8 +28,8 @@ import (
|
||||||
// CodeChange contains the runtime bytecode deployed at an address and the
|
// CodeChange contains the runtime bytecode deployed at an address and the
|
||||||
// transaction index where the deployment took place.
|
// transaction index where the deployment took place.
|
||||||
type CodeChange struct {
|
type CodeChange struct {
|
||||||
TxIndex uint16
|
TxIdx uint16
|
||||||
Code []byte `json:"code,omitempty"`
|
Code []byte `json:"code,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstructionAccountAccess contains post-block account state for mutations as well as
|
// ConstructionAccountAccess contains post-block account state for mutations as well as
|
||||||
|
|
@ -38,26 +39,24 @@ 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
|
CodeChanges map[uint16]CodeChange
|
||||||
// the block.
|
|
||||||
CodeChange *CodeChange `json:"codeChange,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstructionAccountAccess initializes the account access object.
|
// NewConstructionAccountAccess initializes the account access object.
|
||||||
|
|
@ -67,6 +66,7 @@ func NewConstructionAccountAccess() *ConstructionAccountAccess {
|
||||||
StorageReads: make(map[common.Hash]struct{}),
|
StorageReads: make(map[common.Hash]struct{}),
|
||||||
BalanceChanges: make(map[uint16]*uint256.Int),
|
BalanceChanges: make(map[uint16]*uint256.Int),
|
||||||
NonceChanges: make(map[uint16]uint64),
|
NonceChanges: make(map[uint16]uint64),
|
||||||
|
CodeChanges: make(map[uint16]CodeChange),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,83 +77,122 @@ type ConstructionBlockAccessList struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstructionBlockAccessList instantiates an empty access list.
|
// NewConstructionBlockAccessList instantiates an empty access list.
|
||||||
func NewConstructionBlockAccessList() ConstructionBlockAccessList {
|
func NewConstructionBlockAccessList() *ConstructionBlockAccessList {
|
||||||
return ConstructionBlockAccessList{
|
return &ConstructionBlockAccessList{
|
||||||
Accounts: make(map[common.Address]*ConstructionAccountAccess),
|
Accounts: make(map[common.Address]*ConstructionAccountAccess),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 _, ok := c.Accounts[addr]; !ok {
|
||||||
b.Accounts[addr] = NewConstructionAccountAccess()
|
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 _, ok := c.Accounts[address]; !ok {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
if _, ok := b.Accounts[address].StorageWrites[key]; ok {
|
if _, ok := c.Accounts[address].StorageWrites[key]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.Accounts[address].StorageReads[key] = struct{}{}
|
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 _, ok := c.Accounts[address]; !ok {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
if _, ok := b.Accounts[address].StorageWrites[key]; !ok {
|
if _, ok := c.Accounts[address].StorageWrites[key]; !ok {
|
||||||
b.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash)
|
c.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash)
|
||||||
}
|
}
|
||||||
b.Accounts[address].StorageWrites[key][txIdx] = value
|
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 _, ok := c.Accounts[address]; !ok {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
b.Accounts[address].CodeChange = &CodeChange{
|
c.Accounts[address].CodeChanges[txIndex] = CodeChange{
|
||||||
TxIndex: txIndex,
|
TxIdx: txIndex,
|
||||||
Code: bytes.Clone(code),
|
Code: bytes.Clone(code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 _, ok := c.Accounts[address]; !ok {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
b.Accounts[address].NonceChanges[txIdx] = postNonce
|
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 _, ok := c.Accounts[address]; !ok {
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
c.Accounts[address] = NewConstructionAccountAccess()
|
||||||
}
|
}
|
||||||
b.Accounts[address].BalanceChanges[txIdx] = balance.Clone()
|
c.Accounts[address].BalanceChanges[txIdx] = balance.Clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrettyPrint returns a human-readable representation of the access list
|
func (c *ConstructionBlockAccessList) Delete(address common.Address) {
|
||||||
func (b *ConstructionBlockAccessList) PrettyPrint() string {
|
delete(c.Accounts, address)
|
||||||
enc := b.toEncodingObj()
|
}
|
||||||
return enc.PrettyPrint()
|
|
||||||
|
func mergeStorageWrites(cur, next map[common.Hash]map[uint16]common.Hash) map[common.Hash]map[uint16]common.Hash {
|
||||||
|
for slot, _ := range next {
|
||||||
|
if _, ok := cur[slot]; !ok {
|
||||||
|
cur[slot] = next[slot]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, val := range next[slot] {
|
||||||
|
cur[slot][idx] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConstructionBlockAccessList) Merge(next *ConstructionBlockAccessList) {
|
||||||
|
for addr, accountAccess := range next.Accounts {
|
||||||
|
if _, ok := c.Accounts[addr]; !ok {
|
||||||
|
c.Accounts[addr] = accountAccess
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the entries from 'next' into 'c' overwriting 'c' entries with
|
||||||
|
// 'next entries when the bal index matches.
|
||||||
|
next.Accounts[addr].StorageWrites = mergeStorageWrites(next.Accounts[addr].StorageWrites, c.Accounts[addr].StorageWrites)
|
||||||
|
|
||||||
|
for storageRead, _ := range c.Accounts[addr].StorageReads {
|
||||||
|
next.Accounts[addr].StorageReads[storageRead] = struct{}{}
|
||||||
|
}
|
||||||
|
for idx, nonce := range next.Accounts[addr].NonceChanges {
|
||||||
|
c.Accounts[addr].NonceChanges[idx] = nonce
|
||||||
|
}
|
||||||
|
for idx, code := range next.Accounts[addr].CodeChanges {
|
||||||
|
c.Accounts[addr].CodeChanges[idx] = code
|
||||||
|
}
|
||||||
|
for idx, balance := range next.Accounts[addr].BalanceChanges {
|
||||||
|
c.Accounts[addr].BalanceChanges[idx] = balance
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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))
|
||||||
|
|
@ -170,13 +209,221 @@ func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
||||||
aaCopy.BalanceChanges = balances
|
aaCopy.BalanceChanges = balances
|
||||||
aaCopy.NonceChanges = maps.Clone(aa.NonceChanges)
|
aaCopy.NonceChanges = maps.Clone(aa.NonceChanges)
|
||||||
|
|
||||||
if aa.CodeChange != nil {
|
codeChangesCopy := make(map[uint16]CodeChange)
|
||||||
aaCopy.CodeChange = &CodeChange{
|
for idx, codeChange := range aa.CodeChanges {
|
||||||
TxIndex: aa.CodeChange.TxIndex,
|
codeChangesCopy[idx] = CodeChange{
|
||||||
Code: bytes.Clone(aa.CodeChange.Code),
|
TxIdx: idx,
|
||||||
|
Code: bytes.Clone(codeChange.Code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.Accounts[addr] = &aaCopy
|
res.Accounts[addr] = &aaCopy
|
||||||
}
|
}
|
||||||
return &res
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyDiff includes the given changes in the block access list at the given index.
|
||||||
|
func (c *ConstructionBlockAccessList) ApplyDiff(i uint, diff *StateDiff) {
|
||||||
|
idx := uint16(i)
|
||||||
|
for addr, acctDiff := range diff.Mutations {
|
||||||
|
if _, ok := c.Accounts[addr]; !ok {
|
||||||
|
c.Accounts[addr] = &ConstructionAccountAccess{}
|
||||||
|
}
|
||||||
|
if acctDiff.Balance != nil {
|
||||||
|
if c.Accounts[addr].BalanceChanges == nil {
|
||||||
|
c.Accounts[addr].BalanceChanges = make(map[uint16]*uint256.Int)
|
||||||
|
}
|
||||||
|
c.Accounts[addr].BalanceChanges[idx] = acctDiff.Balance
|
||||||
|
}
|
||||||
|
if acctDiff.Nonce != nil {
|
||||||
|
if c.Accounts[addr].NonceChanges == nil {
|
||||||
|
c.Accounts[addr].NonceChanges = make(map[uint16]uint64)
|
||||||
|
}
|
||||||
|
c.Accounts[addr].NonceChanges[idx] = *acctDiff.Nonce
|
||||||
|
}
|
||||||
|
if acctDiff.Code != nil {
|
||||||
|
if c.Accounts[addr].CodeChanges == nil {
|
||||||
|
c.Accounts[addr].CodeChanges = make(map[uint16]CodeChange)
|
||||||
|
}
|
||||||
|
// TODO: make the CodeChanges value just be []byte
|
||||||
|
c.Accounts[addr].CodeChanges[idx] = CodeChange{idx, acctDiff.Code}
|
||||||
|
}
|
||||||
|
if acctDiff.StorageWrites != nil {
|
||||||
|
if c.Accounts[addr].StorageWrites == nil {
|
||||||
|
// TODO: can we instantiate all these maps in the constructor?
|
||||||
|
c.Accounts[addr].StorageWrites = make(map[common.Hash]map[uint16]common.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
for slot, val := range acctDiff.StorageWrites {
|
||||||
|
if c.Accounts[addr].StorageWrites[slot] == nil {
|
||||||
|
c.Accounts[addr].StorageWrites[slot] = make(map[uint16]common.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Accounts[addr].StorageWrites[slot][idx] = val
|
||||||
|
|
||||||
|
delete(c.Accounts[addr].StorageReads, slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StateDiff struct {
|
||||||
|
Mutations map[common.Address]*AccountState `json:"Mutations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StateAccesses map[common.Address]map[common.Hash]struct{}
|
||||||
|
|
||||||
|
type AccountState struct {
|
||||||
|
Balance *uint256.Int `json:"Balance,omitempty"`
|
||||||
|
Nonce *uint64 `json:"Nonce,omitempty"`
|
||||||
|
Code ContractCode `json:"Code,omitempty"`
|
||||||
|
StorageWrites map[common.Hash]common.Hash `json:"StorageWrites,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AccountState) String() string {
|
||||||
|
var res bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&res)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
enc.Encode(a)
|
||||||
|
return res.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the changes of a future AccountState into the caller, resulting in the
|
||||||
|
// combined state changes through next.
|
||||||
|
func (a *AccountState) Merge(next *AccountState) {
|
||||||
|
if next.Balance != nil {
|
||||||
|
a.Balance = next.Balance
|
||||||
|
}
|
||||||
|
if next.Nonce != nil {
|
||||||
|
a.Nonce = next.Nonce
|
||||||
|
}
|
||||||
|
if next.Code != nil {
|
||||||
|
a.Code = next.Code
|
||||||
|
}
|
||||||
|
if next.StorageWrites != nil {
|
||||||
|
if a.StorageWrites == nil {
|
||||||
|
a.StorageWrites = maps.Clone(next.StorageWrites)
|
||||||
|
} else {
|
||||||
|
for key, val := range next.StorageWrites {
|
||||||
|
a.StorageWrites[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEmptyAccountState() *AccountState {
|
||||||
|
return &AccountState{
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AccountState) Eq(other *AccountState) bool {
|
||||||
|
if a.Balance != nil || other.Balance != nil {
|
||||||
|
if a.Balance == nil || other.Balance == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.Balance.Eq(other.Balance) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len(a.Code) != 0 || len(other.Code) != 0) && !bytes.Equal(a.Code, other.Code) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Nonce != nil || other.Nonce != nil {
|
||||||
|
if a.Nonce == nil || other.Nonce == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if *a.Nonce != *other.Nonce {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.StorageWrites != nil || other.StorageWrites != nil {
|
||||||
|
if a.StorageWrites == nil || other.StorageWrites == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !maps.Equal(a.StorageWrites, other.StorageWrites) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AccountState) Copy() *AccountState {
|
||||||
|
res := NewEmptyAccountState()
|
||||||
|
if a.Nonce != nil {
|
||||||
|
res.Nonce = new(uint64)
|
||||||
|
*res.Nonce = *a.Nonce
|
||||||
|
}
|
||||||
|
if a.Code != nil {
|
||||||
|
res.Code = bytes.Clone(a.Code)
|
||||||
|
}
|
||||||
|
if a.Balance != nil {
|
||||||
|
res.Balance = new(uint256.Int).Set(a.Balance)
|
||||||
|
}
|
||||||
|
if a.StorageWrites != nil {
|
||||||
|
res.StorageWrites = maps.Clone(a.StorageWrites)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDiff) String() string {
|
||||||
|
var res bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&res)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
enc.Encode(s)
|
||||||
|
return res.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges the state changes present in next into the caller. After,
|
||||||
|
// the state of the caller is the aggregate diff through next.
|
||||||
|
func (s *StateDiff) Merge(next *StateDiff) {
|
||||||
|
for account, diff := range next.Mutations {
|
||||||
|
if mut, ok := s.Mutations[account]; ok {
|
||||||
|
if diff.Balance != nil {
|
||||||
|
mut.Balance = diff.Balance
|
||||||
|
}
|
||||||
|
if diff.Code != nil {
|
||||||
|
mut.Code = diff.Code
|
||||||
|
}
|
||||||
|
if diff.Nonce != nil {
|
||||||
|
mut.Nonce = diff.Nonce
|
||||||
|
}
|
||||||
|
if len(diff.StorageWrites) > 0 {
|
||||||
|
if mut.StorageWrites == nil {
|
||||||
|
mut.StorageWrites = maps.Clone(diff.StorageWrites)
|
||||||
|
} else {
|
||||||
|
for key, val := range diff.StorageWrites {
|
||||||
|
mut.StorageWrites[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.Mutations[account] = diff.Copy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateDiff) Copy() *StateDiff {
|
||||||
|
res := &StateDiff{make(map[common.Address]*AccountState)}
|
||||||
|
for addr, accountDiff := range s.Mutations {
|
||||||
|
cpy := accountDiff.Copy()
|
||||||
|
res.Mutations[addr] = cpy
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a deep copy of the access list
|
||||||
|
func (e BlockAccessList) Copy() (res BlockAccessList) {
|
||||||
|
for _, accountAccess := range e {
|
||||||
|
res = append(res, accountAccess.Copy())
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ package bal
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -33,26 +33,57 @@ import (
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -out bal_encoding_rlp_generated.go -type BlockAccessList -decoder
|
//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -out bal_encoding_rlp_generated.go -type AccountAccess -decoder
|
||||||
|
|
||||||
// These are objects used as input for the access list encoding. They mirror
|
// These are objects used as input for the access list encoding. They mirror
|
||||||
// the spec format.
|
// the spec format.
|
||||||
|
|
||||||
// BlockAccessList is the encoding format of ConstructionBlockAccessList.
|
// BlockAccessList is the encoding format of ConstructionBlockAccessList.
|
||||||
type BlockAccessList struct {
|
type BlockAccessList []AccountAccess
|
||||||
Accesses []AccountAccess `ssz-max:"300000"`
|
|
||||||
|
func (e BlockAccessList) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
l := w.List()
|
||||||
|
for _, access := range e {
|
||||||
|
access.EncodeRLP(w)
|
||||||
|
}
|
||||||
|
w.ListEnd(l)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var access AccountAccess
|
||||||
|
if err := access.DecodeRLP(dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = append(*e, access)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BlockAccessList) String() string {
|
||||||
|
var res bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&res)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
// TODO: check error
|
||||||
|
enc.Encode(e)
|
||||||
|
return res.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// according to the spec or any code changes are contained which exceed protocol
|
// according to the spec or any code changes are contained which exceed protocol
|
||||||
// max code size.
|
// max code size.
|
||||||
func (e *BlockAccessList) Validate() error {
|
func (e BlockAccessList) Validate() error {
|
||||||
if !slices.IsSortedFunc(e.Accesses, func(a, b AccountAccess) int {
|
if !slices.IsSortedFunc(e, func(a, b AccountAccess) int {
|
||||||
return bytes.Compare(a.Address[:], b.Address[:])
|
return bytes.Compare(a.Address[:], b.Address[:])
|
||||||
}) {
|
}) {
|
||||||
return errors.New("block access list accounts not in lexicographic order")
|
return errors.New("block access list accounts not in lexicographic order")
|
||||||
}
|
}
|
||||||
for _, entry := range e.Accesses {
|
for _, entry := range e {
|
||||||
if err := entry.validate(); err != nil {
|
if err := entry.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -70,42 +101,32 @@ func (e *BlockAccessList) Hash() common.Hash {
|
||||||
// under reasonable conditions.
|
// under reasonable conditions.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
fmt.Printf("bal hash %x\n", enc.Bytes())
|
||||||
return crypto.Keccak256Hash(enc.Bytes())
|
return crypto.Keccak256Hash(enc.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeBalance encodes the provided balance into 16-bytes.
|
|
||||||
func encodeBalance(val *uint256.Int) [16]byte {
|
|
||||||
valBytes := val.Bytes()
|
|
||||||
if len(valBytes) > 16 {
|
|
||||||
panic("can't encode value that is greater than 16 bytes in size")
|
|
||||||
}
|
|
||||||
var enc [16]byte
|
|
||||||
copy(enc[16-len(valBytes):], valBytes[:])
|
|
||||||
return enc
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 `json:"txIndex"`
|
||||||
Balance [16]byte `ssz-size:"16"`
|
Balance *uint256.Int `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 `json:"txIndex"`
|
||||||
Nonce uint64 `ssz-size:"8"`
|
Nonce uint64 `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 `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 `json:"slot"`
|
||||||
Accesses []encodingStorageWrite `ssz-max:"300000"`
|
Accesses []encodingStorageWrite `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 +142,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 `json:"address,omitempty"` // 20-byte Ethereum address
|
||||||
StorageWrites []encodingSlotWrites `ssz-max:"300000"` // Storage changes (slot -> [tx_index -> new_value])
|
StorageChanges []encodingSlotWrites `json:"storageChanges,omitempty"` // Storage changes (slot -> [tx_index -> new_value])
|
||||||
StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys
|
StorageReads []common.Hash `json:"storageReads,omitempty"` // Read-only storage keys
|
||||||
BalanceChanges []encodingBalanceChange `ssz-max:"300000"` // Balance changes ([tx_index -> post_balance])
|
BalanceChanges []encodingBalanceChange `json:"balanceChanges,omitempty"` // Balance changes ([tx_index -> post_balance])
|
||||||
NonceChanges []encodingAccountNonce `ssz-max:"300000"` // Nonce changes ([tx_index -> new_nonce])
|
NonceChanges []encodingAccountNonce `json:"nonceChanges,omitempty"` // Nonce changes ([tx_index -> new_nonce])
|
||||||
Code []CodeChange `ssz-max:"1"` // Code changes ([tx_index -> new_code])
|
CodeChanges []CodeChange `json:"code,omitempty"` // CodeChanges changes ([tx_index -> new_code])
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate converts the account accesses out of encoding format.
|
// validate converts the account accesses out of encoding format.
|
||||||
|
|
@ -134,19 +155,19 @@ type AccountAccess struct {
|
||||||
// spec, an error is returned.
|
// spec, an error is returned.
|
||||||
func (e *AccountAccess) validate() error {
|
func (e *AccountAccess) validate() error {
|
||||||
// Check the storage write slots are sorted in order
|
// Check the storage write slots are sorted in order
|
||||||
if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int {
|
if !slices.IsSortedFunc(e.StorageChanges, func(a, b encodingSlotWrites) int {
|
||||||
return bytes.Compare(a.Slot[:], b.Slot[:])
|
return bytes.Compare(a.Slot[:], b.Slot[:])
|
||||||
}) {
|
}) {
|
||||||
return errors.New("storage writes slots not in lexicographic order")
|
return errors.New("storage writes slots not in lexicographic order")
|
||||||
}
|
}
|
||||||
for _, write := range e.StorageWrites {
|
for _, write := range e.StorageChanges {
|
||||||
if err := write.validate(); err != nil {
|
if err := write.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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")
|
||||||
|
|
@ -167,9 +188,9 @@ func (e *AccountAccess) validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert code change
|
// Convert code change
|
||||||
if len(e.Code) == 1 {
|
for _, codeChange := range e.CodeChanges {
|
||||||
if len(e.Code[0].Code) > params.MaxCodeSize {
|
if len(codeChange.Code) > params.MaxCodeSize {
|
||||||
return errors.New("code change contained oversized code")
|
return fmt.Errorf("code change contained oversized code")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -183,26 +204,25 @@ func (e *AccountAccess) Copy() AccountAccess {
|
||||||
BalanceChanges: slices.Clone(e.BalanceChanges),
|
BalanceChanges: slices.Clone(e.BalanceChanges),
|
||||||
NonceChanges: slices.Clone(e.NonceChanges),
|
NonceChanges: slices.Clone(e.NonceChanges),
|
||||||
}
|
}
|
||||||
for _, storageWrite := range e.StorageWrites {
|
for _, storageWrite := range e.StorageChanges {
|
||||||
res.StorageWrites = append(res.StorageWrites, encodingSlotWrites{
|
res.StorageChanges = append(res.StorageChanges, encodingSlotWrites{
|
||||||
Slot: storageWrite.Slot,
|
Slot: storageWrite.Slot,
|
||||||
Accesses: slices.Clone(storageWrite.Accesses),
|
Accesses: slices.Clone(storageWrite.Accesses),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(e.Code) == 1 {
|
for _, codeChange := range e.CodeChanges {
|
||||||
res.Code = []CodeChange{
|
res.CodeChanges = append(res.CodeChanges,
|
||||||
{
|
CodeChange{
|
||||||
e.Code[0].TxIndex,
|
codeChange.TxIdx,
|
||||||
bytes.Clone(e.Code[0].Code),
|
bytes.Clone(codeChange.Code),
|
||||||
},
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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{}
|
||||||
|
|
@ -212,11 +232,11 @@ var _ rlp.Encoder = &ConstructionBlockAccessList{}
|
||||||
func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess {
|
func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess {
|
||||||
res := AccountAccess{
|
res := AccountAccess{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
StorageWrites: make([]encodingSlotWrites, 0),
|
StorageChanges: 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,
|
CodeChanges: make([]CodeChange, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert write slots
|
// Convert write slots
|
||||||
|
|
@ -237,7 +257,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
ValueAfter: slotWrites[index],
|
ValueAfter: slotWrites[index],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
res.StorageWrites = append(res.StorageWrites, obj)
|
res.StorageChanges = append(res.StorageChanges, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert read slots
|
// Convert read slots
|
||||||
|
|
@ -253,7 +273,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
for _, idx := range balanceIndices {
|
for _, idx := range balanceIndices {
|
||||||
res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{
|
res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{
|
||||||
TxIdx: idx,
|
TxIdx: idx,
|
||||||
Balance: encodeBalance(a.BalanceChanges[idx]),
|
Balance: new(uint256.Int).Set(a.BalanceChanges[idx]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,77 +288,36 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert code change
|
// Convert code change
|
||||||
if a.CodeChange != nil {
|
codeChangeIdxs := slices.Collect(maps.Keys(a.CodeChanges))
|
||||||
res.Code = []CodeChange{
|
slices.SortFunc(codeChangeIdxs, cmp.Compare[uint16])
|
||||||
{
|
for _, idx := range codeChangeIdxs {
|
||||||
a.CodeChange.TxIndex,
|
res.CodeChanges = append(res.CodeChanges, CodeChange{
|
||||||
bytes.Clone(a.CodeChange.Code),
|
idx,
|
||||||
},
|
bytes.Clone(a.CodeChanges[idx].Code),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
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 = append(res, c.Accounts[addr].toEncodingObj(addr))
|
||||||
}
|
}
|
||||||
return &res
|
return &res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *BlockAccessList) PrettyPrint() string {
|
type ContractCode []byte
|
||||||
var res bytes.Buffer
|
|
||||||
printWithIndent := func(indent int, text string) {
|
|
||||||
fmt.Fprintf(&res, "%s%s\n", strings.Repeat(" ", indent), text)
|
|
||||||
}
|
|
||||||
for _, accountDiff := range e.Accesses {
|
|
||||||
printWithIndent(0, fmt.Sprintf("%x:", accountDiff.Address))
|
|
||||||
|
|
||||||
printWithIndent(1, "storage writes:")
|
func (c *ContractCode) MarshalJSON() ([]byte, error) {
|
||||||
for _, sWrite := range accountDiff.StorageWrites {
|
hexStr := fmt.Sprintf("%x", *c)
|
||||||
printWithIndent(2, fmt.Sprintf("%x:", sWrite.Slot))
|
return json.Marshal(hexStr)
|
||||||
for _, access := range sWrite.Accesses {
|
|
||||||
printWithIndent(3, fmt.Sprintf("%d: %x", access.TxIdx, access.ValueAfter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printWithIndent(1, "storage reads:")
|
|
||||||
for _, slot := range accountDiff.StorageReads {
|
|
||||||
printWithIndent(2, fmt.Sprintf("%x", slot))
|
|
||||||
}
|
|
||||||
|
|
||||||
printWithIndent(1, "balance changes:")
|
|
||||||
for _, change := range accountDiff.BalanceChanges {
|
|
||||||
balance := new(uint256.Int).SetBytes(change.Balance[:]).String()
|
|
||||||
printWithIndent(2, fmt.Sprintf("%d: %s", change.TxIdx, balance))
|
|
||||||
}
|
|
||||||
|
|
||||||
printWithIndent(1, "nonce changes:")
|
|
||||||
for _, change := range accountDiff.NonceChanges {
|
|
||||||
printWithIndent(2, fmt.Sprintf("%d: %d", change.TxIdx, change.Nonce))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(accountDiff.Code) > 0 {
|
|
||||||
printWithIndent(1, "code:")
|
|
||||||
printWithIndent(2, fmt.Sprintf("%d: %x", accountDiff.Code[0].TxIndex, accountDiff.Code[0].Code))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a deep copy of the access list
|
|
||||||
func (e *BlockAccessList) Copy() (res BlockAccessList) {
|
|
||||||
for _, accountAccess := range e.Accesses {
|
|
||||||
res.Accesses = append(res.Accesses, accountAccess.Copy())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,275 +2,254 @@
|
||||||
|
|
||||||
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 "github.com/holiman/uint256"
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
func (obj *BlockAccessList) EncodeRLP(_w io.Writer) error {
|
func (obj *AccountAccess) EncodeRLP(_w io.Writer) error {
|
||||||
w := rlp.NewEncoderBuffer(_w)
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
_tmp0 := w.List()
|
_tmp0 := w.List()
|
||||||
|
w.WriteBytes(obj.Address[:])
|
||||||
_tmp1 := w.List()
|
_tmp1 := w.List()
|
||||||
for _, _tmp2 := range obj.Accesses {
|
for _, _tmp2 := range obj.StorageChanges {
|
||||||
_tmp3 := w.List()
|
_tmp3 := w.List()
|
||||||
w.WriteBytes(_tmp2.Address[:])
|
w.WriteBytes(_tmp2.Slot[:])
|
||||||
_tmp4 := w.List()
|
_tmp4 := w.List()
|
||||||
for _, _tmp5 := range _tmp2.StorageWrites {
|
for _, _tmp5 := range _tmp2.Accesses {
|
||||||
_tmp6 := w.List()
|
_tmp6 := w.List()
|
||||||
w.WriteBytes(_tmp5.Slot[:])
|
w.WriteUint64(uint64(_tmp5.TxIdx))
|
||||||
_tmp7 := w.List()
|
w.WriteBytes(_tmp5.ValueAfter[:])
|
||||||
for _, _tmp8 := range _tmp5.Accesses {
|
|
||||||
_tmp9 := w.List()
|
|
||||||
w.WriteUint64(uint64(_tmp8.TxIdx))
|
|
||||||
w.WriteBytes(_tmp8.ValueAfter[:])
|
|
||||||
w.ListEnd(_tmp9)
|
|
||||||
}
|
|
||||||
w.ListEnd(_tmp7)
|
|
||||||
w.ListEnd(_tmp6)
|
w.ListEnd(_tmp6)
|
||||||
}
|
}
|
||||||
w.ListEnd(_tmp4)
|
w.ListEnd(_tmp4)
|
||||||
_tmp10 := w.List()
|
|
||||||
for _, _tmp11 := range _tmp2.StorageReads {
|
|
||||||
w.WriteBytes(_tmp11[:])
|
|
||||||
}
|
|
||||||
w.ListEnd(_tmp10)
|
|
||||||
_tmp12 := w.List()
|
|
||||||
for _, _tmp13 := range _tmp2.BalanceChanges {
|
|
||||||
_tmp14 := w.List()
|
|
||||||
w.WriteUint64(uint64(_tmp13.TxIdx))
|
|
||||||
w.WriteBytes(_tmp13.Balance[:])
|
|
||||||
w.ListEnd(_tmp14)
|
|
||||||
}
|
|
||||||
w.ListEnd(_tmp12)
|
|
||||||
_tmp15 := w.List()
|
|
||||||
for _, _tmp16 := range _tmp2.NonceChanges {
|
|
||||||
_tmp17 := w.List()
|
|
||||||
w.WriteUint64(uint64(_tmp16.TxIdx))
|
|
||||||
w.WriteUint64(_tmp16.Nonce)
|
|
||||||
w.ListEnd(_tmp17)
|
|
||||||
}
|
|
||||||
w.ListEnd(_tmp15)
|
|
||||||
_tmp18 := w.List()
|
|
||||||
for _, _tmp19 := range _tmp2.Code {
|
|
||||||
_tmp20 := w.List()
|
|
||||||
w.WriteUint64(uint64(_tmp19.TxIndex))
|
|
||||||
w.WriteBytes(_tmp19.Code)
|
|
||||||
w.ListEnd(_tmp20)
|
|
||||||
}
|
|
||||||
w.ListEnd(_tmp18)
|
|
||||||
w.ListEnd(_tmp3)
|
w.ListEnd(_tmp3)
|
||||||
}
|
}
|
||||||
w.ListEnd(_tmp1)
|
w.ListEnd(_tmp1)
|
||||||
|
_tmp7 := w.List()
|
||||||
|
for _, _tmp8 := range obj.StorageReads {
|
||||||
|
w.WriteBytes(_tmp8[:])
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp7)
|
||||||
|
_tmp9 := w.List()
|
||||||
|
for _, _tmp10 := range obj.BalanceChanges {
|
||||||
|
_tmp11 := w.List()
|
||||||
|
w.WriteUint64(uint64(_tmp10.TxIdx))
|
||||||
|
if _tmp10.Balance == nil {
|
||||||
|
w.Write(rlp.EmptyString)
|
||||||
|
} else {
|
||||||
|
w.WriteUint256(_tmp10.Balance)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp11)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp9)
|
||||||
|
_tmp12 := w.List()
|
||||||
|
for _, _tmp13 := range obj.NonceChanges {
|
||||||
|
_tmp14 := w.List()
|
||||||
|
w.WriteUint64(uint64(_tmp13.TxIdx))
|
||||||
|
w.WriteUint64(_tmp13.Nonce)
|
||||||
|
w.ListEnd(_tmp14)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp12)
|
||||||
|
_tmp15 := w.List()
|
||||||
|
for _, _tmp16 := range obj.CodeChanges {
|
||||||
|
_tmp17 := w.List()
|
||||||
|
w.WriteUint64(uint64(_tmp16.TxIdx))
|
||||||
|
w.WriteBytes(_tmp16.Code)
|
||||||
|
w.ListEnd(_tmp17)
|
||||||
|
}
|
||||||
|
w.ListEnd(_tmp15)
|
||||||
w.ListEnd(_tmp0)
|
w.ListEnd(_tmp0)
|
||||||
return w.Flush()
|
return w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
func (obj *AccountAccess) DecodeRLP(dec *rlp.Stream) error {
|
||||||
var _tmp0 BlockAccessList
|
var _tmp0 AccountAccess
|
||||||
{
|
{
|
||||||
if _, err := dec.List(); err != nil {
|
if _, err := dec.List(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Accesses:
|
// Address:
|
||||||
var _tmp1 []AccountAccess
|
var _tmp1 common.Address
|
||||||
|
if err := dec.ReadBytes(_tmp1[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.Address = _tmp1
|
||||||
|
// StorageChanges:
|
||||||
|
var _tmp2 []encodingSlotWrites
|
||||||
if _, err := dec.List(); err != nil {
|
if _, err := dec.List(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for dec.MoreDataInList() {
|
for dec.MoreDataInList() {
|
||||||
var _tmp2 AccountAccess
|
var _tmp3 encodingSlotWrites
|
||||||
{
|
{
|
||||||
if _, err := dec.List(); err != nil {
|
if _, err := dec.List(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Address:
|
// Slot:
|
||||||
var _tmp3 [20]byte
|
var _tmp4 common.Hash
|
||||||
if err := dec.ReadBytes(_tmp3[:]); err != nil {
|
if err := dec.ReadBytes(_tmp4[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_tmp2.Address = _tmp3
|
_tmp3.Slot = _tmp4
|
||||||
// StorageWrites:
|
// Accesses:
|
||||||
var _tmp4 []encodingSlotWrites
|
var _tmp5 []encodingStorageWrite
|
||||||
if _, err := dec.List(); err != nil {
|
if _, err := dec.List(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for dec.MoreDataInList() {
|
for dec.MoreDataInList() {
|
||||||
var _tmp5 encodingSlotWrites
|
var _tmp6 encodingStorageWrite
|
||||||
{
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Slot:
|
|
||||||
var _tmp6 [32]byte
|
|
||||||
if err := dec.ReadBytes(_tmp6[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp5.Slot = _tmp6
|
|
||||||
// Accesses:
|
|
||||||
var _tmp7 []encodingStorageWrite
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for dec.MoreDataInList() {
|
|
||||||
var _tmp8 encodingStorageWrite
|
|
||||||
{
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TxIdx:
|
|
||||||
_tmp9, err := dec.Uint16()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp8.TxIdx = _tmp9
|
|
||||||
// ValueAfter:
|
|
||||||
var _tmp10 [32]byte
|
|
||||||
if err := dec.ReadBytes(_tmp10[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp8.ValueAfter = _tmp10
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_tmp7 = append(_tmp7, _tmp8)
|
|
||||||
}
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp5.Accesses = _tmp7
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_tmp4 = append(_tmp4, _tmp5)
|
|
||||||
}
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp2.StorageWrites = _tmp4
|
|
||||||
// StorageReads:
|
|
||||||
var _tmp11 [][32]byte
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for dec.MoreDataInList() {
|
|
||||||
var _tmp12 [32]byte
|
|
||||||
if err := dec.ReadBytes(_tmp12[:]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp11 = append(_tmp11, _tmp12)
|
|
||||||
}
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp2.StorageReads = _tmp11
|
|
||||||
// BalanceChanges:
|
|
||||||
var _tmp13 []encodingBalanceChange
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for dec.MoreDataInList() {
|
|
||||||
var _tmp14 encodingBalanceChange
|
|
||||||
{
|
{
|
||||||
if _, err := dec.List(); err != nil {
|
if _, err := dec.List(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TxIdx:
|
// TxIdx:
|
||||||
_tmp15, err := dec.Uint16()
|
_tmp7, err := dec.Uint16()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_tmp14.TxIdx = _tmp15
|
_tmp6.TxIdx = _tmp7
|
||||||
// Balance:
|
// ValueAfter:
|
||||||
var _tmp16 [16]byte
|
var _tmp8 common.Hash
|
||||||
if err := dec.ReadBytes(_tmp16[:]); err != nil {
|
if err := dec.ReadBytes(_tmp8[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_tmp14.Balance = _tmp16
|
_tmp6.ValueAfter = _tmp8
|
||||||
if err := dec.ListEnd(); err != nil {
|
if err := dec.ListEnd(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_tmp13 = append(_tmp13, _tmp14)
|
_tmp5 = append(_tmp5, _tmp6)
|
||||||
}
|
}
|
||||||
if err := dec.ListEnd(); err != nil {
|
if err := dec.ListEnd(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_tmp2.BalanceChanges = _tmp13
|
_tmp3.Accesses = _tmp5
|
||||||
// NonceChanges:
|
|
||||||
var _tmp17 []encodingAccountNonce
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for dec.MoreDataInList() {
|
|
||||||
var _tmp18 encodingAccountNonce
|
|
||||||
{
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TxIdx:
|
|
||||||
_tmp19, err := dec.Uint16()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp18.TxIdx = _tmp19
|
|
||||||
// Nonce:
|
|
||||||
_tmp20, err := dec.Uint64()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp18.Nonce = _tmp20
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_tmp17 = append(_tmp17, _tmp18)
|
|
||||||
}
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp2.NonceChanges = _tmp17
|
|
||||||
// Code:
|
|
||||||
var _tmp21 []CodeChange
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for dec.MoreDataInList() {
|
|
||||||
var _tmp22 CodeChange
|
|
||||||
{
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// TxIndex:
|
|
||||||
_tmp23, err := dec.Uint16()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp22.TxIndex = _tmp23
|
|
||||||
// Code:
|
|
||||||
_tmp24, err := dec.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp22.Code = _tmp24
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_tmp21 = append(_tmp21, _tmp22)
|
|
||||||
}
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_tmp2.Code = _tmp21
|
|
||||||
if err := dec.ListEnd(); err != nil {
|
if err := dec.ListEnd(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_tmp1 = append(_tmp1, _tmp2)
|
_tmp2 = append(_tmp2, _tmp3)
|
||||||
}
|
}
|
||||||
if err := dec.ListEnd(); err != nil {
|
if err := dec.ListEnd(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_tmp0.Accesses = _tmp1
|
_tmp0.StorageChanges = _tmp2
|
||||||
|
// StorageReads:
|
||||||
|
var _tmp9 []common.Hash
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var _tmp10 common.Hash
|
||||||
|
if err := dec.ReadBytes(_tmp10[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp9 = append(_tmp9, _tmp10)
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.StorageReads = _tmp9
|
||||||
|
// BalanceChanges:
|
||||||
|
var _tmp11 []encodingBalanceChange
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var _tmp12 encodingBalanceChange
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TxIdx:
|
||||||
|
_tmp13, err := dec.Uint16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp12.TxIdx = _tmp13
|
||||||
|
// Balance:
|
||||||
|
var _tmp14 uint256.Int
|
||||||
|
if err := dec.ReadUint256(&_tmp14); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp12.Balance = &_tmp14
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp11 = append(_tmp11, _tmp12)
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.BalanceChanges = _tmp11
|
||||||
|
// NonceChanges:
|
||||||
|
var _tmp15 []encodingAccountNonce
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var _tmp16 encodingAccountNonce
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TxIdx:
|
||||||
|
_tmp17, err := dec.Uint16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp16.TxIdx = _tmp17
|
||||||
|
// Nonce:
|
||||||
|
_tmp18, err := dec.Uint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp16.Nonce = _tmp18
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp15 = append(_tmp15, _tmp16)
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.NonceChanges = _tmp15
|
||||||
|
// CodeChanges:
|
||||||
|
var _tmp19 []CodeChange
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var _tmp20 CodeChange
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TxIdx:
|
||||||
|
_tmp21, err := dec.Uint16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp20.TxIdx = _tmp21
|
||||||
|
// Code:
|
||||||
|
_tmp22, err := dec.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp20.Code = _tmp22
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tmp19 = append(_tmp19, _tmp20)
|
||||||
|
}
|
||||||
|
if err := dec.ListEnd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp0.CodeChanges = _tmp19
|
||||||
if err := dec.ListEnd(); err != nil {
|
if err := dec.ListEnd(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,10 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
||||||
1: 2,
|
1: 2,
|
||||||
2: 6,
|
2: 6,
|
||||||
},
|
},
|
||||||
CodeChange: &CodeChange{
|
CodeChanges: map[uint16]CodeChange{0: {
|
||||||
TxIndex: 0,
|
TxIdx: 0,
|
||||||
Code: common.Hex2Bytes("deadbeef"),
|
Code: common.Hex2Bytes("deadbeef"),
|
||||||
},
|
}},
|
||||||
},
|
},
|
||||||
common.BytesToAddress([]byte{0xff, 0xff, 0xff}): {
|
common.BytesToAddress([]byte{0xff, 0xff, 0xff}): {
|
||||||
StorageWrites: map[common.Hash]map[uint16]common.Hash{
|
StorageWrites: map[common.Hash]map[uint16]common.Hash{
|
||||||
|
|
@ -102,10 +102,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 +113,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 +144,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[:])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +152,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
balances = append(balances, encodingBalanceChange{
|
balances = append(balances, encodingBalanceChange{
|
||||||
TxIdx: uint16(2 * i),
|
TxIdx: uint16(2 * i),
|
||||||
Balance: [16]byte(testrand.Bytes(16)),
|
Balance: new(uint256.Int).SetBytes(testrand.Bytes(32)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if sort {
|
if sort {
|
||||||
|
|
@ -175,14 +175,14 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
|
|
||||||
return AccountAccess{
|
return AccountAccess{
|
||||||
Address: [20]byte(testrand.Bytes(20)),
|
Address: [20]byte(testrand.Bytes(20)),
|
||||||
StorageWrites: storageWrites,
|
StorageChanges: storageWrites,
|
||||||
StorageReads: storageReads,
|
StorageReads: storageReads,
|
||||||
BalanceChanges: balances,
|
BalanceChanges: balances,
|
||||||
NonceChanges: nonces,
|
NonceChanges: nonces,
|
||||||
Code: []CodeChange{
|
CodeChanges: []CodeChange{
|
||||||
{
|
{
|
||||||
TxIndex: 100,
|
TxIdx: 100,
|
||||||
Code: testrand.Bytes(256),
|
Code: testrand.Bytes(256),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -191,10 +191,10 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
func makeTestBAL(sort bool) BlockAccessList {
|
func makeTestBAL(sort bool) BlockAccessList {
|
||||||
list := BlockAccessList{}
|
list := BlockAccessList{}
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
list.Accesses = append(list.Accesses, makeTestAccountAccess(sort))
|
list = append(list, makeTestAccountAccess(sort))
|
||||||
}
|
}
|
||||||
if sort {
|
if sort {
|
||||||
slices.SortFunc(list.Accesses, func(a, b AccountAccess) int {
|
slices.SortFunc(list, func(a, b AccountAccess) int {
|
||||||
return bytes.Compare(a.Address[:], b.Address[:])
|
return bytes.Compare(a.Address[:], b.Address[:])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +214,7 @@ func TestBlockAccessListCopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the mutations on copy won't affect the origin
|
// Make sure the mutations on copy won't affect the origin
|
||||||
for _, aa := range cpyCpy.Accesses {
|
for _, aa := range cpyCpy {
|
||||||
for i := 0; i < len(aa.StorageReads); i++ {
|
for i := 0; i < len(aa.StorageReads); i++ {
|
||||||
aa.StorageReads[i] = [32]byte(testrand.Bytes(32))
|
aa.StorageReads[i] = [32]byte(testrand.Bytes(32))
|
||||||
}
|
}
|
||||||
|
|
@ -245,8 +245,11 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BALReader test ideas
|
||||||
|
// * BAL which doesn't have any pre-tx system contracts should return an empty state diff at idx 0
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,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 +108,9 @@ 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"`
|
||||||
|
|
||||||
|
// BlockAccessListHash was added by EIP-7928 and is ignored in legacy headers.
|
||||||
|
BlockAccessListHash *common.Hash `json:"balHash" rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// field type overrides for gencodec
|
// field type overrides for gencodec
|
||||||
|
|
@ -183,7 +188,8 @@ func (h *Header) EmptyReceipts() bool {
|
||||||
type Body struct {
|
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
|
||||||
|
|
@ -229,7 +237,8 @@ type extblock struct {
|
||||||
Header *Header
|
Header *Header
|
||||||
Txs []*Transaction
|
Txs []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
|
AccessList *bal.BlockAccessList `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.BlockAccessListHash = &balHash
|
||||||
|
b.accessList = body.AccessList
|
||||||
|
}
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,7 +354,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
||||||
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, b.accessList = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals, eb.AccessList
|
||||||
b.size.Store(rlp.ListSize(size))
|
b.size.Store(rlp.ListSize(size))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -357,7 +372,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
// 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 +523,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])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,12 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
cost = params.ColdSloadCostEIP2929
|
cost = params.ColdSloadCostEIP2929
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdStorageRead != nil {
|
||||||
|
// TODO: should these only be called if the cold storage read didn't go OOG?
|
||||||
|
// it's harder to implement, but I lean towards "yes".
|
||||||
|
// need to clarify this in the spec.
|
||||||
|
evm.Config.Tracer.OnColdStorageRead(contract.Address(), slot)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value := common.Hash(y.Bytes32())
|
value := common.Hash(y.Bytes32())
|
||||||
|
|
||||||
|
|
@ -123,6 +129,12 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
||||||
// Check slot presence in the access list
|
// Check slot presence in the access list
|
||||||
if !evm.StateDB.AddressInAccessList(addr) {
|
if !evm.StateDB.AddressInAccessList(addr) {
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
|
|
||||||
|
// TODO: same issue as OnColdSStorageRead. See the TODO above near OnColdStorageRead
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnColdAccountRead != nil {
|
||||||
|
evm.Config.Tracer.OnColdAccountRead(contract.Address())
|
||||||
|
}
|
||||||
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -505,7 +507,7 @@ func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumber) (*stateless.ExtWitness
|
||||||
return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn)
|
return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := bc.ProcessBlock(parent.Root, block, false, true)
|
result, err := bc.ProcessBlock(parent.Root, block, false, true, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -525,10 +527,40 @@ func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWit
|
||||||
return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash)
|
return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := bc.ProcessBlock(parent.Root, block, false, true)
|
result, err := bc.ProcessBlock(parent.Root, block, false, true, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.Witness().ToExtWitness(), nil
|
return result.Witness().ToExtWitness(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBlockAccessList returns a block access list for the given number/hash
|
||||||
|
// or nil if one does not exist.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEncodedBlockAccessList returns a block access list corresponding to a
|
||||||
|
// block number/hash in RLP-encoded form. It returns nil if one does not exist.
|
||||||
|
func (api *DebugAPI) GetEncodedBlockAccessList(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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
// - DATADIR/triedb/verkle.journal
|
// - DATADIR/triedb/verkle.journal
|
||||||
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
||||||
StateSizeTracking: config.EnableStateSizeTracking,
|
StateSizeTracking: config.EnableStateSizeTracking,
|
||||||
|
EnableBALForTesting: config.ExperimentalBAL,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if config.VMTrace != "" {
|
if config.VMTrace != "" {
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,10 @@ type Config struct {
|
||||||
|
|
||||||
// OverrideVerkle (TODO: remove after the fork)
|
// OverrideVerkle (TODO: remove after the fork)
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// ExperimentalBAL enables EIP-7928 block access list creation during execution
|
||||||
|
// of post Cancun blocks, and persistence via embedding the BAL in the block body.
|
||||||
|
ExperimentalBAL bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
// CreateConsensusEngine creates a consensus engine for the given chain config.
|
||||||
|
|
|
||||||
|
|
@ -474,6 +474,16 @@ web3._extend({
|
||||||
params: 1,
|
params: 1,
|
||||||
inputFormatter: [null],
|
inputFormatter: [null],
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'getBlockAccessList',
|
||||||
|
call: 'debug_getBlockAccessList',
|
||||||
|
params: 1
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'getEncodedBlockAccessList',
|
||||||
|
call: 'debug_getEncodedBlockAccessList',
|
||||||
|
params: 1
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
properties: []
|
properties: []
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ type environment struct {
|
||||||
sidecars []*types.BlobTxSidecar
|
sidecars []*types.BlobTxSidecar
|
||||||
blobs int
|
blobs int
|
||||||
|
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
alTracer *core.BlockAccessListTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
// txFits reports whether the transaction fits into the block size limit.
|
// txFits reports whether the transaction fits into the block size limit.
|
||||||
|
|
@ -134,6 +135,9 @@ func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals}
|
body := types.Body{Transactions: work.txs, Withdrawals: genParam.withdrawals}
|
||||||
|
if work.alTracer != nil {
|
||||||
|
body.AccessList = work.alTracer.AccessList().ToEncodingObj()
|
||||||
|
}
|
||||||
|
|
||||||
allLogs := make([]*types.Log, 0)
|
allLogs := make([]*types.Log, 0)
|
||||||
for _, r := range work.receipts {
|
for _, r := range work.receipts {
|
||||||
|
|
@ -273,6 +277,11 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
|
||||||
}
|
}
|
||||||
state.StartPrefetcher("miner", bundle, nil)
|
state.StartPrefetcher("miner", bundle, nil)
|
||||||
}
|
}
|
||||||
|
vmConf := vm.Config{}
|
||||||
|
var alTracer *core.BlockAccessListTracer
|
||||||
|
if miner.chainConfig.IsAmsterdam(header.Number, header.Time) {
|
||||||
|
alTracer, vmConf.Tracer = core.NewBlockAccessListTracer()
|
||||||
|
}
|
||||||
// Note the passed coinbase may be different with header.Coinbase.
|
// Note the passed coinbase may be different with header.Coinbase.
|
||||||
return &environment{
|
return &environment{
|
||||||
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
|
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
|
||||||
|
|
@ -281,7 +290,8 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
header: header,
|
header: header,
|
||||||
witness: state.Witness(),
|
witness: state.Witness(),
|
||||||
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}),
|
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vmConf),
|
||||||
|
alTracer: alTracer,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1067,6 +1067,9 @@ func (c *ChainConfig) LatestFork(time uint64) forks.Fork {
|
||||||
// BlobConfig returns the blob config associated with the provided fork.
|
// BlobConfig returns the blob config associated with the provided fork.
|
||||||
func (c *ChainConfig) BlobConfig(fork forks.Fork) *BlobConfig {
|
func (c *ChainConfig) BlobConfig(fork forks.Fork) *BlobConfig {
|
||||||
switch fork {
|
switch fork {
|
||||||
|
case forks.Amsterdam:
|
||||||
|
// TODO: (????)
|
||||||
|
return c.BlobScheduleConfig.BPO2
|
||||||
case forks.BPO5:
|
case forks.BPO5:
|
||||||
return c.BlobScheduleConfig.BPO5
|
return c.BlobScheduleConfig.BPO5
|
||||||
case forks.BPO4:
|
case forks.BPO4:
|
||||||
|
|
@ -1112,6 +1115,8 @@ func (c *ChainConfig) ActiveSystemContracts(time uint64) map[string]common.Addre
|
||||||
// the fork isn't defined or isn't a time-based fork.
|
// the fork isn't defined or isn't a time-based fork.
|
||||||
func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 {
|
func (c *ChainConfig) Timestamp(fork forks.Fork) *uint64 {
|
||||||
switch {
|
switch {
|
||||||
|
case fork == forks.Amsterdam:
|
||||||
|
return c.AmsterdamTime
|
||||||
case fork == forks.BPO5:
|
case fork == forks.BPO5:
|
||||||
return c.BPO5Time
|
return c.BPO5Time
|
||||||
case fork == forks.BPO4:
|
case fork == forks.BPO4:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue