Skip to content

Commit

Permalink
Add custom chain support to Caplin (#11508)
Browse files Browse the repository at this point in the history
Things added:
* Keep genesis file stored in datadir
* Fixed race conditions in Sentinel
* Fixed scheduling of hard fork for same epoch-hardforks
* Small refactorings

---------

Co-authored-by: Kewei <kewei.train@gmail.com>
  • Loading branch information
Giulio2002 and domiwei authored Aug 15, 2024
1 parent 41465d5 commit f6a39a5
Show file tree
Hide file tree
Showing 37 changed files with 446 additions and 452 deletions.
4 changes: 2 additions & 2 deletions cl/beacon/handler/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ func (a *ApiHandler) getDepositContract(w http.ResponseWriter, r *http.Request)
func (a *ApiHandler) getForkSchedule(w http.ResponseWriter, r *http.Request) (*beaconhttp.BeaconResponse, error) {
response := []cltypes.Fork{}
// create first response (unordered and incomplete)
for currentVersion, epoch := range a.beaconChainCfg.ForkVersionSchedule {
for currentVersion, entry := range a.beaconChainCfg.ForkVersionSchedule {
response = append(response, cltypes.Fork{
CurrentVersion: currentVersion,
Epoch: epoch,
Epoch: entry.Epoch,
})
}
// Sort the responses by epoch
Expand Down
75 changes: 45 additions & 30 deletions cl/clparams/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/erigontech/erigon-lib/chain/networkname"
libcommon "github.com/erigontech/erigon-lib/common"

"github.com/erigontech/erigon/cl/beacon/beacon_router_configuration"
"github.com/erigontech/erigon/cl/utils"
)

Expand All @@ -51,6 +52,32 @@ type CaplinConfig struct {
MevRelayUrl string
// EnableValidatorMonitor is used to enable the validator monitor metrics and corresponding logs
EnableValidatorMonitor bool

// Devnets config
CustomConfigPath string
CustomGenesisStatePath string

// Network stuff
CaplinDiscoveryAddr string
CaplinDiscoveryPort uint64
CaplinDiscoveryTCPPort uint64
SentinelAddr string
SentinelPort uint64
// Erigon Sync
LoopBlockLimit uint64
// Beacon API router configuration
BeaconAPIRouter beacon_router_configuration.RouterConfiguration

BootstrapNodes []string
StaticPeers []string
}

func (c CaplinConfig) IsDevnet() bool {
return c.CustomConfigPath != "" || c.CustomGenesisStatePath != ""
}

func (c CaplinConfig) HaveInvalidDevnetParams() bool {
return c.CustomConfigPath == "" || c.CustomGenesisStatePath == ""
}

func (c CaplinConfig) RelayUrlExist() bool {
Expand All @@ -65,6 +92,8 @@ const (
SepoliaNetwork NetworkType = 11155111
GnosisNetwork NetworkType = 100
ChiadoNetwork NetworkType = 10200

CustomNetwork NetworkType = -1
)

const (
Expand Down Expand Up @@ -158,9 +187,8 @@ type NetworkConfig struct {
SyncCommsSubnetKey string // SyncCommsSubnetKey is the ENR key of the sync committee subnet bitfield in the enr.
MinimumPeersInSubnetSearch uint64 // PeersInSubnetSearch is the required amount of peers that we need to be able to lookup in a subnet search.

ContractDeploymentBlock uint64 // the eth1 block in which the deposit contract is deployed.
BootNodes []string
StaticPeers []string
BootNodes []string
StaticPeers []string
}

var NetworkConfigs map[NetworkType]NetworkConfig = map[NetworkType]NetworkConfig{
Expand All @@ -180,7 +208,6 @@ var NetworkConfigs map[NetworkType]NetworkConfig = map[NetworkType]NetworkConfig
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 11184524,
BootNodes: MainnetBootstrapNodes,
},

Expand All @@ -200,7 +227,6 @@ var NetworkConfigs map[NetworkType]NetworkConfig = map[NetworkType]NetworkConfig
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 1273020,
BootNodes: SepoliaBootstrapNodes,
},

Expand All @@ -220,7 +246,6 @@ var NetworkConfigs map[NetworkType]NetworkConfig = map[NetworkType]NetworkConfig
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 19475089,
BootNodes: GnosisBootstrapNodes,
},

Expand All @@ -240,7 +265,6 @@ var NetworkConfigs map[NetworkType]NetworkConfig = map[NetworkType]NetworkConfig
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 155530,
BootNodes: ChiadoBootstrapNodes,
},

Expand All @@ -260,7 +284,6 @@ var NetworkConfigs map[NetworkType]NetworkConfig = map[NetworkType]NetworkConfig
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 155530,
BootNodes: HoleskyBootstrapNodes,
},
}
Expand Down Expand Up @@ -315,6 +338,11 @@ func (v ConfigForkVersion) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"0x%08x\"", v)), nil
}

type VersionScheduleEntry struct {
Epoch uint64 `yaml:"EPOCH" json:"EPOCH,string"`
StateVersion StateVersion
}

// BeaconChainConfig contains constant configs for node to participate in beacon chain.
type BeaconChainConfig struct {
// Constants (non-configurable)
Expand Down Expand Up @@ -443,8 +471,7 @@ type BeaconChainConfig struct {
ElectraForkVersion ConfigForkVersion `yaml:"ELECTRA_FORK_VERSION" spec:"true" json:"ELECTRA_FORK_VERSION"` // ElectraForkVersion is used to represent the fork version for Electra.
ElectraForkEpoch uint64 `yaml:"ELECTRA_FORK_EPOCH" spec:"true" json:"ELECTRA_FORK_EPOCH,string"` // ElectraForkEpoch is used to represent the assigned fork epoch for Electra.

ForkVersionSchedule map[libcommon.Bytes4]uint64 `json:"-"` // Schedule of fork epochs by version.
ForkVersionNames map[libcommon.Bytes4]string `json:"-"` // Human-readable names of fork versions.
ForkVersionSchedule map[libcommon.Bytes4]VersionScheduleEntry `json:"-"` // Schedule of fork epochs by version.

// New values introduced in Altair hard fork 1.
// Participation flag indices.
Expand Down Expand Up @@ -546,31 +573,19 @@ func (b *BeaconChainConfig) GetCurrentStateVersion(epoch uint64) StateVersion {
// InitializeForkSchedule initializes the schedules forks baked into the config.
func (b *BeaconChainConfig) InitializeForkSchedule() {
b.ForkVersionSchedule = configForkSchedule(b)
b.ForkVersionNames = configForkNames(b)
}

func configForkSchedule(b *BeaconChainConfig) map[libcommon.Bytes4]uint64 {
fvs := map[libcommon.Bytes4]uint64{}
fvs[utils.Uint32ToBytes4(uint32(b.GenesisForkVersion))] = 0
fvs[utils.Uint32ToBytes4(uint32(b.AltairForkVersion))] = b.AltairForkEpoch
fvs[utils.Uint32ToBytes4(uint32(b.BellatrixForkVersion))] = b.BellatrixForkEpoch
fvs[utils.Uint32ToBytes4(uint32(b.CapellaForkVersion))] = b.CapellaForkEpoch
fvs[utils.Uint32ToBytes4(uint32(b.DenebForkVersion))] = b.DenebForkEpoch
fvs[utils.Uint32ToBytes4(uint32(b.ElectraForkVersion))] = b.ElectraForkEpoch
func configForkSchedule(b *BeaconChainConfig) map[libcommon.Bytes4]VersionScheduleEntry {
fvs := map[libcommon.Bytes4]VersionScheduleEntry{}
fvs[utils.Uint32ToBytes4(uint32(b.GenesisForkVersion))] = VersionScheduleEntry{b.GenesisSlot / b.SlotsPerEpoch, Phase0Version}
fvs[utils.Uint32ToBytes4(uint32(b.AltairForkVersion))] = VersionScheduleEntry{b.AltairForkEpoch, AltairVersion}
fvs[utils.Uint32ToBytes4(uint32(b.BellatrixForkVersion))] = VersionScheduleEntry{b.BellatrixForkEpoch, BellatrixVersion}
fvs[utils.Uint32ToBytes4(uint32(b.CapellaForkVersion))] = VersionScheduleEntry{b.CapellaForkEpoch, CapellaVersion}
fvs[utils.Uint32ToBytes4(uint32(b.DenebForkVersion))] = VersionScheduleEntry{b.DenebForkEpoch, DenebVersion}
fvs[utils.Uint32ToBytes4(uint32(b.ElectraForkVersion))] = VersionScheduleEntry{b.ElectraForkEpoch, ElectraVersion}
return fvs
}

func configForkNames(b *BeaconChainConfig) map[libcommon.Bytes4]string {
fvn := map[libcommon.Bytes4]string{}
fvn[utils.Uint32ToBytes4(uint32(b.GenesisForkVersion))] = "phase0"
fvn[utils.Uint32ToBytes4(uint32(b.AltairForkVersion))] = "altair"
fvn[utils.Uint32ToBytes4(uint32(b.BellatrixForkVersion))] = "bellatrix"
fvn[utils.Uint32ToBytes4(uint32(b.CapellaForkVersion))] = "capella"
fvn[utils.Uint32ToBytes4(uint32(b.DenebForkVersion))] = "deneb"
fvn[utils.Uint32ToBytes4(uint32(b.ElectraForkVersion))] = "electra"
return fvn
}

func (b *BeaconChainConfig) ParticipationWeights() []uint64 {
return []uint64{
b.TimelySourceWeight,
Expand Down
2 changes: 1 addition & 1 deletion cl/cltypes/eth1_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func (b *Eth1Block) RlpHeader(parentRoot *libcommon.Hash) (*types.Header, error)

// If the header hash does not match the block hash, return an error.
if header.Hash() != b.BlockHash {
return nil, fmt.Errorf("cannot derive rlp header: mismatching hash: %s != %s", header.Hash(), b.BlockHash)
return nil, fmt.Errorf("cannot derive rlp header: mismatching hash: %s != %s, %d", header.Hash(), b.BlockHash, header.Number)
}

return header, nil
Expand Down
83 changes: 0 additions & 83 deletions cl/persistence/block_store.go

This file was deleted.

1 change: 1 addition & 0 deletions cl/persistence/format/snapshot_format/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
type ExecutionBlockReaderByNumber interface {
Transactions(number uint64, hash libcommon.Hash) (*solid.TransactionsSSZ, error)
Withdrawals(number uint64, hash libcommon.Hash) (*solid.ListSSZ[*cltypes.Withdrawal], error)
SetBeaconChainConfig(beaconCfg *clparams.BeaconChainConfig)
}

var buffersPool = sync.Pool{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ type ExecutionSnapshotReader struct {
db kv.RoDB
}

func NewExecutionSnapshotReader(ctx context.Context, beaconCfg *clparams.BeaconChainConfig, blockReader services.FullBlockReader, db kv.RoDB) *ExecutionSnapshotReader {
return &ExecutionSnapshotReader{ctx: ctx, beaconCfg: beaconCfg, blockReader: blockReader, db: db}
func NewExecutionSnapshotReader(ctx context.Context, blockReader services.FullBlockReader, db kv.RoDB) *ExecutionSnapshotReader {
return &ExecutionSnapshotReader{ctx: ctx, blockReader: blockReader, db: db}
}

func (r *ExecutionSnapshotReader) SetBeaconChainConfig(beaconCfg *clparams.BeaconChainConfig) {
r.beaconCfg = beaconCfg
}

func (r *ExecutionSnapshotReader) Transactions(number uint64, hash libcommon.Hash) (*solid.TransactionsSSZ, error) {
Expand Down
4 changes: 4 additions & 0 deletions cl/persistence/format/snapshot_format/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package snapshot_format

import (
libcommon "github.com/erigontech/erigon-lib/common"
"github.com/erigontech/erigon/cl/clparams"
"github.com/erigontech/erigon/cl/cltypes"
"github.com/erigontech/erigon/cl/cltypes/solid"
)
Expand All @@ -33,3 +34,6 @@ func (t *MockBlockReader) Withdrawals(number uint64, hash libcommon.Hash) (*soli
func (t *MockBlockReader) Transactions(number uint64, hash libcommon.Hash) (*solid.TransactionsSSZ, error) {
return t.Block.Transactions, nil
}

func (t *MockBlockReader) SetBeaconChainConfig(*clparams.BeaconChainConfig) {
}
71 changes: 71 additions & 0 deletions cl/persistence/genesisdb/genesis_db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package genesisdb

import (
"fmt"

"github.com/erigontech/erigon/cl/clparams"
"github.com/erigontech/erigon/cl/phase1/core/state"
"github.com/erigontech/erigon/cl/utils"
"github.com/spf13/afero"
)

const genesisStateFileName = "genesis_state.ssz_snappy"

/*
* GenesisDB only keeps track of one file
* genesis_state.ssz_snappy
* This DB is static and only used to store the genesis state, it is write-once and read-always.
*/
type genesisDB struct {
fs afero.Fs // Use afero to make it easier to test.
beaconConfig *clparams.BeaconChainConfig
}

func NewGenesisDB(beaconConfig *clparams.BeaconChainConfig, genesisDBPath string) GenesisDB {
return &genesisDB{
fs: afero.NewBasePathFs(afero.NewOsFs(), genesisDBPath),
beaconConfig: beaconConfig,
}
}

func (g *genesisDB) IsInitialized() (bool, error) {
return afero.Exists(g.fs, genesisStateFileName)
}

func (g *genesisDB) Initialize(state *state.CachingBeaconState) error {
initialized, err := g.IsInitialized()
if err != nil {
return err
}
// No need to initialize.
if initialized || state == nil {
return nil
}
enc, err := state.EncodeSSZ(nil)
if err != nil {
return err
}
return afero.WriteFile(g.fs, genesisStateFileName, utils.CompressSnappy(enc), 0644)
}

func (g *genesisDB) ReadGenesisState() (*state.CachingBeaconState, error) {
enc, err := afero.ReadFile(g.fs, genesisStateFileName)
if err != nil {
return nil, err
}

decompressedEnc, err := utils.DecompressSnappy(enc)
if err != nil {
return nil, err
}

st := state.New(g.beaconConfig)
slot, err := utils.ExtractSlotFromSerializedBeaconState(decompressedEnc)
if err != nil {
return nil, fmt.Errorf("could not deserialize state slot: %s", err)
}
if err := st.DecodeSSZ(decompressedEnc, int(g.beaconConfig.GetCurrentStateVersion(slot/g.beaconConfig.SlotsPerEpoch))); err != nil {
return nil, fmt.Errorf("could not deserialize state: %s", err)
}
return st, nil
}
13 changes: 13 additions & 0 deletions cl/persistence/genesisdb/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package genesisdb

import "github.com/erigontech/erigon/cl/phase1/core/state"

type GenesisDB interface {
// Initialize initializes the genesis database, with either a given genesis state or the hardcoded databases.
Initialize(state *state.CachingBeaconState) error

IsInitialized() (bool, error)

// ReadGenesisState returns the genesis state.
ReadGenesisState() (*state.CachingBeaconState, error)
}
Loading

0 comments on commit f6a39a5

Please sign in to comment.