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
4 changes: 4 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const (
FlagMaxPendingHeadersAndData = FlagPrefixEvnode + "node.max_pending_headers_and_data"
// FlagLazyBlockTime is a flag for specifying the maximum interval between blocks in lazy aggregation mode
FlagLazyBlockTime = FlagPrefixEvnode + "node.lazy_block_interval"
// FlagReadinessWindowSeconds configures the time window (in seconds) used to calculate readiness threshold
FlagReadinessWindowSeconds = FlagPrefixEvnode + "node.readiness_window_seconds"
// FlagReadinessMaxBlocksBehind configures how many blocks behind best-known head is still considered ready
FlagReadinessMaxBlocksBehind = FlagPrefixEvnode + "node.readiness_max_blocks_behind"
// FlagClearCache is a flag for clearing the cache
Expand Down Expand Up @@ -198,6 +200,7 @@ type NodeConfig struct {
TrustedHash string `mapstructure:"trusted_hash" yaml:"trusted_hash" comment:"Initial trusted hash used to bootstrap the header exchange service. Allows nodes to start synchronizing from a specific trusted point in the chain instead of genesis. When provided, the node will fetch the corresponding header/block from peers using this hash and use it as a starting point for synchronization. If not provided, the node will attempt to fetch the genesis block instead."`

// Readiness / health configuration
ReadinessWindowSeconds uint64 `mapstructure:"readiness_window_seconds" yaml:"readiness_window_seconds" comment:"Time window in seconds used to calculate ReadinessMaxBlocksBehind based on block time. Default: 15 seconds."`
ReadinessMaxBlocksBehind uint64 `mapstructure:"readiness_max_blocks_behind" yaml:"readiness_max_blocks_behind" comment:"How many blocks behind best-known head the node can be and still be considered ready. 0 means must be exactly at head."`
}

Expand Down Expand Up @@ -314,6 +317,7 @@ func AddFlags(cmd *cobra.Command) {
cmd.Flags().Bool(FlagLazyAggregator, def.Node.LazyMode, "produce blocks only when transactions are available or after lazy block time")
cmd.Flags().Uint64(FlagMaxPendingHeadersAndData, def.Node.MaxPendingHeadersAndData, "maximum headers or data pending DA confirmation before pausing block production (0 for no limit)")
cmd.Flags().Duration(FlagLazyBlockTime, def.Node.LazyBlockInterval.Duration, "maximum interval between blocks in lazy aggregation mode")
cmd.Flags().Uint64(FlagReadinessWindowSeconds, def.Node.ReadinessWindowSeconds, "time window in seconds for calculating readiness threshold based on block time (default: 15s)")
cmd.Flags().Uint64(FlagReadinessMaxBlocksBehind, def.Node.ReadinessMaxBlocksBehind, "how many blocks behind best-known head the node can be and still be considered ready (0 = must be at head)")

// Data Availability configuration flags
Expand Down
3 changes: 2 additions & 1 deletion pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func TestAddFlags(t *testing.T) {
assertFlagValue(t, flags, FlagLazyAggregator, DefaultConfig().Node.LazyMode)
assertFlagValue(t, flags, FlagMaxPendingHeadersAndData, DefaultConfig().Node.MaxPendingHeadersAndData)
assertFlagValue(t, flags, FlagLazyBlockTime, DefaultConfig().Node.LazyBlockInterval.Duration)
assertFlagValue(t, flags, FlagReadinessWindowSeconds, DefaultConfig().Node.ReadinessWindowSeconds)
assertFlagValue(t, flags, FlagReadinessMaxBlocksBehind, DefaultConfig().Node.ReadinessMaxBlocksBehind)

// DA flags
Expand Down Expand Up @@ -103,7 +104,7 @@ func TestAddFlags(t *testing.T) {
assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig().RPC.Address)

// Count the number of flags we're explicitly checking
expectedFlagCount := 38 // Update this number if you add more flag checks above
expectedFlagCount := 39 // Update this number if you add more flag checks above

// Get the actual number of flags (both regular and persistent)
actualFlagCount := 0
Expand Down
20 changes: 18 additions & 2 deletions pkg/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,23 @@ func DefaultRootDirWithName(appName string) string {
return filepath.Join(home, "."+appName)
}

// calculateReadinessMaxBlocksBehind calculates how many blocks represent the readiness window
// based on the given block time and window duration in seconds. This allows for normal
// batch-sync latency while detecting stuck nodes.
func calculateReadinessMaxBlocksBehind(blockTime time.Duration, windowSeconds uint64) uint64 {
if blockTime == 0 {
return 30 // fallback to safe default if blockTime is not set
}
if windowSeconds == 0 {
windowSeconds = 15 // fallback to default 15s window
}
return uint64(time.Duration(windowSeconds) * time.Second / blockTime)
}

// DefaultConfig keeps default values of NodeConfig
func DefaultConfig() Config {
defaultBlockTime := DurationWrapper{1 * time.Second}
defaultReadinessWindowSeconds := uint64(15)
return Config{
RootDir: DefaultRootDir,
DBPath: "data",
Expand All @@ -47,12 +62,13 @@ func DefaultConfig() Config {
},
Node: NodeConfig{
Aggregator: false,
BlockTime: DurationWrapper{1 * time.Second},
BlockTime: defaultBlockTime,
LazyMode: false,
LazyBlockInterval: DurationWrapper{60 * time.Second},
Light: false,
TrustedHash: "",
ReadinessMaxBlocksBehind: 3,
ReadinessWindowSeconds: defaultReadinessWindowSeconds,
ReadinessMaxBlocksBehind: calculateReadinessMaxBlocksBehind(defaultBlockTime.Duration, defaultReadinessWindowSeconds),
},
DA: DAConfig{
Address: "http://localhost:7980",
Expand Down
4 changes: 2 additions & 2 deletions pkg/rpc/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,8 @@ func TestHealthReadyEndpoint(t *testing.T) {
}{
{name: "at_head", local: 100, bestKnown: 100, peers: 1, expectedCode: http.StatusOK},
{name: "within_1_block", local: 99, bestKnown: 100, peers: 1, expectedCode: http.StatusOK},
{name: "within_3_blocks", local: 97, bestKnown: 100, peers: 1, expectedCode: http.StatusOK},
{name: "just_over_3_blocks", local: 96, bestKnown: 100, peers: 1, expectedCode: http.StatusServiceUnavailable},
{name: "within_15_blocks", local: 85, bestKnown: 100, peers: 1, expectedCode: http.StatusOK},
{name: "just_over_15_blocks", local: 84, bestKnown: 100, peers: 1, expectedCode: http.StatusServiceUnavailable},
{name: "local_ahead", local: 101, bestKnown: 100, peers: 1, expectedCode: http.StatusOK},
{name: "no_blocks_yet", local: 0, bestKnown: 100, peers: 1, expectedCode: http.StatusServiceUnavailable},
{name: "unknown_best_known", local: 100, bestKnown: 0, peers: 1, expectedCode: http.StatusServiceUnavailable},
Expand Down
Loading