Skip to content
Open
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
17 changes: 9 additions & 8 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ func main() {

var pkg domain.Pkg
var stakerConfig domain.StakerConfig
var warnings []string

// Fetch package info from IPFS if hash is provided (required for test mode, optional for sync mode)
if cfg.IPFSHash != "" {
Expand All @@ -53,16 +52,18 @@ func main() {
if err != nil {
logger.FatalWithPrefix(logPrefix, "Failed to get dnpName from IPFS hash: %v", err)
}
stakerConfig, warnings = domain.StakerConfigForNetwork(&pkg, overrides)
stakerConfig, err = domain.StakerConfigForNetwork(&pkg, overrides)
if err != nil {
logger.FatalWithPrefix(logPrefix, "Failed to resolve staker config: %v", err)
}
} else {
// No IPFS hash: use overrides only (only valid in sync mode)
logger.InfoWithPrefix(logPrefix, "No IPFS hash provided: configuring staker from EXECUTION_CLIENT and CONSENSUS_CLIENT")
stakerConfig, warnings = domain.StakerConfigFromOverrides(overrides)
}

// Log any warnings from client resolution
for _, warning := range warnings {
logger.WarnWithPrefix(logPrefix, "%s", warning)
var err error
stakerConfig, err = domain.StakerConfigFromOverrides(overrides)
if err != nil {
logger.FatalWithPrefix(logPrefix, "Failed to resolve staker config: %v", err)
}
}

// print the staker config for debugging with each item on a new line
Expand Down
1 change: 1 addition & 0 deletions internal/adapters/composite/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (t *ExecutorAdapter) waitForValidatorLiveness(ctx context.Context, report *
logger.Error("No validator indexes returned (attempt %d)", i+1)
} else {
logger.Info("[ValidatorLiveness] Got %d validator indexes (attempt %d)", len(indexes), i+1)
logger.Info("[ValidatorLiveness] Validator indexes: %v", indexes)
break
}
if i < maxSlots-1 {
Expand Down
55 changes: 32 additions & 23 deletions internal/application/domain/stakers.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@ type ClientOverrides struct {
ConsensusClient string // Short name like "prysm", "teku", etc.
}

// ClientOverrideResult holds the result of applying overrides with any warnings
// ClientOverrideResult holds the resolved execution and consensus clients.
type ClientOverrideResult struct {
ExecutionDnpName string
ConsensusDnpName string
Warnings []string
}

const dappmanagerURL = "http://dappmanager.dappnode:7000"
Expand All @@ -54,12 +53,12 @@ var (
// StakerConfigFromOverrides creates a StakerConfig using only the override values.
// Used in sync mode where no IPFS hash/package is provided.
// If overrides are empty, random clients will be selected.
func StakerConfigFromOverrides(overrides ClientOverrides) (StakerConfig, []string) {
func StakerConfigFromOverrides(overrides ClientOverrides) (StakerConfig, error) {
// Pass nil so resolveClientsWithOverrides will use overrides or random
return StakerConfigForNetwork(nil, overrides)
}

func StakerConfigForNetwork(pkg *Pkg, overrides ClientOverrides) (StakerConfig, []string) {
func StakerConfigForNetwork(pkg *Pkg, overrides ClientOverrides) (StakerConfig, error) {
// Only hoodi network is supported
network := "hoodi"
web3signer := "web3signer-hoodi.dnp.dappnode.eth"
Expand All @@ -73,7 +72,10 @@ func StakerConfigForNetwork(pkg *Pkg, overrides ClientOverrides) (StakerConfig,
}

// Resolve execution and consensus clients with override logic
result := resolveClientsWithOverrides(pkg, overrides, hoodiExecClients, hoodiConsClients)
result, err := resolveClientsWithOverrides(pkg, overrides, hoodiExecClients, hoodiConsClients)
if err != nil {
return StakerConfig{}, err
}

ecDnpName := result.ExecutionDnpName
ccDnpName := result.ConsensusDnpName
Expand All @@ -95,14 +97,14 @@ func StakerConfigForNetwork(pkg *Pkg, overrides ClientOverrides) (StakerConfig,
ValidatorContainerName: containerName("validator", ccDnpName),
ExecutionContainerName: containerName(serviceName, ecDnpName),
ExecutionVolumeTargetPath: fmt.Sprintf("/var/lib/docker/volumes/%s/_data", volumeName),
}, result.Warnings
}, nil
}

// resolveClientsWithOverrides determines execution and consensus clients based on:
// 1. If pkg matches an execution/consensus client, use it (overriding any flag/env with warning)
// 1. If pkg matches an execution/consensus client, use it (and fail on conflicting flag/env)
// 2. Otherwise, use the override if provided
// 3. Otherwise, pick a random client
func resolveClientsWithOverrides(pkg *Pkg, overrides ClientOverrides, execClients, consClients []string) ClientOverrideResult {
func resolveClientsWithOverrides(pkg *Pkg, overrides ClientOverrides, execClients, consClients []string) (ClientOverrideResult, error) {
result := ClientOverrideResult{}

// Check if pkg is an execution or consensus client (only if pkg is not nil)
Expand All @@ -114,11 +116,13 @@ func resolveClientsWithOverrides(pkg *Pkg, overrides ClientOverrides, execClient

// Resolve execution client
if pkgMatchedExec != "" {
// Pkg is an execution client - use it
// Pkg is an execution client - conflicting override is an error
if overrides.ExecutionClient != "" {
result.Warnings = append(result.Warnings,
fmt.Sprintf("Package '%s' is an execution client; ignoring --execution-client flag '%s'",
pkg.DnpName, overrides.ExecutionClient))
return ClientOverrideResult{}, fmt.Errorf(
"package '%s' is an execution client; conflicting --execution-client '%s'",
pkg.DnpName,
overrides.ExecutionClient,
)
}
result.ExecutionDnpName = pkgMatchedExec
} else if overrides.ExecutionClient != "" {
Expand All @@ -127,9 +131,7 @@ func resolveClientsWithOverrides(pkg *Pkg, overrides ClientOverrides, execClient
if matched != "" {
result.ExecutionDnpName = matched
} else {
result.Warnings = append(result.Warnings,
fmt.Sprintf("Unknown execution client '%s'; using random", overrides.ExecutionClient))
result.ExecutionDnpName = randomClient(execClients)
return ClientOverrideResult{}, fmt.Errorf("unknown execution client '%s'", overrides.ExecutionClient)
}
} else {
// Pick random
Expand All @@ -138,11 +140,13 @@ func resolveClientsWithOverrides(pkg *Pkg, overrides ClientOverrides, execClient

// Resolve consensus client
if pkgMatchedCons != "" {
// Pkg is a consensus client - use it
// Pkg is a consensus client - conflicting override is an error
if overrides.ConsensusClient != "" {
result.Warnings = append(result.Warnings,
fmt.Sprintf("Package '%s' is a consensus client; ignoring --consensus-client flag '%s'",
pkg.DnpName, overrides.ConsensusClient))
return ClientOverrideResult{}, fmt.Errorf(
"package '%s' is a consensus client; conflicting --consensus-client '%s'",
pkg.DnpName,
overrides.ConsensusClient,
)
}
result.ConsensusDnpName = pkgMatchedCons
} else if overrides.ConsensusClient != "" {
Expand All @@ -151,16 +155,21 @@ func resolveClientsWithOverrides(pkg *Pkg, overrides ClientOverrides, execClient
if matched != "" {
result.ConsensusDnpName = matched
} else {
result.Warnings = append(result.Warnings,
fmt.Sprintf("Unknown consensus client '%s'; using random", overrides.ConsensusClient))
result.ConsensusDnpName = randomClient(consClients)
return ClientOverrideResult{}, fmt.Errorf("unknown consensus client '%s'", overrides.ConsensusClient)
}
} else {
// Pick random
result.ConsensusDnpName = randomClient(consClients)
}

return result
if result.ExecutionDnpName == "" {
return ClientOverrideResult{}, fmt.Errorf("failed to resolve execution client")
}
if result.ConsensusDnpName == "" {
return ClientOverrideResult{}, fmt.Errorf("failed to resolve consensus client")
}

return result, nil
}

// matchClient returns the matching client dnpName if found, empty string otherwise
Expand Down