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
@ -54,6 +54,7 @@ type header struct {
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce *types.BlockNonce `json:"nonce"`
|
||||
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
}
|
||||
|
||||
type headerMarshaling struct {
|
||||
@ -70,6 +71,7 @@ type bbInput struct {
|
||||
Header *header `json:"header,omitempty"`
|
||||
OmmersRlp []string `json:"ommers,omitempty"`
|
||||
TxRlp string `json:"txs,omitempty"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||
Clique *cliqueInput `json:"clique,omitempty"`
|
||||
|
||||
Ethash bool `json:"-"`
|
||||
@ -129,6 +131,7 @@ func (i *bbInput) ToBlock() *types.Block {
|
||||
Extra: i.Header.Extra,
|
||||
MixDigest: i.Header.MixDigest,
|
||||
BaseFee: i.Header.BaseFee,
|
||||
WithdrawalsHash: i.Header.WithdrawalsHash,
|
||||
}
|
||||
|
||||
// Fill optional values.
|
||||
@ -153,7 +156,7 @@ func (i *bbInput) ToBlock() *types.Block {
|
||||
if header.Difficulty != nil {
|
||||
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.
|
||||
@ -261,6 +264,7 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
||||
var (
|
||||
headerStr = ctx.String(InputHeaderFlag.Name)
|
||||
ommersStr = ctx.String(InputOmmersFlag.Name)
|
||||
withdrawalsStr = ctx.String(InputWithdrawalsFlag.Name)
|
||||
txsStr = ctx.String(InputTxsRlpFlag.Name)
|
||||
cliqueStr = ctx.String(SealCliqueFlag.Name)
|
||||
ethashOn = ctx.Bool(SealEthashFlag.Name)
|
||||
@ -312,6 +316,13 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
||||
}
|
||||
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 {
|
||||
var txs string
|
||||
if err := readFile(txsStr, "txs", &txs); err != nil {
|
||||
@ -351,15 +362,14 @@ func readInput(ctx *cli.Context) (*bbInput, error) {
|
||||
// files
|
||||
func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error {
|
||||
raw, _ := rlp.EncodeToBytes(block)
|
||||
|
||||
type blockInfo struct {
|
||||
Rlp hexutil.Bytes `json:"rlp"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
var enc blockInfo
|
||||
enc.Rlp = raw
|
||||
enc.Hash = block.Hash()
|
||||
|
||||
enc := blockInfo{
|
||||
Rlp: raw,
|
||||
Hash: block.Hash(),
|
||||
}
|
||||
b, err := json.MarshalIndent(enc, "", " ")
|
||||
if err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
||||
|
@ -57,6 +57,7 @@ type ExecutionResult struct {
|
||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
||||
}
|
||||
|
||||
type ommer struct {
|
||||
@ -79,6 +80,7 @@ type stEnv struct {
|
||||
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
|
||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||
Ommers []ommer `json:"ommers,omitempty"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
|
||||
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)
|
||||
}
|
||||
// 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
|
||||
root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||
if err != nil {
|
||||
@ -272,6 +280,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
GasUsed: (math.HexOrDecimal64)(gasUsed),
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -112,6 +112,10 @@ var (
|
||||
Name: "input.ommers",
|
||||
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{
|
||||
Name: "input.txs",
|
||||
Usage: "`stdin` or file name of where to find the transactions list in RLP form.",
|
||||
|
@ -34,6 +34,7 @@ func (h header) MarshalJSON() ([]byte, error) {
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce *types.BlockNonce `json:"nonce"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
}
|
||||
var enc header
|
||||
enc.ParentHash = h.ParentHash
|
||||
@ -52,6 +53,7 @@ func (h header) MarshalJSON() ([]byte, error) {
|
||||
enc.MixDigest = h.MixDigest
|
||||
enc.Nonce = h.Nonce
|
||||
enc.BaseFee = (*math.HexOrDecimal256)(h.BaseFee)
|
||||
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
@ -74,6 +76,7 @@ func (h *header) UnmarshalJSON(input []byte) error {
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *types.BlockNonce `json:"nonce"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
}
|
||||
var dec header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@ -131,5 +134,8 @@ func (h *header) UnmarshalJSON(input []byte) error {
|
||||
if dec.BaseFee != nil {
|
||||
h.BaseFee = (*big.Int)(dec.BaseFee)
|
||||
}
|
||||
if dec.WithdrawalsHash != nil {
|
||||
h.WithdrawalsHash = dec.WithdrawalsHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var _ = (*stEnvMarshaling)(nil)
|
||||
@ -29,6 +30,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||
Ommers []ommer `json:"ommers,omitempty"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||
ParentUncleHash common.Hash `json:"parentUncleHash"`
|
||||
}
|
||||
@ -46,6 +48,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp)
|
||||
enc.BlockHashes = s.BlockHashes
|
||||
enc.Ommers = s.Ommers
|
||||
enc.Withdrawals = s.Withdrawals
|
||||
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
||||
enc.ParentUncleHash = s.ParentUncleHash
|
||||
return json.Marshal(&enc)
|
||||
@ -67,6 +70,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
|
||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||
Ommers []ommer `json:"ommers,omitempty"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||
ParentUncleHash *common.Hash `json:"parentUncleHash"`
|
||||
}
|
||||
@ -117,6 +121,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||
if dec.Ommers != nil {
|
||||
s.Ommers = dec.Ommers
|
||||
}
|
||||
if dec.Withdrawals != nil {
|
||||
s.Withdrawals = dec.Withdrawals
|
||||
}
|
||||
if dec.BaseFee != nil {
|
||||
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"))
|
||||
}
|
||||
}
|
||||
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
|
||||
env := prestate.Env
|
||||
if isMerged {
|
||||
|
@ -176,6 +176,7 @@ var blockBuilderCommand = &cli.Command{
|
||||
t8ntool.OutputBlockFlag,
|
||||
t8ntool.InputHeaderFlag,
|
||||
t8ntool.InputOmmersFlag,
|
||||
t8ntool.InputWithdrawalsFlag,
|
||||
t8ntool.InputTxsRlpFlag,
|
||||
t8ntool.SealCliqueFlag,
|
||||
t8ntool.SealEthashFlag,
|
||||
|
@ -251,6 +251,14 @@ func TestT8n(t *testing.T) {
|
||||
output: t8nOutput{alloc: true, result: true},
|
||||
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 = append(args, tc.output.get()...)
|
||||
@ -393,6 +401,7 @@ func TestT9n(t *testing.T) {
|
||||
type b11rInput struct {
|
||||
inEnv string
|
||||
inOmmersRlp string
|
||||
inWithdrawals string
|
||||
inTxsRlp string
|
||||
inClique string
|
||||
ethash bool
|
||||
@ -410,6 +419,10 @@ func (args *b11rInput) get(base string) []string {
|
||||
out = append(out, "--input.ommers")
|
||||
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 != "" {
|
||||
out = append(out, "--input.txs")
|
||||
out = append(out, fmt.Sprintf("%v/%v", base, opt))
|
||||
@ -480,6 +493,16 @@ func TestB11r(t *testing.T) {
|
||||
},
|
||||
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 = 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
|
||||
}
|
||||
// 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
|
||||
@ -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
|
||||
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
|
||||
// and verification. So determine the consensus rules by header type.
|
||||
if !beacon.IsPoSHeader(header) {
|
||||
beacon.ethone.Finalize(chain, header, state, txs, uncles)
|
||||
beacon.ethone.Finalize(chain, header, state, txs, uncles, nil)
|
||||
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
|
||||
// external consensus engine.
|
||||
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
|
||||
// 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
|
||||
// generation and verification. So determine the consensus rules by header type.
|
||||
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
|
||||
beacon.Finalize(chain, header, state, txs, uncles)
|
||||
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil
|
||||
shanghai := chain.Config().IsShanghai(header.Time)
|
||||
if shanghai {
|
||||
// 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
|
||||
|
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 {
|
||||
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 err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
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
|
||||
// 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
|
||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
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,
|
||||
// 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
|
||||
c.Finalize(chain, header, state, txs, uncles)
|
||||
c.Finalize(chain, header, state, txs, uncles, nil)
|
||||
|
||||
// Assemble and return the final block for sealing
|
||||
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 {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
if header.WithdrawalsHash != nil {
|
||||
panic("unexpected withdrawal hash value in clique")
|
||||
}
|
||||
if err := rlp.Encode(w, enc); err != nil {
|
||||
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
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
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
|
||||
// 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
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
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
|
||||
// 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 {
|
||||
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
|
||||
if seal {
|
||||
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,
|
||||
// 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
|
||||
accumulateRewards(chain.Config(), state, header, uncles)
|
||||
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
|
||||
// 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) {
|
||||
// Finalize block
|
||||
ethash.Finalize(chain, header, state, txs, uncles)
|
||||
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) {
|
||||
if len(withdrawals) > 0 {
|
||||
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
|
||||
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 {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
if header.WithdrawalsHash != nil {
|
||||
panic("withdrawal hash set on ethash")
|
||||
}
|
||||
rlp.Encode(hasher, enc)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
|
@ -8,46 +8,53 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var _ = (*payloadAttributesMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (p PayloadAttributesV1) MarshalJSON() ([]byte, error) {
|
||||
type PayloadAttributesV1 struct {
|
||||
func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
||||
type PayloadAttributes struct {
|
||||
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
Random common.Hash `json:"prevRandao" 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.Random = p.Random
|
||||
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
|
||||
enc.Withdrawals = p.Withdrawals
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (p *PayloadAttributesV1) UnmarshalJSON(input []byte) error {
|
||||
type PayloadAttributesV1 struct {
|
||||
func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
||||
type PayloadAttributes struct {
|
||||
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||
Random *common.Hash `json:"prevRandao" 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 {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
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
|
||||
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
|
||||
if dec.Withdrawals != nil {
|
||||
p.Withdrawals = dec.Withdrawals
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -9,13 +9,14 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var _ = (*executableDataMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (e ExecutableDataV1) MarshalJSON() ([]byte, error) {
|
||||
type ExecutableDataV1 struct {
|
||||
func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||
type ExecutableData struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
@ -30,8 +31,9 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) {
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" 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.FeeRecipient = e.FeeRecipient
|
||||
enc.StateRoot = e.StateRoot
|
||||
@ -51,12 +53,13 @@ func (e ExecutableDataV1) MarshalJSON() ([]byte, error) {
|
||||
enc.Transactions[k] = v
|
||||
}
|
||||
}
|
||||
enc.Withdrawals = e.Withdrawals
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error {
|
||||
type ExecutableDataV1 struct {
|
||||
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||
type ExecutableData struct {
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
@ -71,69 +74,73 @@ func (e *ExecutableDataV1) UnmarshalJSON(input []byte) error {
|
||||
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash *common.Hash `json:"blockHash" 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 {
|
||||
return err
|
||||
}
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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))
|
||||
for k, v := range dec.Transactions {
|
||||
e.Transactions[k] = v
|
||||
}
|
||||
if dec.Withdrawals != nil {
|
||||
e.Withdrawals = dec.Withdrawals
|
||||
}
|
||||
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,24 +26,26 @@ import (
|
||||
"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
|
||||
type PayloadAttributesV1 struct {
|
||||
// PayloadAttributes describes the environment context in which a block should
|
||||
// be built.
|
||||
type PayloadAttributes struct {
|
||||
Timestamp uint64 `json:"timestamp" 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 {
|
||||
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
|
||||
type ExecutableDataV1 struct {
|
||||
// ExecutableData is the data necessary to execute an EL payload.
|
||||
type ExecutableData struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
@ -58,6 +60,7 @@ type ExecutableDataV1 struct {
|
||||
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
}
|
||||
|
||||
// JSON type overrides for executableData.
|
||||
@ -72,6 +75,18 @@ type executableDataMarshaling struct {
|
||||
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 {
|
||||
Status string `json:"status"`
|
||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||
@ -141,8 +156,10 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||
// uncleHash = emptyUncleHash
|
||||
// difficulty = 0
|
||||
//
|
||||
// and that the blockhash of the constructed block matches the parameters.
|
||||
func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
||||
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -157,6 +174,14 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
||||
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||
}
|
||||
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
|
||||
// ExecutableData before withdrawals are enabled by marshaling
|
||||
// Withdrawals as the json null value.
|
||||
var withdrawalsRoot *common.Hash
|
||||
if params.Withdrawals != nil {
|
||||
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
||||
withdrawalsRoot = &h
|
||||
}
|
||||
header := &types.Header{
|
||||
ParentHash: params.ParentHash,
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
@ -173,18 +198,19 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
|
||||
BaseFee: params.BaseFeePerGas,
|
||||
Extra: params.ExtraData,
|
||||
MixDigest: params.Random,
|
||||
WithdrawalsHash: withdrawalsRoot,
|
||||
}
|
||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
|
||||
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||
if block.Hash() != params.BlockHash {
|
||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
||||
}
|
||||
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.
|
||||
func BlockToExecutableData(block *types.Block) *ExecutableDataV1 {
|
||||
return &ExecutableDataV1{
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
FeeRecipient: block.Coinbase(),
|
||||
@ -199,5 +225,7 @@ func BlockToExecutableData(block *types.Block) *ExecutableDataV1 {
|
||||
Transactions: encodeTransactions(block.Transactions()),
|
||||
Random: block.MixDigest(),
|
||||
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 {
|
||||
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.HasBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||
return consensus.ErrUnknownAncestor
|
||||
|
@ -4229,7 +4229,7 @@ func TestEIP3651(t *testing.T) {
|
||||
var (
|
||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||
engine = ethash.NewFaker()
|
||||
engine = beacon.NewFaker()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
@ -4237,8 +4237,9 @@ func TestEIP3651(t *testing.T) {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||
config = *params.AllEthashProtocolChanges
|
||||
gspec = &Genesis{
|
||||
Config: params.AllEthashProtocolChanges,
|
||||
Config: &config,
|
||||
Alloc: GenesisAlloc{
|
||||
addr1: {Balance: funds},
|
||||
addr2: {Balance: funds},
|
||||
@ -4275,6 +4276,8 @@ func TestEIP3651(t *testing.T) {
|
||||
|
||||
gspec.Config.BerlinBlock = common.Big0
|
||||
gspec.Config.LondonBlock = common.Big0
|
||||
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||
gspec.Config.ShanghaiTime = u64(0)
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
|
||||
@ -4317,10 +4320,7 @@ func TestEIP3651(t *testing.T) {
|
||||
|
||||
// 3: Ensure that miner received only the tx's tip.
|
||||
actual := state.GetBalance(block.Coinbase())
|
||||
expected := new(big.Int).Add(
|
||||
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
||||
ethash.ConstantinopleBlockReward,
|
||||
)
|
||||
expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64())
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ type BlockGen struct {
|
||||
txs []*types.Transaction
|
||||
receipts []*types.Receipt
|
||||
uncles []*types.Header
|
||||
withdrawals []*types.Withdrawal
|
||||
|
||||
config *params.ChainConfig
|
||||
engine consensus.Engine
|
||||
@ -205,6 +206,26 @@ func (b *BlockGen) AddUncle(h *types.Header) {
|
||||
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
|
||||
// num is greater or equal to the number of the block being generated.
|
||||
// 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)
|
||||
}
|
||||
if b.engine != nil {
|
||||
// Finalize and seal the block
|
||||
block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
|
||||
block, err := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts, b.withdrawals)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Write state changes to db
|
||||
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)
|
||||
}
|
||||
}
|
||||
if g.Config != nil && g.Config.IsShanghai(g.Timestamp) {
|
||||
head.WithdrawalsHash = &types.EmptyRootHash
|
||||
}
|
||||
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 {
|
||||
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.
|
||||
@ -860,7 +860,7 @@ func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
|
||||
}
|
||||
for _, bad := range badBlocks {
|
||||
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
|
||||
@ -879,7 +879,7 @@ func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
|
||||
}
|
||||
var blocks []*types.Block
|
||||
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
|
||||
}
|
||||
|
@ -86,8 +86,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
receipts = append(receipts, receipt)
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"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/misc"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
@ -329,6 +330,8 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||
ArrowGlacierBlock: big.NewInt(0),
|
||||
GrayGlacierBlock: big.NewInt(0),
|
||||
MergeNetsplitBlock: big.NewInt(0),
|
||||
TerminalTotalDifficulty: big.NewInt(0),
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
ShanghaiTime: u64(0),
|
||||
},
|
||||
Alloc: GenesisAlloc{
|
||||
@ -339,7 +342,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
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{}
|
||||
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",
|
||||
},
|
||||
} {
|
||||
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})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
@ -378,15 +381,20 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||
// valid to be considered for import:
|
||||
// - valid pow (fake), ancestry, difficulty, gaslimit etc
|
||||
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
|
||||
header := &types.Header{
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
|
||||
difficulty := big.NewInt(0)
|
||||
if !config.TerminalTotalDifficultyPassed {
|
||||
difficulty = engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
|
||||
Number: parent.Number(),
|
||||
Time: parent.Time(),
|
||||
Difficulty: parent.Difficulty(),
|
||||
UncleHash: parent.UncleHash(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
header := &types.Header{
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: difficulty,
|
||||
GasLimit: parent.GasLimit(),
|
||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
Time: parent.Time() + 10,
|
||||
@ -395,6 +403,9 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
||||
if config.IsLondon(header.Number) {
|
||||
header.BaseFee = misc.CalcBaseFee(config, parent.Header())
|
||||
}
|
||||
if config.IsShanghai(header.Time) {
|
||||
header.WithdrawalsHash = &types.EmptyRootHash
|
||||
}
|
||||
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
|
||||
// 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 *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
|
||||
// Random was added during the merge and contains the BeaconState randomness
|
||||
@ -149,10 +152,13 @@ func (h *Header) SanityCheck() error {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
func (h *Header) EmptyReceipts() bool {
|
||||
@ -164,6 +170,7 @@ func (h *Header) EmptyReceipts() bool {
|
||||
type Body struct {
|
||||
Transactions []*Transaction
|
||||
Uncles []*Header
|
||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||
}
|
||||
|
||||
// Block represents an entire block in the Ethereum blockchain.
|
||||
@ -171,6 +178,7 @@ type Block struct {
|
||||
header *Header
|
||||
uncles []*Header
|
||||
transactions Transactions
|
||||
withdrawals Withdrawals
|
||||
|
||||
// caches
|
||||
hash atomic.Value
|
||||
@ -187,6 +195,7 @@ type extblock struct {
|
||||
Header *Header
|
||||
Txs []*Transaction
|
||||
Uncles []*Header
|
||||
Withdrawals []*Withdrawal `rlp:"optional"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// header data is copied, changes to header and to the field values
|
||||
// will not affect the block.
|
||||
@ -252,6 +283,9 @@ func CopyHeader(h *Header) *Header {
|
||||
cpy.Extra = make([]byte, len(h.Extra))
|
||||
copy(cpy.Extra, h.Extra)
|
||||
}
|
||||
if h.WithdrawalsHash != nil {
|
||||
*cpy.WithdrawalsHash = *h.WithdrawalsHash
|
||||
}
|
||||
return &cpy
|
||||
}
|
||||
|
||||
@ -262,7 +296,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
||||
if err := s.Decode(&eb); err != nil {
|
||||
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))
|
||||
return nil
|
||||
}
|
||||
@ -273,6 +307,7 @@ func (b *Block) EncodeRLP(w io.Writer) error {
|
||||
Header: b.header,
|
||||
Txs: b.transactions,
|
||||
Uncles: b.uncles,
|
||||
Withdrawals: b.withdrawals,
|
||||
})
|
||||
}
|
||||
|
||||
@ -315,10 +350,14 @@ func (b *Block) BaseFee() *big.Int {
|
||||
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) }
|
||||
|
||||
// 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
|
||||
// and returning it, or returning a previously cached value.
|
||||
@ -361,6 +400,7 @@ func (b *Block) WithSeal(header *Header) *Block {
|
||||
header: &cpy,
|
||||
transactions: b.transactions,
|
||||
uncles: b.uncles,
|
||||
withdrawals: b.withdrawals,
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,6 +418,15 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *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.
|
||||
// The hash is computed on the first call and cached thereafter.
|
||||
func (b *Block) Hash() common.Hash {
|
||||
|
@ -32,6 +32,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||
MixDigest common.Hash `json:"mixHash"`
|
||||
Nonce BlockNonce `json:"nonce"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
var enc Header
|
||||
@ -51,6 +52,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||
enc.MixDigest = h.MixDigest
|
||||
enc.Nonce = h.Nonce
|
||||
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
||||
enc.WithdrawalsHash = h.WithdrawalsHash
|
||||
enc.Hash = h.Hash()
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
@ -74,6 +76,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
|
||||
}
|
||||
var dec Header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@ -139,5 +142,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
if dec.BaseFee != nil {
|
||||
h.BaseFee = (*big.Int)(dec.BaseFee)
|
||||
}
|
||||
if dec.WithdrawalsHash != nil {
|
||||
h.WithdrawalsHash = dec.WithdrawalsHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
||||
w.WriteBytes(obj.MixDigest[:])
|
||||
w.WriteBytes(obj.Nonce[:])
|
||||
_tmp1 := obj.BaseFee != nil
|
||||
if _tmp1 {
|
||||
_tmp2 := obj.WithdrawalsHash != nil
|
||||
if _tmp1 || _tmp2 {
|
||||
if obj.BaseFee == nil {
|
||||
w.Write(rlp.EmptyString)
|
||||
} else {
|
||||
@ -51,6 +52,13 @@ func (obj *Header) EncodeRLP(_w io.Writer) error {
|
||||
w.WriteBigInt(obj.BaseFee)
|
||||
}
|
||||
}
|
||||
if _tmp2 {
|
||||
if obj.WithdrawalsHash == nil {
|
||||
w.Write([]byte{0x80})
|
||||
} else {
|
||||
w.WriteBytes(obj.WithdrawalsHash[:])
|
||||
}
|
||||
}
|
||||
w.ListEnd(_tmp0)
|
||||
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)
|
||||
// - reset transient storage(eip 1153)
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
|
||||
cfg.State.CreateAccount(address)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(address, code)
|
||||
@ -153,7 +152,6 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||
// - prepare accessList(post-berlin)
|
||||
// - reset transient storage(eip 1153)
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
code, address, leftOverGas, err := vmenv.Create(
|
||||
sender,
|
||||
|
@ -155,7 +155,19 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
|
||||
//
|
||||
// If there are payloadAttributes: we try to assemble a block with the payloadAttributes
|
||||
// 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()
|
||||
defer api.forkchoiceLock.Unlock()
|
||||
|
||||
@ -285,6 +297,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
|
||||
Timestamp: payloadAttributes.Timestamp,
|
||||
FeeRecipient: payloadAttributes.SuggestedFeeRecipient,
|
||||
Random: payloadAttributes.Random,
|
||||
Withdrawals: payloadAttributes.Withdrawals,
|
||||
}
|
||||
id := args.Id()
|
||||
// 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.
|
||||
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)
|
||||
data := api.localBlocks.get(payloadID)
|
||||
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.
|
||||
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:
|
||||
//
|
||||
// 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()
|
||||
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)
|
||||
if err != nil {
|
||||
log.Debug("Invalid NewPayload params", "params", params, "error", err)
|
||||
|
@ -18,6 +18,7 @@ package catalyst
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
@ -26,6 +27,8 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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/core"
|
||||
"github.com/ethereum/go-ethereum/core/beacon"
|
||||
@ -38,6 +41,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
@ -51,8 +55,14 @@ var (
|
||||
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
|
||||
engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker()))
|
||||
if merged {
|
||||
config.TerminalTotalDifficulty = common.Big0
|
||||
config.TerminalTotalDifficultyPassed = true
|
||||
engine = beaconConsensus.NewFaker()
|
||||
}
|
||||
genesis := &core.Genesis{
|
||||
Config: &config,
|
||||
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
|
||||
@ -69,17 +79,21 @@ func generatePreMergeChain(n int) (*core.Genesis, []*types.Block) {
|
||||
g.AddTx(tx)
|
||||
testNonce++
|
||||
}
|
||||
_, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), n, generate)
|
||||
_, blocks, _ := core.GenerateChainWithGenesis(genesis, engine, n, generate)
|
||||
|
||||
if !merged {
|
||||
totalDifficulty := big.NewInt(0)
|
||||
for _, b := range blocks {
|
||||
totalDifficulty.Add(totalDifficulty, b.Difficulty())
|
||||
}
|
||||
config.TerminalTotalDifficulty = totalDifficulty
|
||||
}
|
||||
|
||||
return genesis, blocks
|
||||
}
|
||||
|
||||
func TestEth2AssembleBlock(t *testing.T) {
|
||||
genesis, blocks := generatePreMergeChain(10)
|
||||
genesis, blocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -90,7 +104,7 @@ func TestEth2AssembleBlock(t *testing.T) {
|
||||
t.Fatalf("error signing transaction, err=%v", err)
|
||||
}
|
||||
ethservice.TxPool().AddLocal(tx)
|
||||
blockParams := beacon.PayloadAttributesV1{
|
||||
blockParams := beacon.PayloadAttributes{
|
||||
Timestamp: blocks[9].Time() + 5,
|
||||
}
|
||||
// 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',
|
||||
// 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-- {
|
||||
execData, err = assembleBlock(api, parentHash, params)
|
||||
if err != nil {
|
||||
@ -118,7 +132,7 @@ func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params
|
||||
}
|
||||
|
||||
func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||
genesis, blocks := generatePreMergeChain(10)
|
||||
genesis, blocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||
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
|
||||
api.eth.TxPool().AddRemotesSync(blocks[9].Transactions())
|
||||
blockParams := beacon.PayloadAttributesV1{
|
||||
blockParams := beacon.PayloadAttributes{
|
||||
Timestamp: blocks[8].Time() + 5,
|
||||
}
|
||||
// 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) {
|
||||
genesis, blocks := generatePreMergeChain(10)
|
||||
genesis, blocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -155,7 +169,7 @@ func TestSetHeadBeforeTotalDifficulty(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
|
||||
genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty())
|
||||
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
|
||||
ethservice.TxPool().AddLocals(blocks[9].Transactions())
|
||||
blockParams := beacon.PayloadAttributesV1{
|
||||
blockParams := beacon.PayloadAttributes{
|
||||
Timestamp: blocks[8].Time() + 5,
|
||||
}
|
||||
fcState := beacon.ForkchoiceStateV1{
|
||||
@ -221,7 +235,7 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan co
|
||||
}
|
||||
|
||||
func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
var (
|
||||
@ -244,7 +258,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("Timestamp test: %v", i), func(t *testing.T) {
|
||||
params := beacon.PayloadAttributesV1{
|
||||
params := beacon.PayloadAttributes{
|
||||
Timestamp: test.time,
|
||||
Random: crypto.Keccak256Hash([]byte{byte(123)}),
|
||||
SuggestedFeeRecipient: parent.Coinbase(),
|
||||
@ -265,7 +279,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEth2NewBlock(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
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)
|
||||
ethservice.TxPool().AddLocal(tx)
|
||||
|
||||
execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributesV1{
|
||||
execData, err := assembleWithTransactions(api, parent.Hash(), &beacon.PayloadAttributes{
|
||||
Timestamp: parent.Time() + 5,
|
||||
}, 1)
|
||||
if err != nil {
|
||||
@ -330,7 +344,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||
)
|
||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
@ -367,7 +381,7 @@ func TestEth2DeepReorg(t *testing.T) {
|
||||
// TODO (MariusVanDerWijden) TestEth2DeepReorg is currently broken, because it tries to reorg
|
||||
// before the totalTerminalDifficulty threshold
|
||||
/*
|
||||
genesis, preMergeBlocks := generatePreMergeChain(core.TriesInMemory * 2)
|
||||
genesis, preMergeBlocks := generateMergeChain(core.TriesInMemory * 2, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -442,7 +456,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
|
||||
}
|
||||
|
||||
func TestFullAPI(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
var (
|
||||
@ -494,7 +508,7 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Bl
|
||||
}
|
||||
|
||||
func TestExchangeTransitionConfig(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -555,7 +569,7 @@ We expect
|
||||
└── P1''
|
||||
*/
|
||||
func TestNewPayloadOnInvalidChain(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -577,7 +591,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
||||
})
|
||||
ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx})
|
||||
var (
|
||||
params = beacon.PayloadAttributesV1{
|
||||
params = beacon.PayloadAttributes{
|
||||
Timestamp: parent.Time() + 1,
|
||||
Random: crypto.Keccak256Hash([]byte{byte(i)}),
|
||||
SuggestedFeeRecipient: parent.Coinbase(),
|
||||
@ -587,7 +601,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
||||
SafeBlockHash: common.Hash{},
|
||||
FinalizedBlockHash: common.Hash{},
|
||||
}
|
||||
payload *beacon.ExecutableDataV1
|
||||
payload *beacon.ExecutableData
|
||||
resp beacon.ForkChoiceResponse
|
||||
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{
|
||||
Parent: parentHash,
|
||||
Timestamp: params.Timestamp,
|
||||
FeeRecipient: params.SuggestedFeeRecipient,
|
||||
Random: params.Random,
|
||||
Withdrawals: params.Withdrawals,
|
||||
}
|
||||
payload, err := api.eth.Miner().BuildPayload(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return payload.ResolveFull(), nil
|
||||
return payload.ResolveFull().ExecutionPayload, nil
|
||||
}
|
||||
|
||||
func TestEmptyBlocks(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -708,8 +723,8 @@ func TestEmptyBlocks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableDataV1 {
|
||||
params := beacon.PayloadAttributesV1{
|
||||
func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Block) *beacon.ExecutableData {
|
||||
params := beacon.PayloadAttributes{
|
||||
Timestamp: parent.Time() + 1,
|
||||
Random: crypto.Keccak256Hash([]byte{byte(1)}),
|
||||
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.
|
||||
// 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)
|
||||
number := big.NewInt(0)
|
||||
number.SetUint64(data.Number)
|
||||
@ -764,7 +779,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||
|
||||
func TestTrickRemoteBlockCache(t *testing.T) {
|
||||
// Setup two nodes
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
|
||||
nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
|
||||
defer nodeA.Close()
|
||||
@ -783,7 +798,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
|
||||
setupBlocks(t, ethserviceA, 10, commonAncestor, func(parent *types.Block) {})
|
||||
commonAncestor = ethserviceA.BlockChain().CurrentBlock()
|
||||
|
||||
var invalidChain []*beacon.ExecutableDataV1
|
||||
var invalidChain []*beacon.ExecutableData
|
||||
// create a valid payload (P1)
|
||||
//payload1 := getNewPayload(t, apiA, commonAncestor)
|
||||
//invalidChain = append(invalidChain, payload1)
|
||||
@ -827,7 +842,7 @@ func TestTrickRemoteBlockCache(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidBloom(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
ethservice.Merger().ReachTTD()
|
||||
defer n.Close()
|
||||
@ -851,12 +866,12 @@ func TestInvalidBloom(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(100)
|
||||
|
||||
genesis, preMergeBlocks := generateMergeChain(100, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
|
||||
|
||||
var (
|
||||
api = NewConsensusAPI(ethservice)
|
||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||
@ -887,7 +902,7 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("error preparing payload, err=%v", err)
|
||||
}
|
||||
data := *payload.Resolve()
|
||||
data := *payload.Resolve().ExecutionPayload
|
||||
resp2, err := api.NewPayloadV1(data)
|
||||
if err != nil {
|
||||
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
|
||||
// well even of the caller is not being 'serial'.
|
||||
func TestSimultaneousNewBlock(t *testing.T) {
|
||||
genesis, preMergeBlocks := generatePreMergeChain(10)
|
||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
@ -910,7 +925,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||
)
|
||||
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,
|
||||
})
|
||||
if err != nil {
|
||||
@ -984,3 +999,117 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
||||
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.
|
||||
func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutableDataV1 {
|
||||
func (q *payloadQueue) get(id beacon.PayloadID) *beacon.ExecutionPayloadEnvelope {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
|
@ -1548,7 +1548,7 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
||||
)
|
||||
blocks := make([]*types.Block, len(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
|
||||
// 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))
|
||||
receipts := make([]types.Receipts, len(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
|
||||
}
|
||||
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 {
|
||||
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())
|
||||
|
||||
// Commit the pivot block as the new head, will require full sync from here on
|
||||
|
@ -275,6 +275,7 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
||||
var (
|
||||
txsHashes = make([]common.Hash, len(bodies))
|
||||
uncleHashes = make([]common.Hash, len(bodies))
|
||||
withdrawalHashes = make([]common.Hash, len(bodies))
|
||||
)
|
||||
hasher := trie.NewStackTrie(nil)
|
||||
for i, body := range bodies {
|
||||
@ -287,7 +288,7 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
|
||||
res := ð.Response{
|
||||
Req: req,
|
||||
Res: (*eth.BlockBodiesPacket)(&bodies),
|
||||
Meta: [][]common.Hash{txsHashes, uncleHashes},
|
||||
Meta: [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes},
|
||||
Time: 1,
|
||||
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
|
||||
// fetcher, unpacking the body data and delivering it to the downloader's queue.
|
||||
func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) {
|
||||
txs, uncles := packet.Res.(*eth.BlockBodiesPacket).Unpack()
|
||||
hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes}
|
||||
txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesPacket).Unpack()
|
||||
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 {
|
||||
case err == nil && len(txs) == 0:
|
||||
peer.log.Trace("Requested bodies delivered")
|
||||
|
@ -67,6 +67,7 @@ type fetchResult struct {
|
||||
Uncles []*types.Header
|
||||
Transactions types.Transactions
|
||||
Receipts types.Receipts
|
||||
Withdrawals types.Withdrawals
|
||||
}
|
||||
|
||||
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.
|
||||
// The method returns the number of blocks bodies accepted from the delivery and
|
||||
// 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()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
@ -775,12 +778,19 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
|
||||
if uncleListHashes[index] != header.UncleHash {
|
||||
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
|
||||
}
|
||||
|
||||
reconstruct := func(index int, result *fetchResult) {
|
||||
result.Transactions = txLists[index]
|
||||
result.Uncles = uncleLists[index]
|
||||
result.Withdrawals = withdrawalLists[index]
|
||||
result.SetBodyDone()
|
||||
}
|
||||
return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
|
||||
|
@ -339,7 +339,7 @@ func XTestDelivery(t *testing.T) {
|
||||
uncleHashes[i] = types.CalcUncleHash(uncles)
|
||||
}
|
||||
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 {
|
||||
fmt.Printf("delivered %d bodies %v\n", len(txset), err)
|
||||
}
|
||||
|
@ -540,8 +540,8 @@ func (f *BlockFetcher) loop() {
|
||||
select {
|
||||
case res := <-resCh:
|
||||
res.Done <- nil
|
||||
|
||||
txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack()
|
||||
// Ignoring withdrawals here, since the block fetcher is not used post-merge.
|
||||
txs, uncles, _ := res.Res.(*eth.BlockBodiesPacket).Unpack()
|
||||
f.FilterBodies(peer, txs, uncles, time.Now())
|
||||
|
||||
case <-timeout.C:
|
||||
|
@ -23,6 +23,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"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/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
@ -45,6 +47,8 @@ var (
|
||||
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
)
|
||||
|
||||
func u64(val uint64) *uint64 { return &val }
|
||||
|
||||
// testBackend is a mock implementation of the live Ethereum message handler. Its
|
||||
// purpose is to allow testing the request/reply workflows and wire serialization
|
||||
// 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.
|
||||
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
|
||||
// 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 {
|
||||
var (
|
||||
// Create a database pre-initialize with a genesis block
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
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{
|
||||
Config: params.TestChainConfig,
|
||||
Config: config,
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
@ -305,7 +341,17 @@ func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) }
|
||||
func testGetBlockBodies(t *testing.T, protocol uint) {
|
||||
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()
|
||||
|
||||
peer, _ := newTestPeer("peer", protocol, backend)
|
||||
@ -355,7 +401,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
|
||||
block := backend.chain.GetBlockByNumber(uint64(num))
|
||||
hashes = append(hashes, block.Hash())
|
||||
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
|
||||
}
|
||||
@ -365,9 +411,10 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
|
||||
hashes = append(hashes, hash)
|
||||
if tt.available[j] && len(bodies) < tt.expected {
|
||||
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
|
||||
p2p.Send(peer.app, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
|
||||
RequestId: 123,
|
||||
@ -426,7 +473,7 @@ func testGetNodeData(t *testing.T, protocol uint, drop bool) {
|
||||
}
|
||||
}
|
||||
// Assemble the test environment
|
||||
backend := newTestBackendWithGenerator(4, generator)
|
||||
backend := newTestBackendWithGenerator(4, false, generator)
|
||||
defer backend.close()
|
||||
|
||||
peer, _ := newTestPeer("peer", protocol, backend)
|
||||
@ -544,7 +591,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
|
||||
}
|
||||
}
|
||||
// Assemble the test environment
|
||||
backend := newTestBackendWithGenerator(4, generator)
|
||||
backend := newTestBackendWithGenerator(4, false, generator)
|
||||
defer backend.close()
|
||||
|
||||
peer, _ := newTestPeer("peer", protocol, backend)
|
||||
|
@ -381,13 +381,17 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
|
||||
var (
|
||||
txsHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
||||
uncleHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
||||
withdrawalHashes = make([]common.Hash, len(res.BlockBodiesPacket))
|
||||
)
|
||||
hasher := trie.NewStackTrie(nil)
|
||||
for i, body := range res.BlockBodiesPacket {
|
||||
txsHashes[i] = types.DeriveSha(types.Transactions(body.Transactions), hasher)
|
||||
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{
|
||||
id: res.RequestId,
|
||||
|
@ -239,19 +239,22 @@ type BlockBodiesRLPPacket66 struct {
|
||||
type BlockBody struct {
|
||||
Transactions []*types.Transaction // Transactions 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
|
||||
// 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 (
|
||||
txset = make([][]*types.Transaction, len(*p))
|
||||
uncleset = make([][]*types.Header, len(*p))
|
||||
withdrawalset = make([][]*types.Withdrawal, len(*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.
|
||||
|
@ -1214,6 +1214,10 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
||||
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
|
||||
}
|
||||
|
||||
if head.WithdrawalsHash != nil {
|
||||
result["withdrawalsRoot"] = head.WithdrawalsHash
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1242,6 +1246,8 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
|
||||
}
|
||||
}
|
||||
fields["transactions"] = transactions
|
||||
// inclTx also expands withdrawals
|
||||
fields["withdrawals"] = block.Withdrawals()
|
||||
}
|
||||
uncles := block.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
|
||||
// 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{}) {
|
||||
log.Warn("Forkchoice requested update to zero hash")
|
||||
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.
|
||||
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"))
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return api.invalid(), err
|
||||
|
@ -130,7 +130,7 @@ func TestExecutePayloadV1(t *testing.T) {
|
||||
BaseFee: block.BaseFee(),
|
||||
}, nil, nil, nil, trie.NewStackTrie(nil))
|
||||
|
||||
_, err := api.ExecutePayloadV1(beacon.ExecutableDataV1{
|
||||
_, err := api.ExecutePayloadV1(beacon.ExecutableData{
|
||||
ParentHash: fakeBlock.ParentHash(),
|
||||
FeeRecipient: fakeBlock.Coinbase(),
|
||||
StateRoot: fakeBlock.Root(),
|
||||
|
@ -38,6 +38,7 @@ type BuildPayloadArgs struct {
|
||||
Timestamp uint64 // The provided timestamp of generated payload
|
||||
FeeRecipient common.Address // The provided recipient address for collecting transaction fee
|
||||
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.
|
||||
@ -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
|
||||
// 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()
|
||||
defer payload.lock.Unlock()
|
||||
|
||||
@ -117,23 +118,23 @@ func (payload *Payload) Resolve() *beacon.ExecutableDataV1 {
|
||||
close(payload.stop)
|
||||
}
|
||||
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.
|
||||
// It's only used in tests.
|
||||
func (payload *Payload) ResolveEmpty() *beacon.ExecutableDataV1 {
|
||||
func (payload *Payload) ResolveEmpty() *beacon.ExecutionPayloadEnvelope {
|
||||
payload.lock.Lock()
|
||||
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.
|
||||
// It's only used in tests.
|
||||
func (payload *Payload) ResolveFull() *beacon.ExecutableDataV1 {
|
||||
func (payload *Payload) ResolveFull() *beacon.ExecutionPayloadEnvelope {
|
||||
payload.lock.Lock()
|
||||
defer payload.lock.Unlock()
|
||||
|
||||
@ -145,7 +146,7 @@ func (payload *Payload) ResolveFull() *beacon.ExecutableDataV1 {
|
||||
}
|
||||
payload.cond.Wait()
|
||||
}
|
||||
return beacon.BlockToExecutableData(payload.full)
|
||||
return beacon.BlockToExecutableData(payload.full, payload.fullFees)
|
||||
}
|
||||
|
||||
// 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
|
||||
// enough to run. The empty payload can at least make sure there is something
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -177,7 +178,7 @@ func (w *worker) buildPayload(args *BuildPayloadArgs) (*Payload, error) {
|
||||
select {
|
||||
case <-timer.C:
|
||||
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 {
|
||||
payload.update(block, fees, time.Since(start))
|
||||
}
|
||||
|
@ -47,20 +47,21 @@ func TestBuildPayload(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to build payload %v", err)
|
||||
}
|
||||
verify := func(data *beacon.ExecutableDataV1, txs int) {
|
||||
if data.ParentHash != b.chain.CurrentBlock().Hash() {
|
||||
verify := func(outer *beacon.ExecutionPayloadEnvelope, txs int) {
|
||||
payload := outer.ExecutionPayload
|
||||
if payload.ParentHash != b.chain.CurrentBlock().Hash() {
|
||||
t.Fatal("Unexpect parent hash")
|
||||
}
|
||||
if data.Random != (common.Hash{}) {
|
||||
if payload.Random != (common.Hash{}) {
|
||||
t.Fatal("Unexpect random value")
|
||||
}
|
||||
if data.Timestamp != timestamp {
|
||||
if payload.Timestamp != timestamp {
|
||||
t.Fatal("Unexpect timestamp")
|
||||
}
|
||||
if data.FeeRecipient != recipient {
|
||||
if payload.FeeRecipient != recipient {
|
||||
t.Fatal("Unexpect fee recipient")
|
||||
}
|
||||
if len(data.Transactions) != txs {
|
||||
if len(payload.Transactions) != txs {
|
||||
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 {
|
||||
return nil, errors.New("invalid node type")
|
||||
}
|
||||
@ -150,7 +150,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
|
||||
if timestamp <= parentTimestamp {
|
||||
timestamp = parentTimestamp + 1
|
||||
}
|
||||
payloadAttribute := beacon.PayloadAttributesV1{
|
||||
payloadAttribute := beacon.PayloadAttributes{
|
||||
Timestamp: timestamp,
|
||||
Random: common.Hash{},
|
||||
SuggestedFeeRecipient: common.HexToAddress("0xdeadbeef"),
|
||||
@ -168,7 +168,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64)
|
||||
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) {
|
||||
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) {
|
||||
return errors.New("invalid node type")
|
||||
}
|
||||
|
@ -968,11 +968,12 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
|
||||
|
||||
// generateParams wraps various of settings for generating sealing task.
|
||||
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
|
||||
parentHash common.Hash // Parent block hash, empty means the latest chain head
|
||||
coinbase common.Address // The fee recipient address for including transaction
|
||||
random common.Hash // The randomness generated by beacon chain, empty before the merge
|
||||
withdrawals types.Withdrawals // List of withdrawals to include in block.
|
||||
noUncle bool // Flag whether the uncle block inclusion is allowed
|
||||
noTxs bool // Flag whether an empty block without any transaction is expected
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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.
|
||||
// https://github.com/ethereum/go-ethereum/issues/24299
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -1224,7 +1226,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
||||
// 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 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{
|
||||
params: &generateParams{
|
||||
timestamp: timestamp,
|
||||
@ -1232,6 +1234,7 @@ func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase
|
||||
parentHash: parent,
|
||||
coinbase: coinbase,
|
||||
random: random,
|
||||
withdrawals: withdrawals,
|
||||
noUncle: true,
|
||||
noTxs: noTxs,
|
||||
},
|
||||
|
@ -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
|
||||
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 err == 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
|
||||
w.start()
|
||||
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 err == nil {
|
||||
t.Error("Expect error but get nil")
|
||||
|
Loading…
Reference in New Issue
Block a user