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
+14
-13
@@ -34,10 +34,11 @@ import (
|
||||
// Check engine-api specification for more details.
|
||||
// https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1
|
||||
type BuildPayloadArgs struct {
|
||||
Parent common.Hash // The parent block to build payload on top
|
||||
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
|
||||
Parent common.Hash // The parent block to build payload on top
|
||||
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")
|
||||
}
|
||||
|
||||
+20
-17
@@ -968,13 +968,14 @@ 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
|
||||
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
|
||||
noUncle bool // Flag whether the uncle block inclusion is allowed
|
||||
noTxs bool // Flag whether an empty block without any transaction is expected
|
||||
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
|
||||
}
|
||||
|
||||
// prepareWork constructs the sealing task according to the given parameters,
|
||||
@@ -1108,7 +1109,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e
|
||||
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout))
|
||||
}
|
||||
}
|
||||
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,16 +1226,17 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
||||
// getSealingBlock generates the sealing block based on the given parameters.
|
||||
// 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,
|
||||
forceTime: true,
|
||||
parentHash: parent,
|
||||
coinbase: coinbase,
|
||||
random: random,
|
||||
noUncle: true,
|
||||
noTxs: noTxs,
|
||||
timestamp: timestamp,
|
||||
forceTime: true,
|
||||
parentHash: parent,
|
||||
coinbase: coinbase,
|
||||
random: random,
|
||||
withdrawals: withdrawals,
|
||||
noUncle: true,
|
||||
noTxs: noTxs,
|
||||
},
|
||||
result: make(chan *newPayloadResult, 1),
|
||||
}
|
||||
|
||||
@@ -637,7 +637,7 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
||||
|
||||
// This API should work even when the automatic sealing is not enabled
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user