forked from cerc-io/plugeth
all: implement withdrawals (EIP-4895) (#26484)
This change implements withdrawals as specified in EIP-4895. Co-authored-by: lightclient@protonmail.com <lightclient@protonmail.com> Co-authored-by: marioevz <marioevz@gmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
2b57a27d9e
commit
2a2b0419fb
@ -38,22 +38,23 @@ import (
|
|||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type header -field-override headerMarshaling -out gen_header.go
|
//go:generate go run github.com/fjl/gencodec -type header -field-override headerMarshaling -out gen_header.go
|
||||||
type header struct {
|
type header struct {
|
||||||
ParentHash common.Hash `json:"parentHash"`
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
OmmerHash *common.Hash `json:"sha3Uncles"`
|
OmmerHash *common.Hash `json:"sha3Uncles"`
|
||||||
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"`
|
TxHash *common.Hash `json:"transactionsRoot"`
|
||||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||||
Bloom types.Bloom `json:"logsBloom"`
|
Bloom types.Bloom `json:"logsBloom"`
|
||||||
Difficulty *big.Int `json:"difficulty"`
|
Difficulty *big.Int `json:"difficulty"`
|
||||||
Number *big.Int `json:"number" gencodec:"required"`
|
Number *big.Int `json:"number" gencodec:"required"`
|
||||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed uint64 `json:"gasUsed"`
|
GasUsed uint64 `json:"gasUsed"`
|
||||||
Time uint64 `json:"timestamp" gencodec:"required"`
|
Time uint64 `json:"timestamp" gencodec:"required"`
|
||||||
Extra []byte `json:"extraData"`
|
Extra []byte `json:"extraData"`
|
||||||
MixDigest common.Hash `json:"mixHash"`
|
MixDigest common.Hash `json:"mixHash"`
|
||||||
Nonce *types.BlockNonce `json:"nonce"`
|
Nonce *types.BlockNonce `json:"nonce"`
|
||||||
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
|
||||||
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type headerMarshaling struct {
|
type headerMarshaling struct {
|
||||||
@ -67,10 +68,11 @@ type headerMarshaling struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type bbInput struct {
|
type bbInput struct {
|
||||||
Header *header `json:"header,omitempty"`
|
Header *header `json:"header,omitempty"`
|
||||||
OmmersRlp []string `json:"ommers,omitempty"`
|
OmmersRlp []string `json:"ommers,omitempty"`
|
||||||
TxRlp string `json:"txs,omitempty"`
|
TxRlp string `json:"txs,omitempty"`
|
||||||
Clique *cliqueInput `json:"clique,omitempty"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
|
Clique *cliqueInput `json:"clique,omitempty"`
|
||||||
|
|
||||||
Ethash bool `json:"-"`
|
Ethash bool `json:"-"`
|
||||||
EthashDir string `json:"-"`
|
EthashDir string `json:"-"`
|
||||||
@ -114,21 +116,22 @@ func (c *cliqueInput) UnmarshalJSON(input []byte) error {
|
|||||||
// ToBlock converts i into a *types.Block
|
// ToBlock converts i into a *types.Block
|
||||||
func (i *bbInput) ToBlock() *types.Block {
|
func (i *bbInput) ToBlock() *types.Block {
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: i.Header.ParentHash,
|
ParentHash: i.Header.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
Coinbase: common.Address{},
|
Coinbase: common.Address{},
|
||||||
Root: i.Header.Root,
|
Root: i.Header.Root,
|
||||||
TxHash: types.EmptyRootHash,
|
TxHash: types.EmptyRootHash,
|
||||||
ReceiptHash: types.EmptyRootHash,
|
ReceiptHash: types.EmptyRootHash,
|
||||||
Bloom: i.Header.Bloom,
|
Bloom: i.Header.Bloom,
|
||||||
Difficulty: common.Big0,
|
Difficulty: common.Big0,
|
||||||
Number: i.Header.Number,
|
Number: i.Header.Number,
|
||||||
GasLimit: i.Header.GasLimit,
|
GasLimit: i.Header.GasLimit,
|
||||||
GasUsed: i.Header.GasUsed,
|
GasUsed: i.Header.GasUsed,
|
||||||
Time: i.Header.Time,
|
Time: i.Header.Time,
|
||||||
Extra: i.Header.Extra,
|
Extra: i.Header.Extra,
|
||||||
MixDigest: i.Header.MixDigest,
|
MixDigest: i.Header.MixDigest,
|
||||||
BaseFee: i.Header.BaseFee,
|
BaseFee: i.Header.BaseFee,
|
||||||
|
WithdrawalsHash: i.Header.WithdrawalsHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill optional values.
|
// Fill optional values.
|
||||||
@ -153,7 +156,7 @@ func (i *bbInput) ToBlock() *types.Block {
|
|||||||
if header.Difficulty != nil {
|
if header.Difficulty != nil {
|
||||||
header.Difficulty = i.Header.Difficulty
|
header.Difficulty = i.Header.Difficulty
|
||||||
}
|
}
|
||||||
return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers)
|
return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers).WithWithdrawals(i.Withdrawals)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SealBlock seals the given block using the configured engine.
|
// SealBlock seals the given block using the configured engine.
|
||||||
@ -259,14 +262,15 @@ func BuildBlock(ctx *cli.Context) error {
|
|||||||
|
|
||||||
func readInput(ctx *cli.Context) (*bbInput, error) {
|
func readInput(ctx *cli.Context) (*bbInput, error) {
|
||||||
var (
|
var (
|
||||||
headerStr = ctx.String(InputHeaderFlag.Name)
|
headerStr = ctx.String(InputHeaderFlag.Name)
|
||||||
ommersStr = ctx.String(InputOmmersFlag.Name)
|
ommersStr = ctx.String(InputOmmersFlag.Name)
|
||||||
txsStr = ctx.String(InputTxsRlpFlag.Name)
|
withdrawalsStr = ctx.String(InputWithdrawalsFlag.Name)
|
||||||
cliqueStr = ctx.String(SealCliqueFlag.Name)
|
txsStr = ctx.String(InputTxsRlpFlag.Name)
|
||||||
ethashOn = ctx.Bool(SealEthashFlag.Name)
|
cliqueStr = ctx.String(SealCliqueFlag.Name)
|
||||||
ethashDir = ctx.String(SealEthashDirFlag.Name)
|
ethashOn = ctx.Bool(SealEthashFlag.Name)
|
||||||
ethashMode = ctx.String(SealEthashModeFlag.Name)
|
ethashDir = ctx.String(SealEthashDirFlag.Name)
|
||||||
inputData = &bbInput{}
|
ethashMode = ctx.String(SealEthashModeFlag.Name)
|
||||||
|
inputData = &bbInput{}
|
||||||
)
|
)
|
||||||
if ethashOn && cliqueStr != "" {
|
if ethashOn && cliqueStr != "" {
|
||||||
return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen"))
|
return nil, NewError(ErrorConfig, fmt.Errorf("both ethash and clique sealing specified, only one may be chosen"))
|
||||||
@ -312,6 +316,13 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
|||||||
}
|
}
|
||||||
inputData.OmmersRlp = ommers
|
inputData.OmmersRlp = ommers
|
||||||
}
|
}
|
||||||
|
if withdrawalsStr != stdinSelector && withdrawalsStr != "" {
|
||||||
|
var withdrawals []*types.Withdrawal
|
||||||
|
if err := readFile(withdrawalsStr, "withdrawals", &withdrawals); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
inputData.Withdrawals = withdrawals
|
||||||
|
}
|
||||||
if txsStr != stdinSelector {
|
if txsStr != stdinSelector {
|
||||||
var txs string
|
var txs string
|
||||||
if err := readFile(txsStr, "txs", &txs); err != nil {
|
if err := readFile(txsStr, "txs", &txs); err != nil {
|
||||||
@ -351,15 +362,14 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
|||||||
// files
|
// files
|
||||||
func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
|
func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
|
||||||
raw, _ := rlp.EncodeToBytes(block)
|
raw, _ := rlp.EncodeToBytes(block)
|
||||||
|
|
||||||
type blockInfo struct {
|
type blockInfo struct {
|
||||||
Rlp hexutil.Bytes `json:"rlp"`
|
Rlp hexutil.Bytes `json:"rlp"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
var enc blockInfo
|
enc := blockInfo{
|
||||||
enc.Rlp = raw
|
Rlp: raw,
|
||||||
enc.Hash = block.Hash()
|
Hash: block.Hash(),
|
||||||
|
}
|
||||||
b, err := json.MarshalIndent(enc, "", " ")
|
b, err := json.MarshalIndent(enc, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
||||||
|
@ -47,16 +47,17 @@ type Prestate struct {
|
|||||||
// ExecutionResult contains the execution status after running a state test, any
|
// ExecutionResult contains the execution status after running a state test, any
|
||||||
// error that might have occurred and a dump of the final state if requested.
|
// error that might have occurred and a dump of the final state if requested.
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
StateRoot common.Hash `json:"stateRoot"`
|
StateRoot common.Hash `json:"stateRoot"`
|
||||||
TxRoot common.Hash `json:"txRoot"`
|
TxRoot common.Hash `json:"txRoot"`
|
||||||
ReceiptRoot common.Hash `json:"receiptsRoot"`
|
ReceiptRoot common.Hash `json:"receiptsRoot"`
|
||||||
LogsHash common.Hash `json:"logsHash"`
|
LogsHash common.Hash `json:"logsHash"`
|
||||||
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
|
Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
|
||||||
Receipts types.Receipts `json:"receipts"`
|
Receipts types.Receipts `json:"receipts"`
|
||||||
Rejected []*rejectedTx `json:"rejected,omitempty"`
|
Rejected []*rejectedTx `json:"rejected,omitempty"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
|
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ommer struct {
|
type ommer struct {
|
||||||
@ -79,6 +80,7 @@ type stEnv struct {
|
|||||||
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
|
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
||||||
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||||
}
|
}
|
||||||
@ -254,6 +256,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
}
|
}
|
||||||
statedb.AddBalance(pre.Env.Coinbase, minerReward)
|
statedb.AddBalance(pre.Env.Coinbase, minerReward)
|
||||||
}
|
}
|
||||||
|
// Apply withdrawals
|
||||||
|
for _, w := range pre.Env.Withdrawals {
|
||||||
|
// Amount is in gwei, turn into wei
|
||||||
|
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
|
||||||
|
statedb.AddBalance(w.Address, amount)
|
||||||
|
}
|
||||||
// Commit block
|
// Commit block
|
||||||
root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
|
root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -272,6 +280,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||||||
GasUsed: (math.HexOrDecimal64)(gasUsed),
|
GasUsed: (math.HexOrDecimal64)(gasUsed),
|
||||||
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
|
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
|
||||||
}
|
}
|
||||||
|
if pre.Env.Withdrawals != nil {
|
||||||
|
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil))
|
||||||
|
execRs.WithdrawalsRoot = &h
|
||||||
|
}
|
||||||
return statedb, execRs, nil
|
return statedb, execRs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +112,10 @@ var (
|
|||||||
Name: "input.ommers",
|
Name: "input.ommers",
|
||||||
Usage: "`stdin` or file name of where to find the list of ommer header RLPs to use.",
|
Usage: "`stdin` or file name of where to find the list of ommer header RLPs to use.",
|
||||||
}
|
}
|
||||||
|
InputWithdrawalsFlag = &cli.StringFlag{
|
||||||
|
Name: "input.withdrawals",
|
||||||
|
Usage: "`stdin` or file name of where to find the list of withdrawals to use.",
|
||||||
|
}
|
||||||
InputTxsRlpFlag = &cli.StringFlag{
|
InputTxsRlpFlag = &cli.StringFlag{
|
||||||
Name: "input.txs",
|
Name: "input.txs",
|
||||||
Usage: "`stdin` or file name of where to find the transactions list in RLP form.",
|
Usage: "`stdin` or file name of where to find the transactions list in RLP form.",
|
||||||
|
@ -18,22 +18,23 @@ 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"`
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
OmmerHash *common.Hash `json:"sha3Uncles"`
|
OmmerHash *common.Hash `json:"sha3Uncles"`
|
||||||
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"`
|
TxHash *common.Hash `json:"transactionsRoot"`
|
||||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||||
Bloom types.Bloom `json:"logsBloom"`
|
Bloom types.Bloom `json:"logsBloom"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||||
Number *math.HexOrDecimal256 `json:"number" gencodec:"required"`
|
Number *math.HexOrDecimal256 `json:"number" gencodec:"required"`
|
||||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
Time math.HexOrDecimal64 `json:"timestamp" gencodec:"required"`
|
Time math.HexOrDecimal64 `json:"timestamp" gencodec:"required"`
|
||||||
Extra hexutil.Bytes `json:"extraData"`
|
Extra hexutil.Bytes `json:"extraData"`
|
||||||
MixDigest common.Hash `json:"mixHash"`
|
MixDigest common.Hash `json:"mixHash"`
|
||||||
Nonce *types.BlockNonce `json:"nonce"`
|
Nonce *types.BlockNonce `json:"nonce"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
|
||||||
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
}
|
}
|
||||||
var enc header
|
var enc header
|
||||||
enc.ParentHash = h.ParentHash
|
enc.ParentHash = h.ParentHash
|
||||||
@ -52,28 +53,30 @@ func (h header) MarshalJSON() ([]byte, error) {
|
|||||||
enc.MixDigest = h.MixDigest
|
enc.MixDigest = h.MixDigest
|
||||||
enc.Nonce = h.Nonce
|
enc.Nonce = h.Nonce
|
||||||
enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee)
|
enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee)
|
||||||
|
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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"`
|
ParentHash *common.Hash `json:"parentHash"`
|
||||||
OmmerHash *common.Hash `json:"sha3Uncles"`
|
OmmerHash *common.Hash `json:"sha3Uncles"`
|
||||||
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"`
|
TxHash *common.Hash `json:"transactionsRoot"`
|
||||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||||
Bloom *types.Bloom `json:"logsBloom"`
|
Bloom *types.Bloom `json:"logsBloom"`
|
||||||
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||||
Number *math.HexOrDecimal256 `json:"number" gencodec:"required"`
|
Number *math.HexOrDecimal256 `json:"number" gencodec:"required"`
|
||||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
Time *math.HexOrDecimal64 `json:"timestamp" gencodec:"required"`
|
Time *math.HexOrDecimal64 `json:"timestamp" gencodec:"required"`
|
||||||
Extra *hexutil.Bytes `json:"extraData"`
|
Extra *hexutil.Bytes `json:"extraData"`
|
||||||
MixDigest *common.Hash `json:"mixHash"`
|
MixDigest *common.Hash `json:"mixHash"`
|
||||||
Nonce *types.BlockNonce `json:"nonce"`
|
Nonce *types.BlockNonce `json:"nonce"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
|
||||||
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
}
|
}
|
||||||
var dec header
|
var dec header
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@ -131,5 +134,8 @@ func (h *header) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.BaseFee != nil {
|
if dec.BaseFee != nil {
|
||||||
h.BaseFee = (*big.Int)(dec.BaseFee)
|
h.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
}
|
}
|
||||||
|
if dec.WithdrawalsHash != nil {
|
||||||
|
h.WithdrawalsHash = dec.WithdrawalsHash
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = (*stEnvMarshaling)(nil)
|
var _ = (*stEnvMarshaling)(nil)
|
||||||
@ -29,6 +30,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
|||||||
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||||
}
|
}
|
||||||
@ -46,6 +48,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
|||||||
enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp)
|
enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp)
|
||||||
enc.BlockHashes = s.BlockHashes
|
enc.BlockHashes = s.BlockHashes
|
||||||
enc.Ommers = s.Ommers
|
enc.Ommers = s.Ommers
|
||||||
|
enc.Withdrawals = s.Withdrawals
|
||||||
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
||||||
enc.ParentUncleHash = s.ParentUncleHash
|
enc.ParentUncleHash = s.ParentUncleHash
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
@ -67,6 +70,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||||||
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||||
Ommers []ommer `json:"ommers,omitempty"`
|
Ommers []ommer `json:"ommers,omitempty"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||||
ParentUncleHash *common.Hash `json:"parentUncleHash"`
|
ParentUncleHash *common.Hash `json:"parentUncleHash"`
|
||||||
}
|
}
|
||||||
@ -117,6 +121,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.Ommers != nil {
|
if dec.Ommers != nil {
|
||||||
s.Ommers = dec.Ommers
|
s.Ommers = dec.Ommers
|
||||||
}
|
}
|
||||||
|
if dec.Withdrawals != nil {
|
||||||
|
s.Withdrawals = dec.Withdrawals
|
||||||
|
}
|
||||||
if dec.BaseFee != nil {
|
if dec.BaseFee != nil {
|
||||||
s.BaseFee = (*big.Int)(dec.BaseFee)
|
s.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
}
|
}
|
||||||
|
@ -262,6 +262,9 @@ func Transition(ctx *cli.Context) error {
|
|||||||
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if chainConfig.IsShanghai(prestate.Env.Number) && prestate.Env.Withdrawals == nil {
|
||||||
|
return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
|
||||||
|
}
|
||||||
isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
|
isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
|
||||||
env := prestate.Env
|
env := prestate.Env
|
||||||
if isMerged {
|
if isMerged {
|
||||||
|
@ -176,6 +176,7 @@ var blockBuilderCommand = &cli.Command{
|
|||||||
t8ntool.OutputBlockFlag,
|
t8ntool.OutputBlockFlag,
|
||||||
t8ntool.InputHeaderFlag,
|
t8ntool.InputHeaderFlag,
|
||||||
t8ntool.InputOmmersFlag,
|
t8ntool.InputOmmersFlag,
|
||||||
|
t8ntool.InputWithdrawalsFlag,
|
||||||
t8ntool.InputTxsRlpFlag,
|
t8ntool.InputTxsRlpFlag,
|
||||||
t8ntool.SealCliqueFlag,
|
t8ntool.SealCliqueFlag,
|
||||||
t8ntool.SealEthashFlag,
|
t8ntool.SealEthashFlag,
|
||||||
|
@ -251,6 +251,14 @@ func TestT8n(t *testing.T) {
|
|||||||
output: t8nOutput{alloc: true, result: true},
|
output: t8nOutput{alloc: true, result: true},
|
||||||
expOut: "exp.json",
|
expOut: "exp.json",
|
||||||
},
|
},
|
||||||
|
{ // Test withdrawals transition
|
||||||
|
base: "./testdata/26",
|
||||||
|
input: t8nInput{
|
||||||
|
"alloc.json", "txs.json", "env.json", "Shanghai", "",
|
||||||
|
},
|
||||||
|
output: t8nOutput{alloc: true, result: true},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
args := []string{"t8n"}
|
args := []string{"t8n"}
|
||||||
args = append(args, tc.output.get()...)
|
args = append(args, tc.output.get()...)
|
||||||
@ -391,13 +399,14 @@ func TestT9n(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type b11rInput struct {
|
type b11rInput struct {
|
||||||
inEnv string
|
inEnv string
|
||||||
inOmmersRlp string
|
inOmmersRlp string
|
||||||
inTxsRlp string
|
inWithdrawals string
|
||||||
inClique string
|
inTxsRlp string
|
||||||
ethash bool
|
inClique string
|
||||||
ethashMode string
|
ethash bool
|
||||||
ethashDir string
|
ethashMode string
|
||||||
|
ethashDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (args *b11rInput) get(base string) []string {
|
func (args *b11rInput) get(base string) []string {
|
||||||
@ -410,6 +419,10 @@ func (args *b11rInput) get(base string) []string {
|
|||||||
out = append(out, "--input.ommers")
|
out = append(out, "--input.ommers")
|
||||||
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
}
|
}
|
||||||
|
if opt := args.inWithdrawals; opt != "" {
|
||||||
|
out = append(out, "--input.withdrawals")
|
||||||
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
|
}
|
||||||
if opt := args.inTxsRlp; opt != "" {
|
if opt := args.inTxsRlp; opt != "" {
|
||||||
out = append(out, "--input.txs")
|
out = append(out, "--input.txs")
|
||||||
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||||
@ -480,6 +493,16 @@ func TestB11r(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expOut: "exp.json",
|
expOut: "exp.json",
|
||||||
},
|
},
|
||||||
|
{ // block with withdrawals
|
||||||
|
base: "./testdata/27",
|
||||||
|
input: b11rInput{
|
||||||
|
inEnv: "header.json",
|
||||||
|
inOmmersRlp: "ommers.json",
|
||||||
|
inWithdrawals: "withdrawals.json",
|
||||||
|
inTxsRlp: "txs.rlp",
|
||||||
|
},
|
||||||
|
expOut: "exp.json",
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
args := []string{"b11r"}
|
args := []string{"b11r"}
|
||||||
args = append(args, tc.input.get(tc.base)...)
|
args = append(args, tc.input.get(tc.base)...)
|
||||||
|
8
cmd/evm/testdata/26/alloc.json
vendored
Normal file
8
cmd/evm/testdata/26/alloc.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0xac",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
}
|
17
cmd/evm/testdata/26/env.json
vendored
Normal file
17
cmd/evm/testdata/26/env.json
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"currentDifficulty": null,
|
||||||
|
"currentRandom": "0xdeadc0de",
|
||||||
|
"currentGasLimit": "0x750a163df65e8a",
|
||||||
|
"currentBaseFee": "0x500",
|
||||||
|
"currentNumber": "1",
|
||||||
|
"currentTimestamp": "1000",
|
||||||
|
"withdrawals": [
|
||||||
|
{
|
||||||
|
"index": "0x42",
|
||||||
|
"validatorIndex": "0x42",
|
||||||
|
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"amount": "0x2a"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
20
cmd/evm/testdata/26/exp.json
vendored
Normal file
20
cmd/evm/testdata/26/exp.json
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"alloc": {
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0x9c7652400",
|
||||||
|
"nonce": "0xac"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"stateRoot": "0x6e061c2f6513af27d267a0e3b07cb9a10f1ba3a0f65ab648d3a17c36e15021d2",
|
||||||
|
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"receipts": [],
|
||||||
|
"currentDifficulty": null,
|
||||||
|
"gasUsed": "0x0",
|
||||||
|
"currentBaseFee": "0x500",
|
||||||
|
"withdrawalsRoot": "0x4921c0162c359755b2ae714a0978a1dad2eb8edce7ff9b38b9b6fc4cbc547eb5"
|
||||||
|
}
|
||||||
|
}
|
1
cmd/evm/testdata/26/txs.json
vendored
Normal file
1
cmd/evm/testdata/26/txs.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
4
cmd/evm/testdata/27/exp.json
vendored
Normal file
4
cmd/evm/testdata/27/exp.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"rlp": "0xf90239f9021aa0d6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082100082c3be83050785808455c5277e80a05865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf88000000000000000080a04921c0162c359755b2ae714a0978a1dad2eb8edce7ff9b38b9b6fc4cbc547eb5c0c0d9d8424394a94f5374fce5edbc8e2a8697c15331677e6ebf0b2a",
|
||||||
|
"hash": "0xdc42abd3698499675819e0a85cc1266f16da90277509b867446a6b25fa2b9d87"
|
||||||
|
}
|
12
cmd/evm/testdata/27/header.json
vendored
Normal file
12
cmd/evm/testdata/27/header.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"parentHash": "0xd6d785d33cbecf30f30d07e00e226af58f72efdf385d46bc3e6326c23b11e34e",
|
||||||
|
"stateRoot": "0x325aea6db48e9d737cddf59034843e99f05bec269453be83c9b9a981a232cc2e",
|
||||||
|
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x1000",
|
||||||
|
"number": "0xc3be",
|
||||||
|
"gasLimit": "0x50785",
|
||||||
|
"gasUsed": "0x0",
|
||||||
|
"timestamp": "0x55c5277e",
|
||||||
|
"mixHash": "0x5865e417635a26db6d1d39ac70d1abf373e5398b3c6fd506acd038fa1334eedf",
|
||||||
|
"withdrawalsRoot": "0x4921c0162c359755b2ae714a0978a1dad2eb8edce7ff9b38b9b6fc4cbc547eb5"
|
||||||
|
}
|
1
cmd/evm/testdata/27/ommers.json
vendored
Normal file
1
cmd/evm/testdata/27/ommers.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
1
cmd/evm/testdata/27/txs.rlp
vendored
Normal file
1
cmd/evm/testdata/27/txs.rlp
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"c0"
|
8
cmd/evm/testdata/27/withdrawals.json
vendored
Normal file
8
cmd/evm/testdata/27/withdrawals.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"index": "0x42",
|
||||||
|
"validatorIndex": "0x43",
|
||||||
|
"address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
|
||||||
|
"amount": "0x2a"
|
||||||
|
}
|
||||||
|
]
|
@ -257,7 +257,18 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||||||
return consensus.ErrInvalidNumber
|
return consensus.ErrInvalidNumber
|
||||||
}
|
}
|
||||||
// Verify the header's EIP-1559 attributes.
|
// Verify the header's EIP-1559 attributes.
|
||||||
return misc.VerifyEip1559Header(chain.Config(), parent, header)
|
if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Verify existence / non-existence of withdrawalsHash.
|
||||||
|
shanghai := chain.Config().IsShanghai(header.Time)
|
||||||
|
if shanghai && header.WithdrawalsHash == nil {
|
||||||
|
return fmt.Errorf("missing withdrawalsHash")
|
||||||
|
}
|
||||||
|
if !shanghai && header.WithdrawalsHash != nil {
|
||||||
|
return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyHeaders is similar to verifyHeader, but verifies a batch of headers
|
// verifyHeaders is similar to verifyHeader, but verifies a batch of headers
|
||||||
@ -316,13 +327,20 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Finalize implements consensus.Engine, setting the final state on the header
|
// Finalize implements consensus.Engine, setting the final state on the header
|
||||||
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
|
func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
|
||||||
// Finalize is different with Prepare, it can be used in both block generation
|
// Finalize is different with Prepare, it can be used in both block generation
|
||||||
// and verification. So determine the consensus rules by header type.
|
// and verification. So determine the consensus rules by header type.
|
||||||
if !beacon.IsPoSHeader(header) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
beacon.ethone.Finalize(chain, header, state, txs, uncles)
|
beacon.ethone.Finalize(chain, header, state, txs, uncles, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Withdrawals processing.
|
||||||
|
for _, w := range withdrawals {
|
||||||
|
// Convert amount from gwei to wei.
|
||||||
|
amount := new(big.Int).SetUint64(w.Amount)
|
||||||
|
amount = amount.Mul(amount, big.NewInt(params.GWei))
|
||||||
|
state.AddBalance(w.Address, amount)
|
||||||
|
}
|
||||||
// The block reward is no longer handled here. It's done by the
|
// The block reward is no longer handled here. It's done by the
|
||||||
// external consensus engine.
|
// external consensus engine.
|
||||||
header.Root = state.IntermediateRoot(true)
|
header.Root = state.IntermediateRoot(true)
|
||||||
@ -330,15 +348,26 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
||||||
// assembling the block.
|
// assembling the block.
|
||||||
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) {
|
||||||
// FinalizeAndAssemble is different with Prepare, it can be used in both block
|
// FinalizeAndAssemble is different with Prepare, it can be used in both block
|
||||||
// generation and verification. So determine the consensus rules by header type.
|
// generation and verification. So determine the consensus rules by header type.
|
||||||
if !beacon.IsPoSHeader(header) {
|
if !beacon.IsPoSHeader(header) {
|
||||||
return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
|
return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts, nil)
|
||||||
}
|
}
|
||||||
// Finalize and assemble the block
|
shanghai := chain.Config().IsShanghai(header.Time)
|
||||||
beacon.Finalize(chain, header, state, txs, uncles)
|
if shanghai {
|
||||||
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil
|
// All blocks after Shanghai must include a withdrawals root.
|
||||||
|
if withdrawals == nil {
|
||||||
|
withdrawals = make([]*types.Withdrawal, 0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(withdrawals) > 0 {
|
||||||
|
return nil, errors.New("withdrawals set before Shanghai activation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finalize and assemble the block.
|
||||||
|
beacon.Finalize(chain, header, state, txs, uncles, withdrawals)
|
||||||
|
return types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
41
consensus/beacon/faker.go
Normal file
41
consensus/beacon/faker.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package beacon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFaker creates a fake consensus engine for testing.
|
||||||
|
// The fake engine simulates a merged network.
|
||||||
|
// It can not be used to test the merge transition.
|
||||||
|
// This type is needed since the fakeChainReader can not be used with
|
||||||
|
// a normal beacon consensus engine.
|
||||||
|
func NewFaker() consensus.Engine {
|
||||||
|
return new(faker)
|
||||||
|
}
|
||||||
|
|
||||||
|
type faker struct {
|
||||||
|
Beacon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faker) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
||||||
|
return beaconDifficulty
|
||||||
|
}
|
@ -298,6 +298,9 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
|||||||
if header.GasLimit > params.MaxGasLimit {
|
if header.GasLimit > params.MaxGasLimit {
|
||||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
|
||||||
}
|
}
|
||||||
|
if chain.Config().IsShanghai(header.Time) {
|
||||||
|
return fmt.Errorf("clique does not support shanghai fork")
|
||||||
|
}
|
||||||
// If all checks passed, validate any special fields for hard forks
|
// If all checks passed, validate any special fields for hard forks
|
||||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -564,7 +567,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
|
|||||||
|
|
||||||
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
||||||
// rewards given.
|
// rewards given.
|
||||||
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
|
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
|
||||||
// No block rewards in PoA, so the state remains as is and uncles are dropped
|
// No block rewards in PoA, so the state remains as is and uncles are dropped
|
||||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||||
header.UncleHash = types.CalcUncleHash(nil)
|
header.UncleHash = types.CalcUncleHash(nil)
|
||||||
@ -572,9 +575,13 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
|
|||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
||||||
// nor block rewards given, and returns the final block.
|
// nor block rewards given, and returns the final block.
|
||||||
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) {
|
||||||
|
if len(withdrawals) > 0 {
|
||||||
|
return nil, errors.New("clique does not support withdrawals")
|
||||||
|
}
|
||||||
|
|
||||||
// Finalize block
|
// Finalize block
|
||||||
c.Finalize(chain, header, state, txs, uncles)
|
c.Finalize(chain, header, state, txs, uncles, nil)
|
||||||
|
|
||||||
// Assemble and return the final block for sealing
|
// Assemble and return the final block for sealing
|
||||||
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil
|
return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil
|
||||||
@ -743,6 +750,9 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
|
|||||||
if header.BaseFee != nil {
|
if header.BaseFee != nil {
|
||||||
enc = append(enc, header.BaseFee)
|
enc = append(enc, header.BaseFee)
|
||||||
}
|
}
|
||||||
|
if header.WithdrawalsHash != nil {
|
||||||
|
panic("unexpected withdrawal hash value in clique")
|
||||||
|
}
|
||||||
if err := rlp.Encode(w, enc); err != nil {
|
if err := rlp.Encode(w, enc); err != nil {
|
||||||
panic("can't encode: " + err.Error())
|
panic("can't encode: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ type Engine interface {
|
|||||||
// Note: The block header and state database might be updated to reflect any
|
// Note: The block header and state database might be updated to reflect any
|
||||||
// consensus rules that happen at finalization (e.g. block rewards).
|
// consensus rules that happen at finalization (e.g. block rewards).
|
||||||
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
||||||
uncles []*types.Header)
|
uncles []*types.Header, withdrawals []*types.Withdrawal)
|
||||||
|
|
||||||
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
|
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
|
||||||
// rewards) and assembles the final block.
|
// rewards) and assembles the final block.
|
||||||
@ -98,7 +98,7 @@ type Engine interface {
|
|||||||
// Note: The block header and state database might be updated to reflect any
|
// Note: The block header and state database might be updated to reflect any
|
||||||
// consensus rules that happen at finalization (e.g. block rewards).
|
// consensus rules that happen at finalization (e.g. block rewards).
|
||||||
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
||||||
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
|
uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error)
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -310,6 +310,9 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||||||
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
|
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
|
||||||
return consensus.ErrInvalidNumber
|
return consensus.ErrInvalidNumber
|
||||||
}
|
}
|
||||||
|
if chain.Config().IsShanghai(header.Time) {
|
||||||
|
return fmt.Errorf("ethash does not support shanghai fork")
|
||||||
|
}
|
||||||
// Verify the engine specific seal securing the block
|
// Verify the engine specific seal securing the block
|
||||||
if seal {
|
if seal {
|
||||||
if err := ethash.verifySeal(chain, header, false); err != nil {
|
if err := ethash.verifySeal(chain, header, false); err != nil {
|
||||||
@ -597,7 +600,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,
|
||||||
// setting the final state on the header
|
// setting the final state on the header
|
||||||
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
|
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) {
|
||||||
// Accumulate any block and uncle rewards and commit the final state root
|
// Accumulate any block and uncle rewards and commit the final state root
|
||||||
accumulateRewards(chain.Config(), state, header, uncles)
|
accumulateRewards(chain.Config(), state, header, uncles)
|
||||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||||
@ -605,10 +608,13 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||||||
|
|
||||||
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
||||||
// uncle rewards, setting the final state and assembling the block.
|
// uncle rewards, setting the final state and assembling the block.
|
||||||
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) {
|
||||||
// Finalize block
|
if len(withdrawals) > 0 {
|
||||||
ethash.Finalize(chain, header, state, txs, uncles)
|
return nil, errors.New("ethash does not support withdrawals")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize block
|
||||||
|
ethash.Finalize(chain, header, state, txs, uncles, nil)
|
||||||
// Header seems complete, assemble into a block and return
|
// Header seems complete, assemble into a block and return
|
||||||
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil
|
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil
|
||||||
}
|
}
|
||||||
@ -635,6 +641,9 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
|||||||
if header.BaseFee != nil {
|
if header.BaseFee != nil {
|
||||||
enc = append(enc, header.BaseFee)
|
enc = append(enc, header.BaseFee)
|
||||||
}
|
}
|
||||||
|
if header.WithdrawalsHash != nil {
|
||||||
|
panic("withdrawal hash set on ethash")
|
||||||
|
}
|
||||||
rlp.Encode(hasher, enc)
|
rlp.Encode(hasher, enc)
|
||||||
hasher.Sum(hash[:0])
|
hasher.Sum(hash[:0])
|
||||||
return hash
|
return hash
|
||||||
|
@ -8,46 +8,53 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = (*payloadAttributesMarshaling)(nil)
|
var _ = (*payloadAttributesMarshaling)(nil)
|
||||||
|
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) {
|
func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
||||||
type PayloadAttributesV1 struct {
|
type PayloadAttributes struct {
|
||||||
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
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"`
|
||||||
}
|
}
|
||||||
var enc PayloadAttributesV1
|
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
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error {
|
func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
||||||
type PayloadAttributesV1 struct {
|
type PayloadAttributes struct {
|
||||||
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
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"`
|
||||||
}
|
}
|
||||||
var dec PayloadAttributesV1
|
var dec PayloadAttributes
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if dec.Timestamp == nil {
|
if dec.Timestamp == nil {
|
||||||
return errors.New("missing required field 'timestamp' for PayloadAttributesV1")
|
return errors.New("missing required field 'timestamp' for PayloadAttributes")
|
||||||
}
|
}
|
||||||
p.Timestamp = uint64(*dec.Timestamp)
|
p.Timestamp = uint64(*dec.Timestamp)
|
||||||
if dec.Random == nil {
|
if dec.Random == nil {
|
||||||
return errors.New("missing required field 'prevRandao' for PayloadAttributesV1")
|
return errors.New("missing required field 'prevRandao' for PayloadAttributes")
|
||||||
}
|
}
|
||||||
p.Random = *dec.Random
|
p.Random = *dec.Random
|
||||||
if dec.SuggestedFeeRecipient == nil {
|
if dec.SuggestedFeeRecipient == nil {
|
||||||
return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributesV1")
|
return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributes")
|
||||||
}
|
}
|
||||||
p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient
|
p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient
|
||||||
|
if dec.Withdrawals != nil {
|
||||||
|
p.Withdrawals = dec.Withdrawals
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -9,29 +9,31 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = (*executableDataMarshaling)(nil)
|
var _ = (*executableDataMarshaling)(nil)
|
||||||
|
|
||||||
// MarshalJSON marshals as JSON.
|
// MarshalJSON marshals as JSON.
|
||||||
func (e ExecutableDataV1) MarshalJSON() ([]byte, error) {
|
func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
type ExecutableDataV1 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"`
|
||||||
}
|
}
|
||||||
var enc ExecutableDataV1
|
var enc ExecutableData
|
||||||
enc.ParentHash = e.ParentHash
|
enc.ParentHash = e.ParentHash
|
||||||
enc.FeeRecipient = e.FeeRecipient
|
enc.FeeRecipient = e.FeeRecipient
|
||||||
enc.StateRoot = e.StateRoot
|
enc.StateRoot = e.StateRoot
|
||||||
@ -51,89 +53,94 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) {
|
|||||||
enc.Transactions[k] = v
|
enc.Transactions[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enc.Withdrawals = e.Withdrawals
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error {
|
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
type ExecutableDataV1 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"`
|
||||||
}
|
}
|
||||||
var dec ExecutableDataV1
|
var dec ExecutableData
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if dec.ParentHash == nil {
|
if dec.ParentHash == nil {
|
||||||
return errors.New("missing required field 'parentHash' for ExecutableDataV1")
|
return errors.New("missing required field 'parentHash' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.ParentHash = *dec.ParentHash
|
e.ParentHash = *dec.ParentHash
|
||||||
if dec.FeeRecipient == nil {
|
if dec.FeeRecipient == nil {
|
||||||
return errors.New("missing required field 'feeRecipient' for ExecutableDataV1")
|
return errors.New("missing required field 'feeRecipient' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.FeeRecipient = *dec.FeeRecipient
|
e.FeeRecipient = *dec.FeeRecipient
|
||||||
if dec.StateRoot == nil {
|
if dec.StateRoot == nil {
|
||||||
return errors.New("missing required field 'stateRoot' for ExecutableDataV1")
|
return errors.New("missing required field 'stateRoot' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.StateRoot = *dec.StateRoot
|
e.StateRoot = *dec.StateRoot
|
||||||
if dec.ReceiptsRoot == nil {
|
if dec.ReceiptsRoot == nil {
|
||||||
return errors.New("missing required field 'receiptsRoot' for ExecutableDataV1")
|
return errors.New("missing required field 'receiptsRoot' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.ReceiptsRoot = *dec.ReceiptsRoot
|
e.ReceiptsRoot = *dec.ReceiptsRoot
|
||||||
if dec.LogsBloom == nil {
|
if dec.LogsBloom == nil {
|
||||||
return errors.New("missing required field 'logsBloom' for ExecutableDataV1")
|
return errors.New("missing required field 'logsBloom' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.LogsBloom = *dec.LogsBloom
|
e.LogsBloom = *dec.LogsBloom
|
||||||
if dec.Random == nil {
|
if dec.Random == nil {
|
||||||
return errors.New("missing required field 'prevRandao' for ExecutableDataV1")
|
return errors.New("missing required field 'prevRandao' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.Random = *dec.Random
|
e.Random = *dec.Random
|
||||||
if dec.Number == nil {
|
if dec.Number == nil {
|
||||||
return errors.New("missing required field 'blockNumber' for ExecutableDataV1")
|
return errors.New("missing required field 'blockNumber' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.Number = uint64(*dec.Number)
|
e.Number = uint64(*dec.Number)
|
||||||
if dec.GasLimit == nil {
|
if dec.GasLimit == nil {
|
||||||
return errors.New("missing required field 'gasLimit' for ExecutableDataV1")
|
return errors.New("missing required field 'gasLimit' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.GasLimit = uint64(*dec.GasLimit)
|
e.GasLimit = uint64(*dec.GasLimit)
|
||||||
if dec.GasUsed == nil {
|
if dec.GasUsed == nil {
|
||||||
return errors.New("missing required field 'gasUsed' for ExecutableDataV1")
|
return errors.New("missing required field 'gasUsed' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.GasUsed = uint64(*dec.GasUsed)
|
e.GasUsed = uint64(*dec.GasUsed)
|
||||||
if dec.Timestamp == nil {
|
if dec.Timestamp == nil {
|
||||||
return errors.New("missing required field 'timestamp' for ExecutableDataV1")
|
return errors.New("missing required field 'timestamp' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.Timestamp = uint64(*dec.Timestamp)
|
e.Timestamp = uint64(*dec.Timestamp)
|
||||||
if dec.ExtraData == nil {
|
if dec.ExtraData == nil {
|
||||||
return errors.New("missing required field 'extraData' for ExecutableDataV1")
|
return errors.New("missing required field 'extraData' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.ExtraData = *dec.ExtraData
|
e.ExtraData = *dec.ExtraData
|
||||||
if dec.BaseFeePerGas == nil {
|
if dec.BaseFeePerGas == nil {
|
||||||
return errors.New("missing required field 'baseFeePerGas' for ExecutableDataV1")
|
return errors.New("missing required field 'baseFeePerGas' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
|
e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
|
||||||
if dec.BlockHash == nil {
|
if dec.BlockHash == nil {
|
||||||
return errors.New("missing required field 'blockHash' for ExecutableDataV1")
|
return errors.New("missing required field 'blockHash' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.BlockHash = *dec.BlockHash
|
e.BlockHash = *dec.BlockHash
|
||||||
if dec.Transactions == nil {
|
if dec.Transactions == nil {
|
||||||
return errors.New("missing required field 'transactions' for ExecutableDataV1")
|
return errors.New("missing required field 'transactions' for ExecutableData")
|
||||||
}
|
}
|
||||||
e.Transactions = make([][]byte, len(dec.Transactions))
|
e.Transactions = make([][]byte, len(dec.Transactions))
|
||||||
for k, v := range dec.Transactions {
|
for k, v := range dec.Transactions {
|
||||||
e.Transactions[k] = v
|
e.Transactions[k] = v
|
||||||
}
|
}
|
||||||
|
if dec.Withdrawals != nil {
|
||||||
|
e.Withdrawals = dec.Withdrawals
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
46
core/beacon/gen_epe.go
Normal file
46
core/beacon/gen_epe.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package beacon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*executionPayloadEnvelopeMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
||||||
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
|
}
|
||||||
|
var enc ExecutionPayloadEnvelope
|
||||||
|
enc.ExecutionPayload = e.ExecutionPayload
|
||||||
|
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||||
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
|
}
|
||||||
|
var dec ExecutionPayloadEnvelope
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.ExecutionPayload == nil {
|
||||||
|
return errors.New("missing required field 'executionPayload' for ExecutionPayloadEnvelope")
|
||||||
|
}
|
||||||
|
e.ExecutionPayload = dec.ExecutionPayload
|
||||||
|
if dec.BlockValue == nil {
|
||||||
|
return errors.New("missing required field 'blockValue' for ExecutionPayloadEnvelope")
|
||||||
|
}
|
||||||
|
e.BlockValue = (*big.Int)(dec.BlockValue)
|
||||||
|
return nil
|
||||||
|
}
|
@ -26,38 +26,41 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type PayloadAttributesV1 -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
||||||
|
|
||||||
// PayloadAttributesV1 structure described at https://github.com/ethereum/execution-apis/pull/74
|
// PayloadAttributes describes the environment context in which a block should
|
||||||
type PayloadAttributesV1 struct {
|
// be built.
|
||||||
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
type PayloadAttributes struct {
|
||||||
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for PayloadAttributesV1.
|
// JSON type overrides for PayloadAttributes.
|
||||||
type payloadAttributesMarshaling struct {
|
type payloadAttributesMarshaling struct {
|
||||||
Timestamp hexutil.Uint64
|
Timestamp hexutil.Uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type ExecutableDataV1 -field-override executableDataMarshaling -out gen_ed.go
|
//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go
|
||||||
|
|
||||||
// ExecutableDataV1 structure described at https://github.com/ethereum/execution-apis/tree/main/src/engine/specification.md
|
// ExecutableData is the data necessary to execute an EL payload.
|
||||||
type ExecutableDataV1 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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// JSON type overrides for executableData.
|
||||||
@ -72,6 +75,18 @@ type executableDataMarshaling struct {
|
|||||||
Transactions []hexutil.Bytes
|
Transactions []hexutil.Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
||||||
|
|
||||||
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
|
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON type overrides for ExecutionPayloadEnvelope.
|
||||||
|
type executionPayloadEnvelopeMarshaling struct {
|
||||||
|
BlockValue *hexutil.Big
|
||||||
|
}
|
||||||
|
|
||||||
type PayloadStatusV1 struct {
|
type PayloadStatusV1 struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||||
@ -141,8 +156,10 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
|||||||
// uncleHash = emptyUncleHash
|
// uncleHash = emptyUncleHash
|
||||||
// difficulty = 0
|
// difficulty = 0
|
||||||
//
|
//
|
||||||
// and that the blockhash of the constructed block matches the parameters.
|
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||||
func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
|
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||||
|
func ExecutableDataToBlock(params ExecutableData) (*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
|
||||||
@ -157,34 +174,43 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
|||||||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||||
}
|
}
|
||||||
header := &types.Header{
|
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
|
||||||
ParentHash: params.ParentHash,
|
// ExecutableData before withdrawals are enabled by marshaling
|
||||||
UncleHash: types.EmptyUncleHash,
|
// Withdrawals as the json null value.
|
||||||
Coinbase: params.FeeRecipient,
|
var withdrawalsRoot *common.Hash
|
||||||
Root: params.StateRoot,
|
if params.Withdrawals != nil {
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
||||||
ReceiptHash: params.ReceiptsRoot,
|
withdrawalsRoot = &h
|
||||||
Bloom: types.BytesToBloom(params.LogsBloom),
|
|
||||||
Difficulty: common.Big0,
|
|
||||||
Number: new(big.Int).SetUint64(params.Number),
|
|
||||||
GasLimit: params.GasLimit,
|
|
||||||
GasUsed: params.GasUsed,
|
|
||||||
Time: params.Timestamp,
|
|
||||||
BaseFee: params.BaseFeePerGas,
|
|
||||||
Extra: params.ExtraData,
|
|
||||||
MixDigest: params.Random,
|
|
||||||
}
|
}
|
||||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
|
header := &types.Header{
|
||||||
|
ParentHash: params.ParentHash,
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
Coinbase: params.FeeRecipient,
|
||||||
|
Root: params.StateRoot,
|
||||||
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: params.ReceiptsRoot,
|
||||||
|
Bloom: types.BytesToBloom(params.LogsBloom),
|
||||||
|
Difficulty: common.Big0,
|
||||||
|
Number: new(big.Int).SetUint64(params.Number),
|
||||||
|
GasLimit: params.GasLimit,
|
||||||
|
GasUsed: params.GasUsed,
|
||||||
|
Time: params.Timestamp,
|
||||||
|
BaseFee: params.BaseFeePerGas,
|
||||||
|
Extra: params.ExtraData,
|
||||||
|
MixDigest: params.Random,
|
||||||
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
|
}
|
||||||
|
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||||
if block.Hash() != params.BlockHash {
|
if block.Hash() != params.BlockHash {
|
||||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
||||||
}
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockToExecutableData constructs the executableDataV1 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) *ExecutableDataV1 {
|
func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope {
|
||||||
return &ExecutableDataV1{
|
data := &ExecutableData{
|
||||||
BlockHash: block.Hash(),
|
BlockHash: block.Hash(),
|
||||||
ParentHash: block.ParentHash(),
|
ParentHash: block.ParentHash(),
|
||||||
FeeRecipient: block.Coinbase(),
|
FeeRecipient: block.Coinbase(),
|
||||||
@ -199,5 +225,7 @@ func BlockToExecutableData(block *types.Block) *ExecutableDataV1 {
|
|||||||
Transactions: encodeTransactions(block.Transactions()),
|
Transactions: encodeTransactions(block.Transactions()),
|
||||||
Random: block.MixDigest(),
|
Random: block.MixDigest(),
|
||||||
ExtraData: block.Extra(),
|
ExtraData: block.Extra(),
|
||||||
|
Withdrawals: block.Withdrawals(),
|
||||||
}
|
}
|
||||||
|
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,11 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
|||||||
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
|
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
|
||||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
||||||
}
|
}
|
||||||
|
if header.WithdrawalsHash != nil {
|
||||||
|
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
|
||||||
|
return fmt.Errorf("withdrawals root hash mismatch: have %x, want %x", hash, *header.WithdrawalsHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
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) {
|
||||||
return consensus.ErrUnknownAncestor
|
return consensus.ErrUnknownAncestor
|
||||||
|
@ -4229,7 +4229,7 @@ func TestEIP3651(t *testing.T) {
|
|||||||
var (
|
var (
|
||||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||||
engine = ethash.NewFaker()
|
engine = beacon.NewFaker()
|
||||||
|
|
||||||
// A sender who makes transactions, has some funds
|
// A sender who makes transactions, has some funds
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
@ -4237,8 +4237,9 @@ func TestEIP3651(t *testing.T) {
|
|||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||||
|
config = *params.AllEthashProtocolChanges
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.AllEthashProtocolChanges,
|
Config: &config,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: GenesisAlloc{
|
||||||
addr1: {Balance: funds},
|
addr1: {Balance: funds},
|
||||||
addr2: {Balance: funds},
|
addr2: {Balance: funds},
|
||||||
@ -4275,6 +4276,8 @@ func TestEIP3651(t *testing.T) {
|
|||||||
|
|
||||||
gspec.Config.BerlinBlock = common.Big0
|
gspec.Config.BerlinBlock = common.Big0
|
||||||
gspec.Config.LondonBlock = common.Big0
|
gspec.Config.LondonBlock = common.Big0
|
||||||
|
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||||
|
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||||
gspec.Config.ShanghaiTime = u64(0)
|
gspec.Config.ShanghaiTime = u64(0)
|
||||||
signer := types.LatestSigner(gspec.Config)
|
signer := types.LatestSigner(gspec.Config)
|
||||||
|
|
||||||
@ -4317,10 +4320,7 @@ func TestEIP3651(t *testing.T) {
|
|||||||
|
|
||||||
// 3: Ensure that miner received only the tx's tip.
|
// 3: Ensure that miner received only the tx's tip.
|
||||||
actual := state.GetBalance(block.Coinbase())
|
actual := state.GetBalance(block.Coinbase())
|
||||||
expected := new(big.Int).Add(
|
expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64())
|
||||||
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
|
||||||
ethash.ConstantinopleBlockReward,
|
|
||||||
)
|
|
||||||
if actual.Cmp(expected) != 0 {
|
if actual.Cmp(expected) != 0 {
|
||||||
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,11 @@ type BlockGen struct {
|
|||||||
header *types.Header
|
header *types.Header
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
|
|
||||||
gasPool *GasPool
|
gasPool *GasPool
|
||||||
txs []*types.Transaction
|
txs []*types.Transaction
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
uncles []*types.Header
|
uncles []*types.Header
|
||||||
|
withdrawals []*types.Withdrawal
|
||||||
|
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
@ -205,6 +206,26 @@ func (b *BlockGen) AddUncle(h *types.Header) {
|
|||||||
b.uncles = append(b.uncles, h)
|
b.uncles = append(b.uncles, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddWithdrawal adds a withdrawal to the generated block.
|
||||||
|
func (b *BlockGen) AddWithdrawal(w *types.Withdrawal) {
|
||||||
|
// The withdrawal will be assigned the next valid index.
|
||||||
|
var idx uint64
|
||||||
|
for i := b.i - 1; i >= 0; i-- {
|
||||||
|
if wd := b.chain[i].Withdrawals(); len(wd) != 0 {
|
||||||
|
idx = wd[len(wd)-1].Index + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
// Correctly set the index if no parent had withdrawals
|
||||||
|
if wd := b.parent.Withdrawals(); len(wd) != 0 {
|
||||||
|
idx = wd[len(wd)-1].Index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Index = idx
|
||||||
|
b.withdrawals = append(b.withdrawals, w)
|
||||||
|
}
|
||||||
|
|
||||||
// PrevBlock returns a previously generated block by number. It panics if
|
// PrevBlock returns a previously generated block by number. It panics if
|
||||||
// num is greater or equal to the number of the block being generated.
|
// num is greater or equal to the number of the block being generated.
|
||||||
// For index -1, PrevBlock returns the parent block given to GenerateChain.
|
// For index -1, PrevBlock returns the parent block given to GenerateChain.
|
||||||
@ -281,8 +302,10 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||||||
gen(i, b)
|
gen(i, b)
|
||||||
}
|
}
|
||||||
if b.engine != nil {
|
if b.engine != nil {
|
||||||
// Finalize and seal the block
|
block, err := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts, b.withdrawals)
|
||||||
block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Write state changes to db
|
// Write state changes to db
|
||||||
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
|
||||||
|
@ -469,6 +469,9 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||||||
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if g.Config != nil && g.Config.IsShanghai(g.Timestamp) {
|
||||||
|
head.WithdrawalsHash = &types.EmptyRootHash
|
||||||
|
}
|
||||||
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil))
|
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,7 +760,7 @@ func ReadBlock(db ethdb.Reader, hash common.Hash, number uint64) *types.Block {
|
|||||||
if body == nil {
|
if body == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
|
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles).WithWithdrawals(body.Withdrawals)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteBlock serializes a block into the database, header and body separately.
|
// WriteBlock serializes a block into the database, header and body separately.
|
||||||
@ -860,7 +860,7 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
|
|||||||
}
|
}
|
||||||
for _, bad := range badBlocks {
|
for _, bad := range badBlocks {
|
||||||
if bad.Header.Hash() == hash {
|
if bad.Header.Hash() == hash {
|
||||||
return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles)
|
return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -879,7 +879,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
|
|||||||
}
|
}
|
||||||
var blocks []*types.Block
|
var blocks []*types.Block
|
||||||
for _, bad := range badBlocks {
|
for _, bad := range badBlocks {
|
||||||
blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles))
|
blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles).WithWithdrawals(bad.Body.Withdrawals))
|
||||||
}
|
}
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
|
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
||||||
|
withdrawals := block.Withdrawals()
|
||||||
|
if len(withdrawals) > 0 && !p.config.IsShanghai(block.Time()) {
|
||||||
|
return nil, nil, 0, fmt.Errorf("withdrawals before shanghai")
|
||||||
|
}
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles())
|
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), withdrawals)
|
||||||
|
|
||||||
return receipts, allLogs, *usedGas, nil
|
return receipts, allLogs, *usedGas, nil
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
@ -314,22 +315,24 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: ¶ms.ChainConfig{
|
Config: ¶ms.ChainConfig{
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
EIP150Block: big.NewInt(0),
|
EIP150Block: big.NewInt(0),
|
||||||
EIP155Block: big.NewInt(0),
|
EIP155Block: big.NewInt(0),
|
||||||
EIP158Block: big.NewInt(0),
|
EIP158Block: big.NewInt(0),
|
||||||
ByzantiumBlock: big.NewInt(0),
|
ByzantiumBlock: big.NewInt(0),
|
||||||
ConstantinopleBlock: big.NewInt(0),
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
PetersburgBlock: big.NewInt(0),
|
PetersburgBlock: big.NewInt(0),
|
||||||
IstanbulBlock: big.NewInt(0),
|
IstanbulBlock: big.NewInt(0),
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
BerlinBlock: big.NewInt(0),
|
BerlinBlock: big.NewInt(0),
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
GrayGlacierBlock: big.NewInt(0),
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
ShanghaiTime: u64(0),
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
},
|
},
|
||||||
Alloc: GenesisAlloc{
|
Alloc: GenesisAlloc{
|
||||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
||||||
@ -339,7 +342,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||||
smallInitCode = [320]byte{}
|
smallInitCode = [320]byte{}
|
||||||
)
|
)
|
||||||
@ -361,7 +364,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
|
want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
block := GenerateBadBlock(genesis, beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("block imported without errors")
|
t.Fatal("block imported without errors")
|
||||||
@ -378,23 +381,31 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
// valid to be considered for import:
|
// valid to be considered for import:
|
||||||
// - valid pow (fake), ancestry, difficulty, gaslimit etc
|
// - valid pow (fake), ancestry, difficulty, gaslimit etc
|
||||||
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
|
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
|
||||||
header := &types.Header{
|
difficulty := big.NewInt(0)
|
||||||
ParentHash: parent.Hash(),
|
if !config.TerminalTotalDifficultyPassed {
|
||||||
Coinbase: parent.Coinbase(),
|
difficulty = engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
|
||||||
Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
|
|
||||||
Number: parent.Number(),
|
Number: parent.Number(),
|
||||||
Time: parent.Time(),
|
Time: parent.Time(),
|
||||||
Difficulty: parent.Difficulty(),
|
Difficulty: parent.Difficulty(),
|
||||||
UncleHash: parent.UncleHash(),
|
UncleHash: parent.UncleHash(),
|
||||||
}),
|
})
|
||||||
GasLimit: parent.GasLimit(),
|
}
|
||||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
|
||||||
Time: parent.Time() + 10,
|
header := &types.Header{
|
||||||
UncleHash: types.EmptyUncleHash,
|
ParentHash: parent.Hash(),
|
||||||
|
Coinbase: parent.Coinbase(),
|
||||||
|
Difficulty: difficulty,
|
||||||
|
GasLimit: parent.GasLimit(),
|
||||||
|
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||||
|
Time: parent.Time() + 10,
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
}
|
}
|
||||||
if config.IsLondon(header.Number) {
|
if config.IsLondon(header.Number) {
|
||||||
header.BaseFee = misc.CalcBaseFee(config, parent.Header())
|
header.BaseFee = misc.CalcBaseFee(config, parent.Header())
|
||||||
}
|
}
|
||||||
|
if config.IsShanghai(header.Time) {
|
||||||
|
header.WithdrawalsHash = &types.EmptyRootHash
|
||||||
|
}
|
||||||
var receipts []*types.Receipt
|
var receipts []*types.Receipt
|
||||||
// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
|
// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
|
||||||
// Preferably something unique. So let's use a combo of blocknum + txhash
|
// Preferably something unique. So let's use a combo of blocknum + txhash
|
||||||
|
@ -87,6 +87,9 @@ type Header struct {
|
|||||||
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
|
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
|
||||||
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
|
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
|
||||||
|
|
||||||
|
// WithdrawalsHash was added by EIP-4895 and is ignored in legacy headers.
|
||||||
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO (MariusVanDerWijden) Add this field once needed
|
TODO (MariusVanDerWijden) Add this field once needed
|
||||||
// Random was added during the merge and contains the BeaconState randomness
|
// Random was added during the merge and contains the BeaconState randomness
|
||||||
@ -149,9 +152,12 @@ func (h *Header) SanityCheck() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EmptyBody returns true if there is no additional 'body' to complete the header
|
// EmptyBody returns true if there is no additional 'body' to complete the header
|
||||||
// that is: no transactions and no uncles.
|
// that is: no transactions, no uncles and no withdrawals.
|
||||||
func (h *Header) EmptyBody() bool {
|
func (h *Header) EmptyBody() bool {
|
||||||
return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash
|
if h.WithdrawalsHash == nil {
|
||||||
|
return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash
|
||||||
|
}
|
||||||
|
return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash && *h.WithdrawalsHash == EmptyRootHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmptyReceipts returns true if there are no receipts for this header/block.
|
// EmptyReceipts returns true if there are no receipts for this header/block.
|
||||||
@ -164,6 +170,7 @@ func (h *Header) EmptyReceipts() bool {
|
|||||||
type Body struct {
|
type Body struct {
|
||||||
Transactions []*Transaction
|
Transactions []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block represents an entire block in the Ethereum blockchain.
|
// Block represents an entire block in the Ethereum blockchain.
|
||||||
@ -171,6 +178,7 @@ type Block struct {
|
|||||||
header *Header
|
header *Header
|
||||||
uncles []*Header
|
uncles []*Header
|
||||||
transactions Transactions
|
transactions Transactions
|
||||||
|
withdrawals Withdrawals
|
||||||
|
|
||||||
// caches
|
// caches
|
||||||
hash atomic.Value
|
hash atomic.Value
|
||||||
@ -184,9 +192,10 @@ type Block struct {
|
|||||||
|
|
||||||
// "external" block encoding. used for eth protocol, etc.
|
// "external" block encoding. used for eth protocol, etc.
|
||||||
type extblock struct {
|
type extblock struct {
|
||||||
Header *Header
|
Header *Header
|
||||||
Txs []*Transaction
|
Txs []*Transaction
|
||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
|
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlock creates a new block. The input data is copied,
|
// NewBlock creates a new block. The input data is copied,
|
||||||
@ -228,6 +237,28 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBlockWithWithdrawals creates a new block with withdrawals. The input data
|
||||||
|
// is copied, changes to header and to the field values will not
|
||||||
|
// affect the block.
|
||||||
|
//
|
||||||
|
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
|
||||||
|
// are ignored and set to values derived from the given txs, uncles
|
||||||
|
// and receipts.
|
||||||
|
func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, hasher TrieHasher) *Block {
|
||||||
|
b := NewBlock(header, txs, uncles, receipts, hasher)
|
||||||
|
|
||||||
|
if withdrawals == nil {
|
||||||
|
b.header.WithdrawalsHash = nil
|
||||||
|
} else if len(withdrawals) == 0 {
|
||||||
|
b.header.WithdrawalsHash = &EmptyRootHash
|
||||||
|
} else {
|
||||||
|
h := DeriveSha(Withdrawals(withdrawals), hasher)
|
||||||
|
b.header.WithdrawalsHash = &h
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.WithWithdrawals(withdrawals)
|
||||||
|
}
|
||||||
|
|
||||||
// NewBlockWithHeader creates a block with the given header data. The
|
// NewBlockWithHeader creates a block with the given header data. The
|
||||||
// header data is copied, changes to header and to the field values
|
// header data is copied, changes to header and to the field values
|
||||||
// will not affect the block.
|
// will not affect the block.
|
||||||
@ -252,6 +283,9 @@ func CopyHeader(h *Header) *Header {
|
|||||||
cpy.Extra = make([]byte, len(h.Extra))
|
cpy.Extra = make([]byte, len(h.Extra))
|
||||||
copy(cpy.Extra, h.Extra)
|
copy(cpy.Extra, h.Extra)
|
||||||
}
|
}
|
||||||
|
if h.WithdrawalsHash != nil {
|
||||||
|
*cpy.WithdrawalsHash = *h.WithdrawalsHash
|
||||||
|
}
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +296,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
|||||||
if err := s.Decode(&eb); err != nil {
|
if err := s.Decode(&eb); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
|
b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals
|
||||||
b.size.Store(rlp.ListSize(size))
|
b.size.Store(rlp.ListSize(size))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -270,9 +304,10 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
|||||||
// EncodeRLP serializes b into the Ethereum RLP block format.
|
// EncodeRLP serializes b into the Ethereum RLP block format.
|
||||||
func (b *Block) EncodeRLP(w io.Writer) error {
|
func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
return rlp.Encode(w, extblock{
|
return rlp.Encode(w, extblock{
|
||||||
Header: b.header,
|
Header: b.header,
|
||||||
Txs: b.transactions,
|
Txs: b.transactions,
|
||||||
Uncles: b.uncles,
|
Uncles: b.uncles,
|
||||||
|
Withdrawals: b.withdrawals,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,10 +350,14 @@ 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) Withdrawals() Withdrawals {
|
||||||
|
return b.withdrawals
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Block) Header() *Header { return CopyHeader(b.header) }
|
func (b *Block) Header() *Header { return CopyHeader(b.header) }
|
||||||
|
|
||||||
// Body returns the non-header content of the block.
|
// Body returns the non-header content of the block.
|
||||||
func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} }
|
func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles, b.withdrawals} }
|
||||||
|
|
||||||
// Size returns the true RLP encoded storage size of the block, either by encoding
|
// Size returns the true RLP encoded storage size of the block, either by encoding
|
||||||
// and returning it, or returning a previously cached value.
|
// and returning it, or returning a previously cached value.
|
||||||
@ -361,6 +400,7 @@ func (b *Block) WithSeal(header *Header) *Block {
|
|||||||
header: &cpy,
|
header: &cpy,
|
||||||
transactions: b.transactions,
|
transactions: b.transactions,
|
||||||
uncles: b.uncles,
|
uncles: b.uncles,
|
||||||
|
withdrawals: b.withdrawals,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,6 +418,15 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
|
|||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithWithdrawals sets the withdrawal contents of a block, does not return a new block.
|
||||||
|
func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block {
|
||||||
|
if withdrawals != nil {
|
||||||
|
b.withdrawals = make([]*Withdrawal, len(withdrawals))
|
||||||
|
copy(b.withdrawals, withdrawals)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// Hash returns the keccak256 hash of b's header.
|
// Hash returns the keccak256 hash of b's header.
|
||||||
// The hash is computed on the first call and cached thereafter.
|
// The hash is computed on the first call and cached thereafter.
|
||||||
func (b *Block) Hash() common.Hash {
|
func (b *Block) Hash() common.Hash {
|
||||||
|
@ -16,23 +16,24 @@ 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"`
|
||||||
Hash common.Hash `json:"hash"`
|
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||||
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
var enc Header
|
var enc Header
|
||||||
enc.ParentHash = h.ParentHash
|
enc.ParentHash = h.ParentHash
|
||||||
@ -51,6 +52,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||||||
enc.MixDigest = h.MixDigest
|
enc.MixDigest = h.MixDigest
|
||||||
enc.Nonce = h.Nonce
|
enc.Nonce = h.Nonce
|
||||||
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
||||||
|
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||||
enc.Hash = h.Hash()
|
enc.Hash = h.Hash()
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
@ -58,22 +60,23 @@ 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"`
|
||||||
}
|
}
|
||||||
var dec Header
|
var dec Header
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@ -139,5 +142,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.BaseFee != nil {
|
if dec.BaseFee != nil {
|
||||||
h.BaseFee = (*big.Int)(dec.BaseFee)
|
h.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
}
|
}
|
||||||
|
if dec.WithdrawalsHash != nil {
|
||||||
|
h.WithdrawalsHash = dec.WithdrawalsHash
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||||||
w.WriteBytes(obj.MixDigest[:])
|
w.WriteBytes(obj.MixDigest[:])
|
||||||
w.WriteBytes(obj.Nonce[:])
|
w.WriteBytes(obj.Nonce[:])
|
||||||
_tmp1 := obj.BaseFee != nil
|
_tmp1 := obj.BaseFee != nil
|
||||||
if _tmp1 {
|
_tmp2 := obj.WithdrawalsHash != nil
|
||||||
|
if _tmp1 || _tmp2 {
|
||||||
if obj.BaseFee == nil {
|
if obj.BaseFee == nil {
|
||||||
w.Write(rlp.EmptyString)
|
w.Write(rlp.EmptyString)
|
||||||
} else {
|
} else {
|
||||||
@ -51,6 +52,13 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
|||||||
w.WriteBigInt(obj.BaseFee)
|
w.WriteBigInt(obj.BaseFee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if _tmp2 {
|
||||||
|
if obj.WithdrawalsHash == nil {
|
||||||
|
w.Write([]byte{0x80})
|
||||||
|
} else {
|
||||||
|
w.WriteBytes(obj.WithdrawalsHash[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
w.ListEnd(_tmp0)
|
w.ListEnd(_tmp0)
|
||||||
return w.Flush()
|
return w.Flush()
|
||||||
}
|
}
|
||||||
|
55
core/types/gen_withdrawal_json.go
Normal file
55
core/types/gen_withdrawal_json.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*withdrawalMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (w Withdrawal) MarshalJSON() ([]byte, error) {
|
||||||
|
type Withdrawal struct {
|
||||||
|
Index hexutil.Uint64 `json:"index"`
|
||||||
|
Validator hexutil.Uint64 `json:"validatorIndex"`
|
||||||
|
Address common.Address `json:"address"`
|
||||||
|
Amount hexutil.Uint64 `json:"amount"`
|
||||||
|
}
|
||||||
|
var enc Withdrawal
|
||||||
|
enc.Index = hexutil.Uint64(w.Index)
|
||||||
|
enc.Validator = hexutil.Uint64(w.Validator)
|
||||||
|
enc.Address = w.Address
|
||||||
|
enc.Amount = hexutil.Uint64(w.Amount)
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (w *Withdrawal) UnmarshalJSON(input []byte) error {
|
||||||
|
type Withdrawal struct {
|
||||||
|
Index *hexutil.Uint64 `json:"index"`
|
||||||
|
Validator *hexutil.Uint64 `json:"validatorIndex"`
|
||||||
|
Address *common.Address `json:"address"`
|
||||||
|
Amount *hexutil.Uint64 `json:"amount"`
|
||||||
|
}
|
||||||
|
var dec Withdrawal
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Index != nil {
|
||||||
|
w.Index = uint64(*dec.Index)
|
||||||
|
}
|
||||||
|
if dec.Validator != nil {
|
||||||
|
w.Validator = uint64(*dec.Validator)
|
||||||
|
}
|
||||||
|
if dec.Address != nil {
|
||||||
|
w.Address = *dec.Address
|
||||||
|
}
|
||||||
|
if dec.Amount != nil {
|
||||||
|
w.Amount = uint64(*dec.Amount)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
20
core/types/gen_withdrawal_rlp.go
Normal file
20
core/types/gen_withdrawal_rlp.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Code generated by rlpgen. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !norlpgen
|
||||||
|
// +build !norlpgen
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/rlp"
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func (obj *Withdrawal) EncodeRLP(_w io.Writer) error {
|
||||||
|
w := rlp.NewEncoderBuffer(_w)
|
||||||
|
_tmp0 := w.List()
|
||||||
|
w.WriteUint64(obj.Index)
|
||||||
|
w.WriteUint64(obj.Validator)
|
||||||
|
w.WriteBytes(obj.Address[:])
|
||||||
|
w.WriteUint64(obj.Amount)
|
||||||
|
w.ListEnd(_tmp0)
|
||||||
|
return w.Flush()
|
||||||
|
}
|
56
core/types/withdrawal.go
Normal file
56
core/types/withdrawal.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type Withdrawal -field-override withdrawalMarshaling -out gen_withdrawal_json.go
|
||||||
|
//go:generate go run ../../rlp/rlpgen -type Withdrawal -out gen_withdrawal_rlp.go
|
||||||
|
|
||||||
|
// Withdrawal represents a validator withdrawal from the consensus layer.
|
||||||
|
type Withdrawal struct {
|
||||||
|
Index uint64 `json:"index"` // monotonically increasing identifier issued by consensus layer
|
||||||
|
Validator uint64 `json:"validatorIndex"` // index of validator associated with withdrawal
|
||||||
|
Address common.Address `json:"address"` // target address for withdrawn ether
|
||||||
|
Amount uint64 `json:"amount"` // value of withdrawal in Gwei
|
||||||
|
}
|
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type withdrawalMarshaling struct {
|
||||||
|
Index hexutil.Uint64
|
||||||
|
Validator hexutil.Uint64
|
||||||
|
Amount hexutil.Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Withdrawals implements DerivableList for withdrawals.
|
||||||
|
type Withdrawals []*Withdrawal
|
||||||
|
|
||||||
|
// Len returns the length of s.
|
||||||
|
func (s Withdrawals) Len() int { return len(s) }
|
||||||
|
|
||||||
|
// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
|
||||||
|
// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
|
||||||
|
// constructed by decoding or via public API in this package.
|
||||||
|
func (s Withdrawals) EncodeIndex(i int, w *bytes.Buffer) {
|
||||||
|
rlp.Encode(w, s[i])
|
||||||
|
}
|
@ -119,7 +119,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||||
|
|
||||||
cfg.State.CreateAccount(address)
|
cfg.State.CreateAccount(address)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
cfg.State.SetCode(address, code)
|
cfg.State.SetCode(address, code)
|
||||||
@ -153,7 +152,6 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
code, address, leftOverGas, err := vmenv.Create(
|
code, address, leftOverGas, err := vmenv.Create(
|
||||||
sender,
|
sender,
|
||||||
|
@ -155,7 +155,19 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
|
|||||||
//
|
//
|
||||||
// If there are payloadAttributes: we try to assemble a block with the payloadAttributes
|
// If there are payloadAttributes: we try to assemble a block with the payloadAttributes
|
||||||
// and return its payloadID.
|
// and return its payloadID.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) {
|
||||||
|
if payloadAttributes != nil && payloadAttributes.Withdrawals != nil {
|
||||||
|
return beacon.STATUS_INVALID, fmt.Errorf("withdrawals not supported in V1")
|
||||||
|
}
|
||||||
|
return api.forkchoiceUpdated(update, payloadAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes.
|
||||||
|
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) {
|
||||||
|
return api.forkchoiceUpdated(update, payloadAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *ConsensusAPI) forkchoiceUpdated(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) {
|
||||||
api.forkchoiceLock.Lock()
|
api.forkchoiceLock.Lock()
|
||||||
defer api.forkchoiceLock.Unlock()
|
defer api.forkchoiceLock.Unlock()
|
||||||
|
|
||||||
@ -285,6 +297,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
|
|||||||
Timestamp: payloadAttributes.Timestamp,
|
Timestamp: payloadAttributes.Timestamp,
|
||||||
FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
|
FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
|
||||||
Random: payloadAttributes.Random,
|
Random: payloadAttributes.Random,
|
||||||
|
Withdrawals: payloadAttributes.Withdrawals,
|
||||||
}
|
}
|
||||||
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
|
||||||
@ -334,7 +347,20 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config beacon.Transit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPayloadV1 returns a cached payload by id.
|
// GetPayloadV1 returns a cached payload by id.
|
||||||
func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
|
func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableData, error) {
|
||||||
|
data, err := api.getPayload(payloadID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data.ExecutionPayload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadV2 returns a cached payload by id.
|
||||||
|
func (api *ConsensusAPI) GetPayloadV2(payloadID beacon.PayloadID) (*beacon.ExecutionPayloadEnvelope, error) {
|
||||||
|
return api.getPayload(payloadID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *ConsensusAPI) getPayload(payloadID beacon.PayloadID) (*beacon.ExecutionPayloadEnvelope, error) {
|
||||||
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
||||||
data := api.localBlocks.get(payloadID)
|
data := api.localBlocks.get(payloadID)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
@ -344,7 +370,19 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.Execu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableData) (beacon.PayloadStatusV1, error) {
|
||||||
|
if params.Withdrawals != nil {
|
||||||
|
return beacon.PayloadStatusV1{Status: beacon.INVALID}, fmt.Errorf("withdrawals not supported in V1")
|
||||||
|
}
|
||||||
|
return api.newPayload(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
|
func (api *ConsensusAPI) NewPayloadV2(params beacon.ExecutableData) (beacon.PayloadStatusV1, error) {
|
||||||
|
return api.newPayload(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *ConsensusAPI) newPayload(params beacon.ExecutableData) (beacon.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
|
||||||
@ -361,7 +399,7 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
|
|||||||
api.newPayloadLock.Lock()
|
api.newPayloadLock.Lock()
|
||||||
defer api.newPayloadLock.Unlock()
|
defer api.newPayloadLock.Unlock()
|
||||||
|
|
||||||
log.Trace("Engine API request received", "method", "ExecutePayload", "number", params.Number, "hash", params.BlockHash)
|
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
|
||||||
block, err := beacon.ExecutableDataToBlock(params)
|
block, err := beacon.ExecutableDataToBlock(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Invalid NewPayload params", "params", params, "error", err)
|
log.Debug("Invalid NewPayload params", "params", params, "error", err)
|
||||||
|
@ -18,6 +18,7 @@ package catalyst
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
@ -26,6 +27,8 @@ 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/consensus"
|
||||||
|
beaconConsensus "github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/beacon"
|
"github.com/ethereum/go-ethereum/core/beacon"
|
||||||
@ -38,6 +41,7 @@ import (
|
|||||||
"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/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -51,8 +55,14 @@ var (
|
|||||||
testBalance = big.NewInt(2e18)
|
testBalance = big.NewInt(2e18)
|
||||||
)
|
)
|
||||||
|
|
||||||
func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
|
func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
|
||||||
config := *params.AllEthashProtocolChanges
|
config := *params.AllEthashProtocolChanges
|
||||||
|
engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker()))
|
||||||
|
if merged {
|
||||||
|
config.TerminalTotalDifficulty = common.Big0
|
||||||
|
config.TerminalTotalDifficultyPassed = true
|
||||||
|
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}},
|
||||||
@ -69,17 +79,21 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
|
|||||||
g.AddTx(tx)
|
g.AddTx(tx)
|
||||||
testNonce++
|
testNonce++
|
||||||
}
|
}
|
||||||
_, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), n, generate)
|
_, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, n, generate)
|
||||||
totalDifficulty := big.NewInt(0)
|
|
||||||
for _, b := range blocks {
|
if !merged {
|
||||||
totalDifficulty.Add(totalDifficulty, b.Difficulty())
|
totalDifficulty := big.NewInt(0)
|
||||||
|
for _, b := range blocks {
|
||||||
|
totalDifficulty.Add(totalDifficulty, b.Difficulty())
|
||||||
|
}
|
||||||
|
config.TerminalTotalDifficulty = totalDifficulty
|
||||||
}
|
}
|
||||||
config.TerminalTotalDifficulty = totalDifficulty
|
|
||||||
return genesis, blocks
|
return genesis, blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEth2AssembleBlock(t *testing.T) {
|
func TestEth2AssembleBlock(t *testing.T) {
|
||||||
genesis, blocks := generatePreMergeChain(10)
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -90,7 +104,7 @@ func TestEth2AssembleBlock(t *testing.T) {
|
|||||||
t.Fatalf("error signing transaction, err=%v", err)
|
t.Fatalf("error signing transaction, err=%v", err)
|
||||||
}
|
}
|
||||||
ethservice.TxPool().AddLocal(tx)
|
ethservice.TxPool().AddLocal(tx)
|
||||||
blockParams := beacon.PayloadAttributesV1{
|
blockParams := beacon.PayloadAttributes{
|
||||||
Timestamp: blocks[9].Time() + 5,
|
Timestamp: blocks[9].Time() + 5,
|
||||||
}
|
}
|
||||||
// The miner needs to pick up on the txs in the pool, so a few retries might be
|
// The miner needs to pick up on the txs in the pool, so a few retries might be
|
||||||
@ -102,7 +116,7 @@ func TestEth2AssembleBlock(t *testing.T) {
|
|||||||
|
|
||||||
// assembleWithTransactions tries to assemble a block, retrying until it has 'want',
|
// assembleWithTransactions tries to assemble a block, retrying until it has 'want',
|
||||||
// number of transactions in it, or it has retried three times.
|
// number of transactions in it, or it has retried three times.
|
||||||
func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1, want int) (execData *beacon.ExecutableDataV1, err error) {
|
func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributes, want int) (execData *beacon.ExecutableData, err error) {
|
||||||
for retries := 3; retries > 0; retries-- {
|
for retries := 3; retries > 0; retries-- {
|
||||||
execData, err = assembleBlock(api, parentHash, params)
|
execData, err = assembleBlock(api, parentHash, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -118,7 +132,7 @@ func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||||
genesis, blocks := generatePreMergeChain(10)
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -126,7 +140,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
|||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
api.eth.TxPool().AddRemotesSync(blocks[9].Transactions())
|
api.eth.TxPool().AddRemotesSync(blocks[9].Transactions())
|
||||||
blockParams := beacon.PayloadAttributesV1{
|
blockParams := beacon.PayloadAttributes{
|
||||||
Timestamp: blocks[8].Time() + 5,
|
Timestamp: blocks[8].Time() + 5,
|
||||||
}
|
}
|
||||||
// The miner needs to pick up on the txs in the pool, so a few retries might be
|
// The miner needs to pick up on the txs in the pool, so a few retries might be
|
||||||
@ -137,7 +151,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
||||||
genesis, blocks := generatePreMergeChain(10)
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -155,7 +169,7 @@ func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEth2PrepareAndGetPayload(t *testing.T) {
|
func TestEth2PrepareAndGetPayload(t *testing.T) {
|
||||||
genesis, blocks := generatePreMergeChain(10)
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
// We need to properly set the terminal total difficulty
|
// We need to properly set the terminal total difficulty
|
||||||
genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
|
genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
@ -165,7 +179,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
|||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
ethservice.TxPool().AddLocals(blocks[9].Transactions())
|
ethservice.TxPool().AddLocals(blocks[9].Transactions())
|
||||||
blockParams := beacon.PayloadAttributesV1{
|
blockParams := beacon.PayloadAttributes{
|
||||||
Timestamp: blocks[8].Time() + 5,
|
Timestamp: blocks[8].Time() + 5,
|
||||||
}
|
}
|
||||||
fcState := beacon.ForkchoiceStateV1{
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
@ -221,7 +235,7 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidPayloadTimestamp(t *testing.T) {
|
func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
var (
|
var (
|
||||||
@ -244,7 +258,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
|||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
|
||||||
params := beacon.PayloadAttributesV1{
|
params := beacon.PayloadAttributes{
|
||||||
Timestamp: test.time,
|
Timestamp: test.time,
|
||||||
Random: crypto.Keccak256Hash([]byte{byte(123)}),
|
Random: crypto.Keccak256Hash([]byte{byte(123)}),
|
||||||
SuggestedFeeRecipient: parent.Coinbase(),
|
SuggestedFeeRecipient: parent.Coinbase(),
|
||||||
@ -265,7 +279,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEth2NewBlock(t *testing.T) {
|
func TestEth2NewBlock(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -288,7 +302,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||||||
tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
|
tx, _ := types.SignTx(types.NewContractCreation(nonce, new(big.Int), 1000000, big.NewInt(2*params.InitialBaseFee), logCode), types.LatestSigner(ethservice.BlockChain().Config()), testKey)
|
||||||
ethservice.TxPool().AddLocal(tx)
|
ethservice.TxPool().AddLocal(tx)
|
||||||
|
|
||||||
execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributesV1{
|
execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributes{
|
||||||
Timestamp: parent.Time() + 5,
|
Timestamp: parent.Time() + 5,
|
||||||
}, 1)
|
}, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -330,7 +344,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||||||
)
|
)
|
||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
|
execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributes{
|
||||||
Timestamp: parent.Time() + 6,
|
Timestamp: parent.Time() + 6,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -367,7 +381,7 @@ func TestEth2DeepReorg(t *testing.T) {
|
|||||||
// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
|
// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
|
||||||
// before the totalTerminalDifficulty threshold
|
// before the totalTerminalDifficulty threshold
|
||||||
/*
|
/*
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
|
genesis, preMergeBlocks := generateMergeChain(core.TriesInMemory * 2, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -442,7 +456,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFullAPI(t *testing.T) {
|
func TestFullAPI(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
var (
|
var (
|
||||||
@ -494,7 +508,7 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Bl
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExchangeTransitionConfig(t *testing.T) {
|
func TestExchangeTransitionConfig(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -555,7 +569,7 @@ We expect
|
|||||||
└── P1''
|
└── P1''
|
||||||
*/
|
*/
|
||||||
func TestNewPayloadOnInvalidChain(t *testing.T) {
|
func TestNewPayloadOnInvalidChain(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -577,7 +591,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
|||||||
})
|
})
|
||||||
ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx})
|
ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx})
|
||||||
var (
|
var (
|
||||||
params = beacon.PayloadAttributesV1{
|
params = beacon.PayloadAttributes{
|
||||||
Timestamp: parent.Time() + 1,
|
Timestamp: parent.Time() + 1,
|
||||||
Random: crypto.Keccak256Hash([]byte{byte(i)}),
|
Random: crypto.Keccak256Hash([]byte{byte(i)}),
|
||||||
SuggestedFeeRecipient: parent.Coinbase(),
|
SuggestedFeeRecipient: parent.Coinbase(),
|
||||||
@ -587,7 +601,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
|||||||
SafeBlockHash: common.Hash{},
|
SafeBlockHash: common.Hash{},
|
||||||
FinalizedBlockHash: common.Hash{},
|
FinalizedBlockHash: common.Hash{},
|
||||||
}
|
}
|
||||||
payload *beacon.ExecutableDataV1
|
payload *beacon.ExecutableData
|
||||||
resp beacon.ForkChoiceResponse
|
resp beacon.ForkChoiceResponse
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
@ -634,22 +648,23 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributesV1) (*beacon.ExecutableDataV1, error) {
|
func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.PayloadAttributes) (*beacon.ExecutableData, error) {
|
||||||
args := &miner.BuildPayloadArgs{
|
args := &miner.BuildPayloadArgs{
|
||||||
Parent: parentHash,
|
Parent: parentHash,
|
||||||
Timestamp: params.Timestamp,
|
Timestamp: params.Timestamp,
|
||||||
FeeRecipient: params.SuggestedFeeRecipient,
|
FeeRecipient: params.SuggestedFeeRecipient,
|
||||||
Random: params.Random,
|
Random: params.Random,
|
||||||
|
Withdrawals: params.Withdrawals,
|
||||||
}
|
}
|
||||||
payload, err := api.eth.Miner().BuildPayload(args)
|
payload, err := api.eth.Miner().BuildPayload(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return payload.ResolveFull(), nil
|
return payload.ResolveFull().ExecutionPayload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyBlocks(t *testing.T) {
|
func TestEmptyBlocks(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -708,8 +723,8 @@ func TestEmptyBlocks(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 {
|
func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableData {
|
||||||
params := beacon.PayloadAttributesV1{
|
params := beacon.PayloadAttributes{
|
||||||
Timestamp: parent.Time() + 1,
|
Timestamp: parent.Time() + 1,
|
||||||
Random: crypto.Keccak256Hash([]byte{byte(1)}),
|
Random: crypto.Keccak256Hash([]byte{byte(1)}),
|
||||||
SuggestedFeeRecipient: parent.Coinbase(),
|
SuggestedFeeRecipient: parent.Coinbase(),
|
||||||
@ -724,7 +739,7 @@ func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon
|
|||||||
|
|
||||||
// setBlockhash sets the blockhash of a modified ExecutableData.
|
// setBlockhash sets the blockhash of a modified ExecutableData.
|
||||||
// Can be used to make modified payloads look valid.
|
// Can be used to make modified payloads look valid.
|
||||||
func setBlockhash(data *beacon.ExecutableDataV1) *beacon.ExecutableDataV1 {
|
func setBlockhash(data *beacon.ExecutableData) *beacon.ExecutableData {
|
||||||
txs, _ := decodeTransactions(data.Transactions)
|
txs, _ := decodeTransactions(data.Transactions)
|
||||||
number := big.NewInt(0)
|
number := big.NewInt(0)
|
||||||
number.SetUint64(data.Number)
|
number.SetUint64(data.Number)
|
||||||
@ -764,7 +779,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
|||||||
|
|
||||||
func TestTrickRemoteBlockCache(t *testing.T) {
|
func TestTrickRemoteBlockCache(t *testing.T) {
|
||||||
// Setup two nodes
|
// Setup two nodes
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
|
nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
|
||||||
nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
|
nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer nodeA.Close()
|
defer nodeA.Close()
|
||||||
@ -783,7 +798,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
|
|||||||
setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
|
setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
|
||||||
commonAncestor = ethserviceA.BlockChain().CurrentBlock()
|
commonAncestor = ethserviceA.BlockChain().CurrentBlock()
|
||||||
|
|
||||||
var invalidChain []*beacon.ExecutableDataV1
|
var invalidChain []*beacon.ExecutableData
|
||||||
// create a valid payload (P1)
|
// create a valid payload (P1)
|
||||||
//payload1 := getNewPayload(t, apiA, commonAncestor)
|
//payload1 := getNewPayload(t, apiA, commonAncestor)
|
||||||
//invalidChain = append(invalidChain, payload1)
|
//invalidChain = append(invalidChain, payload1)
|
||||||
@ -827,7 +842,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidBloom(t *testing.T) {
|
func TestInvalidBloom(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
ethservice.Merger().ReachTTD()
|
ethservice.Merger().ReachTTD()
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
@ -851,12 +866,12 @@ func TestInvalidBloom(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(100)
|
genesis, preMergeBlocks := generateMergeChain(100, false)
|
||||||
|
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
|
genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice)
|
api = NewConsensusAPI(ethservice)
|
||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
@ -887,7 +902,7 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error preparing payload, err=%v", err)
|
t.Fatalf("error preparing payload, err=%v", err)
|
||||||
}
|
}
|
||||||
data := *payload.Resolve()
|
data := *payload.Resolve().ExecutionPayload
|
||||||
resp2, err := api.NewPayloadV1(data)
|
resp2, err := api.NewPayloadV1(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error sending NewPayload, err=%v", err)
|
t.Fatalf("error sending NewPayload, err=%v", err)
|
||||||
@ -901,7 +916,7 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
|||||||
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
||||||
// well even of the caller is not being 'serial'.
|
// well even of the caller is not being 'serial'.
|
||||||
func TestSimultaneousNewBlock(t *testing.T) {
|
func TestSimultaneousNewBlock(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
@ -910,7 +925,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
|||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
)
|
)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributesV1{
|
execData, err := assembleBlock(api, parent.Hash(), &beacon.PayloadAttributes{
|
||||||
Timestamp: parent.Time() + 5,
|
Timestamp: parent.Time() + 5,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -984,3 +999,117 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
|||||||
parent = block
|
parent = block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWithdrawals creates and verifies two post-Shanghai blocks. The first
|
||||||
|
// includes zero withdrawals and the second includes two.
|
||||||
|
func TestWithdrawals(t *testing.T) {
|
||||||
|
genesis, blocks := generateMergeChain(10, true)
|
||||||
|
// Set shanghai time to last block + 5 seconds (first post-merge block)
|
||||||
|
time := blocks[len(blocks)-1].Time() + 5
|
||||||
|
genesis.Config.ShanghaiTime = &time
|
||||||
|
|
||||||
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
|
ethservice.Merger().ReachTTD()
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := NewConsensusAPI(ethservice)
|
||||||
|
|
||||||
|
// 10: Build Shanghai block with no withdrawals.
|
||||||
|
parent := ethservice.BlockChain().CurrentHeader()
|
||||||
|
blockParams := beacon.PayloadAttributes{
|
||||||
|
Timestamp: parent.Time + 5,
|
||||||
|
Withdrawals: make([]*types.Withdrawal, 0),
|
||||||
|
}
|
||||||
|
fcState := beacon.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: parent.Hash(),
|
||||||
|
}
|
||||||
|
resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error preparing payload, err=%v", err)
|
||||||
|
}
|
||||||
|
if resp.PayloadStatus.Status != beacon.VALID {
|
||||||
|
t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, beacon.VALID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10: verify state root is the same as parent
|
||||||
|
payloadID := (&miner.BuildPayloadArgs{
|
||||||
|
Parent: fcState.HeadBlockHash,
|
||||||
|
Timestamp: blockParams.Timestamp,
|
||||||
|
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||||
|
Random: blockParams.Random,
|
||||||
|
}).Id()
|
||||||
|
execData, err := api.GetPayloadV2(payloadID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error getting payload, err=%v", err)
|
||||||
|
}
|
||||||
|
if execData.ExecutionPayload.StateRoot != parent.Root {
|
||||||
|
t.Fatalf("mismatch state roots (got: %s, want: %s)", execData.ExecutionPayload.StateRoot, blocks[8].Root())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10: verify locally built block
|
||||||
|
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
|
||||||
|
t.Fatalf("error validating payload: %v", err)
|
||||||
|
} else if status.Status != beacon.VALID {
|
||||||
|
t.Fatalf("invalid payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: build shanghai block with withdrawal
|
||||||
|
aa := common.Address{0xaa}
|
||||||
|
bb := common.Address{0xbb}
|
||||||
|
blockParams = beacon.PayloadAttributes{
|
||||||
|
Timestamp: execData.ExecutionPayload.Timestamp + 5,
|
||||||
|
Withdrawals: []*types.Withdrawal{
|
||||||
|
{
|
||||||
|
Index: 0,
|
||||||
|
Address: aa,
|
||||||
|
Amount: 32,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Index: 1,
|
||||||
|
Address: bb,
|
||||||
|
Amount: 33,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||||
|
_, err = api.ForkchoiceUpdatedV2(fcState, &blockParams)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error preparing payload, err=%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: verify locally build block.
|
||||||
|
payloadID = (&miner.BuildPayloadArgs{
|
||||||
|
Parent: fcState.HeadBlockHash,
|
||||||
|
Timestamp: blockParams.Timestamp,
|
||||||
|
FeeRecipient: blockParams.SuggestedFeeRecipient,
|
||||||
|
Random: blockParams.Random,
|
||||||
|
}).Id()
|
||||||
|
execData, err = api.GetPayloadV2(payloadID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error getting payload, err=%v", err)
|
||||||
|
}
|
||||||
|
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
|
||||||
|
t.Fatalf("error validating payload: %v", err)
|
||||||
|
} else if status.Status != beacon.VALID {
|
||||||
|
t.Fatalf("invalid payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: set block as head.
|
||||||
|
fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash
|
||||||
|
_, err = api.ForkchoiceUpdatedV2(fcState, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error preparing payload, err=%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11: verify withdrawals were processed.
|
||||||
|
db, _, err := ethservice.APIBackend.StateAndHeaderByNumber(context.Background(), rpc.BlockNumber(execData.ExecutionPayload.Number))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to load db: %v", err)
|
||||||
|
}
|
||||||
|
for i, w := range blockParams.Withdrawals {
|
||||||
|
// w.Amount is in gwei, balance in wei
|
||||||
|
if db.GetBalance(w.Address).Uint64() != w.Amount*params.GWei {
|
||||||
|
t.Fatalf("failed to process withdrawal %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -70,7 +70,7 @@ func (q *payloadQueue) put(id beacon.PayloadID, payload *miner.Payload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get retrieves a previously stored payload item or nil if it does not exist.
|
// get retrieves a previously stored payload item or nil if it does not exist.
|
||||||
func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 {
|
func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutionPayloadEnvelope {
|
||||||
q.lock.RLock()
|
q.lock.RLock()
|
||||||
defer q.lock.RUnlock()
|
defer q.lock.RUnlock()
|
||||||
|
|
||||||
|
@ -1548,7 +1548,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
|||||||
)
|
)
|
||||||
blocks := make([]*types.Block, len(results))
|
blocks := make([]*types.Block, len(results))
|
||||||
for i, result := range results {
|
for i, result := range results {
|
||||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals)
|
||||||
}
|
}
|
||||||
// Downloaded blocks are always regarded as trusted after the
|
// Downloaded blocks are always regarded as trusted after the
|
||||||
// transition. Because the downloaded chain is guided by the
|
// transition. Because the downloaded chain is guided by the
|
||||||
@ -1748,7 +1748,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
|
|||||||
blocks := make([]*types.Block, len(results))
|
blocks := make([]*types.Block, len(results))
|
||||||
receipts := make([]types.Receipts, len(results))
|
receipts := make([]types.Receipts, len(results))
|
||||||
for i, result := range results {
|
for i, result := range results {
|
||||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals)
|
||||||
receipts[i] = result.Receipts
|
receipts[i] = result.Receipts
|
||||||
}
|
}
|
||||||
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
|
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
|
||||||
@ -1759,7 +1759,7 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
||||||
block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles).WithWithdrawals(result.Withdrawals)
|
||||||
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
|
log.Debug("Committing snap sync pivot as new head", "number", block.Number(), "hash", block.Hash())
|
||||||
|
|
||||||
// Commit the pivot block as the new head, will require full sync from here on
|
// Commit the pivot block as the new head, will require full sync from here on
|
||||||
|
@ -273,8 +273,9 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
|||||||
rlp.DecodeBytes(blob, bodies[i])
|
rlp.DecodeBytes(blob, bodies[i])
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
txsHashes = make([]common.Hash, len(bodies))
|
txsHashes = make([]common.Hash, len(bodies))
|
||||||
uncleHashes = make([]common.Hash, len(bodies))
|
uncleHashes = make([]common.Hash, len(bodies))
|
||||||
|
withdrawalHashes = make([]common.Hash, len(bodies))
|
||||||
)
|
)
|
||||||
hasher := trie.NewStackTrie(nil)
|
hasher := trie.NewStackTrie(nil)
|
||||||
for i, body := range bodies {
|
for i, body := range bodies {
|
||||||
@ -287,7 +288,7 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
|||||||
res := ð.Response{
|
res := ð.Response{
|
||||||
Req: req,
|
Req: req,
|
||||||
Res: (*eth.BlockBodiesPacket)(&bodies),
|
Res: (*eth.BlockBodiesPacket)(&bodies),
|
||||||
Meta: [][]common.Hash{txsHashes, uncleHashes},
|
Meta: [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes},
|
||||||
Time: 1,
|
Time: 1,
|
||||||
Done: make(chan error, 1), // Ignore the returned status
|
Done: make(chan error, 1), // Ignore the returned status
|
||||||
}
|
}
|
||||||
|
@ -89,10 +89,10 @@ func (q *bodyQueue) request(peer *peerConnection, req *fetchRequest, resCh chan
|
|||||||
// deliver is responsible for taking a generic response packet from the concurrent
|
// deliver is responsible for taking a generic response packet from the concurrent
|
||||||
// fetcher, unpacking the body data and delivering it to the downloader's queue.
|
// fetcher, unpacking the body data and delivering it to the downloader's queue.
|
||||||
func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
|
func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
|
||||||
txs, uncles := packet.Res.(*eth.BlockBodiesPacket).Unpack()
|
txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesPacket).Unpack()
|
||||||
hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes}
|
hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes}
|
||||||
|
|
||||||
accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1])
|
accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2])
|
||||||
switch {
|
switch {
|
||||||
case err == nil && len(txs) == 0:
|
case err == nil && len(txs) == 0:
|
||||||
peer.log.Trace("Requested bodies delivered")
|
peer.log.Trace("Requested bodies delivered")
|
||||||
|
@ -67,6 +67,7 @@ type fetchResult struct {
|
|||||||
Uncles []*types.Header
|
Uncles []*types.Header
|
||||||
Transactions types.Transactions
|
Transactions types.Transactions
|
||||||
Receipts types.Receipts
|
Receipts types.Receipts
|
||||||
|
Withdrawals types.Withdrawals
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
|
func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
|
||||||
@ -764,7 +765,9 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []comm
|
|||||||
// DeliverBodies injects a block body retrieval response into the results queue.
|
// DeliverBodies injects a block body retrieval response into the results queue.
|
||||||
// The method returns the number of blocks bodies accepted from the delivery and
|
// The method returns the number of blocks bodies accepted from the delivery and
|
||||||
// also wakes any threads waiting for data delivery.
|
// also wakes any threads waiting for data delivery.
|
||||||
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash, uncleLists [][]*types.Header, uncleListHashes []common.Hash) (int, error) {
|
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash,
|
||||||
|
uncleLists [][]*types.Header, uncleListHashes []common.Hash,
|
||||||
|
withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash) (int, error) {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
@ -775,12 +778,19 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
|
|||||||
if uncleListHashes[index] != header.UncleHash {
|
if uncleListHashes[index] != header.UncleHash {
|
||||||
return errInvalidBody
|
return errInvalidBody
|
||||||
}
|
}
|
||||||
|
if header.WithdrawalsHash == nil {
|
||||||
|
// discard any withdrawals if we don't have a withdrawal hash set
|
||||||
|
withdrawalLists[index] = nil
|
||||||
|
} else if withdrawalListHashes[index] != *header.WithdrawalsHash {
|
||||||
|
return errInvalidBody
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
reconstruct := func(index int, result *fetchResult) {
|
reconstruct := func(index int, result *fetchResult) {
|
||||||
result.Transactions = txLists[index]
|
result.Transactions = txLists[index]
|
||||||
result.Uncles = uncleLists[index]
|
result.Uncles = uncleLists[index]
|
||||||
|
result.Withdrawals = withdrawalLists[index]
|
||||||
result.SetBodyDone()
|
result.SetBodyDone()
|
||||||
}
|
}
|
||||||
return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
|
return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
|
||||||
|
@ -339,7 +339,7 @@ func XTestDelivery(t *testing.T) {
|
|||||||
uncleHashes[i] = types.CalcUncleHash(uncles)
|
uncleHashes[i] = types.CalcUncleHash(uncles)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
_, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes)
|
_, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("delivered %d bodies %v\n", len(txset), err)
|
fmt.Printf("delivered %d bodies %v\n", len(txset), err)
|
||||||
}
|
}
|
||||||
|
@ -540,8 +540,8 @@ func (f *BlockFetcher) loop() {
|
|||||||
select {
|
select {
|
||||||
case res := <-resCh:
|
case res := <-resCh:
|
||||||
res.Done <- nil
|
res.Done <- nil
|
||||||
|
// Ignoring withdrawals here, since the block fetcher is not used post-merge.
|
||||||
txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack()
|
txs, uncles, _ := res.Res.(*eth.BlockBodiesPacket).Unpack()
|
||||||
f.FilterBodies(peer, txs, uncles, time.Now())
|
f.FilterBodies(peer, txs, uncles, time.Now())
|
||||||
|
|
||||||
case <-timeout.C:
|
case <-timeout.C:
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
@ -45,6 +47,8 @@ var (
|
|||||||
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func u64(val uint64) *uint64 { return &val }
|
||||||
|
|
||||||
// testBackend is a mock implementation of the live Ethereum message handler. Its
|
// testBackend is a mock implementation of the live Ethereum message handler. Its
|
||||||
// purpose is to allow testing the request/reply workflows and wire serialization
|
// purpose is to allow testing the request/reply workflows and wire serialization
|
||||||
// in the `eth` protocol without actually doing any data processing.
|
// in the `eth` protocol without actually doing any data processing.
|
||||||
@ -56,21 +60,53 @@ type testBackend struct {
|
|||||||
|
|
||||||
// newTestBackend creates an empty chain and wraps it into a mock backend.
|
// newTestBackend creates an empty chain and wraps it into a mock backend.
|
||||||
func newTestBackend(blocks int) *testBackend {
|
func newTestBackend(blocks int) *testBackend {
|
||||||
return newTestBackendWithGenerator(blocks, nil)
|
return newTestBackendWithGenerator(blocks, false, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTestBackend creates a chain with a number of explicitly defined blocks and
|
// newTestBackend creates a chain with a number of explicitly defined blocks and
|
||||||
// wraps it into a mock backend.
|
// wraps it into a mock backend.
|
||||||
func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)) *testBackend {
|
func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
|
||||||
// Create a database pre-initialize with a genesis block
|
var (
|
||||||
db := rawdb.NewMemoryDatabase()
|
// Create a database pre-initialize with a genesis block
|
||||||
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
config = params.TestChainConfig
|
||||||
|
engine consensus.Engine = ethash.NewFaker()
|
||||||
|
)
|
||||||
|
|
||||||
|
if shanghai {
|
||||||
|
config = ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
DAOForkBlock: nil,
|
||||||
|
DAOForkSupport: true,
|
||||||
|
EIP150Block: big.NewInt(0),
|
||||||
|
EIP155Block: big.NewInt(0),
|
||||||
|
EIP158Block: big.NewInt(0),
|
||||||
|
ByzantiumBlock: big.NewInt(0),
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
|
IstanbulBlock: big.NewInt(0),
|
||||||
|
MuirGlacierBlock: big.NewInt(0),
|
||||||
|
BerlinBlock: big.NewInt(0),
|
||||||
|
LondonBlock: big.NewInt(0),
|
||||||
|
ArrowGlacierBlock: big.NewInt(0),
|
||||||
|
GrayGlacierBlock: big.NewInt(0),
|
||||||
|
MergeNetsplitBlock: big.NewInt(0),
|
||||||
|
ShanghaiTime: u64(0),
|
||||||
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
Ethash: new(params.EthashConfig),
|
||||||
|
}
|
||||||
|
engine = beacon.NewFaker()
|
||||||
|
}
|
||||||
|
|
||||||
gspec := &core.Genesis{
|
gspec := &core.Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: config,
|
||||||
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
|
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
|
||||||
}
|
}
|
||||||
chain, _ := core.NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chain, _ := core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||||
|
|
||||||
_, bs, _ := core.GenerateChainWithGenesis(gspec, ethash.NewFaker(), blocks, generator)
|
_, bs, _ := core.GenerateChainWithGenesis(gspec, engine, blocks, generator)
|
||||||
if _, err := chain.InsertChain(bs); err != nil {
|
if _, err := chain.InsertChain(bs); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -305,7 +341,17 @@ func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) }
|
|||||||
func testGetBlockBodies(t *testing.T, protocol uint) {
|
func testGetBlockBodies(t *testing.T, protocol uint) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
backend := newTestBackend(maxBodiesServe + 15)
|
gen := func(n int, g *core.BlockGen) {
|
||||||
|
if n%2 == 0 {
|
||||||
|
w := &types.Withdrawal{
|
||||||
|
Address: common.Address{0xaa},
|
||||||
|
Amount: 42,
|
||||||
|
}
|
||||||
|
g.AddWithdrawal(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend := newTestBackendWithGenerator(maxBodiesServe+15, true, gen)
|
||||||
defer backend.close()
|
defer backend.close()
|
||||||
|
|
||||||
peer, _ := newTestPeer("peer", protocol, backend)
|
peer, _ := newTestPeer("peer", protocol, backend)
|
||||||
@ -355,7 +401,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
|
|||||||
block := backend.chain.GetBlockByNumber(uint64(num))
|
block := backend.chain.GetBlockByNumber(uint64(num))
|
||||||
hashes = append(hashes, block.Hash())
|
hashes = append(hashes, block.Hash())
|
||||||
if len(bodies) < tt.expected {
|
if len(bodies) < tt.expected {
|
||||||
bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
|
bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -365,9 +411,10 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
|
|||||||
hashes = append(hashes, hash)
|
hashes = append(hashes, hash)
|
||||||
if tt.available[j] && len(bodies) < tt.expected {
|
if tt.available[j] && len(bodies) < tt.expected {
|
||||||
block := backend.chain.GetBlockByHash(hash)
|
block := backend.chain.GetBlockByHash(hash)
|
||||||
bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles()})
|
bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the hash request and verify the response
|
// Send the hash request and verify the response
|
||||||
p2p.Send(peer.app, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
|
p2p.Send(peer.app, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
|
||||||
RequestId: 123,
|
RequestId: 123,
|
||||||
@ -426,7 +473,7 @@ func testGetNodeData(t *testing.T, protocol uint, drop bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
backend := newTestBackendWithGenerator(4, generator)
|
backend := newTestBackendWithGenerator(4, false, generator)
|
||||||
defer backend.close()
|
defer backend.close()
|
||||||
|
|
||||||
peer, _ := newTestPeer("peer", protocol, backend)
|
peer, _ := newTestPeer("peer", protocol, backend)
|
||||||
@ -544,7 +591,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
backend := newTestBackendWithGenerator(4, generator)
|
backend := newTestBackendWithGenerator(4, false, generator)
|
||||||
defer backend.close()
|
defer backend.close()
|
||||||
|
|
||||||
peer, _ := newTestPeer("peer", protocol, backend)
|
peer, _ := newTestPeer("peer", protocol, backend)
|
||||||
|
@ -379,15 +379,19 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
|||||||
}
|
}
|
||||||
metadata := func() interface{} {
|
metadata := func() interface{} {
|
||||||
var (
|
var (
|
||||||
txsHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
txsHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
||||||
uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
||||||
|
withdrawalHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
||||||
)
|
)
|
||||||
hasher := trie.NewStackTrie(nil)
|
hasher := trie.NewStackTrie(nil)
|
||||||
for i, body := range res.BlockBodiesPacket {
|
for i, body := range res.BlockBodiesPacket {
|
||||||
txsHashes[i] = types.DeriveSha(types.Transactions(body.Transactions), hasher)
|
txsHashes[i] = types.DeriveSha(types.Transactions(body.Transactions), hasher)
|
||||||
uncleHashes[i] = types.CalcUncleHash(body.Uncles)
|
uncleHashes[i] = types.CalcUncleHash(body.Uncles)
|
||||||
|
if body.Withdrawals != nil {
|
||||||
|
withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return [][]common.Hash{txsHashes, uncleHashes}
|
return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes}
|
||||||
}
|
}
|
||||||
return peer.dispatchResponse(&Response{
|
return peer.dispatchResponse(&Response{
|
||||||
id: res.RequestId,
|
id: res.RequestId,
|
||||||
|
@ -239,19 +239,22 @@ type BlockBodiesRLPPacket66 struct {
|
|||||||
type BlockBody struct {
|
type BlockBody struct {
|
||||||
Transactions []*types.Transaction // Transactions contained within a block
|
Transactions []*types.Transaction // Transactions contained within a block
|
||||||
Uncles []*types.Header // Uncles contained within a block
|
Uncles []*types.Header // Uncles contained within a block
|
||||||
|
Withdrawals []*types.Withdrawal `rlp:"optional"` // Withdrawals contained within a block
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack retrieves the transactions and uncles from the range packet and returns
|
// Unpack retrieves the transactions and uncles from the range packet and returns
|
||||||
// them in a split flat format that's more consistent with the internal data structures.
|
// them in a split flat format that's more consistent with the internal data structures.
|
||||||
func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header) {
|
func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) {
|
||||||
|
// TODO(matt): add support for withdrawals to fetchers
|
||||||
var (
|
var (
|
||||||
txset = make([][]*types.Transaction, len(*p))
|
txset = make([][]*types.Transaction, len(*p))
|
||||||
uncleset = make([][]*types.Header, len(*p))
|
uncleset = make([][]*types.Header, len(*p))
|
||||||
|
withdrawalset = make([][]*types.Withdrawal, len(*p))
|
||||||
)
|
)
|
||||||
for i, body := range *p {
|
for i, body := range *p {
|
||||||
txset[i], uncleset[i] = body.Transactions, body.Uncles
|
txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals
|
||||||
}
|
}
|
||||||
return txset, uncleset
|
return txset, uncleset, withdrawalset
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNodeDataPacket represents a trie node data query.
|
// GetNodeDataPacket represents a trie node data query.
|
||||||
|
@ -1214,6 +1214,10 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
|||||||
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
|
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if head.WithdrawalsHash != nil {
|
||||||
|
result["withdrawalsRoot"] = head.WithdrawalsHash
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1242,6 +1246,8 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fields["transactions"] = transactions
|
fields["transactions"] = transactions
|
||||||
|
// inclTx also expands withdrawals
|
||||||
|
fields["withdrawals"] = block.Withdrawals()
|
||||||
}
|
}
|
||||||
uncles := block.Uncles()
|
uncles := block.Uncles()
|
||||||
uncleHashes := make([]common.Hash, len(uncles))
|
uncleHashes := make([]common.Hash, len(uncles))
|
||||||
|
@ -70,7 +70,7 @@ func NewConsensusAPI(les *les.LightEthereum) *ConsensusAPI {
|
|||||||
//
|
//
|
||||||
// If there are payloadAttributes: we return an error since block creation is not
|
// If there are payloadAttributes: we return an error since block creation is not
|
||||||
// supported in les mode.
|
// supported in les mode.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) {
|
||||||
if heads.HeadBlockHash == (common.Hash{}) {
|
if heads.HeadBlockHash == (common.Hash{}) {
|
||||||
log.Warn("Forkchoice requested update to zero hash")
|
log.Warn("Forkchoice requested update to zero hash")
|
||||||
return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
|
return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
|
||||||
@ -100,12 +100,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads beacon.ForkchoiceStateV1, pay
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPayloadV1 returns a cached payload by id. It's not supported in les mode.
|
// GetPayloadV1 returns a cached payload by id. It's not supported in les mode.
|
||||||
func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableDataV1, error) {
|
func (api *ConsensusAPI) GetPayloadV1(payloadID beacon.PayloadID) (*beacon.ExecutableData, error) {
|
||||||
return nil, beacon.GenericServerError.With(errors.New("not supported in light client mode"))
|
return nil, beacon.GenericServerError.With(errors.New("not supported in light client mode"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// ExecutePayloadV1 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableDataV1) (beacon.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) ExecutePayloadV1(params beacon.ExecutableData) (beacon.PayloadStatusV1, error) {
|
||||||
block, err := beacon.ExecutableDataToBlock(params)
|
block, err := beacon.ExecutableDataToBlock(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.invalid(), err
|
return api.invalid(), err
|
||||||
|
@ -130,7 +130,7 @@ func TestExecutePayloadV1(t *testing.T) {
|
|||||||
BaseFee: block.BaseFee(),
|
BaseFee: block.BaseFee(),
|
||||||
}, nil, nil, nil, trie.NewStackTrie(nil))
|
}, nil, nil, nil, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
_, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{
|
_, err := api.ExecutePayloadV1(beacon.ExecutableData{
|
||||||
ParentHash: fakeBlock.ParentHash(),
|
ParentHash: fakeBlock.ParentHash(),
|
||||||
FeeRecipient: fakeBlock.Coinbase(),
|
FeeRecipient: fakeBlock.Coinbase(),
|
||||||
StateRoot: fakeBlock.Root(),
|
StateRoot: fakeBlock.Root(),
|
||||||
|
@ -34,10 +34,11 @@ import (
|
|||||||
// Check engine-api specification for more details.
|
// Check engine-api specification for more details.
|
||||||
// https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1
|
// https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1
|
||||||
type BuildPayloadArgs struct {
|
type BuildPayloadArgs struct {
|
||||||
Parent common.Hash // The parent block to build payload on top
|
Parent common.Hash // The parent block to build payload on top
|
||||||
Timestamp uint64 // The provided timestamp of generated payload
|
Timestamp uint64 // The provided timestamp of generated payload
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@ -107,7 +108,7 @@ func (payload *Payload) update(block *types.Block, fees *big.Int, elapsed time.D
|
|||||||
|
|
||||||
// Resolve returns the latest built payload and also terminates the background
|
// Resolve returns the latest built payload and also terminates the background
|
||||||
// thread for updating payload. It's safe to be called multiple times.
|
// thread for updating payload. It's safe to be called multiple times.
|
||||||
func (payload *Payload) Resolve() *beacon.ExecutableDataV1 {
|
func (payload *Payload) Resolve() *beacon.ExecutionPayloadEnvelope {
|
||||||
payload.lock.Lock()
|
payload.lock.Lock()
|
||||||
defer payload.lock.Unlock()
|
defer payload.lock.Unlock()
|
||||||
|
|
||||||
@ -117,23 +118,23 @@ func (payload *Payload) Resolve() *beacon.ExecutableDataV1 {
|
|||||||
close(payload.stop)
|
close(payload.stop)
|
||||||
}
|
}
|
||||||
if payload.full != nil {
|
if payload.full != nil {
|
||||||
return beacon.BlockToExecutableData(payload.full)
|
return beacon.BlockToExecutableData(payload.full, payload.fullFees)
|
||||||
}
|
}
|
||||||
return beacon.BlockToExecutableData(payload.empty)
|
return beacon.BlockToExecutableData(payload.empty, big.NewInt(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveEmpty is basically identical to Resolve, but it expects empty block only.
|
// ResolveEmpty is basically identical to Resolve, but it expects empty block only.
|
||||||
// It's only used in tests.
|
// It's only used in tests.
|
||||||
func (payload *Payload) ResolveEmpty() *beacon.ExecutableDataV1 {
|
func (payload *Payload) ResolveEmpty() *beacon.ExecutionPayloadEnvelope {
|
||||||
payload.lock.Lock()
|
payload.lock.Lock()
|
||||||
defer payload.lock.Unlock()
|
defer payload.lock.Unlock()
|
||||||
|
|
||||||
return beacon.BlockToExecutableData(payload.empty)
|
return beacon.BlockToExecutableData(payload.empty, big.NewInt(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveFull is basically identical to Resolve, but it expects full block only.
|
// ResolveFull is basically identical to Resolve, but it expects full block only.
|
||||||
// It's only used in tests.
|
// It's only used in tests.
|
||||||
func (payload *Payload) ResolveFull() *beacon.ExecutableDataV1 {
|
func (payload *Payload) ResolveFull() *beacon.ExecutionPayloadEnvelope {
|
||||||
payload.lock.Lock()
|
payload.lock.Lock()
|
||||||
defer payload.lock.Unlock()
|
defer payload.lock.Unlock()
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ func (payload *Payload) ResolveFull() *beacon.ExecutableDataV1 {
|
|||||||
}
|
}
|
||||||
payload.cond.Wait()
|
payload.cond.Wait()
|
||||||
}
|
}
|
||||||
return beacon.BlockToExecutableData(payload.full)
|
return beacon.BlockToExecutableData(payload.full, payload.fullFees)
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildPayload builds the payload according to the provided parameters.
|
// buildPayload builds the payload according to the provided parameters.
|
||||||
@ -153,7 +154,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
|||||||
// Build the initial version with no transaction included. It should be fast
|
// Build the initial version with no transaction included. It should be fast
|
||||||
// enough to run. The empty payload can at least make sure there is something
|
// enough to run. The empty payload can at least make sure there is something
|
||||||
// to deliver for not missing slot.
|
// to deliver for not missing slot.
|
||||||
empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, true)
|
empty, _, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -177,7 +178,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
|||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, false)
|
block, fees, err := w.getSealingBlock(args.Parent, args.Timestamp, args.FeeRecipient, args.Random, args.Withdrawals, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
payload.update(block, fees, time.Since(start))
|
payload.update(block, fees, time.Since(start))
|
||||||
}
|
}
|
||||||
|
@ -47,20 +47,21 @@ func TestBuildPayload(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to build payload %v", err)
|
t.Fatalf("Failed to build payload %v", err)
|
||||||
}
|
}
|
||||||
verify := func(data *beacon.ExecutableDataV1, txs int) {
|
verify := func(outer *beacon.ExecutionPayloadEnvelope, txs int) {
|
||||||
if data.ParentHash != b.chain.CurrentBlock().Hash() {
|
payload := outer.ExecutionPayload
|
||||||
|
if payload.ParentHash != b.chain.CurrentBlock().Hash() {
|
||||||
t.Fatal("Unexpect parent hash")
|
t.Fatal("Unexpect parent hash")
|
||||||
}
|
}
|
||||||
if data.Random != (common.Hash{}) {
|
if payload.Random != (common.Hash{}) {
|
||||||
t.Fatal("Unexpect random value")
|
t.Fatal("Unexpect random value")
|
||||||
}
|
}
|
||||||
if data.Timestamp != timestamp {
|
if payload.Timestamp != timestamp {
|
||||||
t.Fatal("Unexpect timestamp")
|
t.Fatal("Unexpect timestamp")
|
||||||
}
|
}
|
||||||
if data.FeeRecipient != recipient {
|
if payload.FeeRecipient != recipient {
|
||||||
t.Fatal("Unexpect fee recipient")
|
t.Fatal("Unexpect fee recipient")
|
||||||
}
|
}
|
||||||
if len(data.Transactions) != txs {
|
if len(payload.Transactions) != txs {
|
||||||
t.Fatal("Unexpect transaction set")
|
t.Fatal("Unexpect transaction set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*beacon.ExecutableDataV1, error) {
|
func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) (*beacon.ExecutableData, error) {
|
||||||
if n.typ != eth2MiningNode {
|
if n.typ != eth2MiningNode {
|
||||||
return nil, errors.New("invalid node type")
|
return nil, errors.New("invalid node type")
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
|
|||||||
if timestamp <= parentTimestamp {
|
if timestamp <= parentTimestamp {
|
||||||
timestamp = parentTimestamp + 1
|
timestamp = parentTimestamp + 1
|
||||||
}
|
}
|
||||||
payloadAttribute := beacon.PayloadAttributesV1{
|
payloadAttribute := beacon.PayloadAttributes{
|
||||||
Timestamp: timestamp,
|
Timestamp: timestamp,
|
||||||
Random: common.Hash{},
|
Random: common.Hash{},
|
||||||
SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"),
|
SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"),
|
||||||
@ -168,7 +168,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
|
|||||||
return n.api.GetPayloadV1(*payload.PayloadID)
|
return n.api.GetPayloadV1(*payload.PayloadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error {
|
func (n *ethNode) insertBlock(eb beacon.ExecutableData) error {
|
||||||
if !eth2types(n.typ) {
|
if !eth2types(n.typ) {
|
||||||
return errors.New("invalid node type")
|
return errors.New("invalid node type")
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ func (n *ethNode) insertBlock(eb beacon.ExecutableDataV1) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed beacon.ExecutableDataV1) error {
|
func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed beacon.ExecutableData) error {
|
||||||
if !eth2types(n.typ) {
|
if !eth2types(n.typ) {
|
||||||
return errors.New("invalid node type")
|
return errors.New("invalid node type")
|
||||||
}
|
}
|
||||||
|
@ -968,13 +968,14 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
|
|||||||
|
|
||||||
// generateParams wraps various of settings for generating sealing task.
|
// generateParams wraps various of settings for generating sealing task.
|
||||||
type generateParams struct {
|
type generateParams struct {
|
||||||
timestamp uint64 // The timestamp for sealing task
|
timestamp uint64 // The timstamp for sealing task
|
||||||
forceTime bool // Flag whether the given timestamp is immutable or not
|
forceTime bool // Flag whether the given timestamp is immutable or not
|
||||||
parentHash common.Hash // Parent block hash, empty means the latest chain head
|
parentHash common.Hash // Parent block hash, empty means the latest chain head
|
||||||
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
|
||||||
noUncle bool // Flag whether the uncle block inclusion is allowed
|
withdrawals types.Withdrawals // List of withdrawals to include in block.
|
||||||
noTxs bool // Flag whether an empty block without any transaction is expected
|
noUncle bool // Flag whether the uncle block inclusion is allowed
|
||||||
|
noTxs bool // Flag whether an empty block without any transaction is expected
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareWork constructs the sealing task according to the given parameters,
|
// prepareWork constructs the sealing task according to the given parameters,
|
||||||
@ -1108,7 +1109,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e
|
|||||||
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout))
|
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
|
block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, params.withdrawals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -1193,7 +1194,8 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
|||||||
// Create a local environment copy, avoid the data race with snapshot state.
|
// Create a local environment copy, avoid the data race with snapshot state.
|
||||||
// https://github.com/ethereum/go-ethereum/issues/24299
|
// https://github.com/ethereum/go-ethereum/issues/24299
|
||||||
env := env.copy()
|
env := env.copy()
|
||||||
block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts)
|
// Withdrawals are set to nil here, because this is only called in PoW.
|
||||||
|
block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1224,16 +1226,17 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
|||||||
// getSealingBlock generates the sealing block based on the given parameters.
|
// getSealingBlock generates the sealing block based on the given parameters.
|
||||||
// The generation result will be passed back via the given channel no matter
|
// The generation result will be passed back via the given channel no matter
|
||||||
// the generation itself succeeds or not.
|
// the generation itself succeeds or not.
|
||||||
func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, noTxs bool) (*types.Block, *big.Int, error) {
|
func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash, withdrawals types.Withdrawals, noTxs bool) (*types.Block, *big.Int, error) {
|
||||||
req := &getWorkReq{
|
req := &getWorkReq{
|
||||||
params: &generateParams{
|
params: &generateParams{
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
forceTime: true,
|
forceTime: true,
|
||||||
parentHash: parent,
|
parentHash: parent,
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
random: random,
|
random: random,
|
||||||
noUncle: true,
|
withdrawals: withdrawals,
|
||||||
noTxs: noTxs,
|
noUncle: true,
|
||||||
|
noTxs: noTxs,
|
||||||
},
|
},
|
||||||
result: make(chan *newPayloadResult, 1),
|
result: make(chan *newPayloadResult, 1),
|
||||||
}
|
}
|
||||||
|
@ -637,7 +637,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
|||||||
|
|
||||||
// This API should work even when the automatic sealing is not enabled
|
// This API should work even when the automatic sealing is not enabled
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, false)
|
block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
|
||||||
if c.expectErr {
|
if c.expectErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expect error but get nil")
|
t.Error("Expect error but get nil")
|
||||||
@ -653,7 +653,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
|||||||
// This API should work even when the automatic sealing is enabled
|
// This API should work even when the automatic sealing is enabled
|
||||||
w.start()
|
w.start()
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, false)
|
block, _, err := w.getSealingBlock(c.parent, timestamp, c.coinbase, c.random, nil, false)
|
||||||
if c.expectErr {
|
if c.expectErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expect error but get nil")
|
t.Error("Expect error but get nil")
|
||||||
|
Loading…
Reference in New Issue
Block a user