forked from cerc-io/plugeth
all: implement withdrawals (EIP-4895) (#26484)
This change implements withdrawals as specified in EIP-4895. Co-authored-by: lightclient@protonmail.com <lightclient@protonmail.com> Co-authored-by: marioevz <marioevz@gmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
co-authored by
lightclient@protonmail.com
marioevz
Martin Holst Swende
Felix Lange
parent
2b57a27d9e
commit
2a2b0419fb
+42
-4
@@ -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)
|
||||
|
||||
+168
-39
@@ -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)
|
||||
totalDifficulty := big.NewInt(0)
|
||||
for _, b := range blocks {
|
||||
totalDifficulty.Add(totalDifficulty, b.Difficulty())
|
||||
_, 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
|
||||
}
|
||||
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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user