Skip to content

Commit 1453b0a

Browse files
tac0turtleclaude[bot]tac0turtle
authored
feat: implement header and data namespace separation (#2527)
Implements header and data namespace separation as specified in ADR-014. ## Changes - Add HeaderNamespace and DataNamespace fields to DA configuration - Update SubmitWithHelpers to accept namespace parameter - Modify header/data submission to use separate namespaces - Enhance blob retrieval to fetch from both namespaces - Maintain backward compatibility with legacy namespace field ## Benefits - Enables nodes to sync only headers or only data - Improves DA layer organization and filtering - Foundation for different sync strategies per namespace Closes #2486 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Marko <[email protected]> Co-authored-by: tac0turtle <[email protected]>
1 parent 6bbd431 commit 1453b0a

File tree

25 files changed

+1051
-159
lines changed

25 files changed

+1051
-159
lines changed

block/da_speed_test.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,23 @@ func setupManagerForTest(t *testing.T, initialDAHeight uint64) (*Manager, *rollm
118118
Node: config.NodeConfig{BlockTime: config.DurationWrapper{Duration: blockTime}},
119119
DA: config.DAConfig{BlockTime: config.DurationWrapper{Duration: blockTime}},
120120
},
121-
genesis: genesis.Genesis{ProposerAddress: addr},
122-
daHeight: new(atomic.Uint64),
123-
headerInCh: make(chan NewHeaderEvent),
124-
headerStore: headerStore,
125-
dataInCh: make(chan NewDataEvent),
126-
dataStore: dataStore,
127-
headerCache: cache.NewCache[types.SignedHeader](),
128-
dataCache: cache.NewCache[types.Data](),
129-
headerStoreCh: make(chan struct{}),
130-
dataStoreCh: make(chan struct{}),
131-
retrieveCh: make(chan struct{}),
132-
logger: logger,
133-
lastStateMtx: new(sync.RWMutex),
134-
da: mockDAClient,
135-
signer: noopSigner,
136-
metrics: NopMetrics(),
121+
genesis: genesis.Genesis{ProposerAddress: addr},
122+
daHeight: new(atomic.Uint64),
123+
headerInCh: make(chan NewHeaderEvent),
124+
headerStore: headerStore,
125+
dataInCh: make(chan NewDataEvent),
126+
dataStore: dataStore,
127+
headerCache: cache.NewCache[types.SignedHeader](),
128+
dataCache: cache.NewCache[types.Data](),
129+
headerStoreCh: make(chan struct{}),
130+
dataStoreCh: make(chan struct{}),
131+
retrieveCh: make(chan struct{}),
132+
logger: logger,
133+
lastStateMtx: new(sync.RWMutex),
134+
da: mockDAClient,
135+
namespaceMigrationCompleted: &atomic.Bool{},
136+
signer: noopSigner,
137+
metrics: NopMetrics(),
137138
}
138139
manager.daIncludedHeight.Store(0)
139140
manager.daHeight.Store(initialDAHeight)

block/manager.go

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const (
4747
// This is temporary solution. It will be removed in future versions.
4848
maxSubmitAttempts = 30
4949

50+
// Key for storing namespace migration state in the store
51+
namespaceMigrationKey = "namespace_migration_completed"
52+
5053
// Applies to the headerInCh and dataInCh, 10000 is a large enough number for headers per DA block.
5154
eventInChLength = 10000
5255
)
@@ -168,6 +171,10 @@ type Manager struct {
168171
// validatorHasherProvider is used to provide the validator hash for the header.
169172
// It is used to set the validator hash in the header.
170173
validatorHasherProvider types.ValidatorHasherProvider
174+
175+
// namespaceMigrationCompleted tracks whether we have completed the migration
176+
// from legacy namespace to separate header/data namespaces
177+
namespaceMigrationCompleted *atomic.Bool
171178
}
172179

