@@ -28,11 +28,12 @@ import (
28
28
)
29
29
30
30
// makeSignedHeaderBytes builds a valid SignedHeader and returns its binary encoding and the object
31
- func makeSignedHeaderBytes (t * testing.T , chainID string , height uint64 , proposer []byte , pub crypto.PubKey , signer signerpkg.Signer , appHash []byte ) ([]byte , * types.SignedHeader ) {
31
+ func makeSignedHeaderBytes (t * testing.T , chainID string , height uint64 , proposer []byte , pub crypto.PubKey , signer signerpkg.Signer , appHash []byte , dataHash [] byte ) ([]byte , * types.SignedHeader ) {
32
32
hdr := & types.SignedHeader {
33
33
Header : types.Header {
34
34
BaseHeader : types.BaseHeader {ChainID : chainID , Height : height , Time : uint64 (time .Now ().Add (time .Duration (height ) * time .Second ).UnixNano ())},
35
35
AppHash : appHash ,
36
+ DataHash : dataHash ,
36
37
ProposerAddress : proposer ,
37
38
},
38
39
Signer : types.Signer {PubKey : pub , Address : proposer },
@@ -115,12 +116,8 @@ func TestDARetriever_ProcessBlobs_HeaderAndData_Success(t *testing.T) {
115
116
116
117
r := NewDARetriever (nil , cm , config .DefaultConfig (), gen , common .DefaultBlockOptions (), zerolog .Nop ())
117
118
118
- // Build one header and one data blob at same height
119
- _ , lastState := types.State {}, types.State {}
120
- _ = lastState // placeholder to keep parity with helper pattern
121
-
122
- hdrBin , _ := makeSignedHeaderBytes (t , gen .ChainID , 2 , addr , pub , signer , nil )
123
- dataBin , _ := makeSignedDataBytes (t , gen .ChainID , 2 , addr , pub , signer , 2 )
119
+ dataBin , data := makeSignedDataBytes (t , gen .ChainID , 2 , addr , pub , signer , 2 )
120
+ hdrBin , _ := makeSignedHeaderBytes (t , gen .ChainID , 2 , addr , pub , signer , nil , data .Hash ())
124
121
125
122
events := r .processBlobs (context .Background (), [][]byte {hdrBin , dataBin }, 77 )
126
123
require .Len (t , events , 1 )
@@ -141,7 +138,7 @@ func TestDARetriever_ProcessBlobs_HeaderOnly_EmptyDataExpected(t *testing.T) {
141
138
r := NewDARetriever (nil , cm , config .DefaultConfig (), gen , common .DefaultBlockOptions (), zerolog .Nop ())
142
139
143
140
// Header with no data hash present should trigger empty data creation (per current logic)
144
- hb , _ := makeSignedHeaderBytes (t , gen .ChainID , 3 , addr , pub , signer , nil )
141
+ hb , _ := makeSignedHeaderBytes (t , gen .ChainID , 3 , addr , pub , signer , nil , nil )
145
142
146
143
events := r .processBlobs (context .Background (), [][]byte {hb }, 88 )
147
144
require .Len (t , events , 1 )
@@ -160,7 +157,7 @@ func TestDARetriever_TryDecodeHeaderAndData_Basic(t *testing.T) {
160
157
gen := genesis.Genesis {ChainID : "tchain" , InitialHeight : 1 , StartTime : time .Now ().Add (- time .Second ), ProposerAddress : addr }
161
158
r := NewDARetriever (nil , cm , config .DefaultConfig (), gen , common .DefaultBlockOptions (), zerolog .Nop ())
162
159
163
- hb , sh := makeSignedHeaderBytes (t , gen .ChainID , 5 , addr , pub , signer , nil )
160
+ hb , sh := makeSignedHeaderBytes (t , gen .ChainID , 5 , addr , pub , signer , nil , nil )
164
161
gotH := r .tryDecodeHeader (hb , 123 )
165
162
require .NotNil (t , gotH )
166
163
assert .Equal (t , sh .Hash ().String (), gotH .Hash ().String ())
@@ -225,8 +222,8 @@ func TestDARetriever_RetrieveFromDA_TwoNamespaces_Success(t *testing.T) {
225
222
gen := genesis.Genesis {ChainID : "tchain" , InitialHeight : 1 , StartTime : time .Now ().Add (- time .Second ), ProposerAddress : addr }
226
223
227
224
// Prepare header/data blobs
228
- hdrBin , _ := makeSignedHeaderBytes (t , gen .ChainID , 9 , addr , pub , signer , nil )
229
- dataBin , _ := makeSignedDataBytes (t , gen .ChainID , 9 , addr , pub , signer , 1 )
225
+ dataBin , data := makeSignedDataBytes (t , gen .ChainID , 9 , addr , pub , signer , 1 )
226
+ hdrBin , _ := makeSignedHeaderBytes (t , gen .ChainID , 9 , addr , pub , signer , nil , data . Hash () )
230
227
231
228
cfg := config .DefaultConfig ()
232
229
cfg .DA .Namespace = "nsHdr"
@@ -255,3 +252,118 @@ func TestDARetriever_RetrieveFromDA_TwoNamespaces_Success(t *testing.T) {
255
252
assert .Equal (t , uint64 (9 ), events [0 ].Header .Height ())
256
253
assert .Equal (t , uint64 (9 ), events [0 ].Data .Height ())
257
254
}
255
+
256
+ func TestDARetriever_ProcessBlobs_CrossDAHeightMatching (t * testing.T ) {
257
+ ds := dssync .MutexWrap (datastore .NewMapDatastore ())
258
+ st := store .New (ds )
259
+ cm , err := cache .NewManager (config .DefaultConfig (), st , zerolog .Nop ())
260
+ require .NoError (t , err )
261
+
262
+ addr , pub , signer := buildSyncTestSigner (t )
263
+ gen := genesis.Genesis {ChainID : "tchain" , InitialHeight : 1 , StartTime : time .Now ().Add (- time .Second ), ProposerAddress : addr }
264
+
265
+ r := NewDARetriever (nil , cm , config .DefaultConfig (), gen , common .DefaultBlockOptions (), zerolog .Nop ())
266
+
267
+ // Create header and data for the same block height but from different DA heights
268
+ dataBin , data := makeSignedDataBytes (t , gen .ChainID , 5 , addr , pub , signer , 2 )
269
+ hdrBin , _ := makeSignedHeaderBytes (t , gen .ChainID , 5 , addr , pub , signer , nil , data .Hash ())
270
+
271
+ // Process header from DA height 100 first
272
+ events1 := r .processBlobs (context .Background (), [][]byte {hdrBin }, 100 )
273
+ require .Len (t , events1 , 0 , "should not create event yet - data is missing" )
274
+
275
+ // Verify header is stored in pending headers
276
+ require .Contains (t , r .pendingHeaders , uint64 (5 ), "header should be stored as pending" )
277
+ require .Contains (t , r .headerDAHeights , uint64 (5 ), "header DA height should be tracked" )
278
+ assert .Equal (t , uint64 (100 ), r .headerDAHeights [5 ])
279
+
280
+ // Process data from DA height 102
281
+ events2 := r .processBlobs (context .Background (), [][]byte {dataBin }, 102 )
282
+ require .Len (t , events2 , 1 , "should create event when matching data arrives" )
283
+
284
+ event := events2 [0 ]
285
+ assert .Equal (t , uint64 (5 ), event .Header .Height ())
286
+ assert .Equal (t , uint64 (5 ), event .Data .Height ())
287
+ assert .Equal (t , uint64 (102 ), event .DaHeight , "DaHeight should be the height where data was processed" )
288
+ assert .Equal (t , uint64 (100 ), event .HeaderDaIncludedHeight , "HeaderDaIncludedHeight should be where header was included" )
289
+
290
+ // Verify pending maps are cleared
291
+ require .NotContains (t , r .pendingHeaders , uint64 (5 ), "header should be removed from pending" )
292
+ require .NotContains (t , r .pendingData , uint64 (5 ), "data should be removed from pending" )
293
+ require .NotContains (t , r .headerDAHeights , uint64 (5 ), "header DA height should be removed" )
294
+ }
295
+
296
+ func TestDARetriever_ProcessBlobs_MultipleHeadersCrossDAHeightMatching (t * testing.T ) {
297
+ ds := dssync .MutexWrap (datastore .NewMapDatastore ())
298
+ st := store .New (ds )
299
+ cm , err := cache .NewManager (config .DefaultConfig (), st , zerolog .Nop ())
300
+ require .NoError (t , err )
301
+
302
+ addr , pub , signer := buildSyncTestSigner (t )
303
+ gen := genesis.Genesis {ChainID : "tchain" , InitialHeight : 1 , StartTime : time .Now ().Add (- time .Second ), ProposerAddress : addr }
304
+
305
+ r := NewDARetriever (nil , cm , config .DefaultConfig (), gen , common .DefaultBlockOptions (), zerolog .Nop ())
306
+
307
+ // Create multiple headers and data for different block heights
308
+ data3Bin , data3 := makeSignedDataBytes (t , gen .ChainID , 3 , addr , pub , signer , 1 )
309
+ data4Bin , data4 := makeSignedDataBytes (t , gen .ChainID , 4 , addr , pub , signer , 2 )
310
+ data5Bin , data5 := makeSignedDataBytes (t , gen .ChainID , 5 , addr , pub , signer , 1 )
311
+
312
+ hdr3Bin , _ := makeSignedHeaderBytes (t , gen .ChainID , 3 , addr , pub , signer , nil , data3 .Hash ())
313
+ hdr4Bin , _ := makeSignedHeaderBytes (t , gen .ChainID , 4 , addr , pub , signer , nil , data4 .Hash ())
314
+ hdr5Bin , _ := makeSignedHeaderBytes (t , gen .ChainID , 5 , addr , pub , signer , nil , data5 .Hash ())
315
+
316
+ // Process multiple headers from DA height 200 - should be stored as pending
317
+ events1 := r .processBlobs (context .Background (), [][]byte {hdr3Bin , hdr4Bin , hdr5Bin }, 200 )
318
+ require .Len (t , events1 , 0 , "should not create events yet - all data is missing" )
319
+
320
+ // Verify all headers are stored in pending
321
+ require .Contains (t , r .pendingHeaders , uint64 (3 ), "header 3 should be pending" )
322
+ require .Contains (t , r .pendingHeaders , uint64 (4 ), "header 4 should be pending" )
323
+ require .Contains (t , r .pendingHeaders , uint64 (5 ), "header 5 should be pending" )
324
+ assert .Equal (t , uint64 (200 ), r .headerDAHeights [3 ])
325
+ assert .Equal (t , uint64 (200 ), r .headerDAHeights [4 ])
326
+ assert .Equal (t , uint64 (200 ), r .headerDAHeights [5 ])
327
+
328
+ // Process some data from DA height 203 - should create partial events
329
+ events2 := r .processBlobs (context .Background (), [][]byte {data3Bin , data5Bin }, 203 )
330
+ require .Len (t , events2 , 2 , "should create events for heights 3 and 5" )
331
+
332
+ // Sort events by height for consistent testing
333
+ if events2 [0 ].Header .Height () > events2 [1 ].Header .Height () {
334
+ events2 [0 ], events2 [1 ] = events2 [1 ], events2 [0 ]
335
+ }
336
+
337
+ // Verify event for height 3
338
+ assert .Equal (t , uint64 (3 ), events2 [0 ].Header .Height ())
339
+ assert .Equal (t , uint64 (3 ), events2 [0 ].Data .Height ())
340
+ assert .Equal (t , uint64 (203 ), events2 [0 ].DaHeight )
341
+ assert .Equal (t , uint64 (200 ), events2 [0 ].HeaderDaIncludedHeight )
342
+
343
+ // Verify event for height 5
344
+ assert .Equal (t , uint64 (5 ), events2 [1 ].Header .Height ())
345
+ assert .Equal (t , uint64 (5 ), events2 [1 ].Data .Height ())
346
+ assert .Equal (t , uint64 (203 ), events2 [1 ].DaHeight )
347
+ assert .Equal (t , uint64 (200 ), events2 [1 ].HeaderDaIncludedHeight )
348
+
349
+ // Verify header 4 is still pending (no matching data yet)
350
+ require .Contains (t , r .pendingHeaders , uint64 (4 ), "header 4 should still be pending" )
351
+ require .NotContains (t , r .pendingHeaders , uint64 (3 ), "header 3 should be removed from pending" )
352
+ require .NotContains (t , r .pendingHeaders , uint64 (5 ), "header 5 should be removed from pending" )
353
+
354
+ // Process remaining data from DA height 205
355
+ events3 := r .processBlobs (context .Background (), [][]byte {data4Bin }, 205 )
356
+ require .Len (t , events3 , 1 , "should create event for height 4" )
357
+
358
+ // Verify final event for height 4
359
+ assert .Equal (t , uint64 (4 ), events3 [0 ].Header .Height ())
360
+ assert .Equal (t , uint64 (4 ), events3 [0 ].Data .Height ())
361
+ assert .Equal (t , uint64 (205 ), events3 [0 ].DaHeight )
362
+ assert .Equal (t , uint64 (200 ), events3 [0 ].HeaderDaIncludedHeight )
363
+
364
+ // Verify all pending maps are now clear
365
+ require .NotContains (t , r .pendingHeaders , uint64 (4 ), "header 4 should be removed from pending" )
366
+ require .Len (t , r .pendingHeaders , 0 , "all headers should be processed" )
367
+ require .Len (t , r .pendingData , 0 , "all data should be processed" )
368
+ require .Len (t , r .headerDAHeights , 0 , "all header DA heights should be cleared" )
369
+ }
0 commit comments