diff --git a/beacon/types/beacon_block.go b/beacon/types/beacon_block.go
new file mode 100644
index 0000000000..e8a78ef70c
--- /dev/null
+++ b/beacon/types/beacon_block.go
@@ -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 .
+
+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)
+ case *deneb.BeaconBlock:
+ return convertPayload(&obj.Body.ExecutionPayload)
+ 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))
+ }
+}
+
+// Hash computes the hash of the block.
+func (b *BeaconBlock) Hash() common.Hash {
+ return common.Hash(b.blockObj.HashTreeRoot(configs.Mainnet, tree.GetHashFn()))
+}
diff --git a/beacon/types/config.go b/beacon/types/config.go
index 8cb8808b6f..eb3f061c7d 100644
--- a/beacon/types/config.go
+++ b/beacon/types/config.go
@@ -37,7 +37,7 @@ const syncCommitteeDomain = 7
// Fork describes a single beacon chain fork and also stores the calculated
// signature domain used after this fork.
type Fork struct {
- // Name of the fork in the chain config (config.yaml) file{
+ // Name of the fork in the chain config (config.yaml) file
Name string
// Epoch when given fork version is activated
@@ -110,6 +110,16 @@ type ChainConfig struct {
Forks Forks
}
+// ForkAtEpoch returns the latest active fork at the given epoch.
+func (c *ChainConfig) ForkAtEpoch(epoch uint64) *Fork {
+ for i := len(c.Forks) - 1; i >= 0; i-- {
+ if c.Forks[i].Epoch <= epoch {
+ return c.Forks[i]
+ }
+ }
+ return nil
+}
+
// AddFork adds a new item to the list of forks.
func (c *ChainConfig) AddFork(name string, epoch uint64, version []byte) *ChainConfig {
fork := &Fork{
diff --git a/beacon/types/exec_payload.go b/beacon/types/exec_payload.go
new file mode 100644
index 0000000000..7dbc151e99
--- /dev/null
+++ b/beacon/types/exec_payload.go
@@ -0,0 +1,141 @@
+// 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 .
+
+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"
+ zcommon "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) (*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, &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) {
+ 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, h *types.Header) {
+ 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)
+}
+
+func convertTransactions(list zcommon.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 zcommon.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
+}
diff --git a/beacon/types/header.go b/beacon/types/header.go
index 2ddc4575f1..c8388df1e7 100644
--- a/beacon/types/header.go
+++ b/beacon/types/header.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/beacon/merkle"
"github.com/ethereum/go-ethereum/beacon/params"
"github.com/ethereum/go-ethereum/common"
+ zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
)
//go:generate go run github.com/fjl/gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
@@ -57,6 +58,16 @@ type Header struct {
BodyRoot common.Hash `gencodec:"required" json:"body_root"`
}
+func headerFromZRNT(zh *zrntcommon.BeaconBlockHeader) Header {
+ return Header{
+ Slot: uint64(zh.Slot),
+ ProposerIndex: uint64(zh.ProposerIndex),
+ ParentRoot: common.Hash(zh.ParentRoot),
+ StateRoot: common.Hash(zh.StateRoot),
+ BodyRoot: common.Hash(zh.BodyRoot),
+ }
+}
+
// headerMarshaling is a field type overrides for gencodec.
type headerMarshaling struct {
Slot common.Decimal