mirror of https://github.com/ethereum/go-ethereum
beacon/blsync: support for deneb fork (#29180)
This adds support for the Deneb beacon chain fork, and fork handling in general, to the beacon chain light client implementation. Co-authored-by: Zsolt Felfoldi <zsfelfoldi@gmail.com>pull/29304/head
parent
04bf1c802f
commit
bca6c40709
@ -0,0 +1,147 @@ |
||||
// Copyright 2024 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 blsync |
||||
|
||||
import ( |
||||
"context" |
||||
"strings" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/engine" |
||||
"github.com/ethereum/go-ethereum/beacon/types" |
||||
"github.com/ethereum/go-ethereum/common" |
||||
ctypes "github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/rpc" |
||||
) |
||||
|
||||
type engineClient struct { |
||||
config *lightClientConfig |
||||
rpc *rpc.Client |
||||
rootCtx context.Context |
||||
cancelRoot context.CancelFunc |
||||
wg sync.WaitGroup |
||||
} |
||||
|
||||
func startEngineClient(config *lightClientConfig, rpc *rpc.Client, headCh <-chan types.ChainHeadEvent) *engineClient { |
||||
ctx, cancel := context.WithCancel(context.Background()) |
||||
ec := &engineClient{ |
||||
config: config, |
||||
rpc: rpc, |
||||
rootCtx: ctx, |
||||
cancelRoot: cancel, |
||||
} |
||||
ec.wg.Add(1) |
||||
go ec.updateLoop(headCh) |
||||
return ec |
||||
} |
||||
|
||||
func (ec *engineClient) stop() { |
||||
ec.cancelRoot() |
||||
ec.wg.Wait() |
||||
} |
||||
|
||||
func (ec *engineClient) updateLoop(headCh <-chan types.ChainHeadEvent) { |
||||
defer ec.wg.Done() |
||||
|
||||
for { |
||||
select { |
||||
case <-ec.rootCtx.Done(): |
||||
return |
||||
|
||||
case event := <-headCh: |
||||
if ec.rpc == nil { // dry run, no engine API specified
|
||||
log.Info("New execution block retrieved", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "finalized", event.Finalized) |
||||
continue |
||||
} |
||||
|
||||
fork := ec.config.ForkAtEpoch(event.BeaconHead.Epoch()) |
||||
forkName := strings.ToLower(fork.Name) |
||||
|
||||
if status, err := ec.callNewPayload(forkName, event); err == nil { |
||||
log.Info("Successful NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "status", status) |
||||
} else { |
||||
log.Error("Failed NewPayload", "number", event.Block.NumberU64(), "hash", event.Block.Hash(), "error", err) |
||||
} |
||||
|
||||
if status, err := ec.callForkchoiceUpdated(forkName, event); err == nil { |
||||
log.Info("Successful ForkchoiceUpdated", "head", event.Block.Hash(), "status", status) |
||||
} else { |
||||
log.Error("Failed ForkchoiceUpdated", "head", event.Block.Hash(), "error", err) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (ec *engineClient) callNewPayload(fork string, event types.ChainHeadEvent) (string, error) { |
||||
execData := engine.BlockToExecutableData(event.Block, nil, nil).ExecutionPayload |
||||
|
||||
var ( |
||||
method string |
||||
params = []any{execData} |
||||
) |
||||
switch fork { |
||||
case "deneb": |
||||
method = "engine_newPayloadV3" |
||||
parentBeaconRoot := event.BeaconHead.ParentRoot |
||||
blobHashes := collectBlobHashes(event.Block) |
||||
params = append(params, blobHashes, parentBeaconRoot) |
||||
case "capella": |
||||
method = "engine_newPayloadV2" |
||||
default: |
||||
method = "engine_newPayloadV1" |
||||
} |
||||
|
||||
ctx, cancel := context.WithTimeout(ec.rootCtx, time.Second*5) |
||||
defer cancel() |
||||
var resp engine.PayloadStatusV1 |
||||
err := ec.rpc.CallContext(ctx, &resp, method, params...) |
||||
return resp.Status, err |
||||
} |
||||
|
||||
func collectBlobHashes(b *ctypes.Block) []common.Hash { |
||||
list := make([]common.Hash, 0) |
||||
for _, tx := range b.Transactions() { |
||||
list = append(list, tx.BlobHashes()...) |
||||
} |
||||
return list |
||||
} |
||||
|
||||
func (ec *engineClient) callForkchoiceUpdated(fork string, event types.ChainHeadEvent) (string, error) { |
||||
update := engine.ForkchoiceStateV1{ |
||||
HeadBlockHash: event.Block.Hash(), |
||||
SafeBlockHash: event.Finalized, |
||||
FinalizedBlockHash: event.Finalized, |
||||
} |
||||
|
||||
var method string |
||||
switch fork { |
||||
case "deneb": |
||||
method = "engine_forkchoiceUpdatedV3" |
||||
case "capella": |
||||
method = "engine_forkchoiceUpdatedV2" |
||||
default: |
||||
method = "engine_forkchoiceUpdatedV1" |
||||
} |
||||
|
||||
ctx, cancel := context.WithTimeout(ec.rootCtx, time.Second*5) |
||||
defer cancel() |
||||
var resp engine.ForkChoiceResponse |
||||
err := ec.rpc.CallContext(ctx, &resp, method, update, nil) |
||||
return resp.PayloadStatus.Status, err |
||||
} |
@ -0,0 +1,110 @@ |
||||
// Copyright 2024 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 ( |
||||
"encoding/json" |
||||
"fmt" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/protolambda/zrnt/eth2/beacon/capella" |
||||
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common" |
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb" |
||||
"github.com/protolambda/zrnt/eth2/configs" |
||||
"github.com/protolambda/ztyp/tree" |
||||
) |
||||
|
||||
type blockObject interface { |
||||
HashTreeRoot(spec *zrntcommon.Spec, hFn tree.HashFn) zrntcommon.Root |
||||
Header(spec *zrntcommon.Spec) *zrntcommon.BeaconBlockHeader |
||||
} |
||||
|
||||
// BeaconBlock represents a full block in the beacon chain.
|
||||
type BeaconBlock struct { |
||||
blockObj blockObject |
||||
} |
||||
|
||||
// BlockFromJSON decodes a beacon block from JSON.
|
||||
func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) { |
||||
var obj blockObject |
||||
switch forkName { |
||||
case "deneb": |
||||
obj = new(deneb.BeaconBlock) |
||||
case "capella": |
||||
obj = new(capella.BeaconBlock) |
||||
default: |
||||
return nil, fmt.Errorf("unsupported fork: " + forkName) |
||||
} |
||||
if err := json.Unmarshal(data, obj); err != nil { |
||||
return nil, err |
||||
} |
||||
return &BeaconBlock{obj}, nil |
||||
} |
||||
|
||||
// NewBeaconBlock wraps a ZRNT block.
|
||||
func NewBeaconBlock(obj blockObject) *BeaconBlock { |
||||
switch obj := obj.(type) { |
||||
case *capella.BeaconBlock: |
||||
return &BeaconBlock{obj} |
||||
case *deneb.BeaconBlock: |
||||
return &BeaconBlock{obj} |
||||
default: |
||||
panic(fmt.Errorf("unsupported block type %T", obj)) |
||||
} |
||||
} |
||||
|
||||
// Slot returns the slot number of the block.
|
||||
func (b *BeaconBlock) Slot() uint64 { |
||||
switch obj := b.blockObj.(type) { |
||||
case *capella.BeaconBlock: |
||||
return uint64(obj.Slot) |
||||
case *deneb.BeaconBlock: |
||||
return uint64(obj.Slot) |
||||
default: |
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj)) |
||||
} |
||||
} |
||||
|
||||
// ExecutionPayload parses and returns the execution payload of the block.
|
||||
func (b *BeaconBlock) ExecutionPayload() (*types.Block, error) { |
||||
switch obj := b.blockObj.(type) { |
||||
case *capella.BeaconBlock: |
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot) |
||||
case *deneb.BeaconBlock: |
||||
return convertPayload(&obj.Body.ExecutionPayload, &obj.ParentRoot) |
||||
default: |
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj)) |
||||
} |
||||
} |
||||
|
||||
// Header returns the block's header data.
|
||||
func (b *BeaconBlock) Header() Header { |
||||
switch obj := b.blockObj.(type) { |
||||
case *capella.BeaconBlock: |
||||
return headerFromZRNT(obj.Header(configs.Mainnet)) |
||||
case *deneb.BeaconBlock: |
||||
return headerFromZRNT(obj.Header(configs.Mainnet)) |
||||
default: |
||||
panic(fmt.Errorf("unsupported block type %T", b.blockObj)) |
||||
} |
||||
} |
||||
|
||||
// Root computes the SSZ root hash of the block.
|
||||
func (b *BeaconBlock) Root() common.Hash { |
||||
return common.Hash(b.blockObj.HashTreeRoot(configs.Mainnet, tree.GetHashFn())) |
||||
} |
@ -0,0 +1,77 @@ |
||||
// Copyright 2024 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 ( |
||||
"os" |
||||
"path/filepath" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
) |
||||
|
||||
func TestBlockFromJSON(t *testing.T) { |
||||
type blocktest struct { |
||||
file string |
||||
version string |
||||
wantSlot uint64 |
||||
wantBlockNumber uint64 |
||||
wantBlockHash common.Hash |
||||
} |
||||
tests := []blocktest{ |
||||
{ |
||||
file: "block_deneb.json", |
||||
version: "deneb", |
||||
wantSlot: 8631513, |
||||
wantBlockNumber: 19431837, |
||||
wantBlockHash: common.HexToHash("0x4cf7d9108fc01b50023ab7cab9b372a96068fddcadec551630393b65acb1f34c"), |
||||
}, |
||||
{ |
||||
file: "block_capella.json", |
||||
version: "capella", |
||||
wantSlot: 7378495, |
||||
wantBlockNumber: 18189758, |
||||
wantBlockHash: common.HexToHash("0x802acf5c350f4252e31d83c431fcb259470250fa0edf49e8391cfee014239820"), |
||||
}, |
||||
} |
||||
|
||||
for _, test := range tests { |
||||
t.Run(test.file, func(t *testing.T) { |
||||
data, err := os.ReadFile(filepath.Join("testdata", test.file)) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
beaconBlock, err := BlockFromJSON(test.version, data) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
if beaconBlock.Slot() != test.wantSlot { |
||||
t.Errorf("wrong slot number %d", beaconBlock.Slot()) |
||||
} |
||||
execBlock, err := beaconBlock.ExecutionPayload() |
||||
if err != nil { |
||||
t.Fatalf("payload extraction failed: %v", err) |
||||
} |
||||
if execBlock.NumberU64() != test.wantBlockNumber { |
||||
t.Errorf("wrong block number: %v", execBlock.NumberU64()) |
||||
} |
||||
if execBlock.Hash() != test.wantBlockHash { |
||||
t.Errorf("wrong block hash: %v", execBlock.Hash()) |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,80 @@ |
||||
// Copyright 2024 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 ( |
||||
"encoding/json" |
||||
"fmt" |
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/merkle" |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/protolambda/zrnt/eth2/beacon/capella" |
||||
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common" |
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb" |
||||
"github.com/protolambda/ztyp/tree" |
||||
) |
||||
|
||||
type headerObject interface { |
||||
HashTreeRoot(hFn tree.HashFn) zrntcommon.Root |
||||
} |
||||
|
||||
type ExecutionHeader struct { |
||||
obj headerObject |
||||
} |
||||
|
||||
// HeaderFromJSON decodes an execution header from JSON data provided by
|
||||
// the beacon chain API.
|
||||
func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, error) { |
||||
var obj headerObject |
||||
switch forkName { |
||||
case "capella": |
||||
obj = new(capella.ExecutionPayloadHeader) |
||||
case "deneb": |
||||
obj = new(deneb.ExecutionPayloadHeader) |
||||
default: |
||||
return nil, fmt.Errorf("unsupported fork: " + forkName) |
||||
} |
||||
if err := json.Unmarshal(data, obj); err != nil { |
||||
return nil, err |
||||
} |
||||
return &ExecutionHeader{obj: obj}, nil |
||||
} |
||||
|
||||
func NewExecutionHeader(obj headerObject) *ExecutionHeader { |
||||
switch obj.(type) { |
||||
case *capella.ExecutionPayloadHeader: |
||||
case *deneb.ExecutionPayloadHeader: |
||||
default: |
||||
panic(fmt.Errorf("unsupported ExecutionPayloadHeader type %T", obj)) |
||||
} |
||||
return &ExecutionHeader{obj: obj} |
||||
} |
||||
|
||||
func (eh *ExecutionHeader) PayloadRoot() merkle.Value { |
||||
return merkle.Value(eh.obj.HashTreeRoot(tree.GetHashFn())) |
||||
} |
||||
|
||||
func (eh *ExecutionHeader) BlockHash() common.Hash { |
||||
switch obj := eh.obj.(type) { |
||||
case *capella.ExecutionPayloadHeader: |
||||
return common.Hash(obj.BlockHash) |
||||
case *deneb.ExecutionPayloadHeader: |
||||
return common.Hash(obj.BlockHash) |
||||
default: |
||||
panic(fmt.Errorf("unsupported ExecutionPayloadHeader type %T", obj)) |
||||
} |
||||
} |
@ -0,0 +1,144 @@ |
||||
// Copyright 2024 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 ( |
||||
"fmt" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/trie" |
||||
"github.com/holiman/uint256" |
||||
"github.com/protolambda/zrnt/eth2/beacon/capella" |
||||
zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common" |
||||
"github.com/protolambda/zrnt/eth2/beacon/deneb" |
||||
) |
||||
|
||||
type payloadType interface { |
||||
*capella.ExecutionPayload | *deneb.ExecutionPayload |
||||
} |
||||
|
||||
// convertPayload converts a beacon chain execution payload to types.Block.
|
||||
func convertPayload[T payloadType](payload T, parentRoot *zrntcommon.Root) (*types.Block, error) { |
||||
var ( |
||||
header types.Header |
||||
transactions []*types.Transaction |
||||
withdrawals []*types.Withdrawal |
||||
expectedHash [32]byte |
||||
err error |
||||
) |
||||
switch p := any(payload).(type) { |
||||
case *capella.ExecutionPayload: |
||||
convertCapellaHeader(p, &header) |
||||
transactions, err = convertTransactions(p.Transactions, &header) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
withdrawals = convertWithdrawals(p.Withdrawals, &header) |
||||
expectedHash = p.BlockHash |
||||
case *deneb.ExecutionPayload: |
||||
convertDenebHeader(p, common.Hash(*parentRoot), &header) |
||||
transactions, err = convertTransactions(p.Transactions, &header) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
withdrawals = convertWithdrawals(p.Withdrawals, &header) |
||||
expectedHash = p.BlockHash |
||||
default: |
||||
panic("unsupported block type") |
||||
} |
||||
|
||||
block := types.NewBlockWithHeader(&header) |
||||
block = block.WithBody(transactions, nil) |
||||
block = block.WithWithdrawals(withdrawals) |
||||
hash := block.Hash() |
||||
if hash != expectedHash { |
||||
return block, fmt.Errorf("Sanity check failed, payload hash does not match (expected %x, got %x)", expectedHash, hash) |
||||
} |
||||
return block, nil |
||||
} |
||||
|
||||
func convertCapellaHeader(payload *capella.ExecutionPayload, h *types.Header) { |
||||
// note: h.TxHash is set in convertTransactions
|
||||
h.ParentHash = common.Hash(payload.ParentHash) |
||||
h.UncleHash = types.EmptyUncleHash |
||||
h.Coinbase = common.Address(payload.FeeRecipient) |
||||
h.Root = common.Hash(payload.StateRoot) |
||||
h.ReceiptHash = common.Hash(payload.ReceiptsRoot) |
||||
h.Bloom = types.Bloom(payload.LogsBloom) |
||||
h.Difficulty = common.Big0 |
||||
h.Number = new(big.Int).SetUint64(uint64(payload.BlockNumber)) |
||||
h.GasLimit = uint64(payload.GasLimit) |
||||
h.GasUsed = uint64(payload.GasUsed) |
||||
h.Time = uint64(payload.Timestamp) |
||||
h.Extra = []byte(payload.ExtraData) |
||||
h.MixDigest = common.Hash(payload.PrevRandao) |
||||
h.Nonce = types.BlockNonce{} |
||||
h.BaseFee = (*uint256.Int)(&payload.BaseFeePerGas).ToBig() |
||||
} |
||||
|
||||
func convertDenebHeader(payload *deneb.ExecutionPayload, parentRoot common.Hash, h *types.Header) { |
||||
// note: h.TxHash is set in convertTransactions
|
||||
h.ParentHash = common.Hash(payload.ParentHash) |
||||
h.UncleHash = types.EmptyUncleHash |
||||
h.Coinbase = common.Address(payload.FeeRecipient) |
||||
h.Root = common.Hash(payload.StateRoot) |
||||
h.ReceiptHash = common.Hash(payload.ReceiptsRoot) |
||||
h.Bloom = types.Bloom(payload.LogsBloom) |
||||
h.Difficulty = common.Big0 |
||||
h.Number = new(big.Int).SetUint64(uint64(payload.BlockNumber)) |
||||
h.GasLimit = uint64(payload.GasLimit) |
||||
h.GasUsed = uint64(payload.GasUsed) |
||||
h.Time = uint64(payload.Timestamp) |
||||
h.Extra = []byte(payload.ExtraData) |
||||
h.MixDigest = common.Hash(payload.PrevRandao) |
||||
h.Nonce = types.BlockNonce{} |
||||
h.BaseFee = (*uint256.Int)(&payload.BaseFeePerGas).ToBig() |
||||
// new in deneb
|
||||
h.BlobGasUsed = (*uint64)(&payload.BlobGasUsed) |
||||
h.ExcessBlobGas = (*uint64)(&payload.ExcessBlobGas) |
||||
h.ParentBeaconRoot = &parentRoot |
||||
} |
||||
|
||||
func convertTransactions(list zrntcommon.PayloadTransactions, execHeader *types.Header) ([]*types.Transaction, error) { |
||||
txs := make([]*types.Transaction, len(list)) |
||||
for i, opaqueTx := range list { |
||||
var tx types.Transaction |
||||
if err := tx.UnmarshalBinary(opaqueTx); err != nil { |
||||
return nil, fmt.Errorf("failed to parse tx %d: %v", i, err) |
||||
} |
||||
txs[i] = &tx |
||||
} |
||||
execHeader.TxHash = types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)) |
||||
return txs, nil |
||||
} |
||||
|
||||
func convertWithdrawals(list zrntcommon.Withdrawals, execHeader *types.Header) []*types.Withdrawal { |
||||
withdrawals := make([]*types.Withdrawal, len(list)) |
||||
for i, w := range list { |
||||
withdrawals[i] = &types.Withdrawal{ |
||||
Index: uint64(w.Index), |
||||
Validator: uint64(w.ValidatorIndex), |
||||
Address: common.Address(w.Address), |
||||
Amount: uint64(w.Amount), |
||||
} |
||||
} |
||||
wroot := types.DeriveSha(types.Withdrawals(withdrawals), trie.NewStackTrie(nil)) |
||||
execHeader.WithdrawalsHash = &wroot |
||||
return withdrawals |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,69 +0,0 @@ |
||||
// Copyright 2024 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 main |
||||
|
||||
import ( |
||||
"context" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/engine" |
||||
"github.com/ethereum/go-ethereum/beacon/types" |
||||
"github.com/ethereum/go-ethereum/common" |
||||
|
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/rpc" |
||||
) |
||||
|
||||
func updateEngineApi(client *rpc.Client, headCh chan types.ChainHeadEvent) { |
||||
for event := range headCh { |
||||
if client == nil { // dry run, no engine API specified
|
||||
log.Info("New execution block retrieved", "block number", event.HeadBlock.Number, "block hash", event.HeadBlock.BlockHash, "finalized block hash", event.Finalized) |
||||
} else { |
||||
if status, err := callNewPayloadV2(client, event.HeadBlock); err == nil { |
||||
log.Info("Successful NewPayload", "block number", event.HeadBlock.Number, "block hash", event.HeadBlock.BlockHash, "status", status) |
||||
} else { |
||||
log.Error("Failed NewPayload", "block number", event.HeadBlock.Number, "block hash", event.HeadBlock.BlockHash, "error", err) |
||||
} |
||||
if status, err := callForkchoiceUpdatedV1(client, event.HeadBlock.BlockHash, event.Finalized); err == nil { |
||||
log.Info("Successful ForkchoiceUpdated", "head", event.HeadBlock.BlockHash, "status", status) |
||||
} else { |
||||
log.Error("Failed ForkchoiceUpdated", "head", event.HeadBlock.BlockHash, "error", err) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func callNewPayloadV2(client *rpc.Client, execData *engine.ExecutableData) (string, error) { |
||||
var resp engine.PayloadStatusV1 |
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) |
||||
err := client.CallContext(ctx, &resp, "engine_newPayloadV2", execData) |
||||
cancel() |
||||
return resp.Status, err |
||||
} |
||||
|
||||
func callForkchoiceUpdatedV1(client *rpc.Client, headHash, finalizedHash common.Hash) (string, error) { |
||||
var resp engine.ForkChoiceResponse |
||||
update := engine.ForkchoiceStateV1{ |
||||
HeadBlockHash: headHash, |
||||
SafeBlockHash: finalizedHash, |
||||
FinalizedBlockHash: finalizedHash, |
||||
} |
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) |
||||
err := client.CallContext(ctx, &resp, "engine_forkchoiceUpdatedV1", update, nil) |
||||
cancel() |
||||
return resp.PayloadStatus.Status, err |
||||
} |
@ -1,88 +0,0 @@ |
||||
// Copyright 2024 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 catalyst |
||||
|
||||
import ( |
||||
"github.com/ethereum/go-ethereum/beacon/engine" |
||||
"github.com/ethereum/go-ethereum/beacon/types" |
||||
"github.com/ethereum/go-ethereum/eth" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
) |
||||
|
||||
// Blsync tracks the head of the beacon chain through the beacon light client
|
||||
// and drives the local node via ConsensusAPI.
|
||||
type Blsync struct { |
||||
engine *ConsensusAPI |
||||
client Client |
||||
headCh chan types.ChainHeadEvent |
||||
headSub event.Subscription |
||||
|
||||
quitCh chan struct{} |
||||
} |
||||
|
||||
type Client interface { |
||||
SubscribeChainHeadEvent(ch chan<- types.ChainHeadEvent) event.Subscription |
||||
Start() |
||||
Stop() |
||||
} |
||||
|
||||
// NewBlsync creates a new beacon light syncer.
|
||||
func NewBlsync(client Client, eth *eth.Ethereum) *Blsync { |
||||
return &Blsync{ |
||||
engine: newConsensusAPIWithoutHeartbeat(eth), |
||||
client: client, |
||||
headCh: make(chan types.ChainHeadEvent, 16), |
||||
quitCh: make(chan struct{}), |
||||
} |
||||
} |
||||
|
||||
// Start starts underlying beacon light client and the sync logic for driving
|
||||
// the local node.
|
||||
func (b *Blsync) Start() error { |
||||
log.Info("Beacon light sync started") |
||||
b.headSub = b.client.SubscribeChainHeadEvent(b.headCh) |
||||
go b.client.Start() |
||||
|
||||
for { |
||||
select { |
||||
case <-b.quitCh: |
||||
return nil |
||||
case head := <-b.headCh: |
||||
if _, err := b.engine.NewPayloadV2(*head.HeadBlock); err != nil { |
||||
log.Error("failed to send new payload", "err", err) |
||||
continue |
||||
} |
||||
update := engine.ForkchoiceStateV1{ |
||||
HeadBlockHash: head.HeadBlock.BlockHash, |
||||
SafeBlockHash: head.Finalized, //TODO pass finalized or empty hash here?
|
||||
FinalizedBlockHash: head.Finalized, |
||||
} |
||||
if _, err := b.engine.ForkchoiceUpdatedV1(update, nil); err != nil { |
||||
log.Error("failed to send forkchoice updated", "err", err) |
||||
continue |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Stop signals to the light client and syncer to exit.
|
||||
func (b *Blsync) Stop() error { |
||||
b.client.Stop() |
||||
close(b.quitCh) |
||||
return nil |
||||
} |
Loading…
Reference in new issue