Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ A tool for generating Ethereum consensus layer (beacon chain) genesis states for
## Features

- Generate beacon chain genesis states from execution layer genesis and validator configurations
- Support for all forks up to Electra
- Support for all forks up to Heze
- Support for validator onboarding via mnemonics or direct key imports
- Configurable genesis parameters
- Output in both SSZ and JSON formats
Expand Down Expand Up @@ -89,6 +89,12 @@ eth-genesis-state-generator beaconchain \
DENEB_FORK_EPOCH: 0
ELECTRA_FORK_VERSION: 0x05000000
ELECTRA_FORK_EPOCH: 0
FULU_FORK_VERSION: 0x06000000
FULU_FORK_EPOCH: 0
GLOAS_FORK_VERSION: 0x07000000
GLOAS_FORK_EPOCH: 0
HEZE_FORK_VERSION: 0x08000000
HEZE_FORK_EPOCH: 0
```

#### Validator Mnemonics File
Expand Down
6 changes: 6 additions & 0 deletions beaconchain/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ var ForkConfigs = []ForkConfig{
VersionField: "GLOAS_FORK_VERSION",
BuilderFn: NewGloasBuilder,
},
{
Version: spec.DataVersionHeze,
EpochField: "HEZE_FORK_EPOCH",
VersionField: "HEZE_FORK_VERSION",
BuilderFn: NewHezeBuilder,
},
}

func init() {
Expand Down
205 changes: 205 additions & 0 deletions beaconchain/heze.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package beaconchain

import (
"fmt"

"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethpandaops/go-eth2-client/http"
"github.com/ethpandaops/go-eth2-client/spec"
"github.com/ethpandaops/go-eth2-client/spec/altair"
"github.com/ethpandaops/go-eth2-client/spec/bellatrix"
"github.com/ethpandaops/go-eth2-client/spec/capella"
"github.com/ethpandaops/go-eth2-client/spec/gloas"
"github.com/ethpandaops/go-eth2-client/spec/heze"
"github.com/ethpandaops/go-eth2-client/spec/phase0"
"github.com/sirupsen/logrus"

"github.com/ethpandaops/eth-beacon-genesis/beaconconfig"
"github.com/ethpandaops/eth-beacon-genesis/beaconutils"
"github.com/ethpandaops/eth-beacon-genesis/validators"
dynssz "github.com/pk910/dynamic-ssz"
)

type hezeBuilder struct {
elGenesis *core.Genesis
clConfig *beaconconfig.Config
dynSsz *dynssz.DynSsz
shadowForkBlock *types.Block
validators []*validators.Validator
}

func NewHezeBuilder(elGenesis *core.Genesis, clConfig *beaconconfig.Config) BeaconGenesisBuilder {
return &hezeBuilder{
elGenesis: elGenesis,
clConfig: clConfig,
dynSsz: beaconutils.GetDynSSZ(clConfig),
}
}

func (b *hezeBuilder) SetShadowForkBlock(block *types.Block) {
b.shadowForkBlock = block
}

func (b *hezeBuilder) AddValidators(val []*validators.Validator) {
b.validators = append(b.validators, val...)
}

func (b *hezeBuilder) BuildState() (*spec.VersionedBeaconState, error) {
genesisBlock := b.shadowForkBlock
if genesisBlock == nil {
genesisBlock = b.elGenesis.ToBlock()
}

genesisBlockHash := genesisBlock.Hash()

extra := genesisBlock.Extra()
if len(extra) > 32 {
return nil, fmt.Errorf("extra data is %d bytes, max is %d", len(extra), 32)
}

syncCommitteeSize := b.clConfig.GetUintDefault("SYNC_COMMITTEE_SIZE", 512)
syncCommitteeMaskBytes := syncCommitteeSize / 8

if syncCommitteeSize%8 != 0 {
syncCommitteeMaskBytes++
}

emptyExecutionRequests := &gloas.ExecutionRequests{}

executionRequestsRoot, err := b.dynSsz.HashTreeRoot(emptyExecutionRequests)
if err != nil {
return nil, fmt.Errorf("failed to compute empty execution requests root: %w", err)
}

inclusionListCommitteeSize := b.clConfig.GetUintDefault("INCLUSION_LIST_COMMITTEE_SIZE", 16)
inclusionListBitsBytes := inclusionListCommitteeSize / 8

if inclusionListCommitteeSize%8 != 0 {
inclusionListBitsBytes++
}

genesisBlockBody := &heze.BeaconBlockBody{
SyncAggregate: &altair.SyncAggregate{
SyncCommitteeBits: make([]byte, syncCommitteeMaskBytes),
},
SignedExecutionPayloadBid: &heze.SignedExecutionPayloadBid{
Message: &heze.ExecutionPayloadBid{
ParentBlockHash: phase0.Hash32(genesisBlockHash),
ExecutionRequestsRoot: executionRequestsRoot,
InclusionListBits: make([]byte, inclusionListBitsBytes),
},
Signature: phase0.BLSSignature(make([]byte, 96)),
},
ParentExecutionRequests: emptyExecutionRequests,
}

genesisBlockBodyRoot, err := b.dynSsz.HashTreeRoot(genesisBlockBody)
if err != nil {
return nil, fmt.Errorf("failed to compute genesis block body root: %w", err)
}

genesisBuilders, genesisVals := beaconutils.SeparateBuildersFromValidators(b.validators)
clValidators, validatorsRoot := beaconutils.GetGenesisValidators(b.clConfig, genesisVals)
clBuilders := beaconutils.GetGenesisBuilders(b.clConfig, genesisBuilders)

syncCommittee, err := beaconutils.GetGenesisSyncCommittee(b.clConfig, clValidators, phase0.Hash32(genesisBlockHash))
if err != nil {
return nil, fmt.Errorf("failed to get genesis sync committee: %w", err)
}

proposers, err := beaconutils.GetGenesisProposers(b.clConfig, clValidators, phase0.Hash32(genesisBlockHash))
if err != nil {
return nil, fmt.Errorf("failed to calculate proposer lookahead: %w", err)
}

ptcWindow, err := beaconutils.GetGenesisPTCWindow(b.clConfig, clValidators, phase0.Hash32(genesisBlockHash))
if err != nil {
return nil, fmt.Errorf("failed to calculate PTC window: %w", err)
}

slotsPerEpoch := b.clConfig.GetUintDefault("SLOTS_PER_EPOCH", 32)

emptyBuilderPendingPayments := make([]*gloas.BuilderPendingPayment, slotsPerEpoch*2)
for i := range slotsPerEpoch * 2 {
emptyBuilderPendingPayments[i] = &gloas.BuilderPendingPayment{
Weight: 0,
Withdrawal: &gloas.BuilderPendingWithdrawal{
FeeRecipient: bellatrix.ExecutionAddress{},
Amount: 0,
BuilderIndex: 0,
},
}
}

genesisDelay := b.clConfig.GetUintDefault("GENESIS_DELAY", 604800)
blocksPerHistoricalRoot := b.clConfig.GetUintDefault("SLOTS_PER_HISTORICAL_ROOT", 8192)
epochsPerSlashingVector := b.clConfig.GetUintDefault("EPOCHS_PER_SLASHINGS_VECTOR", 8192)

minGenesisTime := b.clConfig.GetUintDefault("MIN_GENESIS_TIME", 0)
if minGenesisTime == 0 {
minGenesisTime = genesisBlock.Time()
}

genesisState := &heze.BeaconState{
GenesisTime: minGenesisTime + genesisDelay,
GenesisValidatorsRoot: validatorsRoot,
Fork: GetStateForkConfig(spec.DataVersionHeze, b.clConfig),
LatestBlockHeader: &phase0.BeaconBlockHeader{
BodyRoot: genesisBlockBodyRoot,
},
BlockRoots: make([]phase0.Root, blocksPerHistoricalRoot),
StateRoots: make([]phase0.Root, blocksPerHistoricalRoot),
JustificationBits: make([]byte, 1),
PreviousJustifiedCheckpoint: &phase0.Checkpoint{},
CurrentJustifiedCheckpoint: &phase0.Checkpoint{},
FinalizedCheckpoint: &phase0.Checkpoint{},
RANDAOMixes: beaconutils.SeedRandomMixes(phase0.Hash32(genesisBlockHash), b.clConfig),
Validators: clValidators,
Balances: beaconutils.GetGenesisBalances(b.clConfig, genesisVals),
Slashings: make([]phase0.Gwei, epochsPerSlashingVector),
PreviousEpochParticipation: make([]altair.ParticipationFlags, len(clValidators)),
CurrentEpochParticipation: make([]altair.ParticipationFlags, len(clValidators)),
InactivityScores: make([]uint64, len(clValidators)),
CurrentSyncCommittee: syncCommittee,
NextSyncCommittee: syncCommittee,
ProposerLookahead: proposers,
Builders: clBuilders,
LatestExecutionPayloadBid: &heze.ExecutionPayloadBid{
ParentBlockHash: phase0.Hash32(genesisBlockHash),
ExecutionRequestsRoot: executionRequestsRoot,
InclusionListBits: make([]byte, inclusionListBitsBytes),
},
ExecutionPayloadAvailability: beaconutils.MakeAllOnesBitvector(blocksPerHistoricalRoot),
BuilderPendingPayments: emptyBuilderPendingPayments,
PayloadExpectedWithdrawals: []*capella.Withdrawal{},
LatestBlockHash: phase0.Hash32(genesisBlockHash),
PTCWindow: ptcWindow,
}

versionedState := &spec.VersionedBeaconState{
Version: spec.DataVersionHeze,
Heze: genesisState,
}

logrus.Infof("genesis version: heze")
logrus.Infof("genesis time: %v", genesisState.GenesisTime)
logrus.Infof("genesis validators root: 0x%x", genesisState.GenesisValidatorsRoot)

return versionedState, nil
}

func (b *hezeBuilder) Serialize(state *spec.VersionedBeaconState, contentType http.ContentType) ([]byte, error) {
if state.Version != spec.DataVersionHeze {
return nil, fmt.Errorf("unsupported version: %s", state.Version)
}

switch contentType {
case http.ContentTypeSSZ:
return b.dynSsz.MarshalSSZ(state.Heze)
case http.ContentTypeJSON:
return state.Heze.MarshalJSON()
default:
return nil, fmt.Errorf("unsupported content type: %s", contentType)
}
}
14 changes: 14 additions & 0 deletions beaconconfig/presets/mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,17 @@ MAX_DATA_COLUMN_SIDECAR_SIZE: 8585272
MAX_PARTIAL_DATA_COLUMN_SIDECAR_SIZE: 8585741
# 196,932 bytes, ~192 KiB
MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE: 196932

# Mainnet preset - Heze

# Inclusion list committee
# ---------------------------------------------------------------
# 2**4 (= 16) validators
INCLUSION_LIST_COMMITTEE_SIZE: 16

# Type-specific SSZ bounds
# ---------------------------------------------------------------
# 196,934 bytes, ~192 KiB
MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE_HEZE: 196934
# 41,112 bytes, ~40 KiB
MAX_SIGNED_INCLUSION_LIST_SIZE: 41112
14 changes: 14 additions & 0 deletions beaconconfig/presets/minimal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,17 @@ MAX_DATA_COLUMN_SIDECAR_SIZE: 8585272
MAX_PARTIAL_DATA_COLUMN_SIDECAR_SIZE: 8585741
# 196,932 bytes, ~192 KiB
MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE: 196932

# Minimal preset - Heze

# Inclusion list committee
# ---------------------------------------------------------------
# 2**4 (= 16) validators
INCLUSION_LIST_COMMITTEE_SIZE: 16

# Type-specific SSZ bounds
# ---------------------------------------------------------------
# 196,934 bytes, ~192 KiB
MAX_SIGNED_EXECUTION_PAYLOAD_BID_SIZE_HEZE: 196934
# 41,112 bytes, ~40 KiB
MAX_SIGNED_INCLUSION_LIST_SIZE: 41112
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ go 1.26.0

require (
github.com/ethereum/go-ethereum v1.17.6
github.com/ethpandaops/go-eth2-client v0.1.7
github.com/ethpandaops/go-eth2-client v0.1.11-0.20261009113423-329cfee1d481
github.com/herumi/bls-eth-go-binary v1.37.0
github.com/holiman/uint256 v1.3.2
github.com/pk910/dynamic-ssz v1.3.3-0.20260812091520-ef568569f9c1
github.com/pk910/dynamic-ssz v1.3.3-0.20260921152246-990798fa9718
github.com/protolambda/bls12-381-util v0.1.0
github.com/sirupsen/logrus v1.10.2
github.com/tyler-smith/go-bip39 v1.1.0
Expand Down Expand Up @@ -52,7 +52,7 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pk910/hashtree-bindings v0.2.5 // indirect
github.com/pk910/hashtree-bindings v0.2.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJ
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
github.com/ethereum/go-ethereum v1.17.6 h1:27mdzjoN/bjz+rgjjZPGnD6E44W/Nd+vG+FKQFd/heg=
github.com/ethereum/go-ethereum v1.17.6/go.mod h1:nl9wZjMuIjAottU6bq82UihXPbyY0jHHwkYXhnYhmU4=
github.com/ethpandaops/go-eth2-client v0.1.7 h1:EzZP50rrpVsjk2U8cOpN+kgNDTd/9IZNQXkh9t8G9AQ=
github.com/ethpandaops/go-eth2-client v0.1.7/go.mod h1:MXQukU/345puJmB2EikoaeQIFizk3zbekPxwYyxG/t8=
github.com/ethpandaops/go-eth2-client v0.1.11-0.20261009113423-329cfee1d481 h1:GCNA0OapQAQYE+RmrYOJPIpKear76ttdHFjktX0GmrY=
github.com/ethpandaops/go-eth2-client v0.1.11-0.20261009113423-329cfee1d481/go.mod h1:sIVbaug0GsltsJS+hj8ID4dmWBIt8TXoNIesvuBCGt0=
github.com/ferranbt/fastssz v1.0.0 h1:9EXXYsracSqQRBQiHeaVsG/KQeYblPf40hsQPb9Dzk8=
github.com/ferranbt/fastssz v1.0.0/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/fjl/jsonw v0.1.0 h1:V3MyR79fjLpn/+bMgvegdGUIhoJOzjmqWcKDgcOmY1I=
Expand Down Expand Up @@ -166,10 +166,10 @@ github.com/pion/stun/v3 v3.1.2 h1:86IhD8wFn6IDW4b1/0QzoQS+f5PeA8OHHRn8UZW5ErY=
github.com/pion/stun/v3 v3.1.2/go.mod h1:H7gDic7nNwlUL05pbs6T1dtaBehh/KjupxfWw3ZI7cA=
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
github.com/pk910/dynamic-ssz v1.3.3-0.20260812091520-ef568569f9c1 h1:s9bqZutzC2JSpKMOIjFEGqpaCAOPtX4BqLEPLbrv7Wg=
github.com/pk910/dynamic-ssz v1.3.3-0.20260812091520-ef568569f9c1/go.mod h1:5qeouU7Xd1RPblfOr5QBHQyZlNmQJ6WJK9xK+p/5Iwo=
github.com/pk910/hashtree-bindings v0.2.5 h1:zwONqRIgw40yGEOnGXBORvQVYaXtdZE3bS1ZJohc2/s=
github.com/pk910/hashtree-bindings v0.2.5/go.mod h1:zrWt88783JmhBfcgni6kkIMYRdXTZi/FL//OyI5T/l4=
github.com/pk910/dynamic-ssz v1.3.3-0.20260921152246-990798fa9718 h1:ctdxKDq6c360JEH5gkS6jupRpyRO9BYTTPEdE6RUDuQ=
github.com/pk910/dynamic-ssz v1.3.3-0.20260921152246-990798fa9718/go.mod h1:mnRbhjX+4YMyP4jYMncTUA3aDpMzBLqAbjcvfCyOyRI=
github.com/pk910/hashtree-bindings v0.2.6 h1:BjRQho04nWzGrXwBCLVTyQRNV30O59iPcvQRcidFt2o=
github.com/pk910/hashtree-bindings v0.2.6/go.mod h1:zrWt88783JmhBfcgni6kkIMYRdXTZi/FL//OyI5T/l4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
Loading