173180
// getInitialState tries to load lastState from Store, and if it's not available it reads genesis.
@@ -375,38 +382,44 @@ func NewManager(
375382
headerBroadcaster: headerBroadcaster,
376383
dataBroadcaster: dataBroadcaster,
377384
// channels are buffered to avoid blocking on input/output operations, buffer sizes are arbitrary
378-
headerInCh: make(chan NewHeaderEvent, eventInChLength),
379-
dataInCh: make(chan NewDataEvent, eventInChLength),
380-
headerStoreCh: make(chan struct{}, 1),
381-
dataStoreCh: make(chan struct{}, 1),
382-
headerStore: headerStore,
383-
dataStore: dataStore,
384-
lastStateMtx: new(sync.RWMutex),
385-
lastBatchData: lastBatchData,
386-
headerCache: cache.NewCache[types.SignedHeader](),
387-
dataCache: cache.NewCache[types.Data](),
388-
retrieveCh: make(chan struct{}, 1),
389-
daIncluderCh: make(chan struct{}, 1),
390-
logger: logger,
391-
txsAvailable: false,
392-
pendingHeaders: pendingHeaders,
393-
pendingData: pendingData,
394-
metrics: seqMetrics,
395-
sequencer: sequencer,
396-
exec: exec,
397-
da: da,
398-
gasPrice: gasPrice,
399-
gasMultiplier: gasMultiplier,
400-
txNotifyCh: make(chan struct{}, 1), // Non-blocking channel
401-
signaturePayloadProvider: managerOpts.SignaturePayloadProvider,
402-
validatorHasherProvider: managerOpts.ValidatorHasherProvider,
385+
headerInCh: make(chan NewHeaderEvent, eventInChLength),
386+
dataInCh: make(chan NewDataEvent, eventInChLength),
387+
headerStoreCh: make(chan struct{}, 1),
388+
dataStoreCh: make(chan struct{}, 1),
389+
headerStore: headerStore,
390+
dataStore: dataStore,
391+
lastStateMtx: new(sync.RWMutex),
392+
lastBatchData: lastBatchData,
393+
headerCache: cache.NewCache[types.SignedHeader](),
394+
dataCache: cache.NewCache[types.Data](),
395+
retrieveCh: make(chan struct{}, 1),
396+
daIncluderCh: make(chan struct{}, 1),
397+
logger: logger,
398+
txsAvailable: false,
399+
pendingHeaders: pendingHeaders,
400+
pendingData: pendingData,
401+
metrics: seqMetrics,
402+
sequencer: sequencer,
403+
exec: exec,
404+
da: da,
405+
gasPrice: gasPrice,
406+
gasMultiplier: gasMultiplier,
407+
txNotifyCh: make(chan struct{}, 1), // Non-blocking channel
408+
signaturePayloadProvider: managerOpts.SignaturePayloadProvider,
409+
validatorHasherProvider: managerOpts.ValidatorHasherProvider,
410+
namespaceMigrationCompleted: &atomic.Bool{},
403411
}
404412

405413
// initialize da included height
406414
if height, err := m.store.GetMetadata(ctx, storepkg.DAIncludedHeightKey); err == nil && len(height) == 8 {
407415
m.daIncludedHeight.Store(binary.LittleEndian.Uint64(height))
408416
}
409417

418+
// initialize namespace migration state
419+
if migrationData, err := m.store.GetMetadata(ctx, namespaceMigrationKey); err == nil && len(migrationData) > 0 {
420+
m.namespaceMigrationCompleted.Store(migrationData[0] == 1)
421+
}
422+
410423
// Set the default publishBlock implementation
411424
m.publishBlock = m.publishBlockInternal
412425

@@ -418,6 +431,24 @@ func NewManager(
418431
return m, nil
419432
}
420433

434+
// setNamespaceMigrationCompleted marks the namespace migration as completed and persists it to disk
435+
func (m *Manager) setNamespaceMigrationCompleted(ctx context.Context) error {
436+
m.namespaceMigrationCompleted.Store(true)
437+
return m.store.SetMetadata(ctx, namespaceMigrationKey, []byte{1})
438+
}
439+
440+
// loadNamespaceMigrationState loads the namespace migration state from persistent storage
441+
func (m *Manager) loadNamespaceMigrationState(ctx context.Context) (bool, error) {
442+
migrationData, err := m.store.GetMetadata(ctx, namespaceMigrationKey)
443+
if err != nil {
444+
if errors.Is(err, ds.ErrNotFound) {
445+
return false, nil // Migration not completed
446+
}
447+
return false, fmt.Errorf("failed to load migration state: %w", err)
448+
}
449+
return len(migrationData) > 0 && migrationData[0] == 1, nil
450+
}
451+
421452
// PendingHeaders returns the pending headers.
422453
func (m *Manager) PendingHeaders() *PendingHeaders {
423454
return m.pendingHeaders

0 commit comments

Comments
 (0)