Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
23 changes: 14 additions & 9 deletions block/internal/syncing/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,17 +354,22 @@ func (s *Syncer) initializeState() error {
}

// Set DA height to the maximum of the genesis start height, the state's DA height, and the cached DA height.
// Use the DA height from the last executed block instead of the maximum from all blocks,
// because P2P-fetched heights may be lost on restart.
// The cache's DaHeight() is initialized from store metadata, so it's always correct even after cache clear.
// Only use cache.DaHeight() when P2P is actively syncing (headerStore has higher height than current state).
daHeight := max(s.genesis.DAStartHeight, min(state.DAHeight-1, 0))
if state.LastBlockHeight > 0 {
if lastHeaderDA, ok := s.cache.GetHeaderDAIncludedByHeight(state.LastBlockHeight); ok {
daHeight = max(daHeight, lastHeaderDA)
}
if lastDataDA, ok := s.cache.GetDataDAIncludedByHeight(state.LastBlockHeight); ok {
daHeight = max(daHeight, lastDataDA)
}
if s.headerStore != nil && s.headerStore.Height() > state.LastBlockHeight {
daHeight = max(daHeight, s.cache.DaHeight())
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
Comment on lines 356 to 365

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Resume from the last applied block's DA height, not the cache-wide max.

block/internal/cache/manager.go:174-176 defines DaHeight() as the highest DA height ever seen across the caches, while block/internal/submitting/submitter.go:440-458 still persists per-block inclusion heights. Using the global max here can jump the retriever past untouched DA ranges whenever later submissions are already in cache metadata, and those skipped heights are no longer covered by the sequential catchup path when P2P priority hints are dropped. Please derive this bump from the included-by-height entries for state.LastBlockHeight, or leave the recovered watermark unchanged when they are missing.

🛠️ Safer resume logic
-	if s.headerStore != nil && s.headerStore.Height() > state.LastBlockHeight {
-		daHeight = max(daHeight, s.cache.DaHeight())
-	}
+	if s.headerStore != nil && s.headerStore.Height() > state.LastBlockHeight {
+		if headerDAHeight, ok := s.cache.GetHeaderDAIncludedByHeight(state.LastBlockHeight); ok {
+			daHeight = max(daHeight, headerDAHeight)
+		}
+		if dataDAHeight, ok := s.cache.GetDataDAIncludedByHeight(state.LastBlockHeight); ok {
+			daHeight = max(daHeight, dataDAHeight)
+		}
+	}
Based on learnings "DA priority heights (queued via `QueuePriorityHeight` in `block/internal/syncing/da_follower.go`) are untrusted, best-effort optimizations sourced from P2P hints. Dropping a hint on a transient fetch failure is intentional — the sequential catchup loop in `da.Subscriber` will cover every height eventually."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@block/internal/syncing/syncer.go` around lines 356 - 365, The current logic
uses s.cache.DaHeight() (a global max) to bump daHeight when headerStore is
ahead, which can skip DA ranges; instead, derive the bump from the per-block
included-by entry for state.LastBlockHeight (the persisted per-block inclusion
height written by the submitter) or leave daHeight unchanged if that per-block
entry is missing. Concretely: replace the branch that uses s.cache.DaHeight()
with a lookup of the included-height for state.LastBlockHeight (or call the
cache/manager API that returns the included-by height for a specific block) and
max that value into daHeight only when that per-block value exists; otherwise do
not use the cache-wide s.cache.DaHeight(). Ensure you still respect
s.genesis.DAStartHeight and the state.DAHeight-1 logic.


// dev mode for da start height
if startHeight := s.config.DA.StartHeight; startHeight > 0 {
s.logger.Info().
Uint64("previous_da_start_height", daHeight).
Uint64("override_da_start_height", s.config.DA.StartHeight).
Msg("DA start height overridden by flag")
daHeight = startHeight
}

s.daRetrieverHeight.Store(daHeight)

s.logger.Info().
Expand Down
8 changes: 7 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ const (
FlagDABatchMaxDelay = FlagPrefixEvnode + "da.batch_max_delay"
// FlagDABatchMinItems is a flag for specifying the minimum batch items
FlagDABatchMinItems = FlagPrefixEvnode + "da.batch_min_items"
// FlagDAStartHeight is a flag for forcing the DA retrieval height to start from a specific height
FlagDAStartHeight = FlagPrefixEvnode + "da.start_height"

// P2P configuration flags

Expand Down Expand Up @@ -237,6 +239,8 @@ type Config struct {

// DAConfig contains all Data Availability configuration parameters
type DAConfig struct {
StartHeight uint64 `mapstructure:"-" yaml:"-" comment:"Force DA retrieval to start from a specific height (0 for default)"`

Address string `mapstructure:"address" yaml:"address" comment:"Address of the data availability layer service (host:port). This is the endpoint where Rollkit will connect to submit and retrieve data."`
AuthToken string `mapstructure:"auth_token" yaml:"auth_token" comment:"Authentication token for the data availability layer service. Required if the DA service needs authentication."` //nolint:gosec // this is ok.
SubmitOptions string `mapstructure:"submit_options" yaml:"submit_options" comment:"Additional options passed to the DA layer when submitting data. Format depends on the specific DA implementation being used."`
Expand Down Expand Up @@ -563,7 +567,7 @@ func AddFlags(cmd *cobra.Command) {
})

// Add base flags
cmd.Flags().String(FlagDBPath, def.DBPath, "path for the node database")
cmd.Flags().String(FlagDBPath, def.DBPath, "path for for node database")
Comment thread
julienrbrt marked this conversation as resolved.
cmd.Flags().Bool(FlagClearCache, def.ClearCache, "clear the cache")

// Node configuration flags
Expand Down Expand Up @@ -595,6 +599,8 @@ func AddFlags(cmd *cobra.Command) {
cmd.Flags().Float64(FlagDABatchSizeThreshold, def.DA.BatchSizeThreshold, "batch size threshold as fraction of max blob size (0.0-1.0)")
cmd.Flags().Duration(FlagDABatchMaxDelay, def.DA.BatchMaxDelay.Duration, "maximum time to wait before submitting a batch")
cmd.Flags().Uint64(FlagDABatchMinItems, def.DA.BatchMinItems, "minimum number of items to accumulate before submission")
cmd.Flags().Uint64(FlagDAStartHeight, def.DA.StartHeight, "force DA retrieval to start from a specific height (0 for default)")
cmd.Flags().MarkHidden(FlagDAStartHeight)

// P2P configuration flags
cmd.Flags().String(FlagP2PListenAddress, def.P2P.ListenAddress, "P2P listen address (host:port)")
Expand Down
2 changes: 1 addition & 1 deletion pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func TestAddFlags(t *testing.T) {
assertFlagValue(t, flags, FlagPruningInterval, DefaultConfig().Pruning.Interval.Duration)

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

// Get the actual number of flags (both regular and persistent)
actualFlagCount := 0
Expand Down
Loading