forked from cerc-io/plugeth
beacon/engine, eth/catalyst, miner: EIP-4788 CL/EL protocol updates (#27872)
This PR makes EIP-4788 work in the engine API and miner. It also fixes some bugs related to EIP-4844 block processing and mining. Changes in detail: - Header.BeaconRoot has been renamed to ParentBeaconRoot. - The engine API now implements forkchoiceUpdatedV3 - newPayloadV3 method has been updated with the parentBeaconBlockRoot parameter - beacon root is now applied to new blocks in miner - For EIP-4844, block creation now updates the blobGasUsed field of the header
This commit is contained in:
parent
cde462c6bf
commit
6aa88ccdd2
@ -80,6 +80,7 @@ var (
|
|||||||
InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"}
|
InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"}
|
||||||
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
|
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
|
||||||
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
|
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
|
||||||
|
UnsupportedFork = &EngineAPIError{code: -38005, msg: "Unsupported fork"}
|
||||||
|
|
||||||
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
|
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
|
||||||
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
|
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
|
||||||
|
@ -20,12 +20,14 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
|||||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||||
}
|
}
|
||||||
var enc PayloadAttributes
|
var enc PayloadAttributes
|
||||||
enc.Timestamp = hexutil.Uint64(p.Timestamp)
|
enc.Timestamp = hexutil.Uint64(p.Timestamp)
|
||||||
enc.Random = p.Random
|
enc.Random = p.Random
|
||||||
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
|
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
|
||||||
enc.Withdrawals = p.Withdrawals
|
enc.Withdrawals = p.Withdrawals
|
||||||
|
enc.BeaconRoot = p.BeaconRoot
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
|||||||
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||||
}
|
}
|
||||||
var dec PayloadAttributes
|
var dec PayloadAttributes
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@ -56,5 +59,8 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.Withdrawals != nil {
|
if dec.Withdrawals != nil {
|
||||||
p.Withdrawals = dec.Withdrawals
|
p.Withdrawals = dec.Withdrawals
|
||||||
}
|
}
|
||||||
|
if dec.BeaconRoot != nil {
|
||||||
|
p.BeaconRoot = dec.BeaconRoot
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ type PayloadAttributes struct {
|
|||||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for PayloadAttributes.
|
// JSON type overrides for PayloadAttributes.
|
||||||
@ -171,7 +172,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
|||||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||||
// Withdrawals value will propagate through the returned block. Empty
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||||
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash) (*types.Block, error) {
|
func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||||
txs, err := decodeTransactions(params.Transactions)
|
txs, err := decodeTransactions(params.Transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -207,25 +208,25 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash)
|
|||||||
withdrawalsRoot = &h
|
withdrawalsRoot = &h
|
||||||
}
|
}
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: params.ParentHash,
|
ParentHash: params.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
Coinbase: params.FeeRecipient,
|
Coinbase: params.FeeRecipient,
|
||||||
Root: params.StateRoot,
|
Root: params.StateRoot,
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
ReceiptHash: params.ReceiptsRoot,
|
ReceiptHash: params.ReceiptsRoot,
|
||||||
Bloom: types.BytesToBloom(params.LogsBloom),
|
Bloom: types.BytesToBloom(params.LogsBloom),
|
||||||
Difficulty: common.Big0,
|
Difficulty: common.Big0,
|
||||||
Number: new(big.Int).SetUint64(params.Number),
|
Number: new(big.Int).SetUint64(params.Number),
|
||||||
GasLimit: params.GasLimit,
|
GasLimit: params.GasLimit,
|
||||||
GasUsed: params.GasUsed,
|
GasUsed: params.GasUsed,
|
||||||
Time: params.Timestamp,
|
Time: params.Timestamp,
|
||||||
BaseFee: params.BaseFeePerGas,
|
BaseFee: params.BaseFeePerGas,
|
||||||
Extra: params.ExtraData,
|
Extra: params.ExtraData,
|
||||||
MixDigest: params.Random,
|
MixDigest: params.Random,
|
||||||
WithdrawalsHash: withdrawalsRoot,
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
ExcessBlobGas: params.ExcessBlobGas,
|
ExcessBlobGas: params.ExcessBlobGas,
|
||||||
BlobGasUsed: params.BlobGasUsed,
|
BlobGasUsed: params.BlobGasUsed,
|
||||||
// TODO BeaconRoot
|
ParentBeaconRoot: beaconRoot,
|
||||||
}
|
}
|
||||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||||
if block.Hash() != params.BlockHash {
|
if block.Hash() != params.BlockHash {
|
||||||
@ -255,7 +256,6 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
|||||||
Withdrawals: block.Withdrawals(),
|
Withdrawals: block.Withdrawals(),
|
||||||
BlobGasUsed: block.BlobGasUsed(),
|
BlobGasUsed: block.BlobGasUsed(),
|
||||||
ExcessBlobGas: block.ExcessBlobGas(),
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
// TODO BeaconRoot
|
|
||||||
}
|
}
|
||||||
bundle := BlobsBundleV1{
|
bundle := BlobsBundleV1{
|
||||||
Commitments: make([]hexutil.Bytes, 0),
|
Commitments: make([]hexutil.Bytes, 0),
|
||||||
|
@ -277,11 +277,11 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||||
case header.BlobGasUsed != nil:
|
case header.BlobGasUsed != nil:
|
||||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||||
case header.BeaconRoot != nil:
|
case header.ParentBeaconRoot != nil:
|
||||||
return fmt.Errorf("invalid beaconRoot, have %#x, expected nil", header.BeaconRoot)
|
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if header.BeaconRoot == nil {
|
if header.ParentBeaconRoot == nil {
|
||||||
return errors.New("header is missing beaconRoot")
|
return errors.New("header is missing beaconRoot")
|
||||||
}
|
}
|
||||||
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
|
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
|
||||||
|
@ -411,7 +411,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
|
|||||||
excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
|
excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
|
||||||
header.ExcessBlobGas = &excessBlobGas
|
header.ExcessBlobGas = &excessBlobGas
|
||||||
header.BlobGasUsed = new(uint64)
|
header.BlobGasUsed = new(uint64)
|
||||||
header.BeaconRoot = new(common.Hash)
|
header.ParentBeaconRoot = new(common.Hash)
|
||||||
}
|
}
|
||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
@ -483,6 +483,11 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||||||
withdrawals = make([]*types.Withdrawal, 0)
|
withdrawals = make([]*types.Withdrawal, 0)
|
||||||
}
|
}
|
||||||
if conf.IsCancun(num, g.Timestamp) {
|
if conf.IsCancun(num, g.Timestamp) {
|
||||||
|
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||||
|
// the zero hash. This is because the genesis block does not have a parent
|
||||||
|
// by definition.
|
||||||
|
head.ParentBeaconRoot = new(common.Hash)
|
||||||
|
// EIP-4844 fields
|
||||||
head.ExcessBlobGas = g.ExcessBlobGas
|
head.ExcessBlobGas = g.ExcessBlobGas
|
||||||
head.BlobGasUsed = g.BlobGasUsed
|
head.BlobGasUsed = g.BlobGasUsed
|
||||||
if head.ExcessBlobGas == nil {
|
if head.ExcessBlobGas == nil {
|
||||||
@ -491,9 +496,6 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||||||
if head.BlobGasUsed == nil {
|
if head.BlobGasUsed == nil {
|
||||||
head.BlobGasUsed = new(uint64)
|
head.BlobGasUsed = new(uint64)
|
||||||
}
|
}
|
||||||
if head.BeaconRoot == nil {
|
|
||||||
head.BeaconRoot = new(common.Hash)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals)
|
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals)
|
||||||
|
@ -135,6 +135,9 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
|
|||||||
receipt.TxHash = tx.Hash()
|
receipt.TxHash = tx.Hash()
|
||||||
receipt.GasUsed = result.UsedGas
|
receipt.GasUsed = result.UsedGas
|
||||||
|
|
||||||
|
receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob)
|
||||||
|
receipt.BlobGasPrice = tx.BlobGasFeeCap()
|
||||||
|
|
||||||
// If the transaction created a contract, store the creation address in the receipt.
|
// If the transaction created a contract, store the creation address in the receipt.
|
||||||
if msg.To == nil {
|
if msg.To == nil {
|
||||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||||
|
@ -412,7 +412,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||||||
header.BlobGasUsed = &used
|
header.BlobGasUsed = &used
|
||||||
|
|
||||||
beaconRoot := common.HexToHash("0xbeac00")
|
beaconRoot := common.HexToHash("0xbeac00")
|
||||||
header.BeaconRoot = &beaconRoot
|
header.ParentBeaconRoot = &beaconRoot
|
||||||
}
|
}
|
||||||
// Assemble and return the final block for sealing
|
// Assemble and return the final block for sealing
|
||||||
if config.IsShanghai(header.Number, header.Time) {
|
if config.IsShanghai(header.Number, header.Time) {
|
||||||
|
@ -91,8 +91,8 @@ type Header struct {
|
|||||||
// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.
|
// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||||
|
|
||||||
// BeaconRoot was added by EIP-4788 and is ignored in legacy headers.
|
// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
|
||||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// field type overrides for gencodec
|
// field type overrides for gencodec
|
||||||
@ -300,9 +300,9 @@ func CopyHeader(h *Header) *Header {
|
|||||||
cpy.BlobGasUsed = new(uint64)
|
cpy.BlobGasUsed = new(uint64)
|
||||||
*cpy.BlobGasUsed = *h.BlobGasUsed
|
*cpy.BlobGasUsed = *h.BlobGasUsed
|
||||||
}
|
}
|
||||||
if h.BeaconRoot != nil {
|
if h.ParentBeaconRoot != nil {
|
||||||
cpy.BeaconRoot = new(common.Hash)
|
cpy.ParentBeaconRoot = new(common.Hash)
|
||||||
*cpy.BeaconRoot = *h.BeaconRoot
|
*cpy.ParentBeaconRoot = *h.ParentBeaconRoot
|
||||||
}
|
}
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
@ -383,7 +383,7 @@ 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.BeaconRoot }
|
func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot }
|
||||||
|
|
||||||
func (b *Block) ExcessBlobGas() *uint64 {
|
func (b *Block) ExcessBlobGas() *uint64 {
|
||||||
var excessBlobGas *uint64
|
var excessBlobGas *uint64
|
||||||
|
@ -16,27 +16,27 @@ var _ = (*headerMarshaling)(nil)
|
|||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (h Header) MarshalJSON() ([]byte, error) {
|
func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||||
Coinbase common.Address `json:"miner"`
|
Coinbase common.Address `json:"miner"`
|
||||||
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||||
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
Number *hexutil.Big `json:"number" 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"`
|
||||||
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
Time hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
MixDigest common.Hash `json:"mixHash"`
|
MixDigest common.Hash `json:"mixHash"`
|
||||||
Nonce BlockNonce `json:"nonce"`
|
Nonce BlockNonce `json:"nonce"`
|
||||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
var enc Header
|
var enc Header
|
||||||
enc.ParentHash = h.ParentHash
|
enc.ParentHash = h.ParentHash
|
||||||
@ -58,7 +58,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||||||
enc.WithdrawalsHash = h.WithdrawalsHash
|
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||||
enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed)
|
enc.BlobGasUsed = (*hexutil.Uint64)(h.BlobGasUsed)
|
||||||
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
|
enc.ExcessBlobGas = (*hexutil.Uint64)(h.ExcessBlobGas)
|
||||||
enc.BeaconRoot = h.BeaconRoot
|
enc.ParentBeaconRoot = h.ParentBeaconRoot
|
||||||
enc.Hash = h.Hash()
|
enc.Hash = h.Hash()
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
@ -66,26 +66,26 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
|
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||||
Coinbase *common.Address `json:"miner"`
|
Coinbase *common.Address `json:"miner"`
|
||||||
Root *common.Hash `json:"stateRoot" gencodec:"required"`
|
Root *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
|
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||||
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
Number *hexutil.Big `json:"number" 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"`
|
||||||
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
MixDigest *common.Hash `json:"mixHash"`
|
MixDigest *common.Hash `json:"mixHash"`
|
||||||
Nonce *BlockNonce `json:"nonce"`
|
Nonce *BlockNonce `json:"nonce"`
|
||||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||||
}
|
}
|
||||||
var dec Header
|
var dec Header
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@ -160,8 +160,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.ExcessBlobGas != nil {
|
if dec.ExcessBlobGas != nil {
|
||||||
h.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
h.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||||
}
|
}
|
||||||
if dec.BeaconRoot != nil {
|
if dec.ParentBeaconRoot != nil {
|
||||||
h.BeaconRoot = dec.BeaconRoot
|
h.ParentBeaconRoot = dec.ParentBeaconRoot
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||||||
_tmp2 := obj.WithdrawalsHash != nil
|
_tmp2 := obj.WithdrawalsHash != nil
|
||||||
_tmp3 := obj.BlobGasUsed != nil
|
_tmp3 := obj.BlobGasUsed != nil
|
||||||
_tmp4 := obj.ExcessBlobGas != nil
|
_tmp4 := obj.ExcessBlobGas != nil
|
||||||
_tmp5 := obj.BeaconRoot != nil
|
_tmp5 := obj.ParentBeaconRoot != nil
|
||||||
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 {
|
if _tmp1 || _tmp2 || _tmp3 || _tmp4 || _tmp5 {
|
||||||
if obj.BaseFee == nil {
|
if obj.BaseFee == nil {
|
||||||
w.Write(rlp.EmptyString)
|
w.Write(rlp.EmptyString)
|
||||||
@ -77,10 +77,10 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _tmp5 {
|
if _tmp5 {
|
||||||
if obj.BeaconRoot == nil {
|
if obj.ParentBeaconRoot == nil {
|
||||||
w.Write([]byte{0x80})
|
w.Write([]byte{0x80})
|
||||||
} else {
|
} else {
|
||||||
w.WriteBytes(obj.BeaconRoot[:])
|
w.WriteBytes(obj.ParentBeaconRoot[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.ListEnd(_tmp0)
|
w.ListEnd(_tmp0)
|
||||||
|
@ -78,6 +78,7 @@ const (
|
|||||||
var caps = []string{
|
var caps = []string{
|
||||||
"engine_forkchoiceUpdatedV1",
|
"engine_forkchoiceUpdatedV1",
|
||||||
"engine_forkchoiceUpdatedV2",
|
"engine_forkchoiceUpdatedV2",
|
||||||
|
"engine_forkchoiceUpdatedV3",
|
||||||
"engine_exchangeTransitionConfigurationV1",
|
"engine_exchangeTransitionConfigurationV1",
|
||||||
"engine_getPayloadV1",
|
"engine_getPayloadV1",
|
||||||
"engine_getPayloadV2",
|
"engine_getPayloadV2",
|
||||||
@ -192,17 +193,36 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa
|
|||||||
return api.forkchoiceUpdated(update, payloadAttributes)
|
return api.forkchoiceUpdated(update, payloadAttributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root in the payload attributes.
|
||||||
|
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
|
if payloadAttributes != nil {
|
||||||
|
if err := api.verifyPayloadAttributes(payloadAttributes); err != nil {
|
||||||
|
return engine.STATUS_INVALID, engine.InvalidParams.With(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return api.forkchoiceUpdated(update, payloadAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) error {
|
func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) error {
|
||||||
if !api.eth.BlockChain().Config().IsShanghai(api.eth.BlockChain().Config().LondonBlock, attr.Timestamp) {
|
c := api.eth.BlockChain().Config()
|
||||||
// Reject payload attributes with withdrawals before shanghai
|
|
||||||
if attr.Withdrawals != nil {
|
// Verify withdrawals attribute for Shanghai.
|
||||||
return errors.New("withdrawals before shanghai")
|
if err := checkAttribute(c.IsShanghai, attr.Withdrawals != nil, attr.Timestamp); err != nil {
|
||||||
}
|
return fmt.Errorf("invalid withdrawals: %w", err)
|
||||||
} else {
|
}
|
||||||
// Reject payload attributes with nil withdrawals after shanghai
|
// Verify beacon root attribute for Cancun.
|
||||||
if attr.Withdrawals == nil {
|
if err := checkAttribute(c.IsCancun, attr.BeaconRoot != nil, attr.Timestamp); err != nil {
|
||||||
return errors.New("missing withdrawals list")
|
return fmt.Errorf("invalid parent beacon block root: %w", err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAttribute(active func(*big.Int, uint64) bool, exists bool, time uint64) error {
|
||||||
|
if active(common.Big0, time) && !exists {
|
||||||
|
return errors.New("fork active, missing expected attribute")
|
||||||
|
}
|
||||||
|
if !active(common.Big0, time) && exists {
|
||||||
|
return errors.New("fork inactive, unexpected attribute set")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -350,6 +370,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||||||
FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
|
FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
|
||||||
Random: payloadAttributes.Random,
|
Random: payloadAttributes.Random,
|
||||||
Withdrawals: payloadAttributes.Withdrawals,
|
Withdrawals: payloadAttributes.Withdrawals,
|
||||||
|
BeaconRoot: payloadAttributes.BeaconRoot,
|
||||||
}
|
}
|
||||||
id := args.Id()
|
id := args.Id()
|
||||||
// If we already are busy generating this work, then we do not need
|
// If we already are busy generating this work, then we do not need
|
||||||
@ -431,7 +452,7 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl
|
|||||||
if params.Withdrawals != nil {
|
if params.Withdrawals != nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil)
|
return api.newPayload(params, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
@ -446,26 +467,32 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl
|
|||||||
if api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
if api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV2 called post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV2 called post-cancun"))
|
||||||
}
|
}
|
||||||
return api.newPayload(params, nil)
|
return api.newPayload(params, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes *[]common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV3 called pre-cancun"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if params.ExcessBlobGas == nil {
|
if params.ExcessBlobGas == nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||||
}
|
}
|
||||||
var hashes []common.Hash
|
if params.BlobGasUsed == nil {
|
||||||
if versionedHashes != nil {
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed post-cancun"))
|
||||||
hashes = *versionedHashes
|
|
||||||
}
|
}
|
||||||
return api.newPayload(params, hashes)
|
if versionedHashes == nil {
|
||||||
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||||
|
}
|
||||||
|
if beaconRoot == nil {
|
||||||
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil parentBeaconBlockRoot post-cancun"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||||
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 called pre-cancun"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.newPayload(params, versionedHashes, beaconRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
// The locking here is, strictly, not required. Without these locks, this can happen:
|
// The locking here is, strictly, not required. Without these locks, this can happen:
|
||||||
//
|
//
|
||||||
// 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to
|
// 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to
|
||||||
@ -483,7 +510,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
|||||||
defer api.newPayloadLock.Unlock()
|
defer api.newPayloadLock.Unlock()
|
||||||
|
|
||||||
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
|
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
|
||||||
block, err := engine.ExecutableDataToBlock(params, versionedHashes)
|
block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Invalid NewPayload params", "params", params, "error", err)
|
log.Warn("Invalid NewPayload params", "params", params, "error", err)
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, nil
|
return engine.PayloadStatusV1{Status: engine.INVALID}, nil
|
||||||
|
@ -41,12 +41,14 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -68,8 +70,11 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
|
|||||||
engine = beaconConsensus.NewFaker()
|
engine = beaconConsensus.NewFaker()
|
||||||
}
|
}
|
||||||
genesis := &core.Genesis{
|
genesis := &core.Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
|
Alloc: core.GenesisAlloc{
|
||||||
|
testAddr: {Balance: testBalance},
|
||||||
|
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500")},
|
||||||
|
},
|
||||||
ExtraData: []byte("test genesis"),
|
ExtraData: []byte("test genesis"),
|
||||||
Timestamp: 9000,
|
Timestamp: 9000,
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
@ -204,6 +209,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
|||||||
Timestamp: blockParams.Timestamp,
|
Timestamp: blockParams.Timestamp,
|
||||||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||||
Random: blockParams.Random,
|
Random: blockParams.Random,
|
||||||
|
BeaconRoot: blockParams.BeaconRoot,
|
||||||
}).Id()
|
}).Id()
|
||||||
execData, err := api.GetPayloadV1(payloadID)
|
execData, err := api.GetPayloadV1(payloadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -314,7 +320,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create the executable data %v", err)
|
t.Fatalf("Failed to create the executable data %v", err)
|
||||||
}
|
}
|
||||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
block, err := engine.ExecutableDataToBlock(*execData, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||||
}
|
}
|
||||||
@ -356,7 +362,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create the executable data %v", err)
|
t.Fatalf("Failed to create the executable data %v", err)
|
||||||
}
|
}
|
||||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
block, err := engine.ExecutableDataToBlock(*execData, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||||
}
|
}
|
||||||
@ -667,6 +673,7 @@ func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.Pay
|
|||||||
FeeRecipient: params.SuggestedFeeRecipient,
|
FeeRecipient: params.SuggestedFeeRecipient,
|
||||||
Random: params.Random,
|
Random: params.Random,
|
||||||
Withdrawals: params.Withdrawals,
|
Withdrawals: params.Withdrawals,
|
||||||
|
BeaconRoot: params.BeaconRoot,
|
||||||
}
|
}
|
||||||
payload, err := api.eth.Miner().BuildPayload(args)
|
payload, err := api.eth.Miner().BuildPayload(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -988,7 +995,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
|||||||
t.Fatal(testErr)
|
t.Fatal(testErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
block, err := engine.ExecutableDataToBlock(*execData, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||||
}
|
}
|
||||||
@ -1068,6 +1075,7 @@ func TestWithdrawals(t *testing.T) {
|
|||||||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||||
Random: blockParams.Random,
|
Random: blockParams.Random,
|
||||||
Withdrawals: blockParams.Withdrawals,
|
Withdrawals: blockParams.Withdrawals,
|
||||||
|
BeaconRoot: blockParams.BeaconRoot,
|
||||||
}).Id()
|
}).Id()
|
||||||
execData, err := api.GetPayloadV2(payloadID)
|
execData, err := api.GetPayloadV2(payloadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1115,6 +1123,7 @@ func TestWithdrawals(t *testing.T) {
|
|||||||
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||||
Random: blockParams.Random,
|
Random: blockParams.Random,
|
||||||
Withdrawals: blockParams.Withdrawals,
|
Withdrawals: blockParams.Withdrawals,
|
||||||
|
BeaconRoot: blockParams.BeaconRoot,
|
||||||
}).Id()
|
}).Id()
|
||||||
execData, err = api.GetPayloadV2(payloadID)
|
execData, err = api.GetPayloadV2(payloadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1245,6 +1254,7 @@ func TestNilWithdrawals(t *testing.T) {
|
|||||||
Timestamp: test.blockParams.Timestamp,
|
Timestamp: test.blockParams.Timestamp,
|
||||||
FeeRecipient: test.blockParams.SuggestedFeeRecipient,
|
FeeRecipient: test.blockParams.SuggestedFeeRecipient,
|
||||||
Random: test.blockParams.Random,
|
Random: test.blockParams.Random,
|
||||||
|
BeaconRoot: test.blockParams.BeaconRoot,
|
||||||
}).Id()
|
}).Id()
|
||||||
execData, err := api.GetPayloadV2(payloadID)
|
execData, err := api.GetPayloadV2(payloadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1544,8 +1554,91 @@ func TestBlockToPayloadWithBlobs(t *testing.T) {
|
|||||||
if got := len(envelope.BlobsBundle.Blobs); got != want {
|
if got := len(envelope.BlobsBundle.Blobs); got != want {
|
||||||
t.Fatalf("invalid number of blobs: got %v, want %v", got, want)
|
t.Fatalf("invalid number of blobs: got %v, want %v", got, want)
|
||||||
}
|
}
|
||||||
_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1))
|
_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This checks that beaconRoot is applied to the state from the engine API.
|
||||||
|
func TestParentBeaconBlockRoot(t *testing.T) {
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
|
||||||
|
|
||||||
|
genesis, blocks := generateMergeChain(10, true)
|
||||||
|
|
||||||
|
// Set cancun time to last block + 5 seconds
|
||||||
|
time := blocks[len(blocks)-1].Time() + 5
|
||||||
|
genesis.Config.ShanghaiTime = &time
|
||||||
|
genesis.Config.CancunTime = &time
|
||||||
|
|
||||||
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
|
ethservice.Merger().ReachTTD()
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := NewConsensusAPI(ethservice)
|
||||||
|
|
||||||
|
// 11: Build Shanghai block with no withdrawals.
|
||||||
|
parent := ethservice.BlockChain().CurrentHeader()
|
||||||
|
blockParams := engine.PayloadAttributes{
|
||||||
|
Timestamp: parent.Time + 5,
|
||||||
|
Withdrawals: make([]*types.Withdrawal, 0),
|
||||||
|
BeaconRoot: &common.Hash{42},
|
||||||
|
}
|
||||||
|
fcState := engine.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: parent.Hash(),
|
||||||
|
}
|
||||||
|
resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
|
||||||
|
}
|
||||||
|
if resp.PayloadStatus.Status != engine.VALID {
|
||||||
|
t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: verify state root is the same as parent
|
||||||
|
payloadID := (&miner.BuildPayloadArgs{
|
||||||
|
Parent: fcState.HeadBlockHash,
|
||||||
|
Timestamp: blockParams.Timestamp,
|
||||||
|
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||||
|
Random: blockParams.Random,
|
||||||
|
Withdrawals: blockParams.Withdrawals,
|
||||||
|
BeaconRoot: blockParams.BeaconRoot,
|
||||||
|
}).Id()
|
||||||
|
execData, err := api.GetPayloadV3(payloadID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error getting payload, err=%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: verify locally built block
|
||||||
|
if status, err := api.NewPayloadV3(*execData.ExecutionPayload, []common.Hash{}, &common.Hash{42}); err != nil {
|
||||||
|
t.Fatalf("error validating payload: %v", err)
|
||||||
|
} else if status.Status != engine.VALID {
|
||||||
|
t.Fatalf("invalid payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||||
|
resp, err = api.ForkchoiceUpdatedV3(fcState, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
|
||||||
|
}
|
||||||
|
if resp.PayloadStatus.Status != engine.VALID {
|
||||||
|
t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: verify beacon root was processed.
|
||||||
|
db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to load db: %v", err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
timeIdx = common.BigToHash(big.NewInt(int64(execData.ExecutionPayload.Timestamp % 98304)))
|
||||||
|
rootIdx = common.BigToHash(big.NewInt(int64((execData.ExecutionPayload.Timestamp % 98304) + 98304)))
|
||||||
|
)
|
||||||
|
|
||||||
|
if num := db.GetState(params.BeaconRootsStorageAddress, timeIdx); num != timeIdx {
|
||||||
|
t.Fatalf("incorrect number stored: want %s, got %s", timeIdx, num)
|
||||||
|
}
|
||||||
|
if root := db.GetState(params.BeaconRootsStorageAddress, rootIdx); root != *blockParams.BeaconRoot {
|
||||||
|
t.Fatalf("incorrect root stored: want %s, got %s", *blockParams.BeaconRoot, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1345,6 +1345,9 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
|||||||
if head.ExcessBlobGas != nil {
|
if head.ExcessBlobGas != nil {
|
||||||
result["excessBlobGas"] = hexutil.Uint64(*head.ExcessBlobGas)
|
result["excessBlobGas"] = hexutil.Uint64(*head.ExcessBlobGas)
|
||||||
}
|
}
|
||||||
|
if head.ParentBeaconRoot != nil {
|
||||||
|
result["parentBeaconBlockRoot"] = head.ParentBeaconRoot
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ type BuildPayloadArgs struct {
|
|||||||
FeeRecipient common.Address // The provided recipient address for collecting transaction fee
|
FeeRecipient common.Address // The provided recipient address for collecting transaction fee
|
||||||
Random common.Hash // The provided randomness value
|
Random common.Hash // The provided randomness value
|
||||||
Withdrawals types.Withdrawals // The provided withdrawals
|
Withdrawals types.Withdrawals // The provided withdrawals
|
||||||
|
BeaconRoot *common.Hash // The provided beaconRoot (Cancun)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id computes an 8-byte identifier by hashing the components of the payload arguments.
|
// Id computes an 8-byte identifier by hashing the components of the payload arguments.
|
||||||
@ -51,6 +52,9 @@ func (args *BuildPayloadArgs) Id() engine.PayloadID {
|
|||||||
hasher.Write(args.Random[:])
|
hasher.Write(args.Random[:])
|
||||||
hasher.Write(args.FeeRecipient[:])
|
hasher.Write(args.FeeRecipient[:])
|
||||||
rlp.Encode(hasher, args.Withdrawals)
|
rlp.Encode(hasher, args.Withdrawals)
|
||||||
|
if args.BeaconRoot != nil {
|
||||||
|
hasher.Write(args.BeaconRoot[:])
|
||||||
|
}
|
||||||
var out engine.PayloadID
|
var out engine.PayloadID
|
||||||
copy(out[:], hasher.Sum(nil)[:8])
|
copy(out[:], hasher.Sum(nil)[:8])
|
||||||
return out
|
return out
|
||||||
@ -182,6 +186,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
|||||||
coinbase: args.FeeRecipient,
|
coinbase: args.FeeRecipient,
|
||||||
random: args.Random,
|
random: args.Random,
|
||||||
withdrawals: args.Withdrawals,
|
withdrawals: args.Withdrawals,
|
||||||
|
beaconRoot: args.BeaconRoot,
|
||||||
noTxs: true,
|
noTxs: true,
|
||||||
}
|
}
|
||||||
empty := w.getSealingBlock(emptyParams)
|
empty := w.getSealingBlock(emptyParams)
|
||||||
@ -212,6 +217,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
|||||||
coinbase: args.FeeRecipient,
|
coinbase: args.FeeRecipient,
|
||||||
random: args.Random,
|
random: args.Random,
|
||||||
withdrawals: args.Withdrawals,
|
withdrawals: args.Withdrawals,
|
||||||
|
beaconRoot: args.BeaconRoot,
|
||||||
noTxs: false,
|
noTxs: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"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/vm"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
@ -738,36 +739,58 @@ func (w *worker) updateSnapshot(env *environment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
|
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
|
||||||
var (
|
if tx.Type() == types.BlobTxType {
|
||||||
snap = env.state.Snapshot()
|
return w.commitBlobTransaction(env, tx)
|
||||||
gp = env.gasPool.Gas()
|
}
|
||||||
)
|
|
||||||
|
|
||||||
|
receipt, err := w.applyTransaction(env, tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
env.txs = append(env.txs, tx)
|
||||||
|
env.receipts = append(env.receipts, receipt)
|
||||||
|
return receipt.Logs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *worker) commitBlobTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
|
||||||
|
sc := tx.BlobTxSidecar()
|
||||||
|
if sc == nil {
|
||||||
|
panic("blob transaction without blobs in miner")
|
||||||
|
}
|
||||||
// Checking against blob gas limit: It's kind of ugly to perform this check here, but there
|
// Checking against blob gas limit: It's kind of ugly to perform this check here, but there
|
||||||
// isn't really a better place right now. The blob gas limit is checked at block validation time
|
// isn't really a better place right now. The blob gas limit is checked at block validation time
|
||||||
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
||||||
// tx has too many blobs. So we have to explicitly check it here.
|
// tx has too many blobs. So we have to explicitly check it here.
|
||||||
if (env.blobs+len(tx.BlobHashes()))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
|
if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
|
||||||
return nil, errors.New("max data blobs reached")
|
return nil, errors.New("max data blobs reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
|
receipt, err := w.applyTransaction(env, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.RevertToSnapshot(snap)
|
|
||||||
env.gasPool.SetGas(gp)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
env.txs = append(env.txs, tx.WithoutBlobTxSidecar())
|
env.txs = append(env.txs, tx.WithoutBlobTxSidecar())
|
||||||
env.receipts = append(env.receipts, receipt)
|
env.receipts = append(env.receipts, receipt)
|
||||||
|
env.sidecars = append(env.sidecars, sc)
|
||||||
if sc := tx.BlobTxSidecar(); sc != nil {
|
env.blobs += len(sc.Blobs)
|
||||||
env.sidecars = append(env.sidecars, sc)
|
*env.header.BlobGasUsed += receipt.BlobGasUsed
|
||||||
env.blobs += len(sc.Blobs)
|
|
||||||
}
|
|
||||||
|
|
||||||
return receipt.Logs, nil
|
return receipt.Logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
||||||
|
func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
|
||||||
|
var (
|
||||||
|
snap = env.state.Snapshot()
|
||||||
|
gp = env.gasPool.Gas()
|
||||||
|
)
|
||||||
|
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
|
||||||
|
if err != nil {
|
||||||
|
env.state.RevertToSnapshot(snap)
|
||||||
|
env.gasPool.SetGas(gp)
|
||||||
|
}
|
||||||
|
return receipt, err
|
||||||
|
}
|
||||||
|
|
||||||
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
|
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
|
||||||
gasLimit := env.header.GasLimit
|
gasLimit := env.header.GasLimit
|
||||||
if env.gasPool == nil {
|
if env.gasPool == nil {
|
||||||
@ -860,6 +883,7 @@ type generateParams struct {
|
|||||||
coinbase common.Address // The fee recipient address for including transaction
|
coinbase common.Address // The fee recipient address for including transaction
|
||||||
random common.Hash // The randomness generated by beacon chain, empty before the merge
|
random common.Hash // The randomness generated by beacon chain, empty before the merge
|
||||||
withdrawals types.Withdrawals // List of withdrawals to include in block.
|
withdrawals types.Withdrawals // List of withdrawals to include in block.
|
||||||
|
beaconRoot *common.Hash // The beacon root (cancun field).
|
||||||
noTxs bool // Flag whether an empty block without any transaction is expected
|
noTxs bool // Flag whether an empty block without any transaction is expected
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,6 +936,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
|||||||
header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil)
|
header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Apply EIP-4844, EIP-4788.
|
||||||
if w.chainConfig.IsCancun(header.Number, header.Time) {
|
if w.chainConfig.IsCancun(header.Number, header.Time) {
|
||||||
var excessBlobGas uint64
|
var excessBlobGas uint64
|
||||||
if w.chainConfig.IsCancun(parent.Number, parent.Time) {
|
if w.chainConfig.IsCancun(parent.Number, parent.Time) {
|
||||||
@ -920,7 +945,9 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
|||||||
// For the first post-fork block, both parent.data_gas_used and parent.excess_data_gas are evaluated as 0
|
// For the first post-fork block, both parent.data_gas_used and parent.excess_data_gas are evaluated as 0
|
||||||
excessBlobGas = eip4844.CalcExcessBlobGas(0, 0)
|
excessBlobGas = eip4844.CalcExcessBlobGas(0, 0)
|
||||||
}
|
}
|
||||||
|
header.BlobGasUsed = new(uint64)
|
||||||
header.ExcessBlobGas = &excessBlobGas
|
header.ExcessBlobGas = &excessBlobGas
|
||||||
|
header.ParentBeaconRoot = genParams.beaconRoot
|
||||||
}
|
}
|
||||||
// Run the consensus preparation with the default or customized consensus engine.
|
// Run the consensus preparation with the default or customized consensus engine.
|
||||||
if err := w.engine.Prepare(w.chain, header); err != nil {
|
if err := w.engine.Prepare(w.chain, header); err != nil {
|
||||||
@ -935,6 +962,11 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
|||||||
log.Error("Failed to create sealing context", "err", err)
|
log.Error("Failed to create sealing context", "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if header.ParentBeaconRoot != nil {
|
||||||
|
context := core.NewEVMBlockContext(header, w.chain, nil)
|
||||||
|
vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{})
|
||||||
|
core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state)
|
||||||
|
}
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,6 +458,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
|||||||
coinbase: c.coinbase,
|
coinbase: c.coinbase,
|
||||||
random: c.random,
|
random: c.random,
|
||||||
withdrawals: nil,
|
withdrawals: nil,
|
||||||
|
beaconRoot: nil,
|
||||||
noTxs: false,
|
noTxs: false,
|
||||||
forceTime: true,
|
forceTime: true,
|
||||||
})
|
})
|
||||||
@ -482,6 +483,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
|||||||
coinbase: c.coinbase,
|
coinbase: c.coinbase,
|
||||||
random: c.random,
|
random: c.random,
|
||||||
withdrawals: nil,
|
withdrawals: nil,
|
||||||
|
beaconRoot: nil,
|
||||||
noTxs: false,
|
noTxs: false,
|
||||||
forceTime: true,
|
forceTime: true,
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user