mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
all: implement BAL single-threaded execution and validation
This commit is contained in:
parent
3c21252fec
commit
e66e51e04f
38 changed files with 1634 additions and 681 deletions
|
|
@ -21,6 +21,8 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -82,24 +84,25 @@ type payloadAttributesMarshaling struct {
|
||||||
|
|
||||||
// ExecutableData is the data necessary to execute an EL payload.
|
// ExecutableData is the data necessary to execute an EL payload.
|
||||||
type ExecutableData struct {
|
type ExecutableData struct {
|
||||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
Number uint64 `json:"blockNumber" gencodec:"required"`
|
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||||
ExtraData []byte `json:"extraData" gencodec:"required"`
|
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
BlockAccessList *bal.BlockAccessList `json:"blockAccessList"`
|
||||||
|
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// JSON type overrides for executableData.
|
||||||
|
|
@ -303,6 +306,8 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
||||||
requestsHash = &h
|
requestsHash = &h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body := types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}
|
||||||
|
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: data.ParentHash,
|
ParentHash: data.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
|
@ -326,33 +331,40 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
||||||
RequestsHash: requestsHash,
|
RequestsHash: requestsHash,
|
||||||
SlotNumber: data.SlotNumber,
|
SlotNumber: data.SlotNumber,
|
||||||
}
|
}
|
||||||
return types.NewBlockWithHeader(header).
|
|
||||||
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}),
|
if data.BlockAccessList != nil {
|
||||||
nil
|
balHash := data.BlockAccessList.Hash()
|
||||||
|
header.BlockAccessListHash = &balHash
|
||||||
|
block := types.NewBlockWithHeader(header).WithBody(body).WithAccessList(data.BlockAccessList)
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewBlockWithHeader(header).WithBody(body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||||
// fields from the given block. It assumes the given block is post-merge block.
|
// fields from the given block. It assumes the given block is post-merge block.
|
||||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar, requests [][]byte) *ExecutionPayloadEnvelope {
|
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar, requests [][]byte) *ExecutionPayloadEnvelope {
|
||||||
data := &ExecutableData{
|
data := &ExecutableData{
|
||||||
BlockHash: block.Hash(),
|
BlockHash: block.Hash(),
|
||||||
ParentHash: block.ParentHash(),
|
ParentHash: block.ParentHash(),
|
||||||
FeeRecipient: block.Coinbase(),
|
FeeRecipient: block.Coinbase(),
|
||||||
StateRoot: block.Root(),
|
StateRoot: block.Root(),
|
||||||
Number: block.NumberU64(),
|
Number: block.NumberU64(),
|
||||||
GasLimit: block.GasLimit(),
|
GasLimit: block.GasLimit(),
|
||||||
GasUsed: block.GasUsed(),
|
GasUsed: block.GasUsed(),
|
||||||
BaseFeePerGas: block.BaseFee(),
|
BaseFeePerGas: block.BaseFee(),
|
||||||
Timestamp: block.Time(),
|
Timestamp: block.Time(),
|
||||||
ReceiptsRoot: block.ReceiptHash(),
|
ReceiptsRoot: block.ReceiptHash(),
|
||||||
LogsBloom: block.Bloom().Bytes(),
|
LogsBloom: block.Bloom().Bytes(),
|
||||||
Transactions: encodeTransactions(block.Transactions()),
|
Transactions: encodeTransactions(block.Transactions()),
|
||||||
Random: block.MixDigest(),
|
Random: block.MixDigest(),
|
||||||
ExtraData: block.Extra(),
|
ExtraData: block.Extra(),
|
||||||
Withdrawals: block.Withdrawals(),
|
Withdrawals: block.Withdrawals(),
|
||||||
BlobGasUsed: block.BlobGasUsed(),
|
BlobGasUsed: block.BlobGasUsed(),
|
||||||
ExcessBlobGas: block.ExcessBlobGas(),
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
SlotNumber: block.SlotNumber(),
|
BlockAccessList: block.AccessList(),
|
||||||
|
SlotNumber: block.SlotNumber(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add blobs.
|
// Add blobs.
|
||||||
|
|
@ -391,8 +403,9 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||||
|
|
||||||
// ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange
|
// ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange
|
||||||
type ExecutionPayloadBody struct {
|
type ExecutionPayloadBody struct {
|
||||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
AccessList *bal.BlockAccessList `json:"blockAccessList"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client identifiers to support ClientVersionV1.
|
// Client identifiers to support ClientVersionV1.
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,11 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
|
||||||
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
|
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
|
||||||
|
|
||||||
|
# version:spec-tests-bal v5.6.1
|
||||||
|
# https://github.com/ethereum/execution-spec-tests/releases
|
||||||
|
# https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v5.6.1
|
||||||
|
741530c88f6a48c15184d1504316c02c3a76c2322c410a04b643a85185dc62e9 fixtures_bal.tar.gz
|
||||||
|
|
||||||
# version:golang 1.25.9
|
# version:golang 1.25.9
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
0ec9ef8ebcea097aac37decae9f09a7218b451cd96be7d6ed513d8e4bcf909cf go1.25.9.src.tar.gz
|
0ec9ef8ebcea097aac37decae9f09a7218b451cd96be7d6ed513d8e4bcf909cf go1.25.9.src.tar.gz
|
||||||
|
|
|
||||||
17
build/ci.go
17
build/ci.go
|
|
@ -176,6 +176,9 @@ var (
|
||||||
|
|
||||||
// This is where the tests should be unpacked.
|
// This is where the tests should be unpacked.
|
||||||
executionSpecTestsDir = "tests/spec-tests"
|
executionSpecTestsDir = "tests/spec-tests"
|
||||||
|
|
||||||
|
// This is where the bal-specific release of the tests should be unpacked.
|
||||||
|
executionSpecTestsBALDir = "tests/spec-tests-bal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||||
|
|
@ -384,6 +387,7 @@ func doTest(cmdline []string) {
|
||||||
// Get test fixtures.
|
// Get test fixtures.
|
||||||
if !*short {
|
if !*short {
|
||||||
downloadSpecTestFixtures(csdb, *cachedir)
|
downloadSpecTestFixtures(csdb, *cachedir)
|
||||||
|
downloadBALSpecTestFixtures(csdb, *cachedir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure the toolchain.
|
// Configure the toolchain.
|
||||||
|
|
@ -449,6 +453,19 @@ func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string
|
||||||
return filepath.Join(cachedir, base)
|
return filepath.Join(cachedir, base)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func downloadBALSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string {
|
||||||
|
ext := ".tar.gz"
|
||||||
|
base := "fixtures_bal"
|
||||||
|
archivePath := filepath.Join(cachedir, base+ext)
|
||||||
|
if err := csdb.DownloadFileFromKnownURL(archivePath); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := build.ExtractArchive(archivePath, executionSpecTestsBALDir); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return filepath.Join(cachedir, base)
|
||||||
|
}
|
||||||
|
|
||||||
// doCheckGenerate ensures that re-generating generated files does not cause
|
// doCheckGenerate ensures that re-generating generated files does not cause
|
||||||
// any mutations in the source file tree.
|
// any mutations in the source file tree.
|
||||||
func doCheckGenerate() {
|
func doCheckGenerate() {
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
snapshot = statedb.Snapshot()
|
snapshot = statedb.Snapshot()
|
||||||
gp = gaspool.Snapshot()
|
gp = gaspool.Snapshot()
|
||||||
)
|
)
|
||||||
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, evm)
|
_, _, receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||||
|
|
@ -327,11 +327,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
if _, _, err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process withdrawal requests: %v", err))
|
||||||
}
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
if _, _, err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not process consolidation requests: %v", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
|
|
@ -342,18 +344,21 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize implements consensus.Engine and processes withdrawals on top.
|
// Finalize implements consensus.Engine and processes withdrawals on top.
|
||||||
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
|
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) (*bal.StateAccessList, *bal.StateMutations) {
|
||||||
if !beacon.IsPoSHeader(header) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
beacon.ethone.Finalize(chain, header, state, body)
|
return beacon.ethone.Finalize(chain, header, state, body)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
// Withdrawals processing.
|
// Withdrawals processing.
|
||||||
for _, w := range body.Withdrawals {
|
for _, w := range body.Withdrawals {
|
||||||
|
// ensure that target account is included as a read in the BAL even if the withdrawal amount is zero
|
||||||
|
state.GetBalance(w.Address)
|
||||||
|
|
||||||
// Convert amount from gwei to wei.
|
// Convert amount from gwei to wei.
|
||||||
amount := new(uint256.Int).SetUint64(w.Amount)
|
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||||
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
||||||
state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
||||||
}
|
}
|
||||||
|
return state.Finalise(true)
|
||||||
// No block reward which is issued by consensus layer instead.
|
// No block reward which is issued by consensus layer instead.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"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/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
|
|
@ -573,8 +575,9 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
|
||||||
|
|
||||||
// Finalize implements consensus.Engine. There is no post-transaction
|
// Finalize implements consensus.Engine. There is no post-transaction
|
||||||
// consensus rules in clique, do nothing here.
|
// consensus rules in clique, do nothing here.
|
||||||
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
|
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) (*bal.StateAccessList, *bal.StateMutations) {
|
||||||
// No block rewards in PoA, so the state remains as is
|
// No block rewards in PoA, so the state remains as is
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorize injects a private key into the consensus engine to mint new blocks
|
// Authorize injects a private key into the consensus engine to mint new blocks
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
@ -84,7 +85,7 @@ type Engine interface {
|
||||||
//
|
//
|
||||||
// Note: The state database might be updated to reflect any consensus rules
|
// Note: The state database might be updated to reflect any consensus rules
|
||||||
// that happen at finalization (e.g. block rewards).
|
// that happen at finalization (e.g. block rewards).
|
||||||
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body)
|
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) (*bal.StateAccessList, *bal.StateMutations)
|
||||||
|
|
||||||
// Seal generates a new sealing request for the given input block and pushes
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
// the result into the given channel.
|
// the result into the given channel.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set/v2"
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
|
@ -504,9 +506,10 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize implements consensus.Engine, accumulating the block and uncle rewards.
|
// Finalize implements consensus.Engine, accumulating the block and uncle rewards.
|
||||||
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
|
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) (*bal.StateAccessList, *bal.StateMutations) {
|
||||||
// Accumulate any block and uncle rewards
|
// Accumulate any block and uncle rewards
|
||||||
accumulateRewards(chain.Config(), state, header, body.Uncles)
|
accumulateRewards(chain.Config(), state, header, body.Uncles)
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SealHash returns the hash of a block prior to it being sealed.
|
// SealHash returns the hash of a block prior to it being sealed.
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,25 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// block access list hash must be present in header after the Amsterdam hard fork
|
||||||
|
if v.config.IsAmsterdam(block.Number(), block.Time()) {
|
||||||
|
if block.Header().BlockAccessListHash == nil {
|
||||||
|
// TODO: verify that this check isn't also done elsewhere
|
||||||
|
return fmt.Errorf("block access list hash not set in header")
|
||||||
|
}
|
||||||
|
// if the block does not come with an access list, we compute the access list locally
|
||||||
|
// as part of execution and validate against the header's access list hash.
|
||||||
|
if block.AccessList() != nil {
|
||||||
|
if *block.Header().BlockAccessListHash != block.AccessList().Hash() {
|
||||||
|
return fmt.Errorf("access list hash mismatch. local: %x. remote: %x\n", block.AccessList().Hash(), *block.Header().BlockAccessListHash)
|
||||||
|
} else if err := block.AccessList().Validate(len(block.Transactions()), block.GasLimit()); err != nil {
|
||||||
|
return fmt.Errorf("invalid block access list: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if block.AccessList() != nil {
|
||||||
|
return fmt.Errorf("block had access list before 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) {
|
||||||
|
|
|
||||||
|
|
@ -2112,11 +2112,12 @@ type ExecuteConfig struct {
|
||||||
// it writes the block and associated state to database.
|
// it writes the block and associated state to database.
|
||||||
func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, config ExecuteConfig) (result *blockProcessingResult, blockEndErr error) {
|
func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, config ExecuteConfig) (result *blockProcessingResult, blockEndErr error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
interrupt atomic.Bool
|
interrupt atomic.Bool
|
||||||
sdb state.Database
|
sdb state.Database
|
||||||
|
isAmsterdam = bc.chainConfig.IsAmsterdam(block.Number(), block.Time())
|
||||||
)
|
)
|
||||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
|
||||||
|
|
@ -2240,6 +2241,37 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
|
||||||
}
|
}
|
||||||
vtime := time.Since(vstart)
|
vtime := time.Since(vstart)
|
||||||
|
|
||||||
|
if isAmsterdam {
|
||||||
|
computedAccessList := res.AccessList.ToEncodingObj()
|
||||||
|
computedAccessListHash := computedAccessList.Hash()
|
||||||
|
|
||||||
|
if *block.Header().BlockAccessListHash != computedAccessListHash {
|
||||||
|
err := fmt.Errorf("block header access list hash mismatch (remote =%x local=%x)", *block.Header().BlockAccessListHash, computedAccessListHash)
|
||||||
|
bc.reportBadBlock(block, res, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// note that we don't validate that the computed BAL's size aligns with the gas
|
||||||
|
// limit here because it should be impossible case if the parameters in 7928
|
||||||
|
// are tuned correctly.
|
||||||
|
|
||||||
|
if block.AccessList() == nil {
|
||||||
|
// attach the computed access list to the block so it gets persisted
|
||||||
|
// when the block is written to disk
|
||||||
|
block = block.WithAccessList(computedAccessList)
|
||||||
|
}
|
||||||
|
// Failing the access list max size validation should be impossible here
|
||||||
|
// better safe than sorry.
|
||||||
|
if err := computedAccessList.ValidateGasLimit(block.GasLimit()); err != nil {
|
||||||
|
err := fmt.Errorf("block access list validation failed: %v", err)
|
||||||
|
bc.reportBadBlock(block, res, err)
|
||||||
|
return nil, err
|
||||||
|
} else if block.AccessList().Hash() != computedAccessListHash {
|
||||||
|
err := fmt.Errorf("block access list hash mismatch (remote=%x computed=%x)", block.AccessList().Hash(), computedAccessListHash)
|
||||||
|
bc.reportBadBlock(block, res, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
|
|
@ -51,6 +53,8 @@ type BlockGen struct {
|
||||||
withdrawals []*types.Withdrawal
|
withdrawals []*types.Withdrawal
|
||||||
|
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
|
|
||||||
|
accessList *bal.ConstructionBlockAccessList
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCoinbase sets the coinbase of the generated block.
|
// SetCoinbase sets the coinbase of the generated block.
|
||||||
|
|
@ -117,11 +121,15 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
|
||||||
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
|
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
|
||||||
)
|
)
|
||||||
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
|
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
|
||||||
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
|
txAccesses, txMut, receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
b.header.GasUsed = b.gasPool.Used()
|
b.header.GasUsed = b.gasPool.Used()
|
||||||
|
if b.accessList != nil {
|
||||||
|
b.accessList.AddTransactionMutations(txMut, len(b.txs))
|
||||||
|
b.accessList.AddAccesses(txAccesses)
|
||||||
|
}
|
||||||
|
|
||||||
// Merge the tx-local access event into the "block-local" one, in order to collect
|
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||||
// all values, so that the witness can be built.
|
// all values, so that the witness can be built.
|
||||||
|
|
@ -325,17 +333,29 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
|
||||||
if err := ParseDepositLogs(&requests, blockLogs, b.cm.config); err != nil {
|
if err := ParseDepositLogs(&requests, blockLogs, b.cm.config); err != nil {
|
||||||
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
|
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
|
||||||
}
|
}
|
||||||
|
// TODO: these accesses should be accumulated in the BAL
|
||||||
// create EVM for system calls
|
// create EVM for system calls
|
||||||
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||||
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
||||||
|
|
||||||
|
mut := new(bal.StateMutations)
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
withdrawalAccess, withdrawalMut, err := ProcessWithdrawalQueue(&requests, evm)
|
||||||
|
if err != nil {
|
||||||
panic(fmt.Sprintf("could not process withdrawal requests: %v", err))
|
panic(fmt.Sprintf("could not process withdrawal requests: %v", err))
|
||||||
}
|
}
|
||||||
|
mut.Merge(withdrawalMut)
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
consolidationAccess, consolidationMut, err := ProcessConsolidationQueue(&requests, evm)
|
||||||
|
if err != nil {
|
||||||
panic(fmt.Sprintf("could not process consolidation requests: %v", err))
|
panic(fmt.Sprintf("could not process consolidation requests: %v", err))
|
||||||
}
|
}
|
||||||
|
mut.Merge(consolidationMut)
|
||||||
|
if b.cm.config.IsAmsterdam(b.header.Number, b.header.Time) {
|
||||||
|
b.accessList.AddAccesses(withdrawalAccess)
|
||||||
|
b.accessList.AddAccesses(consolidationAccess)
|
||||||
|
b.accessList.AddBlockFinalizeMutations(mut)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
|
|
@ -364,7 +384,10 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||||
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
||||||
b.header = cm.makeHeader(parent, statedb, b.engine)
|
b.header = cm.makeHeader(parent, statedb, b.engine)
|
||||||
|
isAmsterdam := config.IsAmsterdam(b.header.Number, b.header.Time)
|
||||||
|
if isAmsterdam {
|
||||||
|
b.accessList = bal.NewConstructionBlockAccessList()
|
||||||
|
}
|
||||||
// Set the difficulty for clique block. The chain maker doesn't have access
|
// Set the difficulty for clique block. The chain maker doesn't have access
|
||||||
// to a chain, so the difficulty will be left unset (nil). Set it here to the
|
// to a chain, so the difficulty will be left unset (nil). Set it here to the
|
||||||
// correct value.
|
// correct value.
|
||||||
|
|
@ -391,14 +414,32 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
misc.ApplyDAOHardFork(statedb)
|
misc.ApplyDAOHardFork(statedb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preTxMutations := bal.NewStateMutations()
|
||||||
if config.IsPrague(b.header.Number, b.header.Time) || config.IsUBT(b.header.Number, b.header.Time) {
|
if config.IsPrague(b.header.Number, b.header.Time) || config.IsUBT(b.header.Number, b.header.Time) {
|
||||||
// EIP-2935
|
// EIP-2935
|
||||||
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
||||||
blockContext.Random = &common.Hash{} // enable post-merge instruction set
|
blockContext.Random = &common.Hash{} // enable post-merge instruction set
|
||||||
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
|
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
|
||||||
ProcessParentBlockHash(b.header.ParentHash, evm)
|
accesses, mutations := ProcessParentBlockHash(b.header.ParentHash, evm)
|
||||||
|
if isAmsterdam {
|
||||||
|
preTxMutations.Merge(mutations)
|
||||||
|
b.accessList.AddAccesses(accesses)
|
||||||
|
}
|
||||||
|
|
||||||
|
beaconRoot := common.Hash{}
|
||||||
|
if b.header.ParentBeaconRoot != nil {
|
||||||
|
beaconRoot = *b.header.ParentBeaconRoot
|
||||||
|
}
|
||||||
|
reads, writes := ProcessBeaconBlockRoot(beaconRoot, evm)
|
||||||
|
if isAmsterdam {
|
||||||
|
preTxMutations.Merge(writes)
|
||||||
|
b.accessList.AddAccesses(reads)
|
||||||
|
b.accessList.AddBlockInitMutations(preTxMutations)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: what about the parent beacon root, and post-block system contract (forget what it is rn)?
|
||||||
|
|
||||||
// Execute any user modifications to the block
|
// Execute any user modifications to the block
|
||||||
if gen != nil {
|
if gen != nil {
|
||||||
gen(i, b)
|
gen(i, b)
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,175 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateBALChain(t *testing.T) {
|
||||||
|
var (
|
||||||
|
keyHex = "9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c"
|
||||||
|
key, _ = crypto.HexToECDSA(keyHex)
|
||||||
|
address = crypto.PubkeyToAddress(key.PublicKey) // 658bdf435d810c91414ec09147daa6db62406379
|
||||||
|
aa = common.Address{0xaa}
|
||||||
|
bb = common.Address{0xbb}
|
||||||
|
funds = big.NewInt(0).Mul(big.NewInt(1337), big.NewInt(params.Ether))
|
||||||
|
config = *params.MergedTestChainConfig
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: &config,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
address: {Balance: funds},
|
||||||
|
params.BeaconRootsAddress: {Code: params.BeaconRootsCode},
|
||||||
|
params.WithdrawalQueueAddress: {Code: params.WithdrawalQueueCode},
|
||||||
|
params.ConsolidationQueueAddress: {Code: params.ConsolidationQueueCode},
|
||||||
|
params.HistoryStorageAddress: {Code: params.HistoryStorageCode},
|
||||||
|
},
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
Difficulty: common.Big0,
|
||||||
|
GasLimit: 5_000_000,
|
||||||
|
|
||||||
|
// TODO: why do I have to set the bal hash here, and why does it trigger an issue with the genesis
|
||||||
|
// being reported as not present if I omit the following line?
|
||||||
|
BlockAccessListHash: &common.Hash{}, // TODO: probably bad to initialize this to something other than keccak(nil) but idk
|
||||||
|
}
|
||||||
|
gendb = rawdb.NewMemoryDatabase()
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
)
|
||||||
|
|
||||||
|
// init 0xaa with some storage elements
|
||||||
|
storage := make(map[common.Hash]common.Hash)
|
||||||
|
storage[common.Hash{0x00}] = common.Hash{0x00}
|
||||||
|
storage[common.Hash{0x01}] = common.Hash{0x01}
|
||||||
|
storage[common.Hash{0x02}] = common.Hash{0x02}
|
||||||
|
storage[common.Hash{0x03}] = common.HexToHash("0303")
|
||||||
|
gspec.Alloc[aa] = types.Account{
|
||||||
|
Balance: common.Big1,
|
||||||
|
Nonce: 1,
|
||||||
|
Storage: storage,
|
||||||
|
Code: common.Hex2Bytes("6042"),
|
||||||
|
}
|
||||||
|
gspec.Alloc[bb] = types.Account{
|
||||||
|
Balance: common.Big2,
|
||||||
|
Nonce: 1,
|
||||||
|
Storage: storage,
|
||||||
|
Code: common.Hex2Bytes("600154600354"),
|
||||||
|
}
|
||||||
|
genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, triedb.HashDefaults))
|
||||||
|
engine := beacon.New(ethash.NewFaker())
|
||||||
|
|
||||||
|
genchain, genreceipts := GenerateChain(gspec.Config, genesis, engine, gendb, 4, func(i int, gen *BlockGen) {
|
||||||
|
// TODO: I think we can remove SetBeaconRoot entirely
|
||||||
|
// and provide a different mechanism to set it?
|
||||||
|
|
||||||
|
// gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
||||||
|
|
||||||
|
if gspec.Config.IsAmsterdam(gen.header.Number, gen.header.Time) {
|
||||||
|
// TODO: parameterize the slot num
|
||||||
|
gen.header.SlotNumber = new(uint64)
|
||||||
|
*gen.header.SlotNumber = gen.header.Number.Uint64()
|
||||||
|
}
|
||||||
|
// Add value transfer tx.
|
||||||
|
tx := types.MustSignNewTx(key, gen.Signer(), &types.LegacyTx{
|
||||||
|
Nonce: gen.TxNonce(address),
|
||||||
|
To: &address,
|
||||||
|
Value: big.NewInt(1000),
|
||||||
|
Gas: params.TxGas,
|
||||||
|
GasPrice: new(big.Int).Add(gen.BaseFee(), common.Big1),
|
||||||
|
})
|
||||||
|
gen.AddTx(tx)
|
||||||
|
|
||||||
|
// Add withdrawals.
|
||||||
|
if i == 1 {
|
||||||
|
gen.AddWithdrawal(&types.Withdrawal{
|
||||||
|
Validator: 42,
|
||||||
|
Address: common.Address{0xee},
|
||||||
|
Amount: 1337,
|
||||||
|
})
|
||||||
|
gen.AddWithdrawal(&types.Withdrawal{
|
||||||
|
Validator: 13,
|
||||||
|
Address: common.Address{0xee},
|
||||||
|
Amount: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if i == 3 {
|
||||||
|
gen.AddWithdrawal(&types.Withdrawal{
|
||||||
|
Validator: 42,
|
||||||
|
Address: common.Address{0xee},
|
||||||
|
Amount: 1337,
|
||||||
|
})
|
||||||
|
gen.AddWithdrawal(&types.Withdrawal{
|
||||||
|
Validator: 13,
|
||||||
|
Address: common.Address{0xee},
|
||||||
|
Amount: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Import the chain. This runs all block validation rules.
|
||||||
|
blockchain, err := NewBlockChain(db, gspec, engine, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("err is %v\n", err)
|
||||||
|
}
|
||||||
|
defer blockchain.Stop()
|
||||||
|
|
||||||
|
if i, err := blockchain.InsertChain(genchain); err != nil {
|
||||||
|
t.Fatalf("insert error (block %d): %v\n", genchain[i].NumberU64(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// enforce that withdrawal indexes are monotonically increasing from 0
|
||||||
|
var (
|
||||||
|
withdrawalIndex uint64
|
||||||
|
)
|
||||||
|
for i := range genchain {
|
||||||
|
blocknum := genchain[i].NumberU64()
|
||||||
|
block := blockchain.GetBlockByNumber(blocknum)
|
||||||
|
if block == nil {
|
||||||
|
t.Fatalf("block %d not found", blocknum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify receipts.
|
||||||
|
genBlockReceipts := genreceipts[i]
|
||||||
|
for _, r := range genBlockReceipts {
|
||||||
|
if r.BlockNumber.Cmp(block.Number()) != 0 {
|
||||||
|
t.Errorf("receipt has wrong block number %d, want %d", r.BlockNumber, block.Number())
|
||||||
|
}
|
||||||
|
if r.BlockHash != block.Hash() {
|
||||||
|
t.Errorf("receipt has wrong block hash %v, want %v", r.BlockHash, block.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch up empty logs list to make DeepEqual below work
|
||||||
|
if r.Logs == nil {
|
||||||
|
r.Logs = []*types.Log{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockchainReceipts := blockchain.GetReceiptsByHash(block.Hash())
|
||||||
|
if !reflect.DeepEqual(genBlockReceipts, blockchainReceipts) {
|
||||||
|
t.Fatalf("receipts mismatch\ngenerated: %s\nblockchain: %s", spew.Sdump(genBlockReceipts), spew.Sdump(blockchainReceipts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify withdrawals.
|
||||||
|
if len(block.Withdrawals()) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j := 0; j < len(block.Withdrawals()); j++ {
|
||||||
|
if block.Withdrawals()[j].Index != withdrawalIndex {
|
||||||
|
t.Fatalf("withdrawal index %d does not equal expected index %d", block.Withdrawals()[j].Index, withdrawalIndex)
|
||||||
|
}
|
||||||
|
withdrawalIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: can we reinstate the following?
|
||||||
|
/*
|
||||||
|
// Verify parent beacon root.
|
||||||
|
want := common.Hash{byte(blocknum)}
|
||||||
|
if got := block.BeaconRoot(); *got != want {
|
||||||
|
t.Fatalf("block %d, wrong parent beacon root: got %s, want %s", i, got, want)
|
||||||
|
}
|
||||||
|
state, _ := blockchain.State()
|
||||||
|
idx := block.Time()%8191 + 8191
|
||||||
|
got := state.GetState(params.BeaconRootsAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("block %d, wrong parent beacon root in state: got %s, want %s", i, got, want)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleGenerateChain() {
|
func ExampleGenerateChain() {
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
|
||||||
|
|
@ -67,13 +67,14 @@ 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
|
||||||
SlotNumber *uint64 `json:"slotNumber"` // EIP-7843
|
BlockAccessListHash *common.Hash `json:"BlockAccessListHash,omitempty"`
|
||||||
|
SlotNumber *uint64 `json:"slotNumber"` // EIP-7843
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy copies the genesis.
|
// copy copies the genesis.
|
||||||
|
|
@ -487,18 +488,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
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/holiman/uint256"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests block header storage and retrieval operations.
|
// Tests block header storage and retrieval operations.
|
||||||
|
|
@ -906,13 +905,17 @@ func makeTestBAL(t *testing.T) (rlp.RawValue, *bal.BlockAccessList) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
cb := bal.NewConstructionBlockAccessList()
|
cb := bal.NewConstructionBlockAccessList()
|
||||||
addr := common.HexToAddress("0x1111111111111111111111111111111111111111")
|
/*
|
||||||
cb.AccountRead(addr)
|
TODO MariusVanDerWijden fix after rebase
|
||||||
cb.StorageRead(addr, common.BytesToHash([]byte{0x01}))
|
addr := common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||||
cb.StorageWrite(0, addr, common.BytesToHash([]byte{0x02}), common.BytesToHash([]byte{0xaa}))
|
|
||||||
cb.BalanceChange(0, addr, uint256.NewInt(100))
|
|
||||||
cb.NonceChange(addr, 0, 1)
|
|
||||||
|
|
||||||
|
|
||||||
|
cb.AccountRead(addr)
|
||||||
|
cb.StorageRead(addr, common.BytesToHash([]byte{0x01}))
|
||||||
|
cb.StorageWrite(0, addr, common.BytesToHash([]byte{0x02}), common.BytesToHash([]byte{0xaa}))
|
||||||
|
cb.BalanceChange(0, addr, uint256.NewInt(100))
|
||||||
|
cb.NonceChange(addr, 0, 1)
|
||||||
|
*/
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := cb.EncodeRLP(&buf); err != nil {
|
if err := cb.EncodeRLP(&buf); err != nil {
|
||||||
t.Fatalf("failed to encode BAL: %v", err)
|
t.Fatalf("failed to encode BAL: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -53,6 +55,9 @@ type stateObject struct {
|
||||||
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
|
||||||
data types.StateAccount // Account data with all mutations applied in the scope of block
|
data types.StateAccount // Account data with all mutations applied in the scope of block
|
||||||
|
|
||||||
|
txPreBalance *uint256.Int // the account balance after the last call to finalise
|
||||||
|
txPreNonce uint64 // the account nonce after the last call to finalise
|
||||||
|
|
||||||
// Write caches.
|
// Write caches.
|
||||||
trie Trie // storage trie, which becomes non-nil on first access
|
trie Trie // storage trie, which becomes non-nil on first access
|
||||||
code []byte // contract bytecode, which gets set when code is loaded
|
code []byte // contract bytecode, which gets set when code is loaded
|
||||||
|
|
@ -75,6 +80,9 @@ type stateObject struct {
|
||||||
// Cache flags.
|
// Cache flags.
|
||||||
dirtyCode bool // true if the code was updated
|
dirtyCode bool // true if the code was updated
|
||||||
|
|
||||||
|
nonFinalizedCode bool // true if the code has been changed in the current transaction
|
||||||
|
txPrestateCode []byte // set to the value of the code at the beginning of the transaction if it changed in the current transaction
|
||||||
|
|
||||||
// Flag whether the account was marked as self-destructed. The self-destructed
|
// Flag whether the account was marked as self-destructed. The self-destructed
|
||||||
// account is still accessible in the scope of same transaction.
|
// account is still accessible in the scope of same transaction.
|
||||||
selfDestructed bool
|
selfDestructed bool
|
||||||
|
|
@ -107,6 +115,8 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
|
||||||
dirtyStorage: make(Storage),
|
dirtyStorage: make(Storage),
|
||||||
pendingStorage: make(Storage),
|
pendingStorage: make(Storage),
|
||||||
uncommittedStorage: make(Storage),
|
uncommittedStorage: make(Storage),
|
||||||
|
txPreNonce: acct.Nonce,
|
||||||
|
txPreBalance: acct.Balance.Clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,20 +258,59 @@ func (s *stateObject) setState(key common.Hash, value common.Hash, origin common
|
||||||
|
|
||||||
// finalise moves all dirty storage slots into the pending area to be hashed or
|
// finalise moves all dirty storage slots into the pending area to be hashed or
|
||||||
// committed later. It is invoked at the end of every transaction.
|
// committed later. It is invoked at the end of every transaction.
|
||||||
func (s *stateObject) finalise() {
|
func (s *stateObject) finalise() (mutations *bal.AccountMutations) {
|
||||||
|
mutations = &bal.AccountMutations{}
|
||||||
|
if s.Balance().Cmp(s.txPreBalance) != 0 {
|
||||||
|
mutations.Balance = s.Balance()
|
||||||
|
}
|
||||||
|
if s.Nonce() != s.txPreNonce {
|
||||||
|
mutations.Nonce = new(uint64)
|
||||||
|
*mutations.Nonce = s.Nonce()
|
||||||
|
}
|
||||||
|
// include account code changes: created contracts and 7702 delegation authority code changes
|
||||||
|
if s.nonFinalizedCode {
|
||||||
|
if s.code == nil {
|
||||||
|
// code cleared (7702). code must be non-nil in the post to signal that it's part of the diff vs being unchanged.
|
||||||
|
mutations.Code = []byte{}
|
||||||
|
} else {
|
||||||
|
mutations.Code = s.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutations.StorageWrites = make(map[common.Hash]common.Hash)
|
||||||
|
|
||||||
slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage))
|
slotsToPrefetch := make([]common.Hash, 0, len(s.dirtyStorage))
|
||||||
for key, value := range s.dirtyStorage {
|
for key, value := range s.dirtyStorage {
|
||||||
if origin, exist := s.uncommittedStorage[key]; exist && origin == value {
|
if origin, exist := s.uncommittedStorage[key]; exist && origin == value {
|
||||||
|
// non-parallel-execution:
|
||||||
// The slot is reverted to its original value, delete the entry
|
// The slot is reverted to its original value, delete the entry
|
||||||
// to avoid thrashing the data structures.
|
// to avoid thrashing the data structures.
|
||||||
|
//
|
||||||
|
// parallel-exec-with-BAL:
|
||||||
|
// each statedb instance only executes a single transaction so the previous value
|
||||||
|
// of the slot won't be in uncommittedStorage
|
||||||
|
txPrestateVal := s.GetCommittedState(key)
|
||||||
|
if txPrestateVal != value {
|
||||||
|
mutations.StorageWrites[key] = value
|
||||||
|
}
|
||||||
delete(s.uncommittedStorage, key)
|
delete(s.uncommittedStorage, key)
|
||||||
} else if exist {
|
} else if exist {
|
||||||
|
// non-parallel-execution:
|
||||||
// The slot is modified to another value and the slot has been
|
// The slot is modified to another value and the slot has been
|
||||||
// tracked for commit, do nothing here.
|
// tracked for commit in uncommittedStorage.
|
||||||
|
//
|
||||||
|
// parallel-exec-with-BAL:
|
||||||
|
// each statedb instance only executes a single transaction so the previous value
|
||||||
|
// of the slot won't be in uncommittedStorage
|
||||||
|
mutations.StorageWrites[key] = value
|
||||||
} else {
|
} else {
|
||||||
// The slot is different from its original value and hasn't been
|
// The slot is different from its original value and hasn't been
|
||||||
// tracked for commit yet.
|
// tracked for commit yet.
|
||||||
s.uncommittedStorage[key] = s.GetCommittedState(key)
|
// Whether executing parallel with BAL or not, the value of the slot before the execution
|
||||||
|
// of the current transaction is in originStorage
|
||||||
|
origin := s.GetCommittedState(key)
|
||||||
|
mutations.StorageWrites[key] = value
|
||||||
|
s.uncommittedStorage[key] = origin
|
||||||
slotsToPrefetch = append(slotsToPrefetch, key) // Copy needed for closure
|
slotsToPrefetch = append(slotsToPrefetch, key) // Copy needed for closure
|
||||||
}
|
}
|
||||||
// Aggregate the dirty storage slots into the pending area. It might
|
// Aggregate the dirty storage slots into the pending area. It might
|
||||||
|
|
@ -284,6 +333,18 @@ func (s *stateObject) finalise() {
|
||||||
// of the newly-created object as it's no longer eligible for self-destruct
|
// of the newly-created object as it's no longer eligible for self-destruct
|
||||||
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
||||||
s.newContract = false
|
s.newContract = false
|
||||||
|
|
||||||
|
s.nonFinalizedCode = false
|
||||||
|
s.txPrestateCode = nil
|
||||||
|
|
||||||
|
// TODO(jwasinger): I had a bug here where i would set both of these to the value of s.data.* and there were no amsterdam-release test failures. need to figure out why.
|
||||||
|
s.txPreBalance = s.Balance().Clone()
|
||||||
|
s.txPreNonce = s.Nonce()
|
||||||
|
|
||||||
|
if mutations.Nonce == nil && mutations.Code == nil && mutations.Balance == nil && len(mutations.StorageWrites) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateTrie is responsible for persisting cached storage changes into the
|
// updateTrie is responsible for persisting cached storage changes into the
|
||||||
|
|
@ -511,6 +572,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||||
dirtyCode: s.dirtyCode,
|
dirtyCode: s.dirtyCode,
|
||||||
selfDestructed: s.selfDestructed,
|
selfDestructed: s.selfDestructed,
|
||||||
newContract: s.newContract,
|
newContract: s.newContract,
|
||||||
|
txPreBalance: s.txPreBalance.Clone(),
|
||||||
}
|
}
|
||||||
|
|
||||||
switch s.trie.(type) {
|
switch s.trie.(type) {
|
||||||
|
|
@ -587,13 +649,26 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) (prev []byte) {
|
||||||
prev = slices.Clone(s.code)
|
prev = slices.Clone(s.code)
|
||||||
s.db.journal.setCode(s.address, prev)
|
s.db.journal.setCode(s.address, prev)
|
||||||
s.setCode(codeHash, code)
|
s.setCode(codeHash, code)
|
||||||
|
|
||||||
return prev
|
return prev
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
|
prevCode := s.code
|
||||||
|
if s.txPrestateCode == nil {
|
||||||
|
if prevCode == nil {
|
||||||
|
prevCode = []byte{}
|
||||||
|
}
|
||||||
|
s.txPrestateCode = prevCode
|
||||||
|
}
|
||||||
|
if !bytes.Equal(code, s.txPrestateCode) {
|
||||||
|
s.dirtyCode = true
|
||||||
|
s.nonFinalizedCode = true
|
||||||
|
} else {
|
||||||
|
s.nonFinalizedCode = false
|
||||||
|
}
|
||||||
s.code = code
|
s.code = code
|
||||||
s.data.CodeHash = codeHash[:]
|
s.data.CodeHash = codeHash[:]
|
||||||
s.dirtyCode = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetNonce(nonce uint64) {
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,12 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -193,6 +194,7 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
accessList: newAccessList(),
|
accessList: newAccessList(),
|
||||||
transientStorage: newTransientStorage(),
|
transientStorage: newTransientStorage(),
|
||||||
|
stateReadList: bal.NewStateAccessList(),
|
||||||
}
|
}
|
||||||
if db.Type().Is(TypeUBT) {
|
if db.Type().Is(TypeUBT) {
|
||||||
sdb.accessEvents = NewAccessEvents()
|
sdb.accessEvents = NewAccessEvents()
|
||||||
|
|
@ -200,6 +202,13 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
||||||
return sdb, nil
|
return sdb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithReader returns a copy of the StateDB instance with the specified reader.
|
||||||
|
func (s *StateDB) WithReader(reader Reader) *StateDB {
|
||||||
|
cpy := s.Copy()
|
||||||
|
cpy.reader = reader
|
||||||
|
return cpy
|
||||||
|
}
|
||||||
|
|
||||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||||
// state trie concurrently while the state is mutated so that when we reach the
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// commit phase, most of the needed data is already hot.
|
||||||
|
|
@ -799,8 +808,9 @@ func (s *StateDB) LogsForBurnAccounts() []*types.Log {
|
||||||
// Finalise finalises the state by removing the destructed objects and clears
|
// Finalise finalises the state by removing the destructed objects and clears
|
||||||
// the journal as well as the refunds. Finalise, however, will not push any updates
|
// the journal as well as the refunds. Finalise, however, will not push any updates
|
||||||
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
// into the tries just yet. Only IntermediateRoot or Commit will do that.
|
||||||
func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
func (s *StateDB) Finalise(deleteEmptyObjects bool) (accesses *bal.StateAccessList, mutations *bal.StateMutations) {
|
||||||
addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties))
|
addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties))
|
||||||
|
mutations = bal.NewStateMutations()
|
||||||
for addr := range s.journal.dirties {
|
for addr := range s.journal.dirties {
|
||||||
obj, exist := s.stateObjects[addr]
|
obj, exist := s.stateObjects[addr]
|
||||||
if !exist {
|
if !exist {
|
||||||
|
|
@ -822,8 +832,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
||||||
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||||
s.stateObjectsDestruct[obj.address] = obj
|
s.stateObjectsDestruct[obj.address] = obj
|
||||||
}
|
}
|
||||||
|
// a pre-existing account can only be removed from the state under the following circumstance:
|
||||||
|
// it had a balance and was the target of a create2 which selfdestructed in the initcode
|
||||||
|
if !obj.txPreBalance.IsZero() {
|
||||||
|
mutations.Set(addr, &bal.AccountMutations{
|
||||||
|
Balance: uint256.NewInt(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
obj.finalise()
|
mut := obj.finalise()
|
||||||
|
if mut != nil {
|
||||||
|
mutations.Set(addr, mut)
|
||||||
|
}
|
||||||
s.markUpdate(addr)
|
s.markUpdate(addr)
|
||||||
}
|
}
|
||||||
// At this point, also ship the address off to the precacher. The precacher
|
// At this point, also ship the address off to the precacher. The precacher
|
||||||
|
|
@ -838,8 +858,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
||||||
}
|
}
|
||||||
// Invalidate journal because reverting across transactions is not allowed.
|
// Invalidate journal because reverting across transactions is not allowed.
|
||||||
s.clearJournalAndRefund()
|
s.clearJournalAndRefund()
|
||||||
|
return s.stateReadList, mutations
|
||||||
return s.stateReadList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntermediateRoot computes the current root hash of the state trie.
|
// IntermediateRoot computes the current root hash of the state trie.
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,12 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -234,7 +235,7 @@ func (s *hookedStateDB) LogsForBurnAccounts() []*types.Log {
|
||||||
return s.inner.LogsForBurnAccounts()
|
return s.inner.LogsForBurnAccounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) (*bal.StateAccessList, *bal.StateMutations) {
|
||||||
if s.hooks.OnBalanceChange == nil && s.hooks.OnNonceChangeV2 == nil && s.hooks.OnNonceChange == nil && s.hooks.OnCodeChangeV2 == nil && s.hooks.OnCodeChange == nil {
|
if s.hooks.OnBalanceChange == nil && s.hooks.OnNonceChangeV2 == nil && s.hooks.OnNonceChange == nil && s.hooks.OnCodeChangeV2 == nil && s.hooks.OnCodeChange == nil {
|
||||||
// Short circuit if no relevant hooks are set.
|
// Short circuit if no relevant hooks are set.
|
||||||
return s.inner.Finalise(deleteEmptyObjects)
|
return s.inner.Finalise(deleteEmptyObjects)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
|
|
@ -63,13 +65,15 @@ func (p *StateProcessor) chainConfig() *params.ChainConfig {
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
var (
|
var (
|
||||||
config = p.chainConfig()
|
config = p.chainConfig()
|
||||||
receipts = make(types.Receipts, 0, len(block.Transactions()))
|
receipts = make(types.Receipts, 0, len(block.Transactions()))
|
||||||
header = block.Header()
|
header = block.Header()
|
||||||
blockHash = block.Hash()
|
blockHash = block.Hash()
|
||||||
blockNumber = block.Number()
|
blockNumber = block.Number()
|
||||||
allLogs []*types.Log
|
allLogs []*types.Log
|
||||||
gp = NewGasPool(block.GasLimit())
|
gp = NewGasPool(block.GasLimit())
|
||||||
|
computedAccessList = bal.NewConstructionBlockAccessList()
|
||||||
|
isAmsterdam = p.chainConfig().IsAmsterdam(block.Number(), block.Time())
|
||||||
)
|
)
|
||||||
var tracingStateDB = vm.StateDB(statedb)
|
var tracingStateDB = vm.StateDB(statedb)
|
||||||
if hooks := cfg.Tracer; hooks != nil {
|
if hooks := cfg.Tracer; hooks != nil {
|
||||||
|
|
@ -90,10 +94,18 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
||||||
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
evm := vm.NewEVM(context, tracingStateDB, config, cfg)
|
||||||
|
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
accesses, mutations := ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
|
if isAmsterdam {
|
||||||
|
computedAccessList.AddBlockInitMutations(mutations)
|
||||||
|
computedAccessList.AddAccesses(accesses)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if config.IsPrague(block.Number(), block.Time()) || config.IsUBT(block.Number(), block.Time()) {
|
if config.IsPrague(block.Number(), block.Time()) || config.IsUBT(block.Number(), block.Time()) {
|
||||||
ProcessParentBlockHash(block.ParentHash(), evm)
|
accesses, mutations := ProcessParentBlockHash(block.ParentHash(), evm)
|
||||||
|
if isAmsterdam {
|
||||||
|
computedAccessList.AddBlockInitMutations(mutations)
|
||||||
|
computedAccessList.AddAccesses(accesses)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
|
|
@ -108,60 +120,81 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
||||||
telemetry.Int64Attribute("tx.index", int64(i)),
|
telemetry.Int64Attribute("tx.index", int64(i)),
|
||||||
)
|
)
|
||||||
|
|
||||||
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, evm)
|
accesses, mutations, receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
spanEnd(&err)
|
spanEnd(&err)
|
||||||
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
|
|
||||||
|
if isAmsterdam {
|
||||||
|
computedAccessList.AddTransactionMutations(mutations, i)
|
||||||
|
computedAccessList.AddAccesses(accesses)
|
||||||
|
}
|
||||||
spanEnd(nil)
|
spanEnd(nil)
|
||||||
}
|
}
|
||||||
requests, err := postExecution(ctx, config, block, allLogs, evm)
|
postMut := bal.NewStateMutations()
|
||||||
|
postReads, postExecMut, requests, err := postExecution(ctx, config, block, allLogs, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postMut.Merge(postExecMut)
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body())
|
eip4895WithdrawalReads, eip4985WithdrawalMuts := p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body())
|
||||||
|
postMut.Merge(eip4985WithdrawalMuts)
|
||||||
|
if isAmsterdam {
|
||||||
|
computedAccessList.AddBlockFinalizeMutations(postMut)
|
||||||
|
computedAccessList.AddAccesses(postReads)
|
||||||
|
computedAccessList.AddAccesses(eip4895WithdrawalReads)
|
||||||
|
}
|
||||||
|
|
||||||
return &ProcessResult{
|
return &ProcessResult{
|
||||||
Receipts: receipts,
|
Receipts: receipts,
|
||||||
Requests: requests,
|
Requests: requests,
|
||||||
Logs: allLogs,
|
Logs: allLogs,
|
||||||
GasUsed: gp.Used(),
|
GasUsed: gp.Used(),
|
||||||
|
AccessList: computedAccessList,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// postExecution processes the post-execution system calls if Prague is enabled.
|
// postExecution processes the post-execution system calls if Prague is enabled.
|
||||||
func postExecution(ctx context.Context, config *params.ChainConfig, block *types.Block, allLogs []*types.Log, evm *vm.EVM) (requests [][]byte, err error) {
|
func postExecution(ctx context.Context, config *params.ChainConfig, block *types.Block, allLogs []*types.Log, evm *vm.EVM) (accesses *bal.StateAccessList, mutations *bal.StateMutations, requests [][]byte, err error) {
|
||||||
_, _, spanEnd := telemetry.StartSpan(ctx, "core.postExecution")
|
_, _, spanEnd := telemetry.StartSpan(ctx, "core.postExecution")
|
||||||
defer spanEnd(&err)
|
defer spanEnd(&err)
|
||||||
|
mutations = bal.NewStateMutations()
|
||||||
|
accesses = bal.NewStateAccessList()
|
||||||
|
|
||||||
// Read requests if Prague is enabled.
|
// Read requests if Prague is enabled.
|
||||||
if config.IsPrague(block.Number(), block.Time()) {
|
if config.IsPrague(block.Number(), block.Time()) {
|
||||||
requests = [][]byte{}
|
requests = [][]byte{}
|
||||||
// EIP-6110
|
// EIP-6110
|
||||||
if err := ParseDepositLogs(&requests, allLogs, config); err != nil {
|
if err := ParseDepositLogs(&requests, allLogs, config); err != nil {
|
||||||
return requests, fmt.Errorf("failed to parse deposit logs: %w", err)
|
return nil, nil, requests, fmt.Errorf("failed to parse deposit logs: %w", err)
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
if accesses, mutations, err = ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
return requests, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
return nil, nil, requests, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
||||||
}
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
consolidationAccesses, consolidationMutations, err := ProcessConsolidationQueue(&requests, evm)
|
||||||
return requests, fmt.Errorf("failed to process consolidation queue: %w", err)
|
if err != nil {
|
||||||
|
return nil, nil, requests, fmt.Errorf("failed to process consolidation queue: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutations.Merge(consolidationMutations)
|
||||||
|
accesses.Merge(consolidationAccesses)
|
||||||
}
|
}
|
||||||
|
|
||||||
return requests, nil
|
return accesses, mutations, requests, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
// this method takes an already created EVM instance as input.
|
// this method takes an already created EVM instance as input.
|
||||||
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, evm *vm.EVM) (accesses *bal.StateAccessList, mutations *bal.StateMutations, receipt *types.Receipt, err error) {
|
||||||
if hooks := evm.Config.Tracer; hooks != nil {
|
if hooks := evm.Config.Tracer; hooks != nil {
|
||||||
if hooks.OnTxStart != nil {
|
if hooks.OnTxStart != nil {
|
||||||
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
|
@ -173,12 +206,12 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
|
||||||
// Apply the transaction to the current state (included in the env).
|
// Apply the transaction to the current state (included in the env).
|
||||||
result, err := ApplyMessage(evm, msg, gp)
|
result, err := ApplyMessage(evm, msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// Update the state with pending changes.
|
// Update the state with pending changes.
|
||||||
var root []byte
|
var root []byte
|
||||||
if evm.ChainConfig().IsByzantium(blockNumber) {
|
if evm.ChainConfig().IsByzantium(blockNumber) {
|
||||||
evm.StateDB.Finalise(true)
|
accesses, mutations = evm.StateDB.Finalise(true)
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes()
|
root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes()
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +220,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
|
||||||
if statedb.Database().Type().Is(state.TypeUBT) {
|
if statedb.Database().Type().Is(state.TypeUBT) {
|
||||||
statedb.AccessEvents().Merge(evm.AccessEvents)
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, gp.CumulativeUsed(), root), nil
|
return accesses, mutations, MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, gp.CumulativeUsed(), root), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
||||||
|
|
@ -231,10 +264,10 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
|
||||||
// and uses the input parameters for its environment. It returns the receipt
|
// and uses the input parameters for its environment. It returns the receipt
|
||||||
// for the transaction and an error if the transaction failed,
|
// for the transaction and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// indicating the block was invalid.
|
||||||
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction) (*types.Receipt, error) {
|
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction) (*bal.StateAccessList, *bal.StateMutations, *types.Receipt, error) {
|
||||||
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
|
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// Create a new context to be used in the EVM environment
|
// Create a new context to be used in the EVM environment
|
||||||
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
|
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
|
||||||
|
|
@ -242,7 +275,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
|
||||||
|
|
||||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||||
// contract. This method is exported to be used in tests.
|
// contract. This method is exported to be used in tests.
|
||||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) (*bal.StateAccessList, *bal.StateMutations) {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -264,12 +297,12 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
evm.StateDB.Finalise(true)
|
return evm.StateDB.Finalise(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessParentBlockHash stores the parent block hash in the history storage contract
|
// ProcessParentBlockHash stores the parent block hash in the history storage contract
|
||||||
// as per EIP-2935/7709.
|
// as per EIP-2935/7709.
|
||||||
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) (*bal.StateAccessList, *bal.StateMutations) {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -294,22 +327,22 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
evm.StateDB.Finalise(true)
|
return evm.StateDB.Finalise(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
||||||
// It returns the opaque request data returned by the contract.
|
// It returns the opaque request data returned by the contract.
|
||||||
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) error {
|
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) (*bal.StateAccessList, *bal.StateMutations, error) {
|
||||||
return processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
|
return processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
||||||
// It returns the opaque request data returned by the contract.
|
// It returns the opaque request data returned by the contract.
|
||||||
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) error {
|
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) (*bal.StateAccessList, *bal.StateMutations, error) {
|
||||||
return processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
|
return processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) error {
|
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) (*bal.StateAccessList, *bal.StateMutations, error) {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -330,19 +363,19 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
evm.StateDB.Finalise(true)
|
accesses, mutations := evm.StateDB.Finalise(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("system call failed to execute: %v", err)
|
return nil, nil, fmt.Errorf("system call failed to execute: %v", err)
|
||||||
}
|
}
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
return nil // skip empty output
|
return accesses, mutations, nil // skip empty output
|
||||||
}
|
}
|
||||||
// Append prefixed requestsData to the requests list.
|
// Append prefixed requestsData to the requests list.
|
||||||
requestsData := make([]byte, len(ret)+1)
|
requestsData := make([]byte, len(ret)+1)
|
||||||
requestsData[0] = requestType
|
requestsData[0] = requestType
|
||||||
copy(requestsData[1:], ret)
|
copy(requestsData[1:], ret)
|
||||||
*requests = append(*requests, requestsData)
|
*requests = append(*requests, requestsData)
|
||||||
return nil
|
return accesses, mutations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
||||||
|
|
|
||||||
|
|
@ -469,6 +469,7 @@ func TestEIP8037MaxRegularGasValidation(t *testing.T) {
|
||||||
Amsterdam: params.DefaultOsakaBlobConfig,
|
Amsterdam: params.DefaultOsakaBlobConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
rules = config.Rules(common.Big0, true, 0)
|
||||||
signer = types.LatestSigner(config)
|
signer = types.LatestSigner(config)
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
)
|
)
|
||||||
|
|
@ -486,7 +487,7 @@ func TestEIP8037MaxRegularGasValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that floor data gas exceeds MaxTxGas
|
// Verify that floor data gas exceeds MaxTxGas
|
||||||
floorGas, err := FloorDataGas(largeData)
|
floorGas, err := FloorDataGas(rules, largeData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to calculate floor data gas: %v", err)
|
t.Fatalf("Failed to calculate floor data gas: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -54,8 +56,9 @@ type Processor interface {
|
||||||
|
|
||||||
// ProcessResult contains the values computed by Process.
|
// ProcessResult contains the values computed by Process.
|
||||||
type ProcessResult struct {
|
type ProcessResult struct {
|
||||||
Receipts types.Receipts
|
Receipts types.Receipts
|
||||||
Requests [][]byte
|
Requests [][]byte
|
||||||
Logs []*types.Log
|
Logs []*types.Log
|
||||||
GasUsed uint64
|
GasUsed uint64
|
||||||
|
AccessList *bal.ConstructionBlockAccessList
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,3 +92,18 @@ func (s *StateAccessList) Copy() *StateAccessList {
|
||||||
}
|
}
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateAccessList) Eq(other StateAccessList) bool {
|
||||||
|
if len(s.list) != len(other.list) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for addr, accesses := range s.list {
|
||||||
|
if _, ok := other.list[addr]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !maps.Equal(accesses, other.list[addr]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,156 +18,331 @@ package bal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"maps"
|
"maps"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConstructionAccountAccess contains post-block account state for mutations as well as
|
// ConstructionAccountAccesses contains post-block account state for mutations as well as
|
||||||
// all storage keys that were read during execution. It is used when building block
|
// all storage keys that were read during execution. It is used when building block
|
||||||
// access list during execution.
|
// access list during execution.
|
||||||
type ConstructionAccountAccess struct {
|
type ConstructionAccountAccesses 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 contains the post-state contract code of an account keyed
|
CodeChanges map[uint16][]byte
|
||||||
// by tx index.
|
|
||||||
CodeChange map[uint16][]byte `json:"codeChange,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstructionAccountAccess initializes the account access object.
|
func (c *ConstructionAccountAccesses) Copy() (res ConstructionAccountAccesses) {
|
||||||
func NewConstructionAccountAccess() *ConstructionAccountAccess {
|
if c.StorageWrites != nil {
|
||||||
return &ConstructionAccountAccess{
|
res.StorageWrites = make(map[common.Hash]map[uint16]common.Hash)
|
||||||
|
for slot, writes := range c.StorageWrites {
|
||||||
|
res.StorageWrites[slot] = maps.Clone(writes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.StorageReads != nil {
|
||||||
|
res.StorageReads = maps.Clone(c.StorageReads)
|
||||||
|
}
|
||||||
|
if c.BalanceChanges != nil {
|
||||||
|
res.BalanceChanges = maps.Clone(c.BalanceChanges)
|
||||||
|
}
|
||||||
|
if c.NonceChanges != nil {
|
||||||
|
res.NonceChanges = maps.Clone(c.NonceChanges)
|
||||||
|
}
|
||||||
|
if c.CodeChanges != nil {
|
||||||
|
res.CodeChanges = maps.Clone(c.CodeChanges)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
type StateMutations struct {
|
||||||
|
list map[common.Address]AccountMutations
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStateMutations() *StateMutations {
|
||||||
|
return &StateMutations{make(map[common.Address]AccountMutations)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StateMutations) String() string {
|
||||||
|
b, _ := json.MarshalIndent(s, "", " ")
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *StateMutations) Merge(next *StateMutations) {
|
||||||
|
if next == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for account, diff := range next.list {
|
||||||
|
if mut, ok := s.list[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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.list[account] = mut
|
||||||
|
} else {
|
||||||
|
s.list[account] = *diff.Copy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateMutations) Eq(other *StateMutations) bool {
|
||||||
|
if s == nil && other == nil {
|
||||||
|
return true
|
||||||
|
} else if s == nil && other != nil {
|
||||||
|
return false
|
||||||
|
} else if s != nil && other == nil {
|
||||||
|
return false
|
||||||
|
} else if len(s.list) != len(other.list) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr, mut := range s.list {
|
||||||
|
otherMut, ok := other.list[addr]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mut.Eq(&otherMut) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateMutations) Set(addr common.Address, mut *AccountMutations) {
|
||||||
|
s.list[addr] = *mut
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConstructionBlockAccessList struct {
|
||||||
|
list map[common.Address]*ConstructionAccountAccesses
|
||||||
|
transactionCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConstructionBlockAccessList() *ConstructionBlockAccessList {
|
||||||
|
return &ConstructionBlockAccessList{
|
||||||
|
make(map[common.Address]*ConstructionAccountAccesses),
|
||||||
|
0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res := NewConstructionBlockAccessList()
|
||||||
|
for addr, accountAccess := range c.list {
|
||||||
|
aaCopy := accountAccess.Copy()
|
||||||
|
res.list[addr] = &aaCopy
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// must be called after all txs are added
|
||||||
|
func (c *ConstructionBlockAccessList) AddBlockFinalizeMutations(muts *StateMutations) {
|
||||||
|
c.addMutations(muts, c.transactionCount+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConstructionBlockAccessList) AddBlockInitMutations(muts *StateMutations) {
|
||||||
|
c.addMutations(muts, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConstructionBlockAccessList) AddTransactionMutations(muts *StateMutations, txIdx int) {
|
||||||
|
c.transactionCount = max(c.transactionCount, txIdx+1)
|
||||||
|
c.addMutations(muts, c.transactionCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConstructionBlockAccessList) addMutations(muts *StateMutations, index int) {
|
||||||
|
if muts == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TO
|
||||||
|
idx := uint16(index)
|
||||||
|
for addr, mut := range muts.list {
|
||||||
|
if _, exist := c.list[addr]; !exist {
|
||||||
|
c.list[addr] = newConstructionAccountAccesses()
|
||||||
|
}
|
||||||
|
if mut.Nonce != nil {
|
||||||
|
if c.list[addr].NonceChanges == nil {
|
||||||
|
c.list[addr].NonceChanges = make(map[uint16]uint64)
|
||||||
|
}
|
||||||
|
c.list[addr].NonceChanges[idx] = *mut.Nonce
|
||||||
|
}
|
||||||
|
if mut.Balance != nil {
|
||||||
|
if c.list[addr].BalanceChanges == nil {
|
||||||
|
c.list[addr].BalanceChanges = make(map[uint16]*uint256.Int)
|
||||||
|
}
|
||||||
|
c.list[addr].BalanceChanges[idx] = mut.Balance.Clone()
|
||||||
|
}
|
||||||
|
if mut.Code != nil {
|
||||||
|
if c.list[addr].CodeChanges == nil {
|
||||||
|
c.list[addr].CodeChanges = make(map[uint16][]byte)
|
||||||
|
}
|
||||||
|
c.list[addr].CodeChanges[idx] = bytes.Clone(mut.Code)
|
||||||
|
}
|
||||||
|
if len(mut.StorageWrites) > 0 {
|
||||||
|
for key, val := range mut.StorageWrites {
|
||||||
|
if c.list[addr].StorageWrites[key] == nil {
|
||||||
|
c.list[addr].StorageWrites[key] = make(map[uint16]common.Hash)
|
||||||
|
}
|
||||||
|
c.list[addr].StorageWrites[key][idx] = val
|
||||||
|
|
||||||
|
// delete the key from the tracked reads if it was previously read.
|
||||||
|
delete(c.list[addr].StorageReads, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConstructionBlockAccessList) AddAccesses(reads *StateAccessList) {
|
||||||
|
if reads == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for addr, addrReads := range reads.list {
|
||||||
|
if _, ok := c.list[addr]; !ok {
|
||||||
|
c.list[addr] = newConstructionAccountAccesses()
|
||||||
|
}
|
||||||
|
for storageKey, _ := range addrReads {
|
||||||
|
if c.list[addr].StorageWrites != nil {
|
||||||
|
if _, ok := c.list[addr].StorageWrites[storageKey]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.list[addr].StorageReads == nil {
|
||||||
|
c.list[addr].StorageReads = make(map[common.Hash]struct{})
|
||||||
|
}
|
||||||
|
c.list[addr].StorageReads[storageKey] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConstructionAccountAccesses() *ConstructionAccountAccesses {
|
||||||
|
return &ConstructionAccountAccesses{
|
||||||
StorageWrites: make(map[common.Hash]map[uint16]common.Hash),
|
StorageWrites: make(map[common.Hash]map[uint16]common.Hash),
|
||||||
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),
|
||||||
CodeChange: make(map[uint16][]byte),
|
CodeChanges: make(map[uint16][]byte),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstructionBlockAccessList contains post-block modified state and some state accessed
|
type StorageMutations map[common.Hash]common.Hash
|
||||||
// in execution (account addresses and storage keys).
|
|
||||||
type ConstructionBlockAccessList struct {
|
// AccountMutations contains mutations that were made to an account across
|
||||||
Accounts map[common.Address]*ConstructionAccountAccess
|
// one or more access list indices.
|
||||||
|
type AccountMutations struct {
|
||||||
|
Balance *uint256.Int `json:"Balance,omitempty"`
|
||||||
|
Nonce *uint64 `json:"Nonce,omitempty"`
|
||||||
|
Code ContractCode `json:"Code,omitempty"`
|
||||||
|
StorageWrites StorageMutations `json:"StorageWrites,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstructionBlockAccessList instantiates an empty access list.
|
// String returns a human-readable JSON representation of the account mutations.
|
||||||
func NewConstructionBlockAccessList() ConstructionBlockAccessList {
|
func (a *AccountMutations) String() string {
|
||||||
return ConstructionBlockAccessList{
|
var res bytes.Buffer
|
||||||
Accounts: make(map[common.Address]*ConstructionAccountAccess),
|
enc := json.NewEncoder(&res)
|
||||||
}
|
enc.SetIndent("", " ")
|
||||||
|
enc.Encode(a)
|
||||||
|
return res.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountRead records the address of an account that has been read during execution.
|
// Copy returns a deep-copy of the instance.
|
||||||
func (b *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
func (a *AccountMutations) Copy() *AccountMutations {
|
||||||
if _, ok := b.Accounts[addr]; !ok {
|
res := &AccountMutations{
|
||||||
b.Accounts[addr] = NewConstructionAccountAccess()
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
}
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorageRead records a storage key read during execution.
|
// Copy returns a deep copy of the access list
|
||||||
func (b *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) {
|
func (e BlockAccessList) Copy() *BlockAccessList {
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
var res BlockAccessList
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
for _, accountAccess := range e {
|
||||||
}
|
res = append(res, accountAccess.Copy())
|
||||||
if _, ok := b.Accounts[address].StorageWrites[key]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.Accounts[address].StorageReads[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StorageWrite records the post-transaction value of a mutated storage slot.
|
|
||||||
// The storage slot is removed from the list of read slots.
|
|
||||||
func (b *ConstructionBlockAccessList) StorageWrite(txIdx uint16, address common.Address, key, value common.Hash) {
|
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
|
||||||
}
|
|
||||||
if _, ok := b.Accounts[address].StorageWrites[key]; !ok {
|
|
||||||
b.Accounts[address].StorageWrites[key] = make(map[uint16]common.Hash)
|
|
||||||
}
|
|
||||||
b.Accounts[address].StorageWrites[key][txIdx] = value
|
|
||||||
|
|
||||||
delete(b.Accounts[address].StorageReads, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CodeChange records the code of a newly-created contract.
|
|
||||||
func (b *ConstructionBlockAccessList) CodeChange(address common.Address, txIndex uint16, code []byte) {
|
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
|
||||||
}
|
|
||||||
// TODO(rjl493456442) is it essential to deep-copy the code?
|
|
||||||
b.Accounts[address].CodeChange[txIndex] = bytes.Clone(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NonceChange records tx post-state nonce of any contract-like accounts whose
|
|
||||||
// nonce was incremented.
|
|
||||||
func (b *ConstructionBlockAccessList) NonceChange(address common.Address, txIdx uint16, postNonce uint64) {
|
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
|
||||||
}
|
|
||||||
b.Accounts[address].NonceChanges[txIdx] = postNonce
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalanceChange records the post-transaction balance of an account whose
|
|
||||||
// balance changed.
|
|
||||||
func (b *ConstructionBlockAccessList) BalanceChange(txIdx uint16, address common.Address, balance *uint256.Int) {
|
|
||||||
if _, ok := b.Accounts[address]; !ok {
|
|
||||||
b.Accounts[address] = NewConstructionAccountAccess()
|
|
||||||
}
|
|
||||||
b.Accounts[address].BalanceChanges[txIdx] = balance.Clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrettyPrint returns a human-readable representation of the access list
|
|
||||||
func (b *ConstructionBlockAccessList) PrettyPrint() string {
|
|
||||||
enc := b.toEncodingObj()
|
|
||||||
return enc.PrettyPrint()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a deep copy of the access list.
|
|
||||||
func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
|
||||||
res := NewConstructionBlockAccessList()
|
|
||||||
for addr, aa := range b.Accounts {
|
|
||||||
var aaCopy ConstructionAccountAccess
|
|
||||||
|
|
||||||
slotWrites := make(map[common.Hash]map[uint16]common.Hash, len(aa.StorageWrites))
|
|
||||||
for key, m := range aa.StorageWrites {
|
|
||||||
slotWrites[key] = maps.Clone(m)
|
|
||||||
}
|
|
||||||
aaCopy.StorageWrites = slotWrites
|
|
||||||
aaCopy.StorageReads = maps.Clone(aa.StorageReads)
|
|
||||||
|
|
||||||
balances := make(map[uint16]*uint256.Int, len(aa.BalanceChanges))
|
|
||||||
for index, balance := range aa.BalanceChanges {
|
|
||||||
balances[index] = balance.Clone()
|
|
||||||
}
|
|
||||||
aaCopy.BalanceChanges = balances
|
|
||||||
aaCopy.NonceChanges = maps.Clone(aa.NonceChanges)
|
|
||||||
|
|
||||||
codes := make(map[uint16][]byte, len(aa.CodeChange))
|
|
||||||
for index, code := range aa.CodeChange {
|
|
||||||
codes[index] = bytes.Clone(code)
|
|
||||||
}
|
|
||||||
aaCopy.CodeChange = codes
|
|
||||||
res.Accounts[addr] = &aaCopy
|
|
||||||
}
|
}
|
||||||
return &res
|
return &res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eq returns whether the calling instance is equal to the provided one.
|
||||||
|
func (a *AccountMutations) Eq(other *AccountMutations) 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 !maps.Equal(a.StorageWrites, other.StorageWrites) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package bal
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -28,35 +30,132 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"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 AccessListBuilder.
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returns an error if the contents of the access list are not ordered
|
func (e *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = (*e)[:0]
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var access AccountAccess
|
||||||
|
if err := access.DecodeRLP(dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = append(*e, access)
|
||||||
|
}
|
||||||
|
dec.ListEnd()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BlockAccessList) EncodedSize() int {
|
||||||
|
b, err := rlp.EncodeToBytes(e)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: proper to crit here?
|
||||||
|
log.Crit("failed to rlp encode access list", "err", err)
|
||||||
|
}
|
||||||
|
return len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BlockAccessList) JSONString() string {
|
||||||
|
res, _ := json.MarshalIndent(e.StringableRepresentation(), "", " ")
|
||||||
|
return string(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringableRepresentation returns an instance of the block access list
|
||||||
|
// which can be converted to a human-readable JSON representation.
|
||||||
|
func (e *BlockAccessList) StringableRepresentation() interface{} {
|
||||||
|
res := []AccountAccess{}
|
||||||
|
for _, aa := range *e {
|
||||||
|
res = append(res, aa)
|
||||||
|
}
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BlockAccessList) String() string {
|
||||||
|
var res bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&res)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
// TODO: check error
|
||||||
|
enc.Encode(e)
|
||||||
|
return res.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check that no fields are nil in Validate (unless it's valid for them to be nil)
|
||||||
|
|
||||||
|
// 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 {
|
// * the total accounts and storage slots in the access list exceed the protocol max
|
||||||
if !slices.IsSortedFunc(e.Accesses, func(a, b AccountAccess) int {
|
func (e BlockAccessList) Validate(blockTxCount int, blockGasLimit uint64) error {
|
||||||
|
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 {
|
// check that the accounts are unique
|
||||||
if err := entry.validate(); err != nil {
|
addrs := make(map[common.Address]struct{})
|
||||||
|
for _, acct := range e {
|
||||||
|
addr := acct.Address
|
||||||
|
if _, ok := addrs[addr]; ok {
|
||||||
|
return fmt.Errorf("duplicate account in block access list: %x", addr)
|
||||||
|
}
|
||||||
|
addrs[addr] = struct{}{}
|
||||||
|
}
|
||||||
|
// validate individual entries
|
||||||
|
for _, entry := range e {
|
||||||
|
if err := entry.validate(blockTxCount); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check that the total number of items doesn't exceed max
|
||||||
|
return e.ValidateGasLimit(blockGasLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateGasLimit checks that the number of BAL items does not exceed the
|
||||||
|
// block gas limit divided by the per-item cost (EIP-7928).
|
||||||
|
func (e BlockAccessList) ValidateGasLimit(blockGasLimit uint64) error {
|
||||||
|
var balItems uint64
|
||||||
|
for _, account := range e {
|
||||||
|
// Count each address as one item
|
||||||
|
balItems++
|
||||||
|
// Count unique storage keys across both reads and writes
|
||||||
|
uniqueSlots := make(map[common.Hash]struct{})
|
||||||
|
for _, sc := range account.StorageChanges {
|
||||||
|
uniqueSlots[sc.Slot.ToHash()] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, sr := range account.StorageReads {
|
||||||
|
uniqueSlots[sr.ToHash()] = struct{}{}
|
||||||
|
}
|
||||||
|
balItems += uint64(len(uniqueSlots))
|
||||||
|
}
|
||||||
|
limit := blockGasLimit / params.GasBlockAccessListItem
|
||||||
|
if balItems > limit {
|
||||||
|
return fmt.Errorf("block access list exceeds gas limit: %d items exceeds limit of %d", balItems, limit)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,53 +169,135 @@ func (e *BlockAccessList) Hash() common.Hash {
|
||||||
// under reasonable conditions.
|
// under reasonable conditions.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
bal, err := json.MarshalIndent(e.StringableRepresentation(), "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
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 *EncodedStorage `json:"valueAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodedStorage can represent either a storage key or value
|
||||||
|
type EncodedStorage struct {
|
||||||
|
inner *uint256.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ rlp.Encoder = &EncodedStorage{}
|
||||||
|
var _ rlp.Decoder = &EncodedStorage{}
|
||||||
|
|
||||||
|
func (s *EncodedStorage) ToHash() common.Hash {
|
||||||
|
if s == nil {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
return s.inner.Bytes32()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEncodedStorageFromHash(hash common.Hash) *EncodedStorage {
|
||||||
|
return &EncodedStorage{
|
||||||
|
new(uint256.Int).SetBytes(hash[:]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EncodedStorage) UnmarshalJSON(b []byte) error {
|
||||||
|
var str string
|
||||||
|
if err := json.Unmarshal(b, &str); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
str = strings.TrimLeft(str, "0x")
|
||||||
|
if len(str) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(str)%2 == 1 {
|
||||||
|
str = "0" + str
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := hex.DecodeString(str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(val) > 32 {
|
||||||
|
return fmt.Errorf("storage key/value cannot be greater than 32 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check is s == nil ?? should be programmer error
|
||||||
|
|
||||||
|
*s = EncodedStorage{
|
||||||
|
inner: new(uint256.Int).SetBytes(val),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s EncodedStorage) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(s.inner.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EncodedStorage) EncodeRLP(_w io.Writer) error {
|
||||||
|
return s.inner.EncodeRLP(_w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EncodedStorage) DecodeRLP(dec *rlp.Stream) error {
|
||||||
|
if s == nil {
|
||||||
|
*s = EncodedStorage{}
|
||||||
|
}
|
||||||
|
s.inner = uint256.NewInt(0)
|
||||||
|
return dec.ReadUint256(s.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 *EncodedStorage `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
|
||||||
// working representation.
|
// working representation.
|
||||||
func (e *encodingSlotWrites) validate() error {
|
func (e *encodingSlotWrites) validate(blockTxCount int) error {
|
||||||
if slices.IsSortedFunc(e.Accesses, func(a, b encodingStorageWrite) int {
|
if e.Slot == nil {
|
||||||
|
return errors.New("nil slot key")
|
||||||
|
}
|
||||||
|
if !slices.IsSortedFunc(e.Accesses, func(a, b encodingStorageWrite) int {
|
||||||
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
||||||
}) {
|
}) {
|
||||||
return nil
|
return errors.New("storage write tx indices not in order")
|
||||||
}
|
}
|
||||||
return errors.New("storage write tx indices not in order")
|
for i, access := range e.Accesses {
|
||||||
|
if access.ValueAfter == nil {
|
||||||
|
return errors.New("nil storage write post")
|
||||||
|
}
|
||||||
|
if i > 0 && e.Accesses[i-1].TxIdx == access.TxIdx {
|
||||||
|
return errors.New("duplicate storage write index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: add test that covers there are actually storage modifications here
|
||||||
|
// if there aren't, it should be a bad block
|
||||||
|
if len(e.Accesses) == 0 {
|
||||||
|
return fmt.Errorf("empty storage writes")
|
||||||
|
} else if int(e.Accesses[len(e.Accesses)-1].TxIdx) >= blockTxCount+2 {
|
||||||
|
return fmt.Errorf("storage access reported index higher than allowed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodingCodeChange contains the runtime bytecode deployed at an address
|
// encodingCodeChange contains the runtime bytecode deployed at an address
|
||||||
|
|
@ -126,64 +307,120 @@ type encodingCodeChange struct {
|
||||||
Code []byte `ssz-max:"300000"` // TODO(rjl493456442) shall we put the limit here? The limit will be increased gradually
|
Code []byte `ssz-max:"300000"` // TODO(rjl493456442) shall we put the limit here? The limit will be increased gradually
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountAccess is the encoding format of ConstructionAccountAccess.
|
// AccountAccess is the encoding format of ConstructionAccountAccesses.
|
||||||
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"` // EncodedStorage changes (slot -> [tx_index -> new_value])
|
||||||
StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys
|
StorageReads []*EncodedStorage `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])
|
||||||
CodeChanges []encodingCodeChange `ssz-max:"300000"` // Code changes ([tx_index -> new_code])
|
CodeChanges []encodingCodeChange `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.
|
||||||
// If any of the keys in the encoding object are not ordered according to the
|
// If any of the keys in the encoding object are not ordered according to the
|
||||||
// spec, an error is returned.
|
// spec, an error is returned.
|
||||||
func (e *AccountAccess) validate() error {
|
func (e *AccountAccess) validate(blockTxCount int) 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[:])
|
aHash, bHash := a.Slot.ToHash(), b.Slot.ToHash()
|
||||||
|
return bytes.Compare(aHash[:], bHash[:])
|
||||||
}) {
|
}) {
|
||||||
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(blockTxCount); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
readKeys := make(map[common.Hash]struct{})
|
||||||
|
writeKeys := make(map[common.Hash]struct{})
|
||||||
|
for _, readKey := range e.StorageReads {
|
||||||
|
if _, ok := readKeys[readKey.ToHash()]; ok {
|
||||||
|
return errors.New("duplicate read key")
|
||||||
|
}
|
||||||
|
readKeys[readKey.ToHash()] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, write := range e.StorageChanges {
|
||||||
|
writeKey := write.Slot
|
||||||
|
if _, ok := writeKeys[writeKey.ToHash()]; ok {
|
||||||
|
return errors.New("duplicate write key")
|
||||||
|
}
|
||||||
|
writeKeys[writeKey.ToHash()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for readKey := range readKeys {
|
||||||
|
if _, ok := writeKeys[readKey]; ok {
|
||||||
|
return errors.New("storage key reported in both read/write sets")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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 *EncodedStorage) int {
|
||||||
return bytes.Compare(a[:], b[:])
|
aHash, bHash := a.ToHash(), b.ToHash()
|
||||||
|
return bytes.Compare(aHash[:], bHash[:])
|
||||||
}) {
|
}) {
|
||||||
return errors.New("storage read slots not in lexicographic order")
|
return errors.New("storage read slots not in lexicographic order")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the balance changes are sorted in order
|
// Check the balance changes are sorted in order
|
||||||
|
// and that none of them report an index above what is allowed
|
||||||
if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int {
|
if !slices.IsSortedFunc(e.BalanceChanges, func(a, b encodingBalanceChange) int {
|
||||||
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
||||||
}) {
|
}) {
|
||||||
return errors.New("balance changes not in ascending order by tx index")
|
return errors.New("balance changes not in ascending order by tx index")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(e.BalanceChanges) > 0 && int(e.BalanceChanges[len(e.BalanceChanges)-1].TxIdx) > blockTxCount+1 {
|
||||||
|
return errors.New("highest balance change index beyond what is allowed")
|
||||||
|
}
|
||||||
|
// check that the balance values are set and there are no duplicate index entries
|
||||||
|
for i, balanceChange := range e.BalanceChanges {
|
||||||
|
if balanceChange.Balance == nil {
|
||||||
|
return errors.New("nil balance change value")
|
||||||
|
}
|
||||||
|
if i > 0 && e.BalanceChanges[i-1].TxIdx == balanceChange.TxIdx {
|
||||||
|
return errors.New("duplicate index for balance change")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check the nonce changes are sorted in order
|
// Check the nonce changes are sorted in order
|
||||||
|
// and that none of them report an index above what is allowed
|
||||||
if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int {
|
if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int {
|
||||||
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
||||||
}) {
|
}) {
|
||||||
return errors.New("nonce changes not in ascending order by tx index")
|
return errors.New("nonce changes not in ascending order by tx index")
|
||||||
}
|
}
|
||||||
|
if len(e.NonceChanges) > 0 && int(e.NonceChanges[len(e.NonceChanges)-1].TxIdx) >= blockTxCount+2 {
|
||||||
|
return errors.New("highest nonce change index beyond what is allowed")
|
||||||
|
}
|
||||||
|
for i, nonceChange := range e.NonceChanges {
|
||||||
|
if i > 0 && nonceChange.TxIdx == e.NonceChanges[i-1].TxIdx {
|
||||||
|
return errors.New("duplicate index reported in nonce changes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check the code changes are sorted in order
|
// TODO: contact testing team to add a test case which has the code changes out of order,
|
||||||
|
// as it wasn't checked here previously
|
||||||
if !slices.IsSortedFunc(e.CodeChanges, func(a, b encodingCodeChange) int {
|
if !slices.IsSortedFunc(e.CodeChanges, func(a, b encodingCodeChange) int {
|
||||||
return cmp.Compare[uint16](a.TxIndex, b.TxIndex)
|
return cmp.Compare[uint16](a.TxIndex, b.TxIndex)
|
||||||
}) {
|
}) {
|
||||||
return errors.New("code changes not in ascending order by tx index")
|
return errors.New("code changes not in ascending order")
|
||||||
}
|
}
|
||||||
for _, change := range e.CodeChanges {
|
if len(e.CodeChanges) > 0 && int(e.CodeChanges[len(e.CodeChanges)-1].TxIndex) >= blockTxCount+2 {
|
||||||
// TODO(rjl493456442): This check should be fork-aware, since the limit may
|
return errors.New("highest code change index beyond what is allowed")
|
||||||
// differ across forks.
|
}
|
||||||
if len(change.Code) > params.MaxCodeSize {
|
for i, codeChange := range e.CodeChanges {
|
||||||
return errors.New("code change contained oversized code")
|
if i > 0 && codeChange.TxIndex == e.CodeChanges[i-1].TxIndex {
|
||||||
|
return errors.New("duplicate index reported in code changes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate that code changes could plausibly be correct (none exceed
|
||||||
|
// max code size of a contract)
|
||||||
|
for _, codeChange := range e.CodeChanges {
|
||||||
|
if len(codeChange.Code) > params.MaxCodeSizeAmsterdam {
|
||||||
|
return fmt.Errorf("code change contained oversized code")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -196,41 +433,35 @@ func (e *AccountAccess) Copy() AccountAccess {
|
||||||
StorageReads: slices.Clone(e.StorageReads),
|
StorageReads: slices.Clone(e.StorageReads),
|
||||||
BalanceChanges: slices.Clone(e.BalanceChanges),
|
BalanceChanges: slices.Clone(e.BalanceChanges),
|
||||||
NonceChanges: slices.Clone(e.NonceChanges),
|
NonceChanges: slices.Clone(e.NonceChanges),
|
||||||
StorageWrites: make([]encodingSlotWrites, 0, len(e.StorageWrites)),
|
|
||||||
CodeChanges: make([]encodingCodeChange, 0, len(e.CodeChanges)),
|
|
||||||
}
|
}
|
||||||
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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, codeChange := range e.CodeChanges {
|
for _, codeChange := range e.CodeChanges {
|
||||||
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
|
res.CodeChanges = append(res.CodeChanges,
|
||||||
TxIndex: codeChange.TxIndex,
|
encodingCodeChange{
|
||||||
Code: bytes.Clone(codeChange.Code),
|
codeChange.TxIndex,
|
||||||
})
|
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{}
|
||||||
|
|
||||||
// toEncodingObj creates an instance of the ConstructionAccountAccess of the type that is
|
// toEncodingObj creates an instance of the ConstructionAccountAccesses of the type that is
|
||||||
// used as input for the encoding.
|
// used as input for the encoding.
|
||||||
func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess {
|
func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) AccountAccess {
|
||||||
res := AccountAccess{
|
res := AccountAccess{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
StorageWrites: make([]encodingSlotWrites, 0, len(a.StorageWrites)),
|
|
||||||
StorageReads: make([][32]byte, 0, len(a.StorageReads)),
|
|
||||||
BalanceChanges: make([]encodingBalanceChange, 0, len(a.BalanceChanges)),
|
|
||||||
NonceChanges: make([]encodingAccountNonce, 0, len(a.NonceChanges)),
|
|
||||||
CodeChanges: make([]encodingCodeChange, 0, len(a.CodeChange)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert write slots
|
// Convert write slots
|
||||||
|
|
@ -238,7 +469,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
slices.SortFunc(writeSlots, common.Hash.Cmp)
|
slices.SortFunc(writeSlots, common.Hash.Cmp)
|
||||||
for _, slot := range writeSlots {
|
for _, slot := range writeSlots {
|
||||||
var obj encodingSlotWrites
|
var obj encodingSlotWrites
|
||||||
obj.Slot = slot
|
obj.Slot = NewEncodedStorageFromHash(slot)
|
||||||
|
|
||||||
slotWrites := a.StorageWrites[slot]
|
slotWrites := a.StorageWrites[slot]
|
||||||
obj.Accesses = make([]encodingStorageWrite, 0, len(slotWrites))
|
obj.Accesses = make([]encodingStorageWrite, 0, len(slotWrites))
|
||||||
|
|
@ -248,17 +479,17 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
for _, index := range indices {
|
for _, index := range indices {
|
||||||
obj.Accesses = append(obj.Accesses, encodingStorageWrite{
|
obj.Accesses = append(obj.Accesses, encodingStorageWrite{
|
||||||
TxIdx: index,
|
TxIdx: index,
|
||||||
ValueAfter: slotWrites[index],
|
ValueAfter: NewEncodedStorageFromHash(slotWrites[index]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
res.StorageWrites = append(res.StorageWrites, obj)
|
res.StorageChanges = append(res.StorageChanges, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert read slots
|
// Convert read slots
|
||||||
readSlots := slices.Collect(maps.Keys(a.StorageReads))
|
readSlots := slices.Collect(maps.Keys(a.StorageReads))
|
||||||
slices.SortFunc(readSlots, common.Hash.Cmp)
|
slices.SortFunc(readSlots, common.Hash.Cmp)
|
||||||
for _, slot := range readSlots {
|
for _, slot := range readSlots {
|
||||||
res.StorageReads = append(res.StorageReads, slot)
|
res.StorageReads = append(res.StorageReads, NewEncodedStorageFromHash(slot))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert balance changes
|
// Convert balance changes
|
||||||
|
|
@ -267,7 +498,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]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,80 +513,34 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert code change
|
// Convert code change
|
||||||
codeIndices := slices.Collect(maps.Keys(a.CodeChange))
|
codeChangeIdxs := slices.Collect(maps.Keys(a.CodeChanges))
|
||||||
slices.SortFunc(codeIndices, cmp.Compare[uint16])
|
slices.SortFunc(codeChangeIdxs, cmp.Compare[uint16])
|
||||||
for _, idx := range codeIndices {
|
for _, idx := range codeChangeIdxs {
|
||||||
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
|
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
|
||||||
TxIndex: idx,
|
idx,
|
||||||
Code: a.CodeChange[idx],
|
bytes.Clone(a.CodeChanges[idx]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
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 {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var addresses []common.Address
|
var addresses []common.Address
|
||||||
for addr := range b.Accounts {
|
for addr := range c.list {
|
||||||
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.list[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:")
|
|
||||||
for _, sWrite := range accountDiff.StorageWrites {
|
|
||||||
printWithIndent(2, fmt.Sprintf("%x:", sWrite.Slot))
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
printWithIndent(1, "code changes:")
|
|
||||||
for _, change := range accountDiff.CodeChanges {
|
|
||||||
printWithIndent(2, fmt.Sprintf("%d: %x", change.TxIndex, change.Code))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a deep copy of the access list
|
|
||||||
func (e *BlockAccessList) Copy() *BlockAccessList {
|
|
||||||
cpy := &BlockAccessList{
|
|
||||||
Accesses: make([]AccountAccess, 0, len(e.Accesses)),
|
|
||||||
}
|
|
||||||
for _, accountAccess := range e.Accesses {
|
|
||||||
cpy.Accesses = append(cpy.Accesses, accountAccess.Copy())
|
|
||||||
}
|
|
||||||
return cpy
|
|
||||||
}
|
|
||||||
|
|
|
||||||
108
core/types/bal/bal_encoding_json.go
Normal file
108
core/types/bal/bal_encoding_json.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package bal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *ContractCode) MarshalJSON() ([]byte, error) {
|
||||||
|
hexStr := fmt.Sprintf("%x", *c)
|
||||||
|
return json.Marshal(hexStr)
|
||||||
|
}
|
||||||
|
func (e encodingBalanceChange) MarshalJSON() ([]byte, error) {
|
||||||
|
type Alias encodingBalanceChange
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
TxIdx string `json:"txIndex"`
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
TxIdx: fmt.Sprintf("0x%x", e.TxIdx),
|
||||||
|
Alias: (*Alias)(&e),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encodingBalanceChange) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias encodingBalanceChange
|
||||||
|
aux := &struct {
|
||||||
|
TxIdx string `json:"txIndex"`
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(e),
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(aux.TxIdx) >= 2 && aux.TxIdx[:2] == "0x" {
|
||||||
|
if _, err := fmt.Sscanf(aux.TxIdx, "0x%x", &e.TxIdx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (e encodingAccountNonce) MarshalJSON() ([]byte, error) {
|
||||||
|
type Alias encodingAccountNonce
|
||||||
|
return json.Marshal(&struct {
|
||||||
|
TxIdx string `json:"txIndex"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
TxIdx: fmt.Sprintf("0x%x", e.TxIdx),
|
||||||
|
Nonce: fmt.Sprintf("0x%x", e.Nonce),
|
||||||
|
Alias: (*Alias)(&e),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encodingAccountNonce) UnmarshalJSON(data []byte) error {
|
||||||
|
type Alias encodingAccountNonce
|
||||||
|
aux := &struct {
|
||||||
|
TxIdx string `json:"txIndex"`
|
||||||
|
Nonce string `json:"nonce"`
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(e),
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(aux.TxIdx) >= 2 && aux.TxIdx[:2] == "0x" {
|
||||||
|
if _, err := fmt.Sscanf(aux.TxIdx, "0x%x", &e.TxIdx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(aux.Nonce) >= 2 && aux.Nonce[:2] == "0x" {
|
||||||
|
if _, err := fmt.Sscanf(aux.Nonce, "0x%x", &e.Nonce); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler to decode from RLP hex bytes
|
||||||
|
func (b *BlockAccessList) UnmarshalJSON(input []byte) error {
|
||||||
|
// Handle both hex string and object formats
|
||||||
|
var hexBytes hexutil.Bytes
|
||||||
|
if err := json.Unmarshal(input, &hexBytes); err == nil {
|
||||||
|
// It's a hex string, decode from RLP
|
||||||
|
return rlp.DecodeBytes(hexBytes, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise try to unmarshal as structured JSON
|
||||||
|
var tmp []AccountAccess
|
||||||
|
if err := json.Unmarshal(input, &tmp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*b = BlockAccessList(tmp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler to encode as RLP hex bytes
|
||||||
|
func (b BlockAccessList) MarshalJSON() ([]byte, error) {
|
||||||
|
// Encode to RLP then to hex
|
||||||
|
rlpBytes, err := rlp.EncodeToBytes(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(hexutil.Bytes(rlpBytes))
|
||||||
|
}
|
||||||
|
|
@ -2,275 +2,260 @@
|
||||||
|
|
||||||
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[:])
|
if err := _tmp2.Slot.EncodeRLP(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
_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()
|
if err := _tmp5.ValueAfter.EncodeRLP(w); err != nil {
|
||||||
for _, _tmp8 := range _tmp5.Accesses {
|
return err
|
||||||
_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.CodeChanges {
|
|
||||||
_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 {
|
||||||
|
if err := _tmp8.EncodeRLP(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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.TxIndex))
|
||||||
|
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
|
_tmp4 := new(EncodedStorage)
|
||||||
if err := dec.ReadBytes(_tmp3[:]); err != nil {
|
if err := _tmp4.DecodeRLP(dec); 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
|
_tmp8 := new(EncodedStorage)
|
||||||
if err := dec.ReadBytes(_tmp16[:]); err != nil {
|
if err := _tmp8.DecodeRLP(dec); 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
|
|
||||||
// CodeChanges:
|
|
||||||
var _tmp21 []encodingCodeChange
|
|
||||||
if _, err := dec.List(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for dec.MoreDataInList() {
|
|
||||||
var _tmp22 encodingCodeChange
|
|
||||||
{
|
|
||||||
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.CodeChanges = _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 []*EncodedStorage
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
_tmp10 := new(EncodedStorage)
|
||||||
|
if err := _tmp10.DecodeRLP(dec); 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 []encodingCodeChange
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for dec.MoreDataInList() {
|
||||||
|
var _tmp20 encodingCodeChange
|
||||||
|
{
|
||||||
|
if _, err := dec.List(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TxIndex:
|
||||||
|
_tmp21, err := dec.Uint16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_tmp20.TxIndex = _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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/internal/testrand"
|
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
@ -36,9 +37,9 @@ func equalBALs(a *BlockAccessList, b *BlockAccessList) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
func makeTestConstructionBAL() ConstructionBlockAccessList {
|
||||||
return &ConstructionBlockAccessList{
|
return ConstructionBlockAccessList{
|
||||||
map[common.Address]*ConstructionAccountAccess{
|
list: map[common.Address]*ConstructionAccountAccesses{
|
||||||
common.BytesToAddress([]byte{0xff, 0xff}): {
|
common.BytesToAddress([]byte{0xff, 0xff}): {
|
||||||
StorageWrites: map[common.Hash]map[uint16]common.Hash{
|
StorageWrites: map[common.Hash]map[uint16]common.Hash{
|
||||||
common.BytesToHash([]byte{0x01}): {
|
common.BytesToHash([]byte{0x01}): {
|
||||||
|
|
@ -60,9 +61,7 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
||||||
1: 2,
|
1: 2,
|
||||||
2: 6,
|
2: 6,
|
||||||
},
|
},
|
||||||
CodeChange: map[uint16][]byte{
|
CodeChanges: map[uint16][]byte{0: common.Hex2Bytes("deadbeef")},
|
||||||
0: 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{
|
||||||
|
|
@ -70,9 +69,6 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
||||||
2: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6}),
|
2: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6}),
|
||||||
3: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
|
3: common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
|
||||||
},
|
},
|
||||||
common.BytesToHash([]byte{0x10}): {
|
|
||||||
21: common.BytesToHash([]byte{1, 2, 3, 4, 5}),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
StorageReads: map[common.Hash]struct{}{
|
StorageReads: map[common.Hash]struct{}{
|
||||||
common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {},
|
common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {},
|
||||||
|
|
@ -84,11 +80,9 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
||||||
NonceChanges: map[uint16]uint64{
|
NonceChanges: map[uint16]uint64{
|
||||||
1: 2,
|
1: 2,
|
||||||
},
|
},
|
||||||
CodeChange: map[uint16][]byte{
|
|
||||||
0: common.Hex2Bytes("deadbeef"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
transactionCount: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,10 +98,12 @@ 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) {
|
|
||||||
|
// TODO (jwasinger): we should have a fuzzer to expand on what this test case does.
|
||||||
|
if !equalBALs(bal.ToEncodingObj(), &dec) {
|
||||||
t.Fatal("decoded BAL doesn't match")
|
t.Fatal("decoded BAL doesn't match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -115,18 +111,18 @@ 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
|
||||||
)
|
)
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
slot := encodingSlotWrites{
|
slot := encodingSlotWrites{
|
||||||
Slot: testrand.Hash(),
|
Slot: NewEncodedStorageFromHash(testrand.Hash()),
|
||||||
}
|
}
|
||||||
for j := 0; j < 3; j++ {
|
for j := 0; j < 3; j++ {
|
||||||
slot.Accesses = append(slot.Accesses, encodingStorageWrite{
|
slot.Accesses = append(slot.Accesses, encodingStorageWrite{
|
||||||
TxIdx: uint16(2 * j),
|
TxIdx: uint16(i*3 + j),
|
||||||
ValueAfter: testrand.Hash(),
|
ValueAfter: NewEncodedStorageFromHash(testrand.Hash()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if sort {
|
if sort {
|
||||||
|
|
@ -138,7 +134,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
}
|
}
|
||||||
if sort {
|
if sort {
|
||||||
slices.SortFunc(storageWrites, func(a, b encodingSlotWrites) int {
|
slices.SortFunc(storageWrites, func(a, b encodingSlotWrites) int {
|
||||||
return bytes.Compare(a.Slot[:], b.Slot[:])
|
return bytes.Compare(a.Slot.inner.Bytes(), b.Slot.inner.Bytes())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,15 +142,15 @@ 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[:])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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(i),
|
||||||
Balance: [16]byte(testrand.Bytes(16)),
|
Balance: new(uint256.Int).SetBytes(testrand.Bytes(32)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if sort {
|
if sort {
|
||||||
|
|
@ -165,7 +161,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
nonces = append(nonces, encodingAccountNonce{
|
nonces = append(nonces, encodingAccountNonce{
|
||||||
TxIdx: uint16(2 * i),
|
TxIdx: uint16(i),
|
||||||
Nonce: uint64(i + 100),
|
Nonce: uint64(i + 100),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -175,28 +171,32 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encodedStorageReads []*EncodedStorage
|
||||||
|
for _, slot := range storageReads {
|
||||||
|
encodedStorageReads = append(encodedStorageReads, NewEncodedStorageFromHash(slot))
|
||||||
|
}
|
||||||
return AccountAccess{
|
return AccountAccess{
|
||||||
Address: [20]byte(testrand.Bytes(20)),
|
Address: [20]byte(testrand.Bytes(20)),
|
||||||
StorageWrites: storageWrites,
|
StorageChanges: storageWrites,
|
||||||
StorageReads: storageReads,
|
StorageReads: encodedStorageReads,
|
||||||
BalanceChanges: balances,
|
BalanceChanges: balances,
|
||||||
NonceChanges: nonces,
|
NonceChanges: nonces,
|
||||||
CodeChanges: []encodingCodeChange{
|
CodeChanges: []encodingCodeChange{
|
||||||
{
|
{
|
||||||
TxIndex: 100,
|
TxIndex: 3,
|
||||||
Code: testrand.Bytes(256),
|
Code: testrand.Bytes(256),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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[:])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -208,28 +208,29 @@ func TestBlockAccessListCopy(t *testing.T) {
|
||||||
cpy := list.Copy()
|
cpy := list.Copy()
|
||||||
cpyCpy := cpy.Copy()
|
cpyCpy := cpy.Copy()
|
||||||
|
|
||||||
if !reflect.DeepEqual(list, cpy) {
|
if !reflect.DeepEqual(list, *cpy) {
|
||||||
t.Fatal("block access mismatch")
|
t.Fatal("block access mismatch")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(cpy, cpyCpy) {
|
if !reflect.DeepEqual(*cpy, *cpyCpy) {
|
||||||
t.Fatal("block access mismatch")
|
t.Fatal("block access mismatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 i := range *cpyCpy {
|
||||||
for i := 0; i < len(aa.StorageReads); i++ {
|
for j := 0; j < len((*cpyCpy)[i].StorageReads); j++ {
|
||||||
aa.StorageReads[i] = [32]byte(testrand.Bytes(32))
|
(*cpyCpy)[i].StorageReads[j] = NewEncodedStorageFromHash(testrand.Hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(list, cpy) {
|
if !reflect.DeepEqual(list, *cpy) {
|
||||||
t.Fatal("block access mismatch")
|
t.Fatal("block access mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockAccessListValidation(t *testing.T) {
|
func TestBlockAccessListValidation(t *testing.T) {
|
||||||
// Validate the block access list after RLP decoding
|
// Validate the block access list after RLP decoding
|
||||||
|
testBALMaxIndex := 20
|
||||||
enc := makeTestBAL(true)
|
enc := makeTestBAL(true)
|
||||||
if err := enc.Validate(); err != nil {
|
if err := enc.Validate(testBALMaxIndex, params.MaxGasLimit); err != nil {
|
||||||
t.Fatalf("Unexpected validation error: %v", err)
|
t.Fatalf("Unexpected validation error: %v", err)
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -241,14 +242,17 @@ func TestBlockAccessListValidation(t *testing.T) {
|
||||||
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
|
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
|
||||||
t.Fatalf("Unexpected RLP-decode error: %v", err)
|
t.Fatalf("Unexpected RLP-decode error: %v", err)
|
||||||
}
|
}
|
||||||
if err := dec.Validate(); err != nil {
|
if err := dec.Validate(testBALMaxIndex, params.MaxGasLimit); err != nil {
|
||||||
t.Fatalf("Unexpected validation error: %v", err)
|
t.Fatalf("Unexpected validation error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(testBALMaxIndex, params.MaxGasLimit); 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
|
||||||
|
|
|
||||||
|
|
@ -98,5 +98,5 @@ type StateDB interface {
|
||||||
AccessEvents() *state.AccessEvents
|
AccessEvents() *state.AccessEvents
|
||||||
|
|
||||||
// Finalise must be invoked at the end of a transaction
|
// Finalise must be invoked at the end of a transaction
|
||||||
Finalise(bool) *bal.StateAccessList
|
Finalise(bool) (*bal.StateAccessList, *bal.StateMutations)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,14 @@ func makeTestBAL(minSize int) *bal.BlockAccessList {
|
||||||
n := minSize/33 + 1 // 33 bytes per storage read slot in RLP
|
n := minSize/33 + 1 // 33 bytes per storage read slot in RLP
|
||||||
access := bal.AccountAccess{
|
access := bal.AccountAccess{
|
||||||
Address: common.HexToAddress("0x01"),
|
Address: common.HexToAddress("0x01"),
|
||||||
StorageReads: make([][32]byte, n),
|
StorageReads: make([]*bal.EncodedStorage, n),
|
||||||
}
|
}
|
||||||
for i := range access.StorageReads {
|
for i := range access.StorageReads {
|
||||||
binary.BigEndian.PutUint64(access.StorageReads[i][24:], uint64(i))
|
var slot common.Hash
|
||||||
|
binary.BigEndian.PutUint64(slot[24:], uint64(i))
|
||||||
|
access.StorageReads[i] = bal.NewEncodedStorageFromHash(slot)
|
||||||
}
|
}
|
||||||
return &bal.BlockAccessList{Accesses: []bal.AccountAccess{access}}
|
return &bal.BlockAccessList{access}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getChainWithBALs creates a minimal test chain with BALs stored for each block.
|
// getChainWithBALs creates a minimal test chain with BALs stored for each block.
|
||||||
|
|
|
||||||
|
|
@ -1023,7 +1023,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
||||||
|
|
||||||
_, err = core.ApplyTransactionWithEVM(message, core.NewGasPool(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, evm)
|
_, _, _, err = core.ApplyTransactionWithEVM(message, core.NewGasPool(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -620,7 +620,7 @@ func TestSelfdestructStateTracer(t *testing.T) {
|
||||||
}
|
}
|
||||||
context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
|
context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
|
||||||
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
|
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
|
||||||
_, err = core.ApplyTransactionWithEVM(msg, core.NewGasPool(msg.GasLimit), statedb, block.Number(), block.Hash(), block.Time(), tx, evm)
|
_, _, _, err = core.ApplyTransactionWithEVM(msg, core.NewGasPool(msg.GasLimit), statedb, block.Number(), block.Hash(), block.Time(), tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,9 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, map[common.Hash]common.Address, error) {
|
func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, map[common.Hash]common.Address, error) {
|
||||||
|
if sim.chainConfig.IsAmsterdam(header.Number, header.Time) {
|
||||||
|
return nil, nil, nil, fmt.Errorf("eth simulate does not yet support Amsterdam")
|
||||||
|
}
|
||||||
// Set header fields that depend only on parent block.
|
// Set header fields that depend only on parent block.
|
||||||
// Parent hash is needed for evm.GetHashFn to work.
|
// Parent hash is needed for evm.GetHashFn to work.
|
||||||
header.ParentHash = parent.Hash()
|
header.ParentHash = parent.Hash()
|
||||||
|
|
@ -401,11 +404,11 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
if _, _, err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
if _, _, err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,11 +213,11 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil {
|
if _, _, err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil {
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
// EIP-7251 consolidations
|
// EIP-7251 consolidations
|
||||||
if err := core.ProcessConsolidationQueue(&requests, work.evm); err != nil {
|
if _, _, err := core.ProcessConsolidationQueue(&requests, work.evm); err != nil {
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -414,7 +414,7 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
|
||||||
snap = env.state.Snapshot()
|
snap = env.state.Snapshot()
|
||||||
gp = env.gasPool.Snapshot()
|
gp = env.gasPool.Snapshot()
|
||||||
)
|
)
|
||||||
receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx)
|
_, _, receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.RevertToSnapshot(snap)
|
env.state.RevertToSnapshot(snap)
|
||||||
env.gasPool.Set(gp)
|
env.gasPool.Set(gp)
|
||||||
|
|
|
||||||
|
|
@ -323,14 +323,16 @@ var (
|
||||||
CancunTime: newUint64(0),
|
CancunTime: newUint64(0),
|
||||||
PragueTime: newUint64(0),
|
PragueTime: newUint64(0),
|
||||||
OsakaTime: newUint64(0),
|
OsakaTime: newUint64(0),
|
||||||
|
AmsterdamTime: newUint64(0),
|
||||||
UBTTime: nil,
|
UBTTime: nil,
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
Clique: nil,
|
Clique: nil,
|
||||||
BlobScheduleConfig: &BlobScheduleConfig{
|
BlobScheduleConfig: &BlobScheduleConfig{
|
||||||
Cancun: DefaultCancunBlobConfig,
|
Cancun: DefaultCancunBlobConfig,
|
||||||
Prague: DefaultPragueBlobConfig,
|
Prague: DefaultPragueBlobConfig,
|
||||||
Osaka: DefaultOsakaBlobConfig,
|
Osaka: DefaultOsakaBlobConfig,
|
||||||
|
Amsterdam: DefaultOsakaBlobConfig,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -194,8 +194,7 @@ const (
|
||||||
StorageCreationSize = 32
|
StorageCreationSize = 32
|
||||||
AuthorizationCreationSize = 23
|
AuthorizationCreationSize = 23
|
||||||
|
|
||||||
// TODO: Add when EIP-7928 is implemented
|
GasBlockAccessListItem = 2000 // EIP-7928: gas cost per BAL item for gas limit check
|
||||||
// GasBlockAccessListItem = 2000 // EIP-7928: gas cost per BAL item for gas limit check
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ type btHeader struct {
|
||||||
ExcessBlobGas *uint64
|
ExcessBlobGas *uint64
|
||||||
ParentBeaconBlockRoot *common.Hash
|
ParentBeaconBlockRoot *common.Hash
|
||||||
SlotNumber *uint64
|
SlotNumber *uint64
|
||||||
|
BlockAccessListHash *common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
type btHeaderMarshaling struct {
|
type btHeaderMarshaling struct {
|
||||||
|
|
@ -225,8 +226,10 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
|
||||||
Coinbase: t.json.Genesis.Coinbase,
|
Coinbase: t.json.Genesis.Coinbase,
|
||||||
Alloc: t.json.Pre,
|
Alloc: t.json.Pre,
|
||||||
BaseFee: t.json.Genesis.BaseFeePerGas,
|
BaseFee: t.json.Genesis.BaseFeePerGas,
|
||||||
BlobGasUsed: t.json.Genesis.BlobGasUsed,
|
BlobGasUsed: t.json.Genesis.BlobGasUsed,
|
||||||
ExcessBlobGas: t.json.Genesis.ExcessBlobGas,
|
ExcessBlobGas: t.json.Genesis.ExcessBlobGas,
|
||||||
|
SlotNumber: t.json.Genesis.SlotNumber,
|
||||||
|
BlockAccessListHash: t.json.Genesis.BlockAccessListHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
ExcessBlobGas *math.HexOrDecimal64
|
||||||
ParentBeaconBlockRoot *common.Hash
|
ParentBeaconBlockRoot *common.Hash
|
||||||
SlotNumber *math.HexOrDecimal64
|
SlotNumber *math.HexOrDecimal64
|
||||||
|
BlockAccessListHash *common.Hash
|
||||||
}
|
}
|
||||||
var enc btHeader
|
var enc btHeader
|
||||||
enc.Bloom = b.Bloom
|
enc.Bloom = b.Bloom
|
||||||
|
|
@ -63,6 +64,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
|
||||||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(b.ExcessBlobGas)
|
enc.ExcessBlobGas = (*math.HexOrDecimal64)(b.ExcessBlobGas)
|
||||||
enc.ParentBeaconBlockRoot = b.ParentBeaconBlockRoot
|
enc.ParentBeaconBlockRoot = b.ParentBeaconBlockRoot
|
||||||
enc.SlotNumber = (*math.HexOrDecimal64)(b.SlotNumber)
|
enc.SlotNumber = (*math.HexOrDecimal64)(b.SlotNumber)
|
||||||
|
enc.BlockAccessListHash = b.BlockAccessListHash
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +93,7 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
|
||||||
ExcessBlobGas *math.HexOrDecimal64
|
ExcessBlobGas *math.HexOrDecimal64
|
||||||
ParentBeaconBlockRoot *common.Hash
|
ParentBeaconBlockRoot *common.Hash
|
||||||
SlotNumber *math.HexOrDecimal64
|
SlotNumber *math.HexOrDecimal64
|
||||||
|
BlockAccessListHash *common.Hash
|
||||||
}
|
}
|
||||||
var dec btHeader
|
var dec btHeader
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -162,5 +165,8 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
|
||||||
if dec.SlotNumber != nil {
|
if dec.SlotNumber != nil {
|
||||||
b.SlotNumber = (*uint64)(dec.SlotNumber)
|
b.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||||
}
|
}
|
||||||
|
if dec.BlockAccessListHash != nil {
|
||||||
|
b.BlockAccessListHash = dec.BlockAccessListHash
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -490,7 +490,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"OsakaToBPO1AtTime15k": {
|
"OsakaToBPO1AtTime15k": {
|
||||||
|
|
@ -519,7 +519,7 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"BPO2": {
|
"BPO2": {
|
||||||
|
|
@ -549,8 +549,8 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"BPO1ToBPO2AtTime15k": {
|
"BPO1ToBPO2AtTime15k": {
|
||||||
|
|
@ -580,8 +580,8 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"BPO3": {
|
"BPO3": {
|
||||||
|
|
@ -612,8 +612,8 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
BPO3: params.DefaultBPO3BlobConfig,
|
BPO3: params.DefaultBPO3BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -645,8 +645,8 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
BPO3: params.DefaultBPO3BlobConfig,
|
BPO3: params.DefaultBPO3BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -679,8 +679,8 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
BPO3: params.DefaultBPO3BlobConfig,
|
BPO3: params.DefaultBPO3BlobConfig,
|
||||||
BPO4: params.DefaultBPO4BlobConfig,
|
BPO4: params.DefaultBPO4BlobConfig,
|
||||||
},
|
},
|
||||||
|
|
@ -714,8 +714,8 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
BPO3: params.DefaultBPO3BlobConfig,
|
BPO3: params.DefaultBPO3BlobConfig,
|
||||||
BPO4: params.DefaultBPO4BlobConfig,
|
BPO4: params.DefaultBPO4BlobConfig,
|
||||||
},
|
},
|
||||||
|
|
@ -750,11 +750,44 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
Cancun: params.DefaultCancunBlobConfig,
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
Prague: params.DefaultPragueBlobConfig,
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
Osaka: params.DefaultOsakaBlobConfig,
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
BPO1: bpo1BlobConfig,
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
BPO2: bpo2BlobConfig,
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
BPO3: params.DefaultBPO3BlobConfig,
|
BPO3: params.DefaultBPO3BlobConfig,
|
||||||
BPO4: params.DefaultBPO4BlobConfig,
|
BPO4: params.DefaultBPO4BlobConfig,
|
||||||
Amsterdam: params.DefaultBPO4BlobConfig, // TODO update when defined
|
Amsterdam: params.DefaultBPO2BlobConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"BPO2ToAmsterdamAtTime15k": {
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
|
CancunTime: u64(0),
|
||||||
|
PragueTime: u64(0),
|
||||||
|
OsakaTime: u64(0),
|
||||||
|
BPO1Time: u64(0),
|
||||||
|
BPO2Time: u64(0),
|
||||||
|
AmsterdamTime: u64(15_000),
|
||||||
|
DepositContractAddress: params.MainnetChainConfig.DepositContractAddress,
|
||||||
|
BlobScheduleConfig: ¶ms.BlobScheduleConfig{
|
||||||
|
Cancun: params.DefaultCancunBlobConfig,
|
||||||
|
Prague: params.DefaultPragueBlobConfig,
|
||||||
|
Osaka: params.DefaultOsakaBlobConfig,
|
||||||
|
BPO1: params.DefaultBPO1BlobConfig,
|
||||||
|
BPO2: params.DefaultBPO2BlobConfig,
|
||||||
|
Amsterdam: params.DefaultBPO2BlobConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Verkle": {
|
"Verkle": {
|
||||||
|
|
@ -778,18 +811,6 @@ var Forks = map[string]*params.ChainConfig{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var bpo1BlobConfig = ¶ms.BlobConfig{
|
|
||||||
Target: 9,
|
|
||||||
Max: 14,
|
|
||||||
UpdateFraction: 8832827,
|
|
||||||
}
|
|
||||||
|
|
||||||
var bpo2BlobConfig = ¶ms.BlobConfig{
|
|
||||||
Target: 14,
|
|
||||||
Max: 21,
|
|
||||||
UpdateFraction: 13739630,
|
|
||||||
}
|
|
||||||
|
|
||||||
// AvailableForks returns the set of defined fork names
|
// AvailableForks returns the set of defined fork names
|
||||||
func AvailableForks() []string {
|
func AvailableForks() []string {
|
||||||
var availableForks []string
|
var availableForks []string
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ func (tt *TransactionTest) Run() error {
|
||||||
}
|
}
|
||||||
// Intrinsic cost
|
// Intrinsic cost
|
||||||
// TODO (MariusVanDerWijden): correctly set this for post-amsterdam tests.
|
// TODO (MariusVanDerWijden): correctly set this for post-amsterdam tests.
|
||||||
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, *rules, 0)
|
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue