Skip to content

Commit

Permalink
Merge pull request #34 from ethpandaops/feat/custom-networks
Browse files Browse the repository at this point in the history
feat: Add support for custom networks
  • Loading branch information
cortze authored Jul 22, 2024
2 parents 1fe47e9 + a1ad576 commit 07c8380
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 64 deletions.
85 changes: 73 additions & 12 deletions cmd/hermes/cmd_eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ var ethConfig = &struct {
DialConcurrency int
DialTimeout time.Duration
MaxPeers int
GenesisSSZURL string
ConfigURL string
BootnodesURL string
DepositContractBlockURL string
}{
PrivateKeyStr: "", // unset means it'll be generated
Chain: params.MainnetName,
Expand All @@ -48,6 +52,10 @@ var ethConfig = &struct {
DialConcurrency: 16,
DialTimeout: 5 * time.Second,
MaxPeers: 30, // arbitrary
GenesisSSZURL: "",
ConfigURL: "",
BootnodesURL: "",
DepositContractBlockURL: "",
}

var cmdEth = &cli.Command{
Expand Down Expand Up @@ -167,6 +175,34 @@ var cmdEthFlags = []cli.Flag{
Value: ethConfig.MaxPeers,
Destination: &ethConfig.MaxPeers,
},
&cli.StringFlag{
Name: "genesis.ssz.url",
EnvVars: []string{"HERMES_ETH_GENESIS_SSZ_URL"},
Usage: "The .ssz URL from which to fetch the genesis data, requires 'chain=devnet'",
Value: ethConfig.GenesisSSZURL,
Destination: &ethConfig.GenesisSSZURL,
},
&cli.StringFlag{
Name: "config.yaml.url",
EnvVars: []string{"HERMES_ETH_CONFIG_URL"},
Usage: "The .yaml URL from which to fetch the beacon chain config, requires 'chain=devnet'",
Value: ethConfig.ConfigURL,
Destination: &ethConfig.ConfigURL,
},
&cli.StringFlag{
Name: "bootnodes.yaml.url",
EnvVars: []string{"HERMES_ETH_BOOTNODES_URL"},
Usage: "The .yaml URL from which to fetch the bootnode ENRs, requires 'chain=devnet'",
Value: ethConfig.BootnodesURL,
Destination: &ethConfig.BootnodesURL,
},
&cli.StringFlag{
Name: "deposit-contract-block.txt.url",
EnvVars: []string{"HERMES_ETH_DEPOSIT_CONTRACT_BLOCK_URL"},
Usage: "The .txt URL from which to fetch the deposit contract block. Requires 'chain=devnet'",
Value: ethConfig.DepositContractBlockURL,
Destination: &ethConfig.DepositContractBlockURL,
},
}

func cmdEthAction(c *cli.Context) error {
Expand All @@ -176,20 +212,45 @@ func cmdEthAction(c *cli.Context) error {
// Print hermes configuration for debugging purposes
printEthConfig()

// Extract chain configuration parameters based on the given chain name
genConfig, netConfig, beaConfig, err := eth.GetConfigsByNetworkName(ethConfig.Chain)
if err != nil {
return fmt.Errorf("get config for %s: %w", ethConfig.Chain, err)
var config *eth.NetworkConfig
// Derive network configuration
if ethConfig.Chain != params.DevnetName {
slog.Info("Deriving known network config:", "chain", ethConfig.Chain)

c, err := eth.DeriveKnownNetworkConfig(c.Context, ethConfig.Chain)
if err != nil {
return fmt.Errorf("derive network config: %w", err)
}

config = c
} else {
slog.Info("Deriving devnet network config")

c, err := eth.DeriveDevnetConfig(c.Context, eth.DevnetOptions{
ConfigURL: ethConfig.ConfigURL,
BootnodesURL: ethConfig.BootnodesURL,
DepositContractBlockURL: ethConfig.DepositContractBlockURL,
GenesisSSZURL: ethConfig.GenesisSSZURL,
})
if err != nil {
return fmt.Errorf("failed to derive devnet network config: %w", err)
}
config = c
}

genesisRoot := genConfig.GenesisValidatorRoot
genesisTime := genConfig.GenesisTime
// Overriding configuration so that functions like ComputForkDigest take the
// correct input data from the global configuration.
params.OverrideBeaconConfig(config.Beacon)
params.OverrideBeaconNetworkConfig(config.Network)

genesisRoot := config.Genesis.GenesisValidatorRoot
genesisTime := config.Genesis.GenesisTime

// compute fork version and fork digest
currentSlot := slots.Since(genesisTime)
currentEpoch := slots.ToEpoch(currentSlot)

currentForkVersion, err := eth.GetCurrentForkVersion(currentEpoch, beaConfig)
currentForkVersion, err := eth.GetCurrentForkVersion(currentEpoch, config.Beacon)
if err != nil {
return fmt.Errorf("compute fork version for epoch %d: %w", currentEpoch, err)
}
Expand All @@ -201,13 +262,13 @@ func cmdEthAction(c *cli.Context) error {

// Overriding configuration so that functions like ComputForkDigest take the
// correct input data from the global configuration.
params.OverrideBeaconConfig(beaConfig)
params.OverrideBeaconNetworkConfig(netConfig)
params.OverrideBeaconConfig(config.Beacon)
params.OverrideBeaconNetworkConfig(config.Network)

cfg := &eth.NodeConfig{
GenesisConfig: genConfig,
NetworkConfig: netConfig,
BeaconConfig: beaConfig,
GenesisConfig: config.Genesis,
NetworkConfig: config.Network,
BeaconConfig: config.Beacon,
ForkDigest: forkDigest,
ForkVersion: currentForkVersion,
PrivateKeyStr: ethConfig.PrivateKeyStr,
Expand Down
19 changes: 9 additions & 10 deletions cmd/hermes/cmd_eth_chains.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,27 @@ func cmdEthChainsAction(c *cli.Context) error {

slog.Info("Supported chains:")
for _, chain := range chains {

genConfig, _, beaConfig, err := eth.GetConfigsByNetworkName(chain)
config, err := eth.DeriveKnownNetworkConfig(c.Context, chain)
if err != nil {
return fmt.Errorf("get config for %s: %w", chain, err)
}
slog.Info(chain)

forkVersions := [][]byte{
beaConfig.GenesisForkVersion,
beaConfig.AltairForkVersion,
beaConfig.BellatrixForkVersion,
beaConfig.CapellaForkVersion,
beaConfig.DenebForkVersion,
config.Beacon.GenesisForkVersion,
config.Beacon.AltairForkVersion,
config.Beacon.BellatrixForkVersion,
config.Beacon.CapellaForkVersion,
config.Beacon.DenebForkVersion,
}

for _, forkVersion := range forkVersions {
epoch, found := beaConfig.ForkVersionSchedule[[4]byte(forkVersion)]
epoch, found := config.Beacon.ForkVersionSchedule[[4]byte(forkVersion)]
if !found {
return fmt.Errorf("fork version schedule not found for %x", forkVersion)
}

forkName, found := beaConfig.ForkVersionNames[[4]byte(forkVersion)]
forkName, found := config.Beacon.ForkVersionNames[[4]byte(forkVersion)]
if !found {
return fmt.Errorf("fork version name not found for %x", forkVersion)
}
Expand All @@ -58,7 +57,7 @@ func cmdEthChainsAction(c *cli.Context) error {
continue
}

digest, err := signing.ComputeForkDigest(forkVersion, genConfig.GenesisValidatorRoot)
digest, err := signing.ComputeForkDigest(forkVersion, config.Genesis.GenesisValidatorRoot)
if err != nil {
return err
}
Expand Down
121 changes: 121 additions & 0 deletions eth/fetch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package eth

import (
"context"
"encoding/binary"
"io"
"net/http"

"github.com/prysmaticlabs/prysm/v5/config/params"
"gopkg.in/yaml.v3"
)

// FetchConfigFromURL fetches the beacon chain config from a given URL.
func FetchConfigFromURL(ctx context.Context, url string) (*params.BeaconChainConfig, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}

response, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer response.Body.Close()

data, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}

config := params.MainnetConfig().Copy()

out, err := params.UnmarshalConfig(data, config)
if err != nil {
return nil, err
}

return out, nil
}

// FetchBootnodeENRsFromURL fetches the bootnode ENRs from a given URL.
func FetchBootnodeENRsFromURL(ctx context.Context, url string) ([]string, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}

response, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer response.Body.Close()

data, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
}

var enrs []string
err = yaml.Unmarshal(data, &enrs)
if err != nil {
return nil, err
}

return enrs, nil
}

// FetchDepositContractBlockFromURL fetches the deposit contract block from a given URL.
func FetchDepositContractBlockFromURL(ctx context.Context, url string) (uint64, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return 0, err
}

response, err := http.DefaultClient.Do(req)
if err != nil {
return 0, err
}
defer response.Body.Close()

data, err := io.ReadAll(response.Body)
if err != nil {
return 0, err
}

var block uint64

err = yaml.Unmarshal(data, &block)
if err != nil {
return 0, err
}

return block, nil
}

// FetchGenesisDetailsFromURL fetches the genesis time and validators root from a given URL.
func FetchGenesisDetailsFromURL(ctx context.Context, url string) (uint64, [32]byte, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return 0, [32]byte{}, err
}

response, err := http.DefaultClient.Do(req)
if err != nil {
return 0, [32]byte{}, err
}
defer response.Body.Close()

// Read only the first 40 bytes (8 bytes for GenesisTime + 32 bytes for GenesisValidatorsRoot)
data := make([]byte, 40)
_, err = io.ReadFull(response.Body, data)
if err != nil {
return 0, [32]byte{}, err
}

genesisTime := binary.LittleEndian.Uint64(data[:8])
var genesisValidatorsRoot [32]byte
copy(genesisValidatorsRoot[:], data[8:])

return genesisTime, genesisValidatorsRoot, nil
}
17 changes: 0 additions & 17 deletions eth/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,6 @@ type GenesisConfig struct {
GenesisTime time.Time // Time at Genesis
}

// GetConfigsByNetworkName returns the GenesisConfig, NetworkConfig,
// BeaconChainConfig and any error based on the input network name
func GetConfigsByNetworkName(net string) (*GenesisConfig, *params.NetworkConfig, *params.BeaconChainConfig, error) {
switch net {
case params.MainnetName:
return GenesisConfigs[net], params.BeaconNetworkConfig(), params.MainnetConfig(), nil
case params.SepoliaName:
return GenesisConfigs[net], params.BeaconNetworkConfig(), params.SepoliaConfig(), nil
case params.PraterName:
return GenesisConfigs[net], params.BeaconNetworkConfig(), params.PraterConfig(), nil
case params.HoleskyName:
return GenesisConfigs[net], params.BeaconNetworkConfig(), params.HoleskyConfig(), nil
default:
return nil, nil, nil, fmt.Errorf("network %s not found", net)
}
}

var GenesisConfigs = map[string]*GenesisConfig{
params.MainnetName: {
GenesisValidatorRoot: hexToBytes("4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95"),
Expand Down
Loading

0 comments on commit 07c8380

Please sign in to comment.