mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-20 23:09:27 +00:00
core, consensus, internal, eth, miner: construct block accessList (#34957)
This PR finally lands EIP-7928, collecting the block accessList during the block execution and verifying against the block header. --------- Co-authored-by: jwasinger <j-wasinger@hotmail.com> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
This commit is contained in:
parent
e3ce773b8c
commit
1bdc4a60d9
26 changed files with 1883 additions and 199 deletions
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = (*executableDataMarshaling)(nil)
|
var _ = (*executableDataMarshaling)(nil)
|
||||||
|
|
@ -17,24 +18,25 @@ var _ = (*executableDataMarshaling)(nil)
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
type ExecutableData struct {
|
type ExecutableData struct {
|
||||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
|
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
|
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
|
||||||
|
BlockAccessList *bal.BlockAccessList `json:"blockAccessList,omitempty"`
|
||||||
}
|
}
|
||||||
var enc ExecutableData
|
var enc ExecutableData
|
||||||
enc.ParentHash = e.ParentHash
|
enc.ParentHash = e.ParentHash
|
||||||
|
|
@ -60,30 +62,32 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||||
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||||
enc.SlotNumber = (*hexutil.Uint64)(e.SlotNumber)
|
enc.SlotNumber = (*hexutil.Uint64)(e.SlotNumber)
|
||||||
|
enc.BlockAccessList = e.BlockAccessList
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
type ExecutableData struct {
|
type ExecutableData struct {
|
||||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
|
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
|
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||||
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||||
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
|
SlotNumber *hexutil.Uint64 `json:"slotNumber,omitempty"`
|
||||||
|
BlockAccessList *bal.BlockAccessList `json:"blockAccessList,omitempty"`
|
||||||
}
|
}
|
||||||
var dec ExecutableData
|
var dec ExecutableData
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -160,5 +164,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
if dec.SlotNumber != nil {
|
if dec.SlotNumber != nil {
|
||||||
e.SlotNumber = (*uint64)(dec.SlotNumber)
|
e.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||||
}
|
}
|
||||||
|
if dec.BlockAccessList != nil {
|
||||||
|
e.BlockAccessList = dec.BlockAccessList
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
@ -82,24 +83,25 @@ type payloadAttributesMarshaling struct {
|
||||||
|
|
||||||
// ExecutableData is the data necessary to execute an EL payload.
|
// ExecutableData is the data necessary to execute an EL payload.
|
||||||
type ExecutableData struct {
|
type ExecutableData struct {
|
||||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
Number uint64 `json:"blockNumber" gencodec:"required"`
|
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||||
ExtraData []byte `json:"extraData" gencodec:"required"`
|
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
SlotNumber *uint64 `json:"slotNumber,omitempty"`
|
||||||
|
BlockAccessList *bal.BlockAccessList `json:"blockAccessList,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// JSON type overrides for executableData.
|
||||||
|
|
@ -303,56 +305,66 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
||||||
requestsHash = &h
|
requestsHash = &h
|
||||||
}
|
}
|
||||||
|
|
||||||
header := &types.Header{
|
// If Amsterdam is enabled, data.BlockAccessList is always non-nil,
|
||||||
ParentHash: data.ParentHash,
|
// even for empty blocks with no state transitions.
|
||||||
UncleHash: types.EmptyUncleHash,
|
//
|
||||||
Coinbase: data.FeeRecipient,
|
// If Amsterdam is not enabled yet, blockAccessListHash is expected
|
||||||
Root: data.StateRoot,
|
// to be nil.
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
var blockAccessListHash *common.Hash
|
||||||
ReceiptHash: data.ReceiptsRoot,
|
if data.BlockAccessList != nil {
|
||||||
Bloom: types.BytesToBloom(data.LogsBloom),
|
hash := data.BlockAccessList.Hash()
|
||||||
Difficulty: common.Big0,
|
blockAccessListHash = &hash
|
||||||
Number: new(big.Int).SetUint64(data.Number),
|
|
||||||
GasLimit: data.GasLimit,
|
|
||||||
GasUsed: data.GasUsed,
|
|
||||||
Time: data.Timestamp,
|
|
||||||
BaseFee: data.BaseFeePerGas,
|
|
||||||
Extra: data.ExtraData,
|
|
||||||
MixDigest: data.Random,
|
|
||||||
WithdrawalsHash: withdrawalsRoot,
|
|
||||||
ExcessBlobGas: data.ExcessBlobGas,
|
|
||||||
BlobGasUsed: data.BlobGasUsed,
|
|
||||||
ParentBeaconRoot: beaconRoot,
|
|
||||||
RequestsHash: requestsHash,
|
|
||||||
SlotNumber: data.SlotNumber,
|
|
||||||
}
|
}
|
||||||
return types.NewBlockWithHeader(header).
|
header := &types.Header{
|
||||||
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}),
|
ParentHash: data.ParentHash,
|
||||||
nil
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
Coinbase: data.FeeRecipient,
|
||||||
|
Root: data.StateRoot,
|
||||||
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: data.ReceiptsRoot,
|
||||||
|
Bloom: types.BytesToBloom(data.LogsBloom),
|
||||||
|
Difficulty: common.Big0,
|
||||||
|
Number: new(big.Int).SetUint64(data.Number),
|
||||||
|
GasLimit: data.GasLimit,
|
||||||
|
GasUsed: data.GasUsed,
|
||||||
|
Time: data.Timestamp,
|
||||||
|
BaseFee: data.BaseFeePerGas,
|
||||||
|
Extra: data.ExtraData,
|
||||||
|
MixDigest: data.Random,
|
||||||
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
|
ExcessBlobGas: data.ExcessBlobGas,
|
||||||
|
BlobGasUsed: data.BlobGasUsed,
|
||||||
|
ParentBeaconRoot: beaconRoot,
|
||||||
|
RequestsHash: requestsHash,
|
||||||
|
SlotNumber: data.SlotNumber,
|
||||||
|
BlockAccessListHash: blockAccessListHash,
|
||||||
|
}
|
||||||
|
return types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||||
// fields from the given block. It assumes the given block is post-merge block.
|
// fields from the given block. It assumes the given block is post-merge block.
|
||||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar, requests [][]byte) *ExecutionPayloadEnvelope {
|
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar, requests [][]byte) *ExecutionPayloadEnvelope {
|
||||||
data := &ExecutableData{
|
data := &ExecutableData{
|
||||||
BlockHash: block.Hash(),
|
BlockHash: block.Hash(),
|
||||||
ParentHash: block.ParentHash(),
|
ParentHash: block.ParentHash(),
|
||||||
FeeRecipient: block.Coinbase(),
|
FeeRecipient: block.Coinbase(),
|
||||||
StateRoot: block.Root(),
|
StateRoot: block.Root(),
|
||||||
Number: block.NumberU64(),
|
Number: block.NumberU64(),
|
||||||
GasLimit: block.GasLimit(),
|
GasLimit: block.GasLimit(),
|
||||||
GasUsed: block.GasUsed(),
|
GasUsed: block.GasUsed(),
|
||||||
BaseFeePerGas: block.BaseFee(),
|
BaseFeePerGas: block.BaseFee(),
|
||||||
Timestamp: block.Time(),
|
Timestamp: block.Time(),
|
||||||
ReceiptsRoot: block.ReceiptHash(),
|
ReceiptsRoot: block.ReceiptHash(),
|
||||||
LogsBloom: block.Bloom().Bytes(),
|
LogsBloom: block.Bloom().Bytes(),
|
||||||
Transactions: encodeTransactions(block.Transactions()),
|
Transactions: encodeTransactions(block.Transactions()),
|
||||||
Random: block.MixDigest(),
|
Random: block.MixDigest(),
|
||||||
ExtraData: block.Extra(),
|
ExtraData: block.Extra(),
|
||||||
Withdrawals: block.Withdrawals(),
|
Withdrawals: block.Withdrawals(),
|
||||||
BlobGasUsed: block.BlobGasUsed(),
|
BlobGasUsed: block.BlobGasUsed(),
|
||||||
ExcessBlobGas: block.ExcessBlobGas(),
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
SlotNumber: block.SlotNumber(),
|
SlotNumber: block.SlotNumber(),
|
||||||
|
BlockAccessList: block.AccessList(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add blobs.
|
// Add blobs.
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -172,6 +173,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
includedTxs types.Transactions
|
includedTxs types.Transactions
|
||||||
blobGasUsed = uint64(0)
|
blobGasUsed = uint64(0)
|
||||||
receipts = make(types.Receipts, 0)
|
receipts = make(types.Receipts, 0)
|
||||||
|
|
||||||
|
// TODO return blockAccessList as a part of result
|
||||||
|
blockAccessList = bal.NewConstructionBlockAccessList()
|
||||||
)
|
)
|
||||||
vmContext := vm.BlockContext{
|
vmContext := vm.BlockContext{
|
||||||
CanTransfer: core.CanTransfer,
|
CanTransfer: core.CanTransfer,
|
||||||
|
|
@ -231,14 +235,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig)
|
evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig)
|
||||||
if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil {
|
if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm, blockAccessList)
|
||||||
}
|
}
|
||||||
if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) {
|
if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) {
|
||||||
var (
|
var (
|
||||||
prevNumber = pre.Env.Number - 1
|
prevNumber = pre.Env.Number - 1
|
||||||
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
|
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
|
||||||
)
|
)
|
||||||
core.ProcessParentBlockHash(prevHash, evm)
|
core.ProcessParentBlockHash(prevHash, evm, blockAccessList)
|
||||||
}
|
}
|
||||||
for i := 0; txIt.Next(); i++ {
|
for i := 0; txIt.Next(); i++ {
|
||||||
tx, err := txIt.Tx()
|
tx, err := txIt.Tx()
|
||||||
|
|
@ -271,11 +275,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), len(receipts), uint32(len(receipts)+1))
|
statedb.SetTxContext(tx.Hash(), len(receipts), uint32(len(receipts)+1))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
snapshot = statedb.Snapshot()
|
snapshot = statedb.Snapshot()
|
||||||
gp = gaspool.Snapshot()
|
gp = gaspool.Snapshot()
|
||||||
)
|
)
|
||||||
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, evm)
|
receipt, bal, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||||
|
|
@ -292,6 +297,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
blobGasUsed += txBlobGas
|
blobGasUsed += txBlobGas
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
|
blockAccessList.Merge(bal)
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
|
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
|
|
@ -336,10 +342,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
for _, receipt := range receipts {
|
for _, receipt := range receipts {
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
requests, err := core.PostExecution(context.Background(), chainConfig, vmContext.BlockNumber, vmContext.Time, allLogs, evm, uint32(len(receipts)+1))
|
requests, bal, err := core.PostExecution(context.Background(), chainConfig, vmContext.BlockNumber, vmContext.Time, allLogs, evm, uint32(len(receipts)+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("failed to process post-execution: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("failed to process post-execution: %v", err))
|
||||||
}
|
}
|
||||||
|
blockAccessList.Merge(bal)
|
||||||
|
|
||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time))
|
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -342,9 +343,9 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize implements consensus.Engine and processes withdrawals on top.
|
// Finalize implements consensus.Engine and processes withdrawals on top.
|
||||||
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
|
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, blockAccessIndex uint32, bal *bal.ConstructionBlockAccessList) {
|
||||||
if !beacon.IsPoSHeader(header) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
beacon.ethone.Finalize(chain, header, state, body)
|
beacon.ethone.Finalize(chain, header, state, body, blockAccessIndex, bal)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Withdrawals processing.
|
// Withdrawals processing.
|
||||||
|
|
@ -352,7 +353,20 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
||||||
// Convert amount from gwei to wei.
|
// Convert amount from gwei to wei.
|
||||||
amount := new(uint256.Int).SetUint64(w.Amount)
|
amount := new(uint256.Int).SetUint64(w.Amount)
|
||||||
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
amount = amount.Mul(amount, uint256.NewInt(params.GWei))
|
||||||
state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
prev := state.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
||||||
|
|
||||||
|
// Populate the block-level accessList if Amsterdam is enabled
|
||||||
|
if chain.Config().IsAmsterdam(header.Number, header.Time) {
|
||||||
|
if w.Amount == 0 {
|
||||||
|
// Zero amount withdrawal, account is accessed potential
|
||||||
|
// without state changes.
|
||||||
|
bal.AccountRead(w.Address)
|
||||||
|
} else {
|
||||||
|
// Non-zero amount withdrawal, account is accessed with
|
||||||
|
// a balance change.
|
||||||
|
bal.BalanceChange(blockAccessIndex, w.Address, new(uint256.Int).Add(&prev, amount))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// No block reward which is issued by consensus layer instead.
|
// No block reward which is issued by consensus layer instead.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||||
|
|
@ -573,7 +574,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
|
||||||
|
|
||||||
// Finalize implements consensus.Engine. There is no post-transaction
|
// Finalize implements consensus.Engine. There is no post-transaction
|
||||||
// consensus rules in clique, do nothing here.
|
// consensus rules in clique, do nothing here.
|
||||||
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
|
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, blockAccessIndex uint32, bal *bal.ConstructionBlockAccessList) {
|
||||||
// No block rewards in PoA, so the state remains as is
|
// No block rewards in PoA, so the state remains as is
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
@ -79,12 +80,12 @@ type Engine interface {
|
||||||
// rules of a particular engine. The changes are executed inline.
|
// rules of a particular engine. The changes are executed inline.
|
||||||
Prepare(chain ChainHeaderReader, header *types.Header) error
|
Prepare(chain ChainHeaderReader, header *types.Header) error
|
||||||
|
|
||||||
// Finalize runs any post-transaction state modifications (e.g. block rewards
|
// Finalize runs any post-transaction consensus-specific state modifications
|
||||||
// or process withdrawals) but does not assemble the block.
|
// (e.g. block rewards or process withdrawals) but does not assemble the block.
|
||||||
//
|
//
|
||||||
// Note: The state database might be updated to reflect any consensus rules
|
// Note: The state database might be updated to reflect any consensus rules
|
||||||
// that happen at finalization (e.g. block rewards).
|
// that happen at finalization (e.g. block rewards).
|
||||||
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body)
|
Finalize(chain ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, blockAccessIndex uint32, bal *bal.ConstructionBlockAccessList)
|
||||||
|
|
||||||
// Seal generates a new sealing request for the given input block and pushes
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
// the result into the given channel.
|
// the result into the given channel.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -504,7 +505,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize implements consensus.Engine, accumulating the block and uncle rewards.
|
// Finalize implements consensus.Engine, accumulating the block and uncle rewards.
|
||||||
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body) {
|
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state vm.StateDB, body *types.Body, blockAccessIndex uint32, bal *bal.ConstructionBlockAccessList) {
|
||||||
// Accumulate any block and uncle rewards
|
// Accumulate any block and uncle rewards
|
||||||
accumulateRewards(chain.Config(), state, header, body.Uncles)
|
accumulateRewards(chain.Config(), state, header, body.Uncles)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1319
core/bal_test.go
Normal file
1319
core/bal_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -202,7 +203,7 @@ func TestProcessParentBlockHash(t *testing.T) {
|
||||||
}
|
}
|
||||||
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
|
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
|
||||||
evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
|
evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
|
||||||
ProcessParentBlockHash(header.ParentHash, evm)
|
ProcessParentBlockHash(header.ParentHash, evm, bal.NewConstructionBlockAccessList())
|
||||||
}
|
}
|
||||||
// Read block hashes for block 0 .. num-1
|
// Read block hashes for block 0 .. num-1
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,28 @@ 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 {
|
||||||
|
return errors.New("block access list hash not set in header")
|
||||||
|
}
|
||||||
|
// If the block does not include an access list, compute it locally during
|
||||||
|
// execution and validate it against the access list hash in the header.
|
||||||
|
//
|
||||||
|
// If the block includes an attached access list, validate it directly here.
|
||||||
|
if block.AccessList() != nil {
|
||||||
|
computed := block.AccessList().Hash()
|
||||||
|
if *block.Header().BlockAccessListHash != computed {
|
||||||
|
return fmt.Errorf("access list hash mismatch, computed: %x, remote: %x", computed, *block.Header().BlockAccessListHash)
|
||||||
|
} else if err := block.AccessList().Validate(block.GasLimit()); err != nil {
|
||||||
|
return fmt.Errorf("invalid block access list: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if block.Header().BlockAccessListHash != nil || block.AccessList() != nil {
|
||||||
|
return errors.New("block had access list before Amsterdam")
|
||||||
|
}
|
||||||
|
|
||||||
// Ancestor block must be known.
|
// Ancestor block must be known.
|
||||||
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
|
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
|
||||||
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||||
|
|
@ -160,6 +182,23 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||||
} else if res.Requests != nil {
|
} else if res.Requests != nil {
|
||||||
return errors.New("block has requests before prague fork")
|
return errors.New("block has requests before prague fork")
|
||||||
}
|
}
|
||||||
|
// Verify Block-level accessList once Amsterdam is enabled
|
||||||
|
if v.config.IsAmsterdam(block.Number(), block.Time()) {
|
||||||
|
if res.Bal == nil {
|
||||||
|
return errors.New("block access list is not available in amsterdam")
|
||||||
|
}
|
||||||
|
if block.Header().BlockAccessListHash == nil {
|
||||||
|
return errors.New("block access list hash not set in header")
|
||||||
|
}
|
||||||
|
enc := res.Bal.ToEncodingObj()
|
||||||
|
local, remote := enc.Hash(), *block.Header().BlockAccessListHash
|
||||||
|
if local != remote {
|
||||||
|
return fmt.Errorf("access list hash mismatch, local: %x, remote: %x", local, remote)
|
||||||
|
}
|
||||||
|
if err := enc.Validate(block.GasLimit()); err != nil {
|
||||||
|
return fmt.Errorf("invalid block access list: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Validate the state root against the received state root and throw
|
// Validate the state root against the received state root and throw
|
||||||
// an error if they don't match.
|
// an error if they don't match.
|
||||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -50,6 +51,7 @@ type BlockGen struct {
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
uncles []*types.Header
|
uncles []*types.Header
|
||||||
withdrawals []*types.Withdrawal
|
withdrawals []*types.Withdrawal
|
||||||
|
bal *bal.ConstructionBlockAccessList
|
||||||
|
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +101,7 @@ func (b *BlockGen) Difficulty() *big.Int {
|
||||||
func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
|
func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
|
||||||
b.header.ParentBeaconRoot = &root
|
b.header.ParentBeaconRoot = &root
|
||||||
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||||
ProcessBeaconBlockRoot(root, vm.NewEVM(blockContext, b.statedb, b.cm.config, vm.Config{}))
|
ProcessBeaconBlockRoot(root, vm.NewEVM(blockContext, b.statedb, b.cm.config, vm.Config{}), b.bal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addTx adds a transaction to the generated block. If no coinbase has
|
// addTx adds a transaction to the generated block. If no coinbase has
|
||||||
|
|
@ -118,7 +120,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
|
||||||
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
|
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
|
||||||
)
|
)
|
||||||
b.statedb.SetTxContext(tx.Hash(), len(b.txs), uint32(len(b.txs)+1))
|
b.statedb.SetTxContext(tx.Hash(), len(b.txs), uint32(len(b.txs)+1))
|
||||||
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
|
receipt, bal, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -134,6 +136,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
|
||||||
if b.header.BlobGasUsed != nil {
|
if b.header.BlobGasUsed != nil {
|
||||||
*b.header.BlobGasUsed += receipt.BlobGasUsed
|
*b.header.BlobGasUsed += receipt.BlobGasUsed
|
||||||
}
|
}
|
||||||
|
b.bal.Merge(bal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTx adds a transaction to the generated block. If no coinbase has
|
// AddTx adds a transaction to the generated block. If no coinbase has
|
||||||
|
|
@ -304,10 +307,11 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||||
|
|
||||||
// ConsensusLayerRequests returns the EIP-7685 requests which have accumulated so far.
|
// ConsensusLayerRequests returns the EIP-7685 requests which have accumulated so far.
|
||||||
func (b *BlockGen) ConsensusLayerRequests() [][]byte {
|
func (b *BlockGen) ConsensusLayerRequests() [][]byte {
|
||||||
return b.collectRequests(true)
|
requests, _ := b.collectRequests(true)
|
||||||
|
return requests
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
|
func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte, bal *bal.ConstructionBlockAccessList) {
|
||||||
statedb := b.statedb
|
statedb := b.statedb
|
||||||
if readonly {
|
if readonly {
|
||||||
// The system contracts clear themselves on a system-initiated read.
|
// The system contracts clear themselves on a system-initiated read.
|
||||||
|
|
@ -323,11 +327,11 @@ func (b *BlockGen) collectRequests(readonly bool) (requests [][]byte) {
|
||||||
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
|
||||||
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
evm := vm.NewEVM(blockContext, statedb, b.cm.config, vm.Config{})
|
||||||
|
|
||||||
requests, err := PostExecution(context.Background(), b.cm.config, b.header.Number, b.header.Time, blockLogs, evm, uint32(len(b.txs)+1))
|
requests, bal, err := PostExecution(context.Background(), b.cm.config, b.header.Number, b.header.Time, blockLogs, evm, uint32(len(b.txs)+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("failed to run post-execution: %v", err))
|
panic(fmt.Sprintf("failed to run post-execution: %v", err))
|
||||||
}
|
}
|
||||||
return requests
|
return requests, bal
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateChain creates a chain of n blocks. The first block's
|
// GenerateChain creates a chain of n blocks. The first block's
|
||||||
|
|
@ -354,6 +358,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||||
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
||||||
b.header = cm.makeHeader(parent, statedb, b.engine)
|
b.header = cm.makeHeader(parent, statedb, b.engine)
|
||||||
|
b.bal = bal.NewConstructionBlockAccessList()
|
||||||
|
|
||||||
// Set the difficulty for clique block. The chain maker doesn't have access
|
// Set the difficulty for clique block. The chain maker doesn't have access
|
||||||
// to a chain, so the difficulty will be left unset (nil). Set it here to the
|
// to a chain, so the difficulty will be left unset (nil). Set it here to the
|
||||||
|
|
@ -386,7 +391,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
|
||||||
blockContext.Random = &common.Hash{} // enable post-merge instruction set
|
blockContext.Random = &common.Hash{} // enable post-merge instruction set
|
||||||
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
|
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
|
||||||
ProcessParentBlockHash(b.header.ParentHash, evm)
|
ProcessParentBlockHash(b.header.ParentHash, evm, b.bal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute any user modifications to the block
|
// Execute any user modifications to the block
|
||||||
|
|
@ -394,11 +399,12 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
gen(i, b)
|
gen(i, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
requests := b.collectRequests(false)
|
requests, bal := b.collectRequests(false)
|
||||||
if requests != nil {
|
if requests != nil {
|
||||||
reqHash := types.CalcRequestsHash(requests)
|
reqHash := types.CalcRequestsHash(requests)
|
||||||
b.header.RequestsHash = &reqHash
|
b.header.RequestsHash = &reqHash
|
||||||
}
|
}
|
||||||
|
b.bal.Merge(bal)
|
||||||
|
|
||||||
body := types.Body{
|
body := types.Body{
|
||||||
Transactions: b.txs,
|
Transactions: b.txs,
|
||||||
|
|
@ -414,8 +420,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
body.Withdrawals = make([]*types.Withdrawal, 0)
|
body.Withdrawals = make([]*types.Withdrawal, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Apply the consensus-specific post-transaction changes
|
||||||
|
b.engine.Finalize(cm, b.header, statedb, &body, uint32(len(b.txs)+1), b.bal)
|
||||||
|
|
||||||
// Assemble the block for delivery.
|
// Assemble the block for delivery.
|
||||||
block := AssembleBlock(b.engine, cm, b.header, statedb, &body, b.receipts)
|
block := AssembleBlock(cm, b.header, statedb, &body, b.receipts, b.bal)
|
||||||
|
|
||||||
// Write state changes to db
|
// Write state changes to db
|
||||||
root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number), config.IsCancun(b.header.Number, b.header.Time))
|
root, err := statedb.Commit(b.header.Number.Uint64(), config.IsEIP158(b.header.Number), config.IsCancun(b.header.Number, b.header.Time))
|
||||||
|
|
|
||||||
|
|
@ -555,6 +555,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||||
if head.SlotNumber == nil {
|
if head.SlotNumber == nil {
|
||||||
head.SlotNumber = new(uint64)
|
head.SlotNumber = new(uint64)
|
||||||
}
|
}
|
||||||
|
head.BlockAccessListHash = &types.EmptyBlockAccessListHash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
|
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/internal/telemetry"
|
"github.com/ethereum/go-ethereum/internal/telemetry"
|
||||||
|
|
@ -81,13 +82,16 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
||||||
misc.ApplyDAOHardFork(tracingStateDB)
|
misc.ApplyDAOHardFork(tracingStateDB)
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
context = NewEVMBlockContext(header, p.chain, nil)
|
context = NewEVMBlockContext(header, p.chain, nil)
|
||||||
signer = types.MakeSigner(config, header.Number, header.Time)
|
signer = types.MakeSigner(config, header.Number, header.Time)
|
||||||
evm = vm.NewEVM(context, tracingStateDB, config, cfg)
|
evm = vm.NewEVM(context, tracingStateDB, config, cfg)
|
||||||
|
blockAccessList = bal.NewConstructionBlockAccessList()
|
||||||
)
|
)
|
||||||
defer evm.Release()
|
defer evm.Release()
|
||||||
|
|
||||||
// Run the pre-execution system calls
|
// Run the pre-execution system calls
|
||||||
PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), config, evm, block.Number(), block.Time())
|
blockAccessList.Merge(PreExecution(ctx, block.BeaconRoot(), block.ParentHash(), config, evm, block.Number(), block.Time()))
|
||||||
|
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
|
|
@ -99,76 +103,92 @@ func (p *StateProcessor) Process(ctx context.Context, block *types.Block, stated
|
||||||
telemetry.StringAttribute("tx.hash", tx.Hash().Hex()),
|
telemetry.StringAttribute("tx.hash", tx.Hash().Hex()),
|
||||||
telemetry.Int64Attribute("tx.index", int64(i)),
|
telemetry.Int64Attribute("tx.index", int64(i)),
|
||||||
)
|
)
|
||||||
|
receipt, bal, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, evm)
|
||||||
receipt, err := ApplyTransactionWithEVM(msg, gp, statedb, blockNumber, blockHash, context.Time, tx, evm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
spanEnd(&err)
|
spanEnd(&err)
|
||||||
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
|
blockAccessList.Merge(bal)
|
||||||
spanEnd(nil)
|
spanEnd(nil)
|
||||||
}
|
}
|
||||||
requests, err := PostExecution(ctx, config, block.Number(), block.Time(), allLogs, evm, uint32(len(block.Transactions())+1))
|
requests, bal, err := PostExecution(ctx, config, block.Number(), block.Time(), allLogs, evm, uint32(len(block.Transactions())+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
blockAccessList.Merge(bal)
|
||||||
p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body())
|
|
||||||
|
// Finalize the block, applying any consensus engine specific extras
|
||||||
|
// (e.g. block rewards).
|
||||||
|
//
|
||||||
|
// TODO(rjl493456442) integrate it into the PostExecution.
|
||||||
|
p.chain.Engine().Finalize(p.chain, header, tracingStateDB, block.Body(), uint32(len(block.Transactions())+1), blockAccessList)
|
||||||
|
|
||||||
return &ProcessResult{
|
return &ProcessResult{
|
||||||
Receipts: receipts,
|
Receipts: receipts,
|
||||||
Requests: requests,
|
Requests: requests,
|
||||||
Logs: allLogs,
|
Logs: allLogs,
|
||||||
GasUsed: gp.Used(),
|
GasUsed: gp.Used(),
|
||||||
|
Bal: blockAccessList,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreExecution processes pre-execution system calls.
|
// PreExecution processes pre-execution system calls.
|
||||||
func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent common.Hash, config *params.ChainConfig, evm *vm.EVM, number *big.Int, time uint64) {
|
func PreExecution(ctx context.Context, beaconRoot *common.Hash, parent common.Hash, config *params.ChainConfig, evm *vm.EVM, number *big.Int, time uint64) *bal.ConstructionBlockAccessList {
|
||||||
_, _, spanEnd := telemetry.StartSpan(ctx, "core.preExecution")
|
_, _, spanEnd := telemetry.StartSpan(ctx, "core.preExecution")
|
||||||
defer spanEnd(nil)
|
defer spanEnd(nil)
|
||||||
|
|
||||||
|
var blockAccessList *bal.ConstructionBlockAccessList
|
||||||
|
if config.IsAmsterdam(number, time) {
|
||||||
|
blockAccessList = bal.NewConstructionBlockAccessList()
|
||||||
|
}
|
||||||
// EIP-4788
|
// EIP-4788
|
||||||
if beaconRoot != nil {
|
if beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, evm)
|
ProcessBeaconBlockRoot(*beaconRoot, evm, blockAccessList)
|
||||||
}
|
}
|
||||||
// EIP-2935
|
// EIP-2935
|
||||||
if config.IsPrague(number, time) || config.IsUBT(number, time) {
|
if config.IsPrague(number, time) || config.IsUBT(number, time) {
|
||||||
ProcessParentBlockHash(parent, evm)
|
ProcessParentBlockHash(parent, evm, blockAccessList)
|
||||||
}
|
}
|
||||||
|
return blockAccessList
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostExecution processes post-execution system calls when Prague is enabled.
|
// PostExecution processes post-execution system calls when Prague is enabled.
|
||||||
// If Prague is not activated, it returns null requests to differentiate from
|
// If Prague is not activated, it returns null requests to differentiate from
|
||||||
// empty requests.
|
// empty requests.
|
||||||
func PostExecution(ctx context.Context, config *params.ChainConfig, number *big.Int, time uint64, allLogs []*types.Log, evm *vm.EVM, blockAccessIndex uint32) (requests [][]byte, err error) {
|
func PostExecution(ctx context.Context, config *params.ChainConfig, number *big.Int, time uint64, allLogs []*types.Log, evm *vm.EVM, blockAccessIndex uint32) (requests [][]byte, blockAccessList *bal.ConstructionBlockAccessList, err error) {
|
||||||
_, _, spanEnd := telemetry.StartSpan(ctx, "core.postExecution")
|
_, _, spanEnd := telemetry.StartSpan(ctx, "core.postExecution")
|
||||||
defer spanEnd(&err)
|
defer spanEnd(&err)
|
||||||
|
|
||||||
|
if config.IsAmsterdam(number, time) {
|
||||||
|
blockAccessList = bal.NewConstructionBlockAccessList()
|
||||||
|
}
|
||||||
// Read requests if Prague is enabled.
|
// Read requests if Prague is enabled.
|
||||||
if config.IsPrague(number, time) {
|
if config.IsPrague(number, time) {
|
||||||
|
rules := config.Rules(number, true, time) // IsMerge is always true
|
||||||
|
|
||||||
requests = [][]byte{}
|
requests = [][]byte{}
|
||||||
// EIP-6110
|
// EIP-6110
|
||||||
if err := ParseDepositLogs(&requests, allLogs, config); err != nil {
|
if err := ParseDepositLogs(&requests, allLogs, config); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse deposit logs: %w", err)
|
return nil, nil, fmt.Errorf("failed to parse deposit logs: %w", err)
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := ProcessWithdrawalQueue(&requests, evm, blockAccessIndex); err != nil {
|
if err := ProcessWithdrawalQueue(&requests, rules, evm, blockAccessIndex, blockAccessList); err != nil {
|
||||||
return nil, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
return nil, nil, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
||||||
}
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := ProcessConsolidationQueue(&requests, evm, blockAccessIndex); err != nil {
|
if err := ProcessConsolidationQueue(&requests, rules, evm, blockAccessIndex, blockAccessList); err != nil {
|
||||||
return nil, fmt.Errorf("failed to process consolidation queue: %w", err)
|
return nil, nil, fmt.Errorf("failed to process consolidation queue: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return requests, nil
|
return requests, blockAccessList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
// this method takes an already created EVM instance as input.
|
// this method takes an already created EVM instance as input.
|
||||||
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, evm *vm.EVM) (receipt *types.Receipt, bal *bal.ConstructionBlockAccessList, err error) {
|
||||||
if hooks := evm.Config.Tracer; hooks != nil {
|
if hooks := evm.Config.Tracer; hooks != nil {
|
||||||
if hooks.OnTxStart != nil {
|
if hooks.OnTxStart != nil {
|
||||||
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
|
@ -180,12 +200,12 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
|
||||||
// Apply the transaction to the current state (included in the env).
|
// Apply the transaction to the current state (included in the env).
|
||||||
result, err := ApplyMessage(evm, msg, gp)
|
result, err := ApplyMessage(evm, msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Update the state with pending changes.
|
// Update the state with pending changes.
|
||||||
var root []byte
|
var root []byte
|
||||||
if evm.ChainConfig().IsByzantium(blockNumber) {
|
if evm.ChainConfig().IsByzantium(blockNumber) {
|
||||||
evm.StateDB.Finalise(true)
|
bal = evm.StateDB.Finalise(true)
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes()
|
root = statedb.IntermediateRoot(evm.ChainConfig().IsEIP158(blockNumber)).Bytes()
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +214,7 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
|
||||||
if statedb.Database().Type().Is(state.TypeUBT) {
|
if statedb.Database().Type().Is(state.TypeUBT) {
|
||||||
statedb.AccessEvents().Merge(evm.AccessEvents)
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, gp.CumulativeUsed(), root), nil
|
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, blockTime, tx, gp.CumulativeUsed(), root), bal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
||||||
|
|
@ -239,10 +259,10 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
|
||||||
// and uses the input parameters for its environment. It returns the receipt
|
// and uses the input parameters for its environment. It returns the receipt
|
||||||
// for the transaction and an error if the transaction failed,
|
// for the transaction and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// indicating the block was invalid.
|
||||||
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction) (*types.Receipt, error) {
|
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction) (*types.Receipt, *bal.ConstructionBlockAccessList, error) {
|
||||||
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
|
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Create a new context to be used in the EVM environment
|
// Create a new context to be used in the EVM environment
|
||||||
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
|
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), header.Time, tx, evm)
|
||||||
|
|
@ -250,7 +270,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
|
||||||
|
|
||||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||||
// contract. This method is exported to be used in tests.
|
// contract. This method is exported to be used in tests.
|
||||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList *bal.ConstructionBlockAccessList) {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -267,18 +287,19 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
|
||||||
Data: beaconRoot[:],
|
Data: beaconRoot[:],
|
||||||
}
|
}
|
||||||
evm.SetTxContext(NewEVMTxContext(msg))
|
evm.SetTxContext(NewEVMTxContext(msg))
|
||||||
|
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
|
||||||
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
||||||
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
|
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
|
||||||
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
|
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
evm.StateDB.Finalise(true)
|
blockAccessList.Merge(evm.StateDB.Finalise(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessParentBlockHash stores the parent block hash in the history storage contract
|
// ProcessParentBlockHash stores the parent block hash in the history storage contract
|
||||||
// as per EIP-2935/7709.
|
// as per EIP-2935/7709.
|
||||||
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *bal.ConstructionBlockAccessList) {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -295,6 +316,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
||||||
Data: prevHash.Bytes(),
|
Data: prevHash.Bytes(),
|
||||||
}
|
}
|
||||||
evm.SetTxContext(NewEVMTxContext(msg))
|
evm.SetTxContext(NewEVMTxContext(msg))
|
||||||
|
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
|
||||||
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
|
||||||
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
|
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
|
||||||
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
|
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
|
||||||
|
|
@ -304,22 +326,22 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
evm.StateDB.Finalise(true)
|
blockAccessList.Merge(evm.StateDB.Finalise(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
|
||||||
// It returns the opaque request data returned by the contract.
|
// It returns the opaque request data returned by the contract.
|
||||||
func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM, blockAccessIndex uint32) error {
|
func ProcessWithdrawalQueue(requests *[][]byte, rules params.Rules, evm *vm.EVM, blockAccessIndex uint32, blockAccessList *bal.ConstructionBlockAccessList) error {
|
||||||
return processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress, blockAccessIndex)
|
return processRequestsSystemCall(requests, rules, evm, 0x01, params.WithdrawalQueueAddress, blockAccessIndex, blockAccessList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
|
||||||
// It returns the opaque request data returned by the contract.
|
// It returns the opaque request data returned by the contract.
|
||||||
func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM, blockAccessIndex uint32) error {
|
func ProcessConsolidationQueue(requests *[][]byte, rules params.Rules, evm *vm.EVM, blockAccessIndex uint32, blockAccessList *bal.ConstructionBlockAccessList) error {
|
||||||
return processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress, blockAccessIndex)
|
return processRequestsSystemCall(requests, rules, evm, 0x02, params.ConsolidationQueueAddress, blockAccessIndex, blockAccessList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address, blockAccessIndex uint32) error {
|
func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.EVM, requestType byte, addr common.Address, blockAccessIndex uint32, blockAccessList *bal.ConstructionBlockAccessList) error {
|
||||||
if tracer := evm.Config.Tracer; tracer != nil {
|
if tracer := evm.Config.Tracer; tracer != nil {
|
||||||
onSystemCallStart(tracer, evm.GetVMContext())
|
onSystemCallStart(tracer, evm.GetVMContext())
|
||||||
if tracer.OnSystemCallEnd != nil {
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
|
@ -335,16 +357,19 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
|
||||||
To: &addr,
|
To: &addr,
|
||||||
}
|
}
|
||||||
evm.SetTxContext(NewEVMTxContext(msg))
|
evm.SetTxContext(NewEVMTxContext(msg))
|
||||||
|
evm.StateDB.Prepare(rules, common.Address{}, common.Address{}, nil, nil, nil)
|
||||||
evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex)
|
evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex)
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
|
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
|
||||||
if evm.StateDB.AccessEvents() != nil {
|
if evm.StateDB.AccessEvents() != nil {
|
||||||
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
|
||||||
}
|
}
|
||||||
evm.StateDB.Finalise(true)
|
bal := evm.StateDB.Finalise(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("system call failed to execute: %v", err)
|
return fmt.Errorf("system call failed to execute: %v", err)
|
||||||
}
|
}
|
||||||
|
blockAccessList.Merge(bal)
|
||||||
|
|
||||||
if len(ret) == 0 {
|
if len(ret) == 0 {
|
||||||
return nil // skip empty output
|
return nil // skip empty output
|
||||||
}
|
}
|
||||||
|
|
@ -387,8 +412,16 @@ func onSystemCallStart(tracer *tracing.Hooks, ctx *tracing.VMContext) {
|
||||||
|
|
||||||
// AssembleBlock finalizes the state and assembles the block with provided
|
// AssembleBlock finalizes the state and assembles the block with provided
|
||||||
// body and receipts.
|
// body and receipts.
|
||||||
func AssembleBlock(engine consensus.Engine, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) *types.Block {
|
func AssembleBlock(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt, blockAccessList *bal.ConstructionBlockAccessList) *types.Block {
|
||||||
engine.Finalize(chain, header, state, body)
|
// Assign the post-transition state root
|
||||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
|
||||||
|
if !chain.Config().IsAmsterdam(header.Number, header.Time) {
|
||||||
|
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||||
|
}
|
||||||
|
// Assign the BlockAccessListHash if Amsterdam has been enabled
|
||||||
|
bal := blockAccessList.ToEncodingObj()
|
||||||
|
balHash := bal.Hash()
|
||||||
|
header.BlockAccessListHash = &balHash
|
||||||
|
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)).WithAccessListUnsafe(bal)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -608,7 +608,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
|
|
||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(EIP-1153)
|
||||||
|
// - enable block-level accessList construction (EIP-7928)
|
||||||
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -58,4 +59,8 @@ type ProcessResult struct {
|
||||||
Requests [][]byte
|
Requests [][]byte
|
||||||
Logs []*types.Log
|
Logs []*types.Log
|
||||||
GasUsed uint64
|
GasUsed uint64
|
||||||
|
|
||||||
|
// BAL is only meaningful for post-Amsterdam blocks. Please ensure
|
||||||
|
// fork validation is performed before accessing it.
|
||||||
|
Bal *bal.ConstructionBlockAccessList
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,10 +138,62 @@ func (b *ConstructionBlockAccessList) BalanceChange(txIdx uint32, address common
|
||||||
|
|
||||||
// PrettyPrint returns a human-readable representation of the access list
|
// PrettyPrint returns a human-readable representation of the access list
|
||||||
func (b *ConstructionBlockAccessList) PrettyPrint() string {
|
func (b *ConstructionBlockAccessList) PrettyPrint() string {
|
||||||
enc := b.toEncodingObj()
|
enc := b.ToEncodingObj()
|
||||||
return enc.PrettyPrint()
|
return enc.PrettyPrint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge applies other on top of the local block access list. For colliding
|
||||||
|
// entries (a (slot, txIdx) write or a txIdx-keyed balance/nonce/code change),
|
||||||
|
// the value from other wins, matching the semantics of applying the local
|
||||||
|
// effects first and then other's. Storage reads are unioned; any slot
|
||||||
|
// written by either side is dropped from StorageReads.
|
||||||
|
//
|
||||||
|
// Typically each list covers its own tx index, so txIdx-level collisions are
|
||||||
|
// not expected; the exception is pre/post-transition system calls, which
|
||||||
|
// share a single tx index. In that case callers must pass block-accessList
|
||||||
|
// in order strictly.
|
||||||
|
//
|
||||||
|
// other is referenced (not deep copied), after the call both lists share
|
||||||
|
// inner maps and other must not be mutated.
|
||||||
|
func (b *ConstructionBlockAccessList) Merge(other *ConstructionBlockAccessList) {
|
||||||
|
if other == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for addr, otherAcc := range other.Accounts {
|
||||||
|
acc, ok := b.Accounts[addr]
|
||||||
|
if !ok {
|
||||||
|
b.Accounts[addr] = otherAcc
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for key, writes := range otherAcc.StorageWrites {
|
||||||
|
existing, ok := acc.StorageWrites[key]
|
||||||
|
if !ok {
|
||||||
|
acc.StorageWrites[key] = writes
|
||||||
|
} else {
|
||||||
|
for txIdx, value := range writes {
|
||||||
|
existing[txIdx] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(acc.StorageReads, key)
|
||||||
|
}
|
||||||
|
for key := range otherAcc.StorageReads {
|
||||||
|
if _, ok := acc.StorageWrites[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
acc.StorageReads[key] = struct{}{}
|
||||||
|
}
|
||||||
|
for txIdx, balance := range otherAcc.BalanceChanges {
|
||||||
|
acc.BalanceChanges[txIdx] = balance
|
||||||
|
}
|
||||||
|
for txIdx, nonce := range otherAcc.NonceChanges {
|
||||||
|
acc.NonceChanges[txIdx] = nonce
|
||||||
|
}
|
||||||
|
for txIdx, code := range otherAcc.CodeChange {
|
||||||
|
acc.CodeChange[txIdx] = code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy returns a deep copy of the access list.
|
// Copy returns a deep copy of the access list.
|
||||||
func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
func (b *ConstructionBlockAccessList) Copy() *ConstructionBlockAccessList {
|
||||||
res := NewConstructionBlockAccessList()
|
res := NewConstructionBlockAccessList()
|
||||||
|
|
|
||||||
|
|
@ -78,17 +78,43 @@ func (e *BlockAccessList) DecodeRLP(s *rlp.Stream) error {
|
||||||
// Validate returns an error if the contents of the access list are not ordered
|
// Validate returns an error if the contents of the access list are not ordered
|
||||||
// according to the spec or any code changes are contained which exceed protocol
|
// according to the spec or any code changes are contained which exceed protocol
|
||||||
// max code size.
|
// max code size.
|
||||||
func (e *BlockAccessList) Validate(rules params.Rules) error {
|
func (e *BlockAccessList) Validate(blockGasLimit uint64) error {
|
||||||
if !slices.IsSortedFunc(*e, func(a, b AccountAccess) int {
|
if !slices.IsSortedFunc(*e, func(a, b AccountAccess) int {
|
||||||
return bytes.Compare(a.Address[:], b.Address[:])
|
return bytes.Compare(a.Address[:], b.Address[:])
|
||||||
}) {
|
}) {
|
||||||
return errors.New("block access list accounts not in lexicographic order")
|
return errors.New("block access list accounts not in lexicographic order")
|
||||||
}
|
}
|
||||||
for _, entry := range *e {
|
for _, entry := range *e {
|
||||||
if err := entry.validate(rules); err != nil {
|
if err := entry.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return e.ValidateSize(blockGasLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// itemCount returns the number of items in the BAL for EIP-7928 size-constraint
|
||||||
|
// purposes: the count of distinct addresses plus every storage key (writes +
|
||||||
|
// reads) carried by those accounts. A storage slot is counted once regardless
|
||||||
|
// of how many transactions wrote to it.
|
||||||
|
func (e *BlockAccessList) itemCount() uint64 {
|
||||||
|
count := uint64(len(*e)) // distinct addresses
|
||||||
|
for i := range *e {
|
||||||
|
count += uint64(len((*e)[i].StorageWrites)) + uint64(len((*e)[i].StorageReads))
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateSize returns an error if the BAL violates the EIP-7928 size
|
||||||
|
// constraint for the given block gas limit:
|
||||||
|
//
|
||||||
|
// itemCount() <= blockGasLimit / params.BALItemCost
|
||||||
|
func (e *BlockAccessList) ValidateSize(blockGasLimit uint64) error {
|
||||||
|
items := e.itemCount()
|
||||||
|
limit := blockGasLimit / params.BALItemCost
|
||||||
|
if items > limit {
|
||||||
|
return fmt.Errorf("block access list exceeds size constraint: items=%d, limit=%d (block gas limit %d / %d)",
|
||||||
|
items, limit, blockGasLimit, params.BALItemCost)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +185,7 @@ type AccountAccess struct {
|
||||||
// validate converts the account accesses out of encoding format.
|
// validate converts the account accesses out of encoding format.
|
||||||
// If any of the keys in the encoding object are not ordered according to the
|
// If any of the keys in the encoding object are not ordered according to the
|
||||||
// spec, an error is returned.
|
// spec, an error is returned.
|
||||||
func (e *AccountAccess) validate(rules params.Rules) error {
|
func (e *AccountAccess) validate() error {
|
||||||
// Check the storage write slots are sorted in order
|
// Check the storage write slots are sorted in order
|
||||||
if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int {
|
if !slices.IsSortedFunc(e.StorageWrites, func(a, b encodingSlotWrites) int {
|
||||||
return a.Slot.Cmp(b.Slot)
|
return a.Slot.Cmp(b.Slot)
|
||||||
|
|
@ -200,14 +226,7 @@ func (e *AccountAccess) validate(rules params.Rules) error {
|
||||||
return errors.New("code changes not in ascending order by tx index")
|
return errors.New("code changes not in ascending order by tx index")
|
||||||
}
|
}
|
||||||
for _, change := range e.CodeChanges {
|
for _, change := range e.CodeChanges {
|
||||||
var sizeLimit int
|
if len(change.Code) > params.MaxCodeSizeAmsterdam {
|
||||||
switch {
|
|
||||||
case rules.IsAmsterdam:
|
|
||||||
sizeLimit = params.MaxCodeSizeAmsterdam
|
|
||||||
default:
|
|
||||||
sizeLimit = params.MaxCodeSize
|
|
||||||
}
|
|
||||||
if len(change.Code) > sizeLimit {
|
|
||||||
return errors.New("code change contained oversized code")
|
return errors.New("code change contained oversized code")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +276,7 @@ func (e *AccountAccess) Copy() AccountAccess {
|
||||||
|
|
||||||
// EncodeRLP returns the RLP-encoded access list
|
// EncodeRLP returns the RLP-encoded access list
|
||||||
func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error {
|
func (b *ConstructionBlockAccessList) EncodeRLP(wr io.Writer) error {
|
||||||
return b.toEncodingObj().EncodeRLP(wr)
|
return b.ToEncodingObj().EncodeRLP(wr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ rlp.Encoder = &ConstructionBlockAccessList{}
|
var _ rlp.Encoder = &ConstructionBlockAccessList{}
|
||||||
|
|
@ -340,9 +359,9 @@ func (a *ConstructionAccountAccess) toEncodingObj(addr common.Address) AccountAc
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// toEncodingObj returns an instance of the access list expressed as the type
|
// ToEncodingObj returns an instance of the access list expressed as the type
|
||||||
// which is used as input for the encoding/decoding.
|
// which is used as input for the encoding/decoding.
|
||||||
func (b *ConstructionBlockAccessList) toEncodingObj() *BlockAccessList {
|
func (b *ConstructionBlockAccessList) ToEncodingObj() *BlockAccessList {
|
||||||
var addresses []common.Address
|
var addresses []common.Address
|
||||||
for addr := range b.Accounts {
|
for addr := range b.Accounts {
|
||||||
addresses = append(addresses, addr)
|
addresses = append(addresses, addr)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package bal
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -98,14 +99,65 @@ func TestBALEncoding(t *testing.T) {
|
||||||
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
|
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
|
||||||
t.Fatalf("decoding failed: %v\n", err)
|
t.Fatalf("decoding failed: %v\n", err)
|
||||||
}
|
}
|
||||||
if dec.Hash() != bal.toEncodingObj().Hash() {
|
if dec.Hash() != bal.ToEncodingObj().Hash() {
|
||||||
t.Fatalf("encoded block hash doesn't match decoded")
|
t.Fatalf("encoded block hash doesn't match decoded")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(bal.toEncodingObj(), &dec) {
|
if !reflect.DeepEqual(bal.ToEncodingObj(), &dec) {
|
||||||
t.Fatal("decoded BAL doesn't match")
|
t.Fatal("decoded BAL doesn't match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConstructionBALMerge(t *testing.T) {
|
||||||
|
var (
|
||||||
|
addrA = common.BytesToAddress([]byte{0xAA})
|
||||||
|
addrB = common.BytesToAddress([]byte{0xBB})
|
||||||
|
slot1 = common.BytesToHash([]byte{0x01})
|
||||||
|
slot2 = common.BytesToHash([]byte{0x02})
|
||||||
|
slot3 = common.BytesToHash([]byte{0x03})
|
||||||
|
)
|
||||||
|
a := NewConstructionBlockAccessList()
|
||||||
|
a.StorageWrite(1, addrA, slot1, common.BytesToHash([]byte{0x11}))
|
||||||
|
a.StorageRead(addrA, slot2) // demoted by other's write below
|
||||||
|
a.BalanceChange(1, addrA, uint256.NewInt(100))
|
||||||
|
a.NonceChange(addrA, 1, 7)
|
||||||
|
|
||||||
|
b := NewConstructionBlockAccessList()
|
||||||
|
b.StorageWrite(2, addrA, slot1, common.BytesToHash([]byte{0x22})) // same slot, disjoint txIdx
|
||||||
|
b.StorageWrite(2, addrA, slot2, common.BytesToHash([]byte{0x33}))
|
||||||
|
b.StorageRead(addrA, slot3)
|
||||||
|
b.BalanceChange(2, addrA, uint256.NewInt(200))
|
||||||
|
b.NonceChange(addrA, 2, 8)
|
||||||
|
b.CodeChange(addrB, 2, []byte{0xde, 0xad}) // account only in other
|
||||||
|
|
||||||
|
a.Merge(b)
|
||||||
|
|
||||||
|
accA := a.Accounts[addrA]
|
||||||
|
wantWrites := map[common.Hash]map[uint32]common.Hash{
|
||||||
|
slot1: {1: common.BytesToHash([]byte{0x11}), 2: common.BytesToHash([]byte{0x22})},
|
||||||
|
slot2: {2: common.BytesToHash([]byte{0x33})},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(accA.StorageWrites, wantWrites) {
|
||||||
|
t.Fatalf("storage writes mismatch: got %v, want %v", accA.StorageWrites, wantWrites)
|
||||||
|
}
|
||||||
|
wantReads := map[common.Hash]struct{}{slot3: {}}
|
||||||
|
if !reflect.DeepEqual(accA.StorageReads, wantReads) {
|
||||||
|
t.Fatalf("storage reads mismatch: got %v, want %v", accA.StorageReads, wantReads)
|
||||||
|
}
|
||||||
|
if accA.BalanceChanges[1].Uint64() != 100 || accA.BalanceChanges[2].Uint64() != 200 {
|
||||||
|
t.Fatalf("balance changes mismatch: %v", accA.BalanceChanges)
|
||||||
|
}
|
||||||
|
if accA.NonceChanges[1] != 7 || accA.NonceChanges[2] != 8 {
|
||||||
|
t.Fatalf("nonce changes mismatch: %v", accA.NonceChanges)
|
||||||
|
}
|
||||||
|
accB, ok := a.Accounts[addrB]
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("account only present in other was not adopted")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(accB.CodeChange[2], []byte{0xde, 0xad}) {
|
||||||
|
t.Fatalf("code change for adopted account missing: %x", accB.CodeChange[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeTestAccountAccess(sort bool) AccountAccess {
|
func makeTestAccountAccess(sort bool) AccountAccess {
|
||||||
var (
|
var (
|
||||||
storageWrites []encodingSlotWrites
|
storageWrites []encodingSlotWrites
|
||||||
|
|
@ -231,10 +283,82 @@ func TestBlockAccessListCopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockAccessListItemCount(t *testing.T) {
|
||||||
|
empty := &BlockAccessList{}
|
||||||
|
if got := empty.itemCount(); got != 0 {
|
||||||
|
t.Fatalf("empty BAL item count: got %d, want 0", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr1 := [20]byte(testrand.Bytes(20))
|
||||||
|
addr2 := [20]byte(testrand.Bytes(20))
|
||||||
|
one := func() *uint256.Int { return new(uint256.Int).SetBytes(testrand.Bytes(32)) }
|
||||||
|
bal := &BlockAccessList{
|
||||||
|
AccountAccess{
|
||||||
|
Address: addr1,
|
||||||
|
StorageWrites: []encodingSlotWrites{
|
||||||
|
{Slot: one(), Accesses: []encodingStorageWrite{{TxIdx: 0, ValueAfter: one()}, {TxIdx: 1, ValueAfter: one()}}},
|
||||||
|
{Slot: one()},
|
||||||
|
},
|
||||||
|
StorageReads: []*uint256.Int{one()},
|
||||||
|
},
|
||||||
|
AccountAccess{Address: addr2}, // address-only, no slots
|
||||||
|
}
|
||||||
|
// 2 addresses + 2 write-slots + 1 read-slot = 5 items.
|
||||||
|
// (Multiple TxIdx writes to the same slot count as ONE item.)
|
||||||
|
if got := bal.itemCount(); got != 5 {
|
||||||
|
t.Fatalf("item count: got %d, want 5", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockAccessListValidateSize(t *testing.T) {
|
||||||
|
// Build a BAL with exactly 30 items: 3 addresses, each with 9 storage
|
||||||
|
// slots (some writes, some reads). 3 + 9*3 = 30.
|
||||||
|
one := func() *uint256.Int { return new(uint256.Int).SetBytes(testrand.Bytes(32)) }
|
||||||
|
bal := make(BlockAccessList, 3)
|
||||||
|
for i := range bal {
|
||||||
|
bal[i].Address = [20]byte(testrand.Bytes(20))
|
||||||
|
for j := 0; j < 5; j++ {
|
||||||
|
bal[i].StorageWrites = append(bal[i].StorageWrites, encodingSlotWrites{
|
||||||
|
Slot: one(), Accesses: []encodingStorageWrite{{TxIdx: 0, ValueAfter: one()}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
bal[i].StorageReads = append(bal[i].StorageReads, one())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got := bal.itemCount(); got != 30 {
|
||||||
|
t.Fatalf("setup: item count = %d, want 30", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit = blockGasLimit / BALItemCost.
|
||||||
|
// 30 items requires limit >= 30, i.e. gasLimit >= 30 * 2000 = 60_000.
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
gasLimit uint64
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{"exactly at limit", 30 * params.BALItemCost, false},
|
||||||
|
{"well above limit", 60_000_000, false},
|
||||||
|
{"one below limit", 30*params.BALItemCost - 1, true},
|
||||||
|
{"zero gas limit", 0, true},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
err := bal.ValidateSize(tc.gasLimit)
|
||||||
|
if (err != nil) != tc.expectError {
|
||||||
|
t.Errorf("%s: got err=%v, expectError=%v", tc.name, err, tc.expectError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty BAL is always valid (even with 0 gas limit).
|
||||||
|
if err := (&BlockAccessList{}).ValidateSize(0); err != nil {
|
||||||
|
t.Fatalf("empty BAL must pass any limit: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBlockAccessListValidation(t *testing.T) {
|
func TestBlockAccessListValidation(t *testing.T) {
|
||||||
// Validate the block access list after RLP decoding
|
// Validate the block access list after RLP decoding
|
||||||
enc := makeTestBAL(true)
|
enc := makeTestBAL(true)
|
||||||
if err := enc.Validate(params.Rules{}); err != nil {
|
if err := enc.Validate(math.MaxUint64); err != nil {
|
||||||
t.Fatalf("Unexpected validation error: %v", err)
|
t.Fatalf("Unexpected validation error: %v", err)
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
@ -246,14 +370,14 @@ func TestBlockAccessListValidation(t *testing.T) {
|
||||||
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
|
if err := dec.DecodeRLP(rlp.NewStream(bytes.NewReader(buf.Bytes()), 0)); err != nil {
|
||||||
t.Fatalf("Unexpected RLP-decode error: %v", err)
|
t.Fatalf("Unexpected RLP-decode error: %v", err)
|
||||||
}
|
}
|
||||||
if err := dec.Validate(params.Rules{}); err != nil {
|
if err := dec.Validate(math.MaxUint64); err != nil {
|
||||||
t.Fatalf("Unexpected validation error: %v", err)
|
t.Fatalf("Unexpected validation error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the derived block access list
|
// Validate the derived block access list
|
||||||
cBAL := makeTestConstructionBAL()
|
cBAL := makeTestConstructionBAL()
|
||||||
listB := cBAL.toEncodingObj()
|
listB := cBAL.ToEncodingObj()
|
||||||
if err := listB.Validate(params.Rules{}); err != nil {
|
if err := listB.Validate(math.MaxUint64); err != nil {
|
||||||
t.Fatalf("Unexpected validation error: %v", err)
|
t.Fatalf("Unexpected validation error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -413,8 +413,9 @@ func (b *Block) BaseFee() *big.Int {
|
||||||
return new(big.Int).Set(b.header.BaseFee)
|
return new(big.Int).Set(b.header.BaseFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot }
|
func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot }
|
||||||
func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash }
|
func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash }
|
||||||
|
func (b *Block) BlockAccessListHash() *common.Hash { return b.header.BlockAccessListHash }
|
||||||
|
|
||||||
func (b *Block) ExcessBlobGas() *uint64 {
|
func (b *Block) ExcessBlobGas() *uint64 {
|
||||||
var excessBlobGas *uint64
|
var excessBlobGas *uint64
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,9 @@ var (
|
||||||
// EmptyRequestsHash is the known hash of an empty request set, sha256("").
|
// EmptyRequestsHash is the known hash of an empty request set, sha256("").
|
||||||
EmptyRequestsHash = common.HexToHash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
EmptyRequestsHash = common.HexToHash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||||
|
|
||||||
|
// EmptyBlockAccessListHash is the known hash of an empty block accessList, keccak256(rlp.encode([])).
|
||||||
|
EmptyBlockAccessListHash = common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")
|
||||||
|
|
||||||
// EmptyBinaryHash is the known hash of an empty binary trie.
|
// EmptyBinaryHash is the known hash of an empty binary trie.
|
||||||
EmptyBinaryHash = common.Hash{}
|
EmptyBinaryHash = common.Hash{}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -709,3 +709,8 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
|
||||||
StateDB: evm.StateDB,
|
StateDB: evm.StateDB,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRules returns the chain rules used throughout the EVM execution.
|
||||||
|
func (evm *EVM) GetRules() params.Rules {
|
||||||
|
return evm.chainRules
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1018,7 +1018,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
||||||
// Call Prepare to clear out the statedb access list
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex, uint32(txctx.TxIndex+1))
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex, uint32(txctx.TxIndex+1))
|
||||||
|
|
||||||
_, err = core.ApplyTransactionWithEVM(message, core.NewGasPool(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, evm)
|
_, _, err = core.ApplyTransactionWithEVM(message, core.NewGasPool(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, vmctx.Time, tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -620,7 +620,7 @@ func TestSelfdestructStateTracer(t *testing.T) {
|
||||||
}
|
}
|
||||||
context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
|
context := core.NewEVMBlockContext(block.Header(), blockchain, nil)
|
||||||
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
|
evm := vm.NewEVM(context, hookedState, tt.genesis.Config, vm.Config{Tracer: tracer.Hooks()})
|
||||||
_, err = core.ApplyTransactionWithEVM(msg, core.NewGasPool(msg.GasLimit), statedb, block.Number(), block.Hash(), block.Time(), tx, evm)
|
_, _, err = core.ApplyTransactionWithEVM(msg, core.NewGasPool(msg.GasLimit), statedb, block.Number(), block.Hash(), block.Time(), tx, evm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
"github.com/ethereum/go-ethereum/internal/ethapi/override"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -292,9 +293,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
gp = core.NewGasPool(blockContext.GasLimit)
|
gp = core.NewGasPool(blockContext.GasLimit)
|
||||||
blobGasUsed uint64
|
blobGasUsed uint64
|
||||||
|
|
||||||
txes = make([]*types.Transaction, len(block.Calls))
|
txes = make([]*types.Transaction, len(block.Calls))
|
||||||
callResults = make([]simCallResult, len(block.Calls))
|
callResults = make([]simCallResult, len(block.Calls))
|
||||||
receipts = make([]*types.Receipt, len(block.Calls))
|
receipts = make([]*types.Receipt, len(block.Calls))
|
||||||
|
blockAccessList = bal.NewConstructionBlockAccessList()
|
||||||
|
|
||||||
// Block hash will be repaired after execution.
|
// Block hash will be repaired after execution.
|
||||||
tracer = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), blockContext.Time, common.Hash{}, common.Hash{}, 0)
|
tracer = newTracer(sim.traceTransfers, blockContext.BlockNumber.Uint64(), blockContext.Time, common.Hash{}, common.Hash{}, 0)
|
||||||
|
|
@ -313,13 +315,14 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
|
evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
|
||||||
defer evm.Release()
|
defer evm.Release()
|
||||||
|
|
||||||
// It is possible to override precompiles with EVM bytecode, or
|
// It is possible to override precompiles with EVM bytecode, or
|
||||||
// move them to another address.
|
// move them to another address.
|
||||||
if precompiles != nil {
|
if precompiles != nil {
|
||||||
evm.SetPrecompiles(precompiles)
|
evm.SetPrecompiles(precompiles)
|
||||||
}
|
}
|
||||||
// Run pre-execution system calls
|
// Run pre-execution system calls
|
||||||
core.PreExecution(ctx, header.ParentBeaconRoot, header.ParentHash, sim.chainConfig, evm, header.Number, header.Time)
|
blockAccessList.Merge(core.PreExecution(ctx, header.ParentBeaconRoot, header.ParentHash, sim.chainConfig, evm, header.Number, header.Time))
|
||||||
|
|
||||||
var allLogs []*types.Log
|
var allLogs []*types.Log
|
||||||
for i, call := range block.Calls {
|
for i, call := range block.Calls {
|
||||||
|
|
@ -350,7 +353,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
// Update the state with pending changes.
|
// Update the state with pending changes.
|
||||||
var root []byte
|
var root []byte
|
||||||
if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
|
if sim.chainConfig.IsByzantium(blockContext.BlockNumber) {
|
||||||
tracingStateDB.Finalise(true)
|
blockAccessList.Merge(tracingStateDB.Finalise(true))
|
||||||
} else {
|
} else {
|
||||||
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
|
root = sim.state.IntermediateRoot(sim.chainConfig.IsEIP158(blockContext.BlockNumber)).Bytes()
|
||||||
}
|
}
|
||||||
|
|
@ -391,7 +394,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process EIP-7685 requests
|
// Process EIP-7685 requests
|
||||||
requests, err := core.PostExecution(ctx, sim.chainConfig, header.Number, header.Time, allLogs, evm, uint32(len(block.Calls)+1))
|
requests, bal, err := core.PostExecution(ctx, sim.chainConfig, header.Number, header.Time, allLogs, evm, uint32(len(block.Calls)+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -399,6 +402,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
reqHash := types.CalcRequestsHash(requests)
|
reqHash := types.CalcRequestsHash(requests)
|
||||||
header.RequestsHash = &reqHash
|
header.RequestsHash = &reqHash
|
||||||
}
|
}
|
||||||
|
blockAccessList.Merge(bal)
|
||||||
|
|
||||||
blockBody := &types.Body{
|
blockBody := &types.Body{
|
||||||
Transactions: txes,
|
Transactions: txes,
|
||||||
|
|
@ -411,8 +415,11 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
}
|
}
|
||||||
chainHeadReader := &simChainHeadReader{ctx, sim.b}
|
chainHeadReader := &simChainHeadReader{ctx, sim.b}
|
||||||
|
|
||||||
|
// Apply the consensus-specific post-transaction changes
|
||||||
|
sim.b.Engine().Finalize(chainHeadReader, header, sim.state, blockBody, uint32(len(block.Calls)+1), blockAccessList)
|
||||||
|
|
||||||
// Assemble the block
|
// Assemble the block
|
||||||
b := core.AssembleBlock(sim.b.Engine(), chainHeadReader, header, sim.state, blockBody, receipts)
|
b := core.AssembleBlock(chainHeadReader, header, sim.state, blockBody, receipts, blockAccessList)
|
||||||
|
|
||||||
repairLogs(callResults, b.Hash())
|
repairLogs(callResults, b.Hash())
|
||||||
return b, callResults, senders, nil
|
return b, callResults, senders, nil
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types/bal"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/internal/telemetry"
|
"github.com/ethereum/go-ethereum/internal/telemetry"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -71,6 +72,7 @@ type environment struct {
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
sidecars []*types.BlobTxSidecar
|
sidecars []*types.BlobTxSidecar
|
||||||
blobs int
|
blobs int
|
||||||
|
bal *bal.ConstructionBlockAccessList
|
||||||
|
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +210,7 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect consensus-layer requests if Prague is enabled.
|
// Collect consensus-layer requests if Prague is enabled.
|
||||||
requests, err := core.PostExecution(ctx, miner.chainConfig, work.header.Number, work.header.Time, allLogs, work.evm, uint32(work.tcount+1))
|
requests, bal, err := core.PostExecution(ctx, miner.chainConfig, work.header.Number, work.header.Time, allLogs, work.evm, uint32(work.tcount+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &newPayloadResult{err: err}
|
return &newPayloadResult{err: err}
|
||||||
}
|
}
|
||||||
|
|
@ -216,9 +218,14 @@ func (miner *Miner) generateWork(ctx context.Context, genParam *generateParams,
|
||||||
reqHash := types.CalcRequestsHash(requests)
|
reqHash := types.CalcRequestsHash(requests)
|
||||||
work.header.RequestsHash = &reqHash
|
work.header.RequestsHash = &reqHash
|
||||||
}
|
}
|
||||||
|
work.bal.Merge(bal)
|
||||||
|
|
||||||
|
// Apply the consensus-specific post-transaction changes
|
||||||
|
miner.engine.Finalize(miner.chain, work.header, work.state, &body, uint32(work.tcount+1), work.bal)
|
||||||
|
|
||||||
// Assemble the block for delivery.
|
// Assemble the block for delivery.
|
||||||
_, _, assembleSpanEnd := telemetry.StartSpan(ctx, "miner.AssembleBlock")
|
_, _, assembleSpanEnd := telemetry.StartSpan(ctx, "miner.AssembleBlock")
|
||||||
block := core.AssembleBlock(miner.engine, miner.chain, work.header, work.state, &body, work.receipts)
|
block := core.AssembleBlock(miner.chain, work.header, work.state, &body, work.receipts, work.bal)
|
||||||
assembleSpanEnd(nil)
|
assembleSpanEnd(nil)
|
||||||
|
|
||||||
return &newPayloadResult{
|
return &newPayloadResult{
|
||||||
|
|
@ -318,7 +325,7 @@ func (miner *Miner) prepareWork(ctx context.Context, genParams *generateParams,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Run pre-execution system calls
|
// Run pre-execution system calls
|
||||||
core.PreExecution(ctx, header.ParentBeaconRoot, header.ParentHash, miner.chainConfig, env.evm, header.Number, header.Time)
|
env.bal.Merge(core.PreExecution(ctx, header.ParentBeaconRoot, header.ParentHash, miner.chainConfig, env.evm, header.Number, header.Time))
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,6 +344,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.StartPrefetcher("miner", bundle)
|
state.StartPrefetcher("miner", bundle)
|
||||||
|
|
||||||
// Note the passed coinbase may be different with header.Coinbase.
|
// Note the passed coinbase may be different with header.Coinbase.
|
||||||
return &environment{
|
return &environment{
|
||||||
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
|
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
|
||||||
|
|
@ -345,6 +353,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
gasPool: core.NewGasPool(header.GasLimit),
|
gasPool: core.NewGasPool(header.GasLimit),
|
||||||
header: header,
|
header: header,
|
||||||
|
bal: bal.NewConstructionBlockAccessList(),
|
||||||
witness: state.Witness(),
|
witness: state.Witness(),
|
||||||
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}),
|
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -356,7 +365,7 @@ func (miner *Miner) commitTransaction(ctx context.Context, env *environment, tx
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
return miner.commitBlobTransaction(env, tx)
|
return miner.commitBlobTransaction(env, tx)
|
||||||
}
|
}
|
||||||
receipt, err := miner.applyTransaction(env, tx)
|
receipt, bal, err := miner.applyTransaction(env, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -364,6 +373,7 @@ func (miner *Miner) commitTransaction(ctx context.Context, env *environment, tx
|
||||||
env.receipts = append(env.receipts, receipt)
|
env.receipts = append(env.receipts, receipt)
|
||||||
env.size += tx.Size()
|
env.size += tx.Size()
|
||||||
env.tcount++
|
env.tcount++
|
||||||
|
env.bal.Merge(bal)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,7 +390,7 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
|
||||||
if env.blobs+len(sc.Blobs) > maxBlobs {
|
if env.blobs+len(sc.Blobs) > maxBlobs {
|
||||||
return errors.New("max data blobs reached")
|
return errors.New("max data blobs reached")
|
||||||
}
|
}
|
||||||
receipt, err := miner.applyTransaction(env, tx)
|
receipt, bal, err := miner.applyTransaction(env, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -392,23 +402,24 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
|
||||||
env.size += txNoBlob.Size()
|
env.size += txNoBlob.Size()
|
||||||
*env.header.BlobGasUsed += receipt.BlobGasUsed
|
*env.header.BlobGasUsed += receipt.BlobGasUsed
|
||||||
env.tcount++
|
env.tcount++
|
||||||
|
env.bal.Merge(bal)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
||||||
func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
|
func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, *bal.ConstructionBlockAccessList, error) {
|
||||||
var (
|
var (
|
||||||
snap = env.state.Snapshot()
|
snap = env.state.Snapshot()
|
||||||
gp = env.gasPool.Snapshot()
|
gp = env.gasPool.Snapshot()
|
||||||
)
|
)
|
||||||
receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx)
|
receipt, bal, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.RevertToSnapshot(snap)
|
env.state.RevertToSnapshot(snap)
|
||||||
env.gasPool.Set(gp)
|
env.gasPool.Set(gp)
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
env.header.GasUsed = env.gasPool.Used()
|
env.header.GasUsed = env.gasPool.Used()
|
||||||
return receipt, nil
|
return receipt, bal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (miner *Miner) commitTransactions(ctx context.Context, env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
|
func (miner *Miner) commitTransactions(ctx context.Context, env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,16 @@ const (
|
||||||
HistoryServeWindow = 8191 // Number of blocks to serve historical block hashes for, EIP-2935.
|
HistoryServeWindow = 8191 // Number of blocks to serve historical block hashes for, EIP-2935.
|
||||||
|
|
||||||
MaxBlockSize = 8_388_608 // maximum size of an RLP-encoded block
|
MaxBlockSize = 8_388_608 // maximum size of an RLP-encoded block
|
||||||
|
|
||||||
|
// BALItemCost is the gas-cost divisor for the EIP-7928 block access list
|
||||||
|
// size constraint: bal_items <= block_gas_limit / BALItemCost, where
|
||||||
|
// bal_items counts every distinct address in the BAL plus every storage
|
||||||
|
// key (writes + reads) carried by those accounts.
|
||||||
|
//
|
||||||
|
// The value (2000) is set deliberately below COLD_SLOAD_COST (2100) so
|
||||||
|
// the bound has a small safety margin for system-contract accesses that
|
||||||
|
// don't consume block gas.
|
||||||
|
BALItemCost uint64 = 2000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue