Skip to content

Commit

Permalink
Withdrawals part 1 (erigontech#6009)
Browse files Browse the repository at this point in the history
This PR partially implements
[EIP-4895](https://eips.ethereum.org/EIPS/eip-4895): Beacon chain push
withdrawals as operations. The new Engine API methods
(ethereum/execution-apis#195) are implemented.

_Body downloader and saving withdrawals into DB are not implemented
yet!_
  • Loading branch information
yperbasis authored Dec 1, 2022
1 parent fd3c44f commit d82c778
Show file tree
Hide file tree
Showing 43 changed files with 1,049 additions and 310 deletions.
1 change: 1 addition & 0 deletions cmd/evm/internal/t8ntool/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type stEnv struct {
Ommers []ommer `json:"ommers,omitempty"`
BaseFee *big.Int `json:"currentBaseFee,omitempty"`
ParentUncleHash common.Hash `json:"parentUncleHash"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
}

type stEnvMarshaling struct {
Expand Down
9 changes: 7 additions & 2 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,13 @@ func Main(ctx *cli.Context) error {

// Sanity check, to not `panic` in state_transition
if prestate.Env.Random != nil && !eip1559 {
return NewError(ErrorVMConfig, errors.New("can only apply RANDOM on top of London chainrules"))
return NewError(ErrorVMConfig, errors.New("can only apply RANDOM on top of London chain rules"))
}

if chainConfig.IsShanghai(prestate.Env.Number) && prestate.Env.Withdrawals == nil {
return NewError(ErrorVMConfig, errors.New("Shanghai config but missing 'withdrawals' in env section"))
}

if env := prestate.Env; env.Difficulty == nil {
// If difficulty was not provided by caller, we need to calculate it.
switch {
Expand All @@ -259,7 +264,7 @@ func Main(ctx *cli.Context) error {
ommerN.SetUint64(header.Number.Uint64() - ommer.Delta)
ommerHeaders[i] = &types.Header{Coinbase: ommer.Address, Number: &ommerN}
}
block := types.NewBlock(header, txs, ommerHeaders, nil)
block := types.NewBlock(header, txs, ommerHeaders, nil /* receipts */, prestate.Env.Withdrawals)

var hashError error
getHash := func(num uint64) common.Hash {
Expand Down
3 changes: 3 additions & 0 deletions cmd/rpcdaemon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,11 @@ The following table shows the current implementation status of Erigon's RPC daem
| eth_unsubscribe | Yes | Websock Only |
| | | |
| engine_newPayloadV1 | Yes | |
| engine_newPayloadV2 | Yes | |
| engine_forkchoiceUpdatedV1 | Yes | |
| engine_forkchoiceUpdatedV2 | Yes | |
| engine_getPayloadV1 | Yes | |
| engine_getPayloadV2 | Yes | |
| engine_exchangeTransitionConfigurationV1 | Yes | |
| | | |
| debug_accountRange | Yes | Private Erigon debug module |
Expand Down
306 changes: 236 additions & 70 deletions cmd/rpcdaemon/commands/engine_api.go

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion cmd/rpcdaemon/commands/engine_api_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package commands

import (
"context"
"testing"

"github.com/ledgerwatch/erigon-lib/gointerfaces"
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
"github.com/ledgerwatch/erigon/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// Test case for https://github.com/ethereum/execution-apis/pull/217 responses
func TestZeroLatestValidHash(t *testing.T) {
payloadStatus := remote.EnginePayloadStatus{Status: remote.EngineStatus_INVALID, LatestValidHash: gointerfaces.ConvertHashToH256(common.Hash{})}
json := convertPayloadStatus(&payloadStatus)
json, err := convertPayloadStatus(context.TODO(), nil, &payloadStatus)
require.NoError(t, err)
assert.Equal(t, "INVALID", json["status"])
assert.Equal(t, common.Hash{}, json["latestValidHash"])
}
14 changes: 14 additions & 0 deletions cmd/rpcdaemon/rpcservices/eth_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,30 @@ func (back *RemoteBackend) EngineNewPayloadV1(ctx context.Context, payload *type
return back.remoteEthBackend.EngineNewPayloadV1(ctx, payload)
}

func (back *RemoteBackend) EngineNewPayloadV2(ctx context.Context, payload *types2.ExecutionPayloadV2) (res *remote.EnginePayloadStatus, err error) {
return back.remoteEthBackend.EngineNewPayloadV2(ctx, payload)
}

func (back *RemoteBackend) EngineForkchoiceUpdatedV1(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequest) (*remote.EngineForkChoiceUpdatedReply, error) {
return back.remoteEthBackend.EngineForkChoiceUpdatedV1(ctx, request)
}

func (back *RemoteBackend) EngineForkchoiceUpdatedV2(ctx context.Context, request *remote.EngineForkChoiceUpdatedRequestV2) (*remote.EngineForkChoiceUpdatedReply, error) {
return back.remoteEthBackend.EngineForkChoiceUpdatedV2(ctx, request)
}

func (back *RemoteBackend) EngineGetPayloadV1(ctx context.Context, payloadId uint64) (res *types2.ExecutionPayload, err error) {
return back.remoteEthBackend.EngineGetPayloadV1(ctx, &remote.EngineGetPayloadRequest{
PayloadId: payloadId,
})
}

func (back *RemoteBackend) EngineGetPayloadV2(ctx context.Context, payloadId uint64) (res *types2.ExecutionPayloadV2, err error) {
return back.remoteEthBackend.EngineGetPayloadV2(ctx, &remote.EngineGetPayloadRequest{
PayloadId: payloadId,
})
}

func (back *RemoteBackend) NodeInfo(ctx context.Context, limit uint32) ([]p2p.NodeInfo, error) {
nodes, err := back.remoteEthBackend.NodeInfo(ctx, &remote.NodesInfoRequest{Limit: limit})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/state/commands/erigon2.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ func processBlock(trace bool, txNumStart uint64, rw *ReaderWrapper, ww *WriterWr
ibs := state.New(rw)

// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
if _, _, _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, block.Transactions(), block.Uncles(), receipts, nil, nil, nil, nil); err != nil {
if _, _, _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, block.Transactions(), block.Uncles(), receipts, block.Withdrawals(), nil, nil, nil, nil); err != nil {
return 0, nil, fmt.Errorf("finalize of block %d failed: %w", block.NumberU64(), err)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/state/commands/erigon4.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ func processBlock23(startTxNum uint64, trace bool, txNumStart uint64, rw *Reader
}

// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
if _, _, err := engine.Finalize(chainConfig, header, ibs, block.Transactions(), block.Uncles(), receipts, nil, nil, nil); err != nil {
if _, _, err := engine.Finalize(chainConfig, header, ibs, block.Transactions(), block.Uncles(), receipts, block.Withdrawals(), nil, nil, nil); err != nil {
return 0, nil, fmt.Errorf("finalize of block %d failed: %w", block.NumberU64(), err)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/state/commands/opcode_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ func runBlock(engine consensus.Engine, ibs *state.IntraBlockState, txnWriter sta
if !vmConfig.ReadOnly {
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
tx := block.Transactions()
if _, _, _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, tx, block.Uncles(), receipts, nil, nil, nil, nil); err != nil {
if _, _, _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, tx, block.Uncles(), receipts, block.Withdrawals(), nil, nil, nil, nil); err != nil {
return nil, fmt.Errorf("finalize of block %d failed: %w", block.NumberU64(), err)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/state/exec3/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func (rw *Worker) RunTxTask(txTask *exec22.TxTask) {
syscall := func(contract common.Address, data []byte) ([]byte, error) {
return core.SysCallContract(contract, data, *rw.chainConfig, ibs, header, rw.engine, false /* constCall */)
}
if _, _, err := rw.engine.Finalize(rw.chainConfig, header, ibs, txTask.Txs, txTask.Uncles, nil /* receipts */, rw.epoch, rw.chain, syscall); err != nil {
if _, _, err := rw.engine.Finalize(rw.chainConfig, header, ibs, txTask.Txs, txTask.Uncles, nil /* receipts */, nil /* withdrawals */, rw.epoch, rw.chain, syscall); err != nil {
//fmt.Printf("error=%v\n", err)
txTask.Error = err
} else {
Expand Down
2 changes: 1 addition & 1 deletion cmd/state/exec3/state_recon.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (rw *ReconWorker) runTxTask(txTask *exec22.TxTask) {
syscall := func(contract common.Address, data []byte) ([]byte, error) {
return core.SysCallContract(contract, data, *rw.chainConfig, ibs, txTask.Header, rw.engine, false /* constCall */)
}
if _, _, err := rw.engine.Finalize(rw.chainConfig, txTask.Header, ibs, txTask.Txs, txTask.Uncles, nil /* receipts */, rw.epoch, rw.chain, syscall); err != nil {
if _, _, err := rw.engine.Finalize(rw.chainConfig, txTask.Header, ibs, txTask.Txs, txTask.Uncles, nil /* receipts */, nil /* withdrawals */, rw.epoch, rw.chain, syscall); err != nil {
panic(fmt.Errorf("finalize of block %d failed: %w", txTask.BlockNum, err))
}
}
Expand Down
12 changes: 6 additions & 6 deletions consensus/aura/aura.go
Original file line number Diff line number Diff line change
Expand Up @@ -815,8 +815,8 @@ func (c *AuRa) Initialize(config *params.ChainConfig, chain consensus.ChainHeade

// word `signal epoch` == word `pending epoch`
func (c *AuRa) Finalize(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, e consensus.EpochReader,
chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
) (types.Transactions, types.Receipts, error) {
// accumulateRewards retrieves rewards for a block and applies them to the coinbase accounts for miner and uncle miners
beneficiaries, _, rewards, err := AccumulateRewards(config, c, header, uncles, syscall)
Expand Down Expand Up @@ -961,16 +961,16 @@ func allHeadersUntil(chain consensus.ChainHeaderReader, from *types.Header, to c

// FinalizeAndAssemble implements consensus.Engine
func (c *AuRa) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, e consensus.EpochReader,
chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
) (*types.Block, types.Transactions, types.Receipts, error) {
outTxs, outReceipts, err := c.Finalize(chainConfig, header, state, txs, uncles, receipts, e, chain, syscall)
outTxs, outReceipts, err := c.Finalize(chainConfig, header, state, txs, uncles, receipts, withdrawals, e, chain, syscall)
if err != nil {
return nil, nil, nil, err
}

// Assemble and return the final block for sealing
return types.NewBlock(header, outTxs, uncles, outReceipts), outTxs, outReceipts, nil
return types.NewBlock(header, outTxs, uncles, outReceipts, withdrawals), outTxs, outReceipts, nil
}

// Authorize injects a private key into the consensus engine to mint new blocks
Expand Down
17 changes: 13 additions & 4 deletions consensus/bor/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,10 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
return err
}

if header.WithdrawalsHash != nil {
return consensus.ErrUnexpectedWithdrawals
}

if parent.Time+c.config.CalculatePeriod(number) > header.Time {
return ErrInvalidTimestamp
}
Expand Down Expand Up @@ -712,7 +716,10 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header, s

// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given.
func (c *Bor) Finalize(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs types.Transactions, uncles []*types.Header, r types.Receipts, e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall) (types.Transactions, types.Receipts, error) {
func (c *Bor) Finalize(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
) (types.Transactions, types.Receipts, error) {
var err error
headerNumber := header.Number.Uint64()
if isSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) {
Expand Down Expand Up @@ -778,8 +785,10 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.Intra

// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Bor) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState, txs types.Transactions, uncles []*types.Header, receipts types.Receipts,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call) (*types.Block, types.Transactions, types.Receipts, error) {
func (c *Bor) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
) (*types.Block, types.Transactions, types.Receipts, error) {
// stateSyncData := []*types.StateSyncData{}

headerNumber := header.Number.Uint64()
Expand Down Expand Up @@ -813,7 +822,7 @@ func (c *Bor) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types
header.UncleHash = types.CalcUncleHash(nil)

// Assemble block
block := types.NewBlock(header, txs, nil, receipts)
block := types.NewBlock(header, txs, nil, receipts, withdrawals)

// set state sync
// bc := chain.(*core.BlockChain)
Expand Down
10 changes: 5 additions & 5 deletions consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,8 @@ func (c *Clique) Initialize(config *params.ChainConfig, chain consensus.ChainHea
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given.
func (c *Clique) Finalize(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, r types.Receipts, e consensus.EpochReader,
chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
) (types.Transactions, types.Receipts, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.UncleHash = types.CalcUncleHash(nil)
Expand All @@ -373,14 +373,14 @@ func (c *Clique) Finalize(config *params.ChainConfig, header *types.Header, stat
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, e consensus.EpochReader,
chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
) (*types.Block, types.Transactions, types.Receipts, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.UncleHash = types.CalcUncleHash(nil)

// Assemble and return the final block for sealing
return types.NewBlock(header, txs, nil, receipts), txs, receipts, nil
return types.NewBlock(header, txs, nil, receipts, withdrawals), txs, receipts, nil
}

// Authorize injects a private key into the consensus engine to mint new blocks
Expand Down
4 changes: 4 additions & 0 deletions consensus/clique/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
}
}

if header.WithdrawalsHash != nil {
return consensus.ErrUnexpectedWithdrawals
}

// If all checks passed, validate any special fields for hard forks
if err := misc.VerifyForkHashes(c.chainConfig, header, false); err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,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(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
e EpochReader, chain ChainHeaderReader, syscall SystemCall,
) (types.Transactions, types.Receipts, error)

Expand All @@ -113,7 +113,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(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts,
txs types.Transactions, uncles []*types.Header, receipts types.Receipts, withdrawals []*types.Withdrawal,
e EpochReader, chain ChainHeaderReader, syscall SystemCall, call Call,
) (*types.Block, types.Transactions, types.Receipts, error)

Expand Down
3 changes: 3 additions & 0 deletions consensus/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ var (
// ErrInvalidNumber is returned if a block's number doesn't equal its parent's
// plus one.
ErrInvalidNumber = errors.New("invalid block number")

// ErrUnexpectedWithdrawals is returned if a pre-Shanghai block has withdrawals.
ErrUnexpectedWithdrawals = errors.New("unexpected withdrawals")
)
16 changes: 10 additions & 6 deletions consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ func VerifyHeaderBasics(chain consensus.ChainHeaderReader, header, parent *types
return consensus.ErrInvalidNumber
}

if header.WithdrawalsHash != nil {
return consensus.ErrUnexpectedWithdrawals
}

// If all checks passed, validate any special fields for hard forks
if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil {
return err
Expand Down Expand Up @@ -551,8 +555,8 @@ func (ethash *Ethash) Initialize(config *params.ChainConfig, chain consensus.Cha
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state on the header
func (ethash *Ethash) Finalize(config *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, r types.Receipts, e consensus.EpochReader,
chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall,
) (types.Transactions, types.Receipts, error) {
// Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(config, state, header, uncles)
Expand All @@ -562,17 +566,17 @@ func (ethash *Ethash) Finalize(config *params.ChainConfig, header *types.Header,
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chainConfig *params.ChainConfig, header *types.Header, state *state.IntraBlockState,
txs types.Transactions, uncles []*types.Header, r types.Receipts, e consensus.EpochReader,
chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
txs types.Transactions, uncles []*types.Header, r types.Receipts, withdrawals []*types.Withdrawal,
e consensus.EpochReader, chain consensus.ChainHeaderReader, syscall consensus.SystemCall, call consensus.Call,
) (*types.Block, types.Transactions, types.Receipts, error) {

// Finalize block
outTxs, outR, err := ethash.Finalize(chainConfig, header, state, txs, uncles, r, e, chain, syscall)
outTxs, outR, err := ethash.Finalize(chainConfig, header, state, txs, uncles, r, withdrawals, e, chain, syscall)
if err != nil {
return nil, nil, nil, err
}
// Header seems complete, assemble into a block and return
return types.NewBlock(header, outTxs, uncles, outR), outTxs, outR, nil
return types.NewBlock(header, outTxs, uncles, outR, withdrawals), outTxs, outR, nil
}

// SealHash returns the hash of a block prior to it being sealed.
Expand Down
Loading

0 comments on commit d82c778

Please sign in to comment.