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
decfab7f6f
commit
01f79c0927
36 changed files with 1632 additions and 677 deletions
|
|
@ -21,6 +21,8 @@ import (
|
|||
"math/big"
|
||||
"slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -82,24 +84,25 @@ type payloadAttributesMarshaling struct {
|
|||
|
||||
// ExecutableData is the data necessary to execute an EL payload.
|
||||
type ExecutableData struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||
BlockAccessList *bal.BlockAccessList `json:"blockAccessList"`
|
||||
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
||||
}
|
||||
|
||||
// JSON type overrides for executableData.
|
||||
|
|
@ -303,6 +306,8 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
|||
requestsHash = &h
|
||||
}
|
||||
|
||||
body := types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}
|
||||
|
||||
header := &types.Header{
|
||||
ParentHash: data.ParentHash,
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
|
|
@ -326,33 +331,40 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
|||
RequestsHash: requestsHash,
|
||||
SlotNumber: data.SlotNumber,
|
||||
}
|
||||
return types.NewBlockWithHeader(header).
|
||||
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}),
|
||||
nil
|
||||
|
||||
if data.BlockAccessList != 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
|
||||
// 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 {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
FeeRecipient: block.Coinbase(),
|
||||
StateRoot: block.Root(),
|
||||
Number: block.NumberU64(),
|
||||
GasLimit: block.GasLimit(),
|
||||
GasUsed: block.GasUsed(),
|
||||
BaseFeePerGas: block.BaseFee(),
|
||||
Timestamp: block.Time(),
|
||||
ReceiptsRoot: block.ReceiptHash(),
|
||||
LogsBloom: block.Bloom().Bytes(),
|
||||
Transactions: encodeTransactions(block.Transactions()),
|
||||
Random: block.MixDigest(),
|
||||
ExtraData: block.Extra(),
|
||||
Withdrawals: block.Withdrawals(),
|
||||
BlobGasUsed: block.BlobGasUsed(),
|
||||
ExcessBlobGas: block.ExcessBlobGas(),
|
||||
SlotNumber: block.SlotNumber(),
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
FeeRecipient: block.Coinbase(),
|
||||
StateRoot: block.Root(),
|
||||
Number: block.NumberU64(),
|
||||
GasLimit: block.GasLimit(),
|
||||
GasUsed: block.GasUsed(),
|
||||
BaseFeePerGas: block.BaseFee(),
|
||||
Timestamp: block.Time(),
|
||||
ReceiptsRoot: block.ReceiptHash(),
|
||||
LogsBloom: block.Bloom().Bytes(),
|
||||
Transactions: encodeTransactions(block.Transactions()),
|
||||
Random: block.MixDigest(),
|
||||
ExtraData: block.Extra(),
|
||||
Withdrawals: block.Withdrawals(),
|
||||
BlobGasUsed: block.BlobGasUsed(),
|
||||
ExcessBlobGas: block.ExcessBlobGas(),
|
||||
BlockAccessList: block.AccessList(),
|
||||
SlotNumber: block.SlotNumber(),
|
||||
}
|
||||
|
||||
// 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
|
||||
type ExecutionPayloadBody struct {
|
||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
AccessList *bal.BlockAccessList `json:"blockAccessList"`
|
||||
}
|
||||
|
||||
// Client identifiers to support ClientVersionV1.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@
|
|||
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
|
||||
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
|
||||
# https://go.dev/dl/
|
||||
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.
|
||||
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"))
|
||||
|
|
@ -384,6 +387,7 @@ func doTest(cmdline []string) {
|
|||
// Get test fixtures.
|
||||
if !*short {
|
||||
downloadSpecTestFixtures(csdb, *cachedir)
|
||||
downloadBALSpecTestFixtures(csdb, *cachedir)
|
||||
}
|
||||
|
||||
// Configure the toolchain.
|
||||
|
|
@ -449,6 +453,19 @@ func downloadSpecTestFixtures(csdb *download.ChecksumDB, cachedir string) string
|
|||
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
|
||||
// any mutations in the source file tree.
|
||||
func doCheckGenerate() {
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
snapshot = statedb.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 {
|
||||
statedb.RevertToSnapshot(snapshot)
|
||||
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))
|
||||
}
|
||||
// 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))
|
||||
}
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"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.
|
||||
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) {
|
||||
beacon.ethone.Finalize(chain, header, state, body)
|
||||
return
|
||||
return beacon.ethone.Finalize(chain, header, state, body)
|
||||
}
|
||||
// Withdrawals processing.
|
||||
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.
|
||||
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
||||
state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
||||
}
|
||||
return state.Finalise(true)
|
||||
// No block reward which is issued by consensus layer instead.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"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
|
||||
// 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
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"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
|
||||
// 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
|
||||
// the result into the given channel.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import (
|
|||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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.
|
||||
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
|
||||
accumulateRewards(chain.Config(), state, header, body.Uncles)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
if !v.bc.HasBlockAndState(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.
|
||||
func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, config ExecuteConfig) (result *blockProcessingResult, blockEndErr error) {
|
||||
var (
|
||||
err error
|
||||
startTime = time.Now()
|
||||
statedb *state.StateDB
|
||||
interrupt atomic.Bool
|
||||
sdb state.Database
|
||||
err error
|
||||
startTime = time.Now()
|
||||
statedb *state.StateDB
|
||||
interrupt atomic.Bool
|
||||
sdb state.Database
|
||||
isAmsterdam = bc.chainConfig.IsAmsterdam(block.Number(), block.Time())
|
||||
)
|
||||
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)
|
||||
|
||||
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
|
||||
// that now. Self validation should *never* run in production, it's more of
|
||||
// a tight integration to enable running *all* consensus tests through the
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
|
|
@ -51,6 +53,8 @@ type BlockGen struct {
|
|||
withdrawals []*types.Withdrawal
|
||||
|
||||
engine consensus.Engine
|
||||
|
||||
accessList *bal.ConstructionBlockAccessList
|
||||
}
|
||||
|
||||
// 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)
|
||||
)
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
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
|
||||
// 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 {
|
||||
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
|
||||
}
|
||||
// TODO: these accesses should be accumulated in the BAL
|
||||
// create EVM for system calls
|
||||
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
||||
|
||||
mut := new(bal.StateMutations)
|
||||
// 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))
|
||||
}
|
||||
mut.Merge(withdrawalMut)
|
||||
// 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))
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
@ -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) {
|
||||
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: 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
|
||||
// to a chain, so the difficulty will be left unset (nil). Set it here to the
|
||||
// correct value.
|
||||
|
|
@ -391,14 +414,32 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||
misc.ApplyDAOHardFork(statedb)
|
||||
}
|
||||
|
||||
preTxMutations := bal.NewStateMutations()
|
||||
if config.IsPrague(b.header.Number, b.header.Time) || config.IsUBT(b.header.Number, b.header.Time) {
|
||||
// EIP-2935
|
||||
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
||||
blockContext.Random = &common.Hash{} // enable post-merge instruction set
|
||||
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
|
||||
if gen != nil {
|
||||
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() {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
|
|
|||
|
|
@ -67,13 +67,14 @@ type Genesis struct {
|
|||
|
||||
// These fields are used for consensus tests. Please don't use them
|
||||
// in actual genesis blocks.
|
||||
Number uint64 `json:"number"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
|
||||
SlotNumber *uint64 `json:"slotNumber"` // EIP-7843
|
||||
Number uint64 `json:"number"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
|
||||
BlockAccessListHash *common.Hash `json:"BlockAccessListHash,omitempty"`
|
||||
SlotNumber *uint64 `json:"slotNumber"` // EIP-7843
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||
head := &types.Header{
|
||||
Number: new(big.Int).SetUint64(g.Number),
|
||||
Nonce: types.EncodeNonce(g.Nonce),
|
||||
Time: g.Timestamp,
|
||||
ParentHash: g.ParentHash,
|
||||
Extra: g.ExtraData,
|
||||
GasLimit: g.GasLimit,
|
||||
GasUsed: g.GasUsed,
|
||||
BaseFee: g.BaseFee,
|
||||
Difficulty: g.Difficulty,
|
||||
MixDigest: g.Mixhash,
|
||||
Coinbase: g.Coinbase,
|
||||
Root: root,
|
||||
Number: new(big.Int).SetUint64(g.Number),
|
||||
Nonce: types.EncodeNonce(g.Nonce),
|
||||
Time: g.Timestamp,
|
||||
ParentHash: g.ParentHash,
|
||||
Extra: g.ExtraData,
|
||||
GasLimit: g.GasLimit,
|
||||
GasUsed: g.GasUsed,
|
||||
BaseFee: g.BaseFee,
|
||||
Difficulty: g.Difficulty,
|
||||
MixDigest: g.Mixhash,
|
||||
Coinbase: g.Coinbase,
|
||||
BlockAccessListHash: g.BlockAccessListHash,
|
||||
Root: root,
|
||||
}
|
||||
if g.GasLimit == 0 {
|
||||
head.GasLimit = params.GenesisGasLimit
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
|
|
@ -906,13 +905,17 @@ func makeTestBAL(t *testing.T) (rlp.RawValue, *bal.BlockAccessList) {
|
|||
t.Helper()
|
||||
|
||||
cb := bal.NewConstructionBlockAccessList()
|
||||
addr := common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
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)
|
||||
/*
|
||||
TODO MariusVanDerWijden fix after rebase
|
||||
addr := common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
|
||||
|
||||
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
|
||||
if err := cb.EncodeRLP(&buf); err != nil {
|
||||
t.Fatalf("failed to encode BAL: %v", err)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import (
|
|||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"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
|
||||
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.
|
||||
trie Trie // storage trie, which becomes non-nil on first access
|
||||
code []byte // contract bytecode, which gets set when code is loaded
|
||||
|
|
@ -75,6 +80,9 @@ type stateObject struct {
|
|||
// Cache flags.
|
||||
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
|
||||
// account is still accessible in the scope of same transaction.
|
||||
selfDestructed bool
|
||||
|
|
@ -107,6 +115,8 @@ func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *s
|
|||
dirtyStorage: make(Storage),
|
||||
pendingStorage: 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
|
||||
// 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))
|
||||
for key, value := range s.dirtyStorage {
|
||||
if origin, exist := s.uncommittedStorage[key]; exist && origin == value {
|
||||
// non-parallel-execution:
|
||||
// The slot is reverted to its original value, delete the entry
|
||||
// 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)
|
||||
} else if exist {
|
||||
// non-parallel-execution:
|
||||
// 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 {
|
||||
// The slot is different from its original value and hasn't been
|
||||
// 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
|
||||
}
|
||||
// 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
|
||||
// by EIP-6780. For non-newly-created objects, it's a no-op.
|
||||
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
|
||||
|
|
@ -511,6 +572,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
|||
dirtyCode: s.dirtyCode,
|
||||
selfDestructed: s.selfDestructed,
|
||||
newContract: s.newContract,
|
||||
txPreBalance: s.txPreBalance.Clone(),
|
||||
}
|
||||
|
||||
switch s.trie.(type) {
|
||||
|
|
@ -587,13 +649,26 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) (prev []byte) {
|
|||
prev = slices.Clone(s.code)
|
||||
s.db.journal.setCode(s.address, prev)
|
||||
s.setCode(codeHash, code)
|
||||
|
||||
return prev
|
||||
}
|
||||
|
||||
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.data.CodeHash = codeHash[:]
|
||||
s.dirtyCode = true
|
||||
}
|
||||
|
||||
func (s *stateObject) SetNonce(nonce uint64) {
|
||||
|
|
|
|||
|
|
@ -27,11 +27,12 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"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/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -193,6 +194,7 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
|||
journal: newJournal(),
|
||||
accessList: newAccessList(),
|
||||
transientStorage: newTransientStorage(),
|
||||
stateReadList: bal.NewStateAccessList(),
|
||||
}
|
||||
if db.Type().Is(TypeUBT) {
|
||||
sdb.accessEvents = NewAccessEvents()
|
||||
|
|
@ -200,6 +202,13 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
|||
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
|
||||
// state trie concurrently while the state is mutated so that when we reach the
|
||||
// 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
|
||||
// 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.
|
||||
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))
|
||||
mutations = bal.NewStateMutations()
|
||||
for addr := range s.journal.dirties {
|
||||
obj, exist := s.stateObjects[addr]
|
||||
if !exist {
|
||||
|
|
@ -822,8 +832,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
|||
if _, ok := s.stateObjectsDestruct[obj.address]; !ok {
|
||||
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 {
|
||||
obj.finalise()
|
||||
mut := obj.finalise()
|
||||
if mut != nil {
|
||||
mutations.Set(addr, mut)
|
||||
}
|
||||
s.markUpdate(addr)
|
||||
}
|
||||
// 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.
|
||||
s.clearJournalAndRefund()
|
||||
|
||||
return s.stateReadList
|
||||
return s.stateReadList, mutations
|
||||
}
|
||||
|
||||
// IntermediateRoot computes the current root hash of the state trie.
|
||||
|
|
|
|||
|
|
@ -21,11 +21,12 @@ import (
|
|||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"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/params"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -234,7 +235,7 @@ func (s *hookedStateDB) LogsForBurnAccounts() []*types.Log {
|
|||
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 {
|
||||
// Short circuit if no relevant hooks are set.
|
||||
return s.inner.Finalise(deleteEmptyObjects)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"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.
|
||||
func (p *StateProcessor) Process(ctx context.Context, block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||
var (
|
||||
config = p.chainConfig()
|
||||
receipts = make(types.Receipts, 0, len(block.Transactions()))
|
||||
header = block.Header()
|
||||
blockHash = block.Hash()
|
||||
blockNumber = block.Number()
|
||||
allLogs []*types.Log
|
||||
gp = NewGasPool(block.GasLimit())
|
||||
config = p.chainConfig()
|
||||
receipts = make(types.Receipts, 0, len(block.Transactions()))
|
||||
header = block.Header()
|
||||
blockHash = block.Hash()
|
||||
blockNumber = block.Number()
|
||||
allLogs []*types.Log
|
||||
gp = NewGasPool(block.GasLimit())
|
||||
computedAccessList = bal.NewConstructionBlockAccessList()
|
||||
isAmsterdam = p.chainConfig().IsAmsterdam(block.Number(), block.Time())
|
||||
)
|
||||
var tracingStateDB = vm.StateDB(statedb)
|
||||
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)
|
||||
|
||||
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()) {
|
||||
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
|
||||
|
|
@ -108,60 +120,81 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
|||
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 {
|
||||
spanEnd(&err)
|
||||
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||
}
|
||||
receipts = append(receipts, receipt)
|
||||
allLogs = append(allLogs, receipt.Logs...)
|
||||
|
||||
if isAmsterdam {
|
||||
computedAccessList.AddTransactionMutations(mutations, i)
|
||||
computedAccessList.AddAccesses(accesses)
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
postMut.Merge(postExecMut)
|
||||
|
||||
// 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{
|
||||
Receipts: receipts,
|
||||
Requests: requests,
|
||||
Logs: allLogs,
|
||||
GasUsed: gp.Used(),
|
||||
Receipts: receipts,
|
||||
Requests: requests,
|
||||
Logs: allLogs,
|
||||
GasUsed: gp.Used(),
|
||||
AccessList: computedAccessList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
defer spanEnd(&err)
|
||||
mutations = bal.NewStateMutations()
|
||||
accesses = bal.NewStateAccessList()
|
||||
|
||||
// Read requests if Prague is enabled.
|
||||
if config.IsPrague(block.Number(), block.Time()) {
|
||||
requests = [][]byte{}
|
||||
// EIP-6110
|
||||
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
|
||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return requests, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
||||
if accesses, mutations, err = ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return nil, nil, requests, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
||||
}
|
||||
// EIP-7251
|
||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
return requests, fmt.Errorf("failed to process consolidation queue: %w", err)
|
||||
consolidationAccesses, consolidationMutations, err := ProcessConsolidationQueue(&requests, evm)
|
||||
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
|
||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||
// 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.OnTxStart != nil {
|
||||
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).
|
||||
result, err := ApplyMessage(evm, msg, gp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// Update the state with pending changes.
|
||||
var root []byte
|
||||
if evm.ChainConfig().IsByzantium(blockNumber) {
|
||||
evm.StateDB.Finalise(true)
|
||||
accesses, mutations = evm.StateDB.Finalise(true)
|
||||
} else {
|
||||
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) {
|
||||
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.
|
||||
|
|
@ -232,10 +265,10 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
|
|||
// and uses the input parameters for its environment. It returns the receipt
|
||||
// for the transaction and an error if the transaction failed,
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// Create a new context to be used in the EVM environment
|
||||
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
|
||||
|
|
@ -243,7 +276,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
|
|||
|
||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||
// 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 {
|
||||
onSystemCallStart(tracer, evm.GetVMContext())
|
||||
if tracer.OnSystemCallEnd != nil {
|
||||
|
|
@ -265,12 +298,12 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
|||
if evm.StateDB.AccessEvents() != nil {
|
||||
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
|
||||
// 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 {
|
||||
onSystemCallStart(tracer, evm.GetVMContext())
|
||||
if tracer.OnSystemCallEnd != nil {
|
||||
|
|
@ -295,22 +328,22 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
|||
if evm.StateDB.AccessEvents() != nil {
|
||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||
}
|
||||
evm.StateDB.Finalise(true)
|
||||
return evm.StateDB.Finalise(true)
|
||||
}
|
||||
|
||||
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue 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)
|
||||
}
|
||||
|
||||
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
onSystemCallStart(tracer, evm.GetVMContext())
|
||||
if tracer.OnSystemCallEnd != nil {
|
||||
|
|
@ -331,19 +364,19 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
|||
if evm.StateDB.AccessEvents() != nil {
|
||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||
}
|
||||
evm.StateDB.Finalise(true)
|
||||
accesses, mutations := evm.StateDB.Finalise(true)
|
||||
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 {
|
||||
return nil // skip empty output
|
||||
return accesses, mutations, nil // skip empty output
|
||||
}
|
||||
// Append prefixed requestsData to the requests list.
|
||||
requestsData := make([]byte, len(ret)+1)
|
||||
requestsData[0] = requestType
|
||||
copy(requestsData[1:], ret)
|
||||
*requests = append(*requests, requestsData)
|
||||
return nil
|
||||
return accesses, mutations, nil
|
||||
}
|
||||
|
||||
var depositTopic = common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -54,8 +56,9 @@ type Processor interface {
|
|||
|
||||
// ProcessResult contains the values computed by Process.
|
||||
type ProcessResult struct {
|
||||
Receipts types.Receipts
|
||||
Requests [][]byte
|
||||
Logs []*types.Log
|
||||
GasUsed uint64
|
||||
Receipts types.Receipts
|
||||
Requests [][]byte
|
||||
Logs []*types.Log
|
||||
GasUsed uint64
|
||||
AccessList *bal.ConstructionBlockAccessList
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,3 +92,18 @@ func (s *StateAccessList) Copy() *StateAccessList {
|
|||
}
|
||||
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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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
|
||||
// access list during execution.
|
||||
type ConstructionAccountAccess struct {
|
||||
type ConstructionAccountAccesses struct {
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
StorageReads map[common.Hash]struct{} `json:"storageReads,omitempty"`
|
||||
StorageReads map[common.Hash]struct{}
|
||||
|
||||
// BalanceChanges contains the post-transaction balances of an account,
|
||||
// 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
|
||||
// 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
|
||||
// by tx index.
|
||||
CodeChange map[uint16][]byte `json:"codeChange,omitempty"`
|
||||
CodeChanges map[uint16][]byte
|
||||
}
|
||||
|
||||
// NewConstructionAccountAccess initializes the account access object.
|
||||
func NewConstructionAccountAccess() *ConstructionAccountAccess {
|
||||
return &ConstructionAccountAccess{
|
||||
func (c *ConstructionAccountAccesses) Copy() (res ConstructionAccountAccesses) {
|
||||
if c.StorageWrites != nil {
|
||||
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),
|
||||
StorageReads: make(map[common.Hash]struct{}),
|
||||
BalanceChanges: make(map[uint16]*uint256.Int),
|
||||
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
|
||||
// in execution (account addresses and storage keys).
|
||||
type ConstructionBlockAccessList struct {
|
||||
Accounts map[common.Address]*ConstructionAccountAccess
|
||||
type StorageMutations map[common.Hash]common.Hash
|
||||
|
||||
// AccountMutations contains mutations that were made to an account across
|
||||
// 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.
|
||||
func NewConstructionBlockAccessList() ConstructionBlockAccessList {
|
||||
return ConstructionBlockAccessList{
|
||||
Accounts: make(map[common.Address]*ConstructionAccountAccess),
|
||||
}
|
||||
// String returns a human-readable JSON representation of the account mutations.
|
||||
func (a *AccountMutations) String() string {
|
||||
var res bytes.Buffer
|
||||
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.
|
||||
func (b *ConstructionBlockAccessList) AccountRead(addr common.Address) {
|
||||
if _, ok := b.Accounts[addr]; !ok {
|
||||
b.Accounts[addr] = NewConstructionAccountAccess()
|
||||
// Copy returns a deep-copy of the instance.
|
||||
func (a *AccountMutations) Copy() *AccountMutations {
|
||||
res := &AccountMutations{
|
||||
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.
|
||||
func (b *ConstructionBlockAccessList) StorageRead(address common.Address, key common.Hash) {
|
||||
if _, ok := b.Accounts[address]; !ok {
|
||||
b.Accounts[address] = NewConstructionAccountAccess()
|
||||
}
|
||||
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
|
||||
// Copy returns a deep copy of the access list
|
||||
func (e BlockAccessList) Copy() *BlockAccessList {
|
||||
var res BlockAccessList
|
||||
for _, accountAccess := range e {
|
||||
res = append(res, accountAccess.Copy())
|
||||
}
|
||||
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 (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -28,35 +30,132 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"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
|
||||
// the spec format.
|
||||
|
||||
// BlockAccessList is the encoding format of ConstructionBlockAccessList.
|
||||
type BlockAccessList struct {
|
||||
Accesses []AccountAccess `ssz-max:"300000"`
|
||||
// BlockAccessList is the encoding format of AccessListBuilder.
|
||||
type BlockAccessList []AccountAccess
|
||||
|
||||
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
|
||||
// max code size.
|
||||
func (e *BlockAccessList) Validate() error {
|
||||
if !slices.IsSortedFunc(e.Accesses, func(a, b AccountAccess) int {
|
||||
// * the total accounts and storage slots in the access list exceed the protocol max
|
||||
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 errors.New("block access list accounts not in lexicographic order")
|
||||
}
|
||||
for _, entry := range e.Accesses {
|
||||
if err := entry.validate(); err != nil {
|
||||
// check that the accounts are unique
|
||||
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
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
@ -70,53 +169,135 @@ func (e *BlockAccessList) Hash() common.Hash {
|
|||
// under reasonable conditions.
|
||||
panic(err)
|
||||
}
|
||||
/*
|
||||
bal, err := json.MarshalIndent(e.StringableRepresentation(), "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
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.
|
||||
type encodingBalanceChange struct {
|
||||
TxIdx uint16 `ssz-size:"2"`
|
||||
Balance [16]byte `ssz-size:"16"`
|
||||
TxIdx uint16 `json:"txIndex"`
|
||||
Balance *uint256.Int `json:"balance"`
|
||||
}
|
||||
|
||||
// encodingAccountNonce is the encoding format of NonceChange.
|
||||
type encodingAccountNonce struct {
|
||||
TxIdx uint16 `ssz-size:"2"`
|
||||
Nonce uint64 `ssz-size:"8"`
|
||||
TxIdx uint16 `json:"txIndex"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
}
|
||||
|
||||
// encodingStorageWrite is the encoding format of StorageWrites.
|
||||
type encodingStorageWrite struct {
|
||||
TxIdx uint16
|
||||
ValueAfter [32]byte `ssz-size:"32"`
|
||||
TxIdx uint16 `json:"txIndex"`
|
||||
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.
|
||||
type encodingSlotWrites struct {
|
||||
Slot [32]byte `ssz-size:"32"`
|
||||
Accesses []encodingStorageWrite `ssz-max:"300000"`
|
||||
Slot *EncodedStorage `json:"slot"`
|
||||
Accesses []encodingStorageWrite `json:"accesses"`
|
||||
}
|
||||
|
||||
// validate returns an instance of the encoding-representation slot writes in
|
||||
// working representation.
|
||||
func (e *encodingSlotWrites) validate() error {
|
||||
if slices.IsSortedFunc(e.Accesses, func(a, b encodingStorageWrite) int {
|
||||
func (e *encodingSlotWrites) validate(blockTxCount int) error {
|
||||
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 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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
// AccountAccess is the encoding format of ConstructionAccountAccess.
|
||||
// AccountAccess is the encoding format of ConstructionAccountAccesses.
|
||||
type AccountAccess struct {
|
||||
Address [20]byte `ssz-size:"20"` // 20-byte Ethereum address
|
||||
StorageWrites []encodingSlotWrites `ssz-max:"300000"` // Storage changes (slot -> [tx_index -> new_value])
|
||||
StorageReads [][32]byte `ssz-max:"300000"` // Read-only storage keys
|
||||
BalanceChanges []encodingBalanceChange `ssz-max:"300000"` // Balance changes ([tx_index -> post_balance])
|
||||
NonceChanges []encodingAccountNonce `ssz-max:"300000"` // Nonce changes ([tx_index -> new_nonce])
|
||||
CodeChanges []encodingCodeChange `ssz-max:"300000"` // Code changes ([tx_index -> new_code])
|
||||
Address common.Address `json:"address,omitempty"` // 20-byte Ethereum address
|
||||
StorageChanges []encodingSlotWrites `json:"storageChanges,omitempty"` // EncodedStorage changes (slot -> [tx_index -> new_value])
|
||||
StorageReads []*EncodedStorage `json:"storageReads,omitempty"` // Read-only storage keys
|
||||
BalanceChanges []encodingBalanceChange `json:"balanceChanges,omitempty"` // Balance changes ([tx_index -> post_balance])
|
||||
NonceChanges []encodingAccountNonce `json:"nonceChanges,omitempty"` // Nonce changes ([tx_index -> new_nonce])
|
||||
CodeChanges []encodingCodeChange `json:"code,omitempty"` // CodeChanges changes ([tx_index -> new_code])
|
||||
}
|
||||
|
||||
// validate converts the account accesses out of encoding format.
|
||||
// If any of the keys in the encoding object are not ordered according to the
|
||||
// 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
|
||||
if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int {
|
||||
return bytes.Compare(a.Slot[:], b.Slot[:])
|
||||
if !slices.IsSortedFunc(e.StorageChanges, func(a, b encodingSlotWrites) int {
|
||||
aHash, bHash := a.Slot.ToHash(), b.Slot.ToHash()
|
||||
return bytes.Compare(aHash[:], bHash[:])
|
||||
}) {
|
||||
return errors.New("storage writes slots not in lexicographic order")
|
||||
}
|
||||
for _, write := range e.StorageWrites {
|
||||
if err := write.validate(); err != nil {
|
||||
for _, write := range e.StorageChanges {
|
||||
if err := write.validate(blockTxCount); err != nil {
|
||||
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
|
||||
if !slices.IsSortedFunc(e.StorageReads, func(a, b [32]byte) int {
|
||||
return bytes.Compare(a[:], b[:])
|
||||
if !slices.IsSortedFunc(e.StorageReads, func(a, b *EncodedStorage) int {
|
||||
aHash, bHash := a.ToHash(), b.ToHash()
|
||||
return bytes.Compare(aHash[:], bHash[:])
|
||||
}) {
|
||||
return errors.New("storage read slots not in lexicographic 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 {
|
||||
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
||||
}) {
|
||||
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
|
||||
// and that none of them report an index above what is allowed
|
||||
if !slices.IsSortedFunc(e.NonceChanges, func(a, b encodingAccountNonce) int {
|
||||
return cmp.Compare[uint16](a.TxIdx, b.TxIdx)
|
||||
}) {
|
||||
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 {
|
||||
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 {
|
||||
// TODO(rjl493456442): This check should be fork-aware, since the limit may
|
||||
// differ across forks.
|
||||
if len(change.Code) > params.MaxCodeSize {
|
||||
return errors.New("code change contained oversized code")
|
||||
if len(e.CodeChanges) > 0 && int(e.CodeChanges[len(e.CodeChanges)-1].TxIndex) >= blockTxCount+2 {
|
||||
return errors.New("highest code change index beyond what is allowed")
|
||||
}
|
||||
for i, codeChange := range e.CodeChanges {
|
||||
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
|
||||
|
|
@ -196,41 +433,35 @@ func (e *AccountAccess) Copy() AccountAccess {
|
|||
StorageReads: slices.Clone(e.StorageReads),
|
||||
BalanceChanges: slices.Clone(e.BalanceChanges),
|
||||
NonceChanges: slices.Clone(e.NonceChanges),
|
||||
StorageWrites: make([]encodingSlotWrites, 0, len(e.StorageWrites)),
|
||||
CodeChanges: make([]encodingCodeChange, 0, len(e.CodeChanges)),
|
||||
}
|
||||
for _, storageWrite := range e.StorageWrites {
|
||||
res.StorageWrites = append(res.StorageWrites, encodingSlotWrites{
|
||||
for _, storageWrite := range e.StorageChanges {
|
||||
res.StorageChanges = append(res.StorageChanges, encodingSlotWrites{
|
||||
Slot: storageWrite.Slot,
|
||||
Accesses: slices.Clone(storageWrite.Accesses),
|
||||
})
|
||||
}
|
||||
for _, codeChange := range e.CodeChanges {
|
||||
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
|
||||
TxIndex: codeChange.TxIndex,
|
||||
Code: bytes.Clone(codeChange.Code),
|
||||
})
|
||||
res.CodeChanges = append(res.CodeChanges,
|
||||
encodingCodeChange{
|
||||
codeChange.TxIndex,
|
||||
bytes.Clone(codeChange.Code),
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// EncodeRLP returns the RLP-encoded access list
|
||||
func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error {
|
||||
return b.toEncodingObj().EncodeRLP(wr)
|
||||
func (c ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error {
|
||||
return c.ToEncodingObj().EncodeRLP(wr)
|
||||
}
|
||||
|
||||
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.
|
||||
func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAccess {
|
||||
func (a *ConstructionAccountAccesses) toEncodingObj(addr common.Address) AccountAccess {
|
||||
res := AccountAccess{
|
||||
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)),
|
||||
Address: addr,
|
||||
}
|
||||
|
||||
// Convert write slots
|
||||
|
|
@ -238,7 +469,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
|||
slices.SortFunc(writeSlots, common.Hash.Cmp)
|
||||
for _, slot := range writeSlots {
|
||||
var obj encodingSlotWrites
|
||||
obj.Slot = slot
|
||||
obj.Slot = NewEncodedStorageFromHash(slot)
|
||||
|
||||
slotWrites := a.StorageWrites[slot]
|
||||
obj.Accesses = make([]encodingStorageWrite, 0, len(slotWrites))
|
||||
|
|
@ -248,17 +479,17 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
|||
for _, index := range indices {
|
||||
obj.Accesses = append(obj.Accesses, encodingStorageWrite{
|
||||
TxIdx: index,
|
||||
ValueAfter: slotWrites[index],
|
||||
ValueAfter: NewEncodedStorageFromHash(slotWrites[index]),
|
||||
})
|
||||
}
|
||||
res.StorageWrites = append(res.StorageWrites, obj)
|
||||
res.StorageChanges = append(res.StorageChanges, obj)
|
||||
}
|
||||
|
||||
// Convert read slots
|
||||
readSlots := slices.Collect(maps.Keys(a.StorageReads))
|
||||
slices.SortFunc(readSlots, common.Hash.Cmp)
|
||||
for _, slot := range readSlots {
|
||||
res.StorageReads = append(res.StorageReads, slot)
|
||||
res.StorageReads = append(res.StorageReads, NewEncodedStorageFromHash(slot))
|
||||
}
|
||||
|
||||
// Convert balance changes
|
||||
|
|
@ -267,7 +498,7 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
|||
for _, idx := range balanceIndices {
|
||||
res.BalanceChanges = append(res.BalanceChanges, encodingBalanceChange{
|
||||
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
|
||||
codeIndices := slices.Collect(maps.Keys(a.CodeChange))
|
||||
slices.SortFunc(codeIndices, cmp.Compare[uint16])
|
||||
for _, idx := range codeIndices {
|
||||
codeChangeIdxs := slices.Collect(maps.Keys(a.CodeChanges))
|
||||
slices.SortFunc(codeChangeIdxs, cmp.Compare[uint16])
|
||||
for _, idx := range codeChangeIdxs {
|
||||
res.CodeChanges = append(res.CodeChanges, encodingCodeChange{
|
||||
TxIndex: idx,
|
||||
Code: a.CodeChange[idx],
|
||||
idx,
|
||||
bytes.Clone(a.CodeChanges[idx]),
|
||||
})
|
||||
}
|
||||
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.
|
||||
func (b *ConstructionBlockAccessList) toEncodingObj() *BlockAccessList {
|
||||
func (c *ConstructionBlockAccessList) ToEncodingObj() *BlockAccessList {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
var addresses []common.Address
|
||||
for addr := range b.Accounts {
|
||||
for addr := range c.list {
|
||||
addresses = append(addresses, addr)
|
||||
}
|
||||
slices.SortFunc(addresses, common.Address.Cmp)
|
||||
|
||||
var res BlockAccessList
|
||||
for _, addr := range addresses {
|
||||
res.Accesses = append(res.Accesses, b.Accounts[addr].toEncodingObj(addr))
|
||||
res = append(res, c.list[addr].toEncodingObj(addr))
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func (e *BlockAccessList) PrettyPrint() string {
|
||||
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
|
||||
}
|
||||
type ContractCode []byte
|
||||
|
|
|
|||
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
|
||||
|
||||
import "github.com/ethereum/go-ethereum/common"
|
||||
import "github.com/ethereum/go-ethereum/rlp"
|
||||
import "github.com/holiman/uint256"
|
||||
import "io"
|
||||
|
||||
func (obj *BlockAccessList) EncodeRLP(_w io.Writer) error {
|
||||
func (obj *AccountAccess) EncodeRLP(_w io.Writer) error {
|
||||
w := rlp.NewEncoderBuffer(_w)
|
||||
_tmp0 := w.List()
|
||||
w.WriteBytes(obj.Address[:])
|
||||
_tmp1 := w.List()
|
||||
for _, _tmp2 := range obj.Accesses {
|
||||
for _, _tmp2 := range obj.StorageChanges {
|
||||
_tmp3 := w.List()
|
||||
w.WriteBytes(_tmp2.Address[:])
|
||||
if err := _tmp2.Slot.EncodeRLP(w); err != nil {
|
||||
return err
|
||||
}
|
||||
_tmp4 := w.List()
|
||||
for _, _tmp5 := range _tmp2.StorageWrites {
|
||||
for _, _tmp5 := range _tmp2.Accesses {
|
||||
_tmp6 := w.List()
|
||||
w.WriteBytes(_tmp5.Slot[:])
|
||||
_tmp7 := w.List()
|
||||
for _, _tmp8 := range _tmp5.Accesses {
|
||||
_tmp9 := w.List()
|
||||
w.WriteUint64(uint64(_tmp8.TxIdx))
|
||||
w.WriteBytes(_tmp8.ValueAfter[:])
|
||||
w.ListEnd(_tmp9)
|
||||
w.WriteUint64(uint64(_tmp5.TxIdx))
|
||||
if err := _tmp5.ValueAfter.EncodeRLP(w); err != nil {
|
||||
return err
|
||||
}
|
||||
w.ListEnd(_tmp7)
|
||||
w.ListEnd(_tmp6)
|
||||
}
|
||||
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(_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)
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func (obj *BlockAccessList) DecodeRLP(dec *rlp.Stream) error {
|
||||
var _tmp0 BlockAccessList
|
||||
func (obj *AccountAccess) DecodeRLP(dec *rlp.Stream) error {
|
||||
var _tmp0 AccountAccess
|
||||
{
|
||||
if _, err := dec.List(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Accesses:
|
||||
var _tmp1 []AccountAccess
|
||||
// Address:
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
for dec.MoreDataInList() {
|
||||
var _tmp2 AccountAccess
|
||||
var _tmp3 encodingSlotWrites
|
||||
{
|
||||
if _, err := dec.List(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Address:
|
||||
var _tmp3 [20]byte
|
||||
if err := dec.ReadBytes(_tmp3[:]); err != nil {
|
||||
// Slot:
|
||||
_tmp4 := new(EncodedStorage)
|
||||
if err := _tmp4.DecodeRLP(dec); err != nil {
|
||||
return err
|
||||
}
|
||||
_tmp2.Address = _tmp3
|
||||
// StorageWrites:
|
||||
var _tmp4 []encodingSlotWrites
|
||||
_tmp3.Slot = _tmp4
|
||||
// Accesses:
|
||||
var _tmp5 []encodingStorageWrite
|
||||
if _, err := dec.List(); err != nil {
|
||||
return err
|
||||
}
|
||||
for dec.MoreDataInList() {
|
||||
var _tmp5 encodingSlotWrites
|
||||
{
|
||||
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
|
||||
var _tmp6 encodingStorageWrite
|
||||
{
|
||||
if _, err := dec.List(); err != nil {
|
||||
return err
|
||||
}
|
||||
// TxIdx:
|
||||
_tmp15, err := dec.Uint16()
|
||||
_tmp7, err := dec.Uint16()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_tmp14.TxIdx = _tmp15
|
||||
// Balance:
|
||||
var _tmp16 [16]byte
|
||||
if err := dec.ReadBytes(_tmp16[:]); err != nil {
|
||||
_tmp6.TxIdx = _tmp7
|
||||
// ValueAfter:
|
||||
_tmp8 := new(EncodedStorage)
|
||||
if err := _tmp8.DecodeRLP(dec); err != nil {
|
||||
return err
|
||||
}
|
||||
_tmp14.Balance = _tmp16
|
||||
_tmp6.ValueAfter = _tmp8
|
||||
if err := dec.ListEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_tmp13 = append(_tmp13, _tmp14)
|
||||
_tmp5 = append(_tmp5, _tmp6)
|
||||
}
|
||||
if err := dec.ListEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
_tmp2.BalanceChanges = _tmp13
|
||||
// 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
|
||||
_tmp3.Accesses = _tmp5
|
||||
if err := dec.ListEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_tmp1 = append(_tmp1, _tmp2)
|
||||
_tmp2 = append(_tmp2, _tmp3)
|
||||
}
|
||||
if err := dec.ListEnd(); err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
|
@ -36,9 +37,9 @@ func equalBALs(a *BlockAccessList, b *BlockAccessList) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
||||
return &ConstructionBlockAccessList{
|
||||
map[common.Address]*ConstructionAccountAccess{
|
||||
func makeTestConstructionBAL() ConstructionBlockAccessList {
|
||||
return ConstructionBlockAccessList{
|
||||
list: map[common.Address]*ConstructionAccountAccesses{
|
||||
common.BytesToAddress([]byte{0xff, 0xff}): {
|
||||
StorageWrites: map[common.Hash]map[uint16]common.Hash{
|
||||
common.BytesToHash([]byte{0x01}): {
|
||||
|
|
@ -60,9 +61,7 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
|||
1: 2,
|
||||
2: 6,
|
||||
},
|
||||
CodeChange: map[uint16][]byte{
|
||||
0: common.Hex2Bytes("deadbeef"),
|
||||
},
|
||||
CodeChanges: map[uint16][]byte{0: common.Hex2Bytes("deadbeef")},
|
||||
},
|
||||
common.BytesToAddress([]byte{0xff, 0xff, 0xff}): {
|
||||
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}),
|
||||
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{}{
|
||||
common.BytesToHash([]byte{1, 2, 3, 4, 5, 6, 7, 8}): {},
|
||||
|
|
@ -84,11 +80,9 @@ func makeTestConstructionBAL() *ConstructionBlockAccessList {
|
|||
NonceChanges: map[uint16]uint64{
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
@ -115,18 +111,18 @@ func TestBALEncoding(t *testing.T) {
|
|||
func makeTestAccountAccess(sort bool) AccountAccess {
|
||||
var (
|
||||
storageWrites []encodingSlotWrites
|
||||
storageReads [][32]byte
|
||||
storageReads []common.Hash
|
||||
balances []encodingBalanceChange
|
||||
nonces []encodingAccountNonce
|
||||
)
|
||||
for i := 0; i < 5; i++ {
|
||||
slot := encodingSlotWrites{
|
||||
Slot: testrand.Hash(),
|
||||
Slot: NewEncodedStorageFromHash(testrand.Hash()),
|
||||
}
|
||||
for j := 0; j < 3; j++ {
|
||||
slot.Accesses = append(slot.Accesses, encodingStorageWrite{
|
||||
TxIdx: uint16(2 * j),
|
||||
ValueAfter: testrand.Hash(),
|
||||
TxIdx: uint16(i*3 + j),
|
||||
ValueAfter: NewEncodedStorageFromHash(testrand.Hash()),
|
||||
})
|
||||
}
|
||||
if sort {
|
||||
|
|
@ -138,7 +134,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
|||
}
|
||||
if sort {
|
||||
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())
|
||||
}
|
||||
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[:])
|
||||
})
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
balances = append(balances, encodingBalanceChange{
|
||||
TxIdx: uint16(2 * i),
|
||||
Balance: [16]byte(testrand.Bytes(16)),
|
||||
TxIdx: uint16(i),
|
||||
Balance: new(uint256.Int).SetBytes(testrand.Bytes(32)),
|
||||
})
|
||||
}
|
||||
if sort {
|
||||
|
|
@ -165,7 +161,7 @@ func makeTestAccountAccess(sort bool) AccountAccess {
|
|||
|
||||
for i := 0; i < 5; i++ {
|
||||
nonces = append(nonces, encodingAccountNonce{
|
||||
TxIdx: uint16(2 * i),
|
||||
TxIdx: uint16(i),
|
||||
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{
|
||||
Address: [20]byte(testrand.Bytes(20)),
|
||||
StorageWrites: storageWrites,
|
||||
StorageReads: storageReads,
|
||||
StorageChanges: storageWrites,
|
||||
StorageReads: encodedStorageReads,
|
||||
BalanceChanges: balances,
|
||||
NonceChanges: nonces,
|
||||
CodeChanges: []encodingCodeChange{
|
||||
{
|
||||
TxIndex: 100,
|
||||
TxIndex: 3,
|
||||
Code: testrand.Bytes(256),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestBAL(sort bool) *BlockAccessList {
|
||||
list := &BlockAccessList{}
|
||||
func makeTestBAL(sort bool) BlockAccessList {
|
||||
list := BlockAccessList{}
|
||||
for i := 0; i < 5; i++ {
|
||||
list.Accesses = append(list.Accesses, makeTestAccountAccess(sort))
|
||||
list = append(list, makeTestAccountAccess(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[:])
|
||||
})
|
||||
}
|
||||
|
|
@ -208,28 +208,29 @@ func TestBlockAccessListCopy(t *testing.T) {
|
|||
cpy := list.Copy()
|
||||
cpyCpy := cpy.Copy()
|
||||
|
||||
if !reflect.DeepEqual(list, cpy) {
|
||||
if !reflect.DeepEqual(list, *cpy) {
|
||||
t.Fatal("block access mismatch")
|
||||
}
|
||||
if !reflect.DeepEqual(cpy, cpyCpy) {
|
||||
if !reflect.DeepEqual(*cpy, *cpyCpy) {
|
||||
t.Fatal("block access mismatch")
|
||||
}
|
||||
|
||||
// Make sure the mutations on copy won't affect the origin
|
||||
for _, aa := range cpyCpy.Accesses {
|
||||
for i := 0; i < len(aa.StorageReads); i++ {
|
||||
aa.StorageReads[i] = [32]byte(testrand.Bytes(32))
|
||||
for i := range *cpyCpy {
|
||||
for j := 0; j < len((*cpyCpy)[i].StorageReads); j++ {
|
||||
(*cpyCpy)[i].StorageReads[j] = NewEncodedStorageFromHash(testrand.Hash())
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(list, cpy) {
|
||||
if !reflect.DeepEqual(list, *cpy) {
|
||||
t.Fatal("block access mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockAccessListValidation(t *testing.T) {
|
||||
// Validate the block access list after RLP decoding
|
||||
testBALMaxIndex := 20
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
// Validate the derived block access list
|
||||
cBAL := makeTestConstructionBAL()
|
||||
listB := cBAL.toEncodingObj()
|
||||
if err := listB.Validate(); err != nil {
|
||||
listB := cBAL.ToEncodingObj()
|
||||
if err := listB.Validate(testBALMaxIndex, params.MaxGasLimit); err != nil {
|
||||
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
|
||||
|
||||
// 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
|
||||
access := bal.AccountAccess{
|
||||
Address: common.HexToAddress("0x01"),
|
||||
StorageReads: make([][32]byte, n),
|
||||
StorageReads: make([]*bal.EncodedStorage, n),
|
||||
}
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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 {
|
||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -620,7 +620,7 @@ func TestSelfdestructStateTracer(t *testing.T) {
|
|||
}
|
||||
context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
|
||||
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 {
|
||||
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) {
|
||||
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.
|
||||
// Parent hash is needed for evm.GetHashFn to work.
|
||||
header.ParentHash = parent.Hash()
|
||||
|
|
@ -401,11 +404,11 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
|||
return nil, nil, nil, err
|
||||
}
|
||||
// EIP-7002
|
||||
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
if _, _, err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// EIP-7251
|
||||
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
if _, _, err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,11 +213,11 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
|
|||
return &newPayloadResult{err: err}
|
||||
}
|
||||
// EIP-7002
|
||||
if err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil {
|
||||
if _, _, err := core.ProcessWithdrawalQueue(&requests, work.evm); err != nil {
|
||||
return &newPayloadResult{err: err}
|
||||
}
|
||||
// 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}
|
||||
}
|
||||
}
|
||||
|
|
@ -414,7 +414,7 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
|
|||
snap = env.state.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 {
|
||||
env.state.RevertToSnapshot(snap)
|
||||
env.gasPool.Set(gp)
|
||||
|
|
|
|||
|
|
@ -323,14 +323,16 @@ var (
|
|||
CancunTime: newUint64(0),
|
||||
PragueTime: newUint64(0),
|
||||
OsakaTime: newUint64(0),
|
||||
AmsterdamTime: newUint64(0),
|
||||
UBTTime: nil,
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
Ethash: new(EthashConfig),
|
||||
Clique: nil,
|
||||
BlobScheduleConfig: &BlobScheduleConfig{
|
||||
Cancun: DefaultCancunBlobConfig,
|
||||
Prague: DefaultPragueBlobConfig,
|
||||
Osaka: DefaultOsakaBlobConfig,
|
||||
Cancun: DefaultCancunBlobConfig,
|
||||
Prague: DefaultPragueBlobConfig,
|
||||
Osaka: DefaultOsakaBlobConfig,
|
||||
Amsterdam: DefaultOsakaBlobConfig,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,8 @@ const (
|
|||
HistoryServeWindow = 8191 // Number of blocks to serve historical block hashes for, EIP-2935.
|
||||
|
||||
MaxBlockSize = 8_388_608 // maximum size of an RLP-encoded block
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ type btHeader struct {
|
|||
ExcessBlobGas *uint64
|
||||
ParentBeaconBlockRoot *common.Hash
|
||||
SlotNumber *uint64
|
||||
BlockAccessListHash *common.Hash
|
||||
}
|
||||
|
||||
type btHeaderMarshaling struct {
|
||||
|
|
@ -225,8 +226,10 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
|
|||
Coinbase: t.json.Genesis.Coinbase,
|
||||
Alloc: t.json.Pre,
|
||||
BaseFee: t.json.Genesis.BaseFeePerGas,
|
||||
BlobGasUsed: t.json.Genesis.BlobGasUsed,
|
||||
ExcessBlobGas: t.json.Genesis.ExcessBlobGas,
|
||||
BlobGasUsed: t.json.Genesis.BlobGasUsed,
|
||||
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
|
||||
ParentBeaconBlockRoot *common.Hash
|
||||
SlotNumber *math.HexOrDecimal64
|
||||
BlockAccessListHash *common.Hash
|
||||
}
|
||||
var enc btHeader
|
||||
enc.Bloom = b.Bloom
|
||||
|
|
@ -63,6 +64,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
|
|||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(b.ExcessBlobGas)
|
||||
enc.ParentBeaconBlockRoot = b.ParentBeaconBlockRoot
|
||||
enc.SlotNumber = (*math.HexOrDecimal64)(b.SlotNumber)
|
||||
enc.BlockAccessListHash = b.BlockAccessListHash
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +93,7 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
|
|||
ExcessBlobGas *math.HexOrDecimal64
|
||||
ParentBeaconBlockRoot *common.Hash
|
||||
SlotNumber *math.HexOrDecimal64
|
||||
BlockAccessListHash *common.Hash
|
||||
}
|
||||
var dec btHeader
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -162,5 +165,8 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
|
|||
if dec.SlotNumber != nil {
|
||||
b.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||
}
|
||||
if dec.BlockAccessListHash != nil {
|
||||
b.BlockAccessListHash = dec.BlockAccessListHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -490,7 +490,7 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
},
|
||||
},
|
||||
"OsakaToBPO1AtTime15k": {
|
||||
|
|
@ -519,7 +519,7 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
},
|
||||
},
|
||||
"BPO2": {
|
||||
|
|
@ -549,8 +549,8 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
},
|
||||
},
|
||||
"BPO1ToBPO2AtTime15k": {
|
||||
|
|
@ -580,8 +580,8 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
},
|
||||
},
|
||||
"BPO3": {
|
||||
|
|
@ -612,8 +612,8 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
BPO3: params.DefaultBPO3BlobConfig,
|
||||
},
|
||||
},
|
||||
|
|
@ -645,8 +645,8 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
BPO3: params.DefaultBPO3BlobConfig,
|
||||
},
|
||||
},
|
||||
|
|
@ -679,8 +679,8 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
BPO3: params.DefaultBPO3BlobConfig,
|
||||
BPO4: params.DefaultBPO4BlobConfig,
|
||||
},
|
||||
|
|
@ -714,8 +714,8 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
BPO3: params.DefaultBPO3BlobConfig,
|
||||
BPO4: params.DefaultBPO4BlobConfig,
|
||||
},
|
||||
|
|
@ -750,11 +750,44 @@ var Forks = map[string]*params.ChainConfig{
|
|||
Cancun: params.DefaultCancunBlobConfig,
|
||||
Prague: params.DefaultPragueBlobConfig,
|
||||
Osaka: params.DefaultOsakaBlobConfig,
|
||||
BPO1: bpo1BlobConfig,
|
||||
BPO2: bpo2BlobConfig,
|
||||
BPO1: params.DefaultBPO1BlobConfig,
|
||||
BPO2: params.DefaultBPO2BlobConfig,
|
||||
BPO3: params.DefaultBPO3BlobConfig,
|
||||
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": {
|
||||
|
|
@ -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
|
||||
func AvailableForks() []string {
|
||||
var availableForks []string
|
||||
|
|
|
|||
Loading…
Reference in a new issue