@@ -237,40 +237,43 @@ func (d *Datastore) WriteBlockHashFirstSeen(ctx context.Context, hash common.Has
237237 }
238238
239239 d .runAsync (func () {
240- key := datastore .NameKey (BlocksKind , hash .Hex (), nil )
241-
242- _ , err := d .client .RunInTransaction (ctx , func (tx * datastore.Transaction ) error {
243- var block DatastoreBlock
244- err := tx .Get (key , & block )
245-
246- shouldWrite := false
247-
248- // If block doesn't exist, create partial entry
249- if err != nil || block .DatastoreHeader == nil {
250- shouldWrite = true
251- block .DatastoreHeader = & DatastoreHeader {
252- TimeFirstSeenHash : tfsh ,
253- SensorFirstSeenHash : d .sensorID ,
254- }
255- } else if block .DatastoreHeader .TimeFirstSeenHash .IsZero () || tfsh .Before (block .DatastoreHeader .TimeFirstSeenHash ) {
256- // Update if no time set yet, or if new time is earlier
257- shouldWrite = true
258- block .DatastoreHeader .TimeFirstSeenHash = tfsh
259- block .DatastoreHeader .SensorFirstSeenHash = d .sensorID
260- }
240+ d .writeBlockHashFirstSeen (ctx , hash , tfsh )
241+ })
242+ }
243+
244+ // writeBlockHashFirstSeen performs the actual transaction to write or update the block hash first seen time.
245+ func (d * Datastore ) writeBlockHashFirstSeen (ctx context.Context , hash common.Hash , tfsh time.Time ) {
246+ key := datastore .NameKey (BlocksKind , hash .Hex (), nil )
261247
262- if shouldWrite {
263- _ , err = tx .Put (key , & block )
264- return err
248+ _ , err := d .client .RunInTransaction (ctx , func (tx * datastore.Transaction ) error {
249+ var block DatastoreBlock
250+ err := tx .Get (key , & block )
251+
252+ // If block doesn't exist, create partial entry
253+ if err != nil || block .DatastoreHeader == nil {
254+ block .DatastoreHeader = & DatastoreHeader {
255+ TimeFirstSeenHash : tfsh ,
256+ SensorFirstSeenHash : d .sensorID ,
265257 }
258+ _ , err = tx .Put (key , & block )
259+ return err
260+ }
266261
262+ // If timestamp already set and not earlier, no update needed
263+ if ! block .DatastoreHeader .TimeFirstSeenHash .IsZero () && ! tfsh .Before (block .DatastoreHeader .TimeFirstSeenHash ) {
267264 return nil
268- }, datastore .MaxAttempts (MaxAttempts ))
269-
270- if err != nil {
271- log .Error ().Err (err ).Str ("hash" , hash .Hex ()).Msg ("Failed to write block hash first seen" )
272265 }
273- })
266+
267+ // Update with earlier timestamp
268+ block .DatastoreHeader .TimeFirstSeenHash = tfsh
269+ block .DatastoreHeader .SensorFirstSeenHash = d .sensorID
270+ _ , err = tx .Put (key , & block )
271+ return err
272+ }, datastore .MaxAttempts (MaxAttempts ))
273+
274+ if err != nil {
275+ log .Error ().Err (err ).Str ("hash" , hash .Hex ()).Msg ("Failed to write block hash first seen" )
276+ }
274277}
275278
276279// WriteTransactions will write the transactions and transaction events to datastore.
@@ -389,6 +392,37 @@ func (d *Datastore) newDatastoreHeader(header *types.Header, tfs time.Time, isPa
389392 }
390393}
391394
395+ // writeFirstSeen updates timing fields on a header, preserving earlier timestamps from existingHeader.
396+ func (d * Datastore ) writeFirstSeen (newHeader * DatastoreHeader , existingHeader * DatastoreHeader , tfs time.Time ) {
397+ // If no existing header, set hash timing to current time
398+ if existingHeader == nil {
399+ newHeader .TimeFirstSeenHash = tfs
400+ newHeader .SensorFirstSeenHash = d .sensorID
401+ return
402+ }
403+
404+ // Preserve earlier header timing if it exists
405+ if ! existingHeader .TimeFirstSeen .IsZero () && existingHeader .TimeFirstSeen .Before (tfs ) {
406+ newHeader .TimeFirstSeen = existingHeader .TimeFirstSeen
407+ newHeader .SensorFirstSeen = existingHeader .SensorFirstSeen
408+ }
409+
410+ // Preserve earlier hash timing if it exists
411+ if ! existingHeader .TimeFirstSeenHash .IsZero () && existingHeader .TimeFirstSeenHash .Before (tfs ) {
412+ newHeader .TimeFirstSeenHash = existingHeader .TimeFirstSeenHash
413+ newHeader .SensorFirstSeenHash = existingHeader .SensorFirstSeenHash
414+ } else {
415+ newHeader .TimeFirstSeenHash = tfs
416+ newHeader .SensorFirstSeenHash = d .sensorID
417+ }
418+
419+ // If hash was seen before header, also update header timing
420+ if newHeader .TimeFirstSeenHash .Before (newHeader .TimeFirstSeen ) {
421+ newHeader .TimeFirstSeen = newHeader .TimeFirstSeenHash
422+ newHeader .SensorFirstSeen = newHeader .SensorFirstSeenHash
423+ }
424+ }
425+
392426// newDatastoreTransaction creates a DatastoreTransaction from a types.Transaction. Some
393427// values are converted into strings to prevent a loss of precision.
394428func (d * Datastore ) newDatastoreTransaction (tx * types.Transaction , tfs time.Time ) * DatastoreTransaction {
@@ -442,21 +476,13 @@ func (d *Datastore) writeBlock(ctx context.Context, block *types.Block, td *big.
442476
443477 if dsBlock .DatastoreHeader == nil || dsBlock .DatastoreHeader .Number == "" {
444478 shouldWrite = true
479+
480+ // Create new header with current timing
445481 header := d .newDatastoreHeader (block .Header (), tfs , false )
446- // Preserve earlier timestamps from partial write (hash announcement)
447- if dsBlock .DatastoreHeader != nil && ! dsBlock .DatastoreHeader .TimeFirstSeenHash .IsZero () {
448- // Hash was announced earlier, use those timestamps
449- if dsBlock .DatastoreHeader .TimeFirstSeenHash .Before (tfs ) {
450- header .TimeFirstSeen = dsBlock .DatastoreHeader .TimeFirstSeenHash
451- header .SensorFirstSeen = dsBlock .DatastoreHeader .SensorFirstSeenHash
452- }
453- header .TimeFirstSeenHash = dsBlock .DatastoreHeader .TimeFirstSeenHash
454- header .SensorFirstSeenHash = dsBlock .DatastoreHeader .SensorFirstSeenHash
455- } else {
456- // No prior hash announcement, set TimeFirstSeenHash same as TimeFirstSeen
457- header .TimeFirstSeenHash = tfs
458- header .SensorFirstSeenHash = d .sensorID
459- }
482+
483+ // Preserve hash timing from any earlier announcement
484+ d .writeFirstSeen (header , dsBlock .DatastoreHeader , tfs )
485+
460486 dsBlock .DatastoreHeader = header
461487 }
462488
@@ -555,18 +581,14 @@ func (d *Datastore) writeBlockHeader(ctx context.Context, header *types.Header,
555581 return nil
556582 }
557583
584+ // Create new header with current timing
558585 newHeader := d .newDatastoreHeader (header , tfs , isParent )
559- // Preserve earlier timestamps from partial write (hash announcement)
560- if err == nil && block .DatastoreHeader != nil && ! block .DatastoreHeader .TimeFirstSeenHash .IsZero () {
561- // Hash was announced earlier, use those timestamps
562- if block .DatastoreHeader .TimeFirstSeenHash .Before (tfs ) {
563- newHeader .TimeFirstSeen = block .DatastoreHeader .TimeFirstSeenHash
564- newHeader .SensorFirstSeen = block .DatastoreHeader .SensorFirstSeenHash
565- }
566- newHeader .TimeFirstSeenHash = block .DatastoreHeader .TimeFirstSeenHash
567- newHeader .SensorFirstSeenHash = block .DatastoreHeader .SensorFirstSeenHash
586+
587+ // Preserve hash timing from any earlier announcement
588+ if err == nil {
589+ d .writeFirstSeen (newHeader , block .DatastoreHeader , tfs )
568590 } else {
569- // No prior hash announcement, set TimeFirstSeenHash same as TimeFirstSeen
591+ // Block doesn't exist at all, no prior hash announcement
570592 newHeader .TimeFirstSeenHash = tfs
571593 newHeader .SensorFirstSeenHash = d .sensorID
572594 }
0 commit comments