@@ -12,6 +12,7 @@ import (
1212 "math/big"
1313 "os"
1414 "path/filepath"
15+ "slices"
1516 "sync"
1617 "time"
1718
@@ -22,6 +23,7 @@ import (
2223 "github.com/ethereum/go-ethereum/rpc"
2324
2425 "github.com/ethstorage/go-ethstorage/ethstorage"
26+ "github.com/ethstorage/go-ethstorage/ethstorage/email"
2527 "github.com/ethstorage/go-ethstorage/ethstorage/eth"
2628)
2729
@@ -70,10 +72,11 @@ type Downloader struct {
7072 dlLatestReq chan struct {}
7173 dlFinalizedReq chan struct {}
7274
73- lg log.Logger
74- done chan struct {}
75- wg sync.WaitGroup
76- mu sync.Mutex
75+ emailConfig * email.EmailConfig
76+ lg log.Logger
77+ done chan struct {}
78+ wg sync.WaitGroup
79+ mu sync.Mutex
7780}
7881
7982type blob struct {
@@ -105,28 +108,27 @@ func NewDownloader(
105108 db ethdb.Database ,
106109 sm * ethstorage.StorageManager ,
107110 cache BlobCache ,
108- downloadStart int64 ,
109- downloadDump string ,
110111 minDurationForBlobsRequest uint64 ,
111- downloadThreadNum int ,
112+ downloadConfig Config ,
112113 lg log.Logger ,
113114) * Downloader {
114- sm .DownloadThreadNum = downloadThreadNum
115+ sm .DownloadThreadNum = downloadConfig . DownloadThreadNum
115116 return & Downloader {
116117 Cache : cache ,
117118 l1Source : l1Source ,
118119 l1Beacon : l1Beacon ,
119120 daClient : daClient ,
120121 db : db ,
121122 sm : sm ,
122- dumpDir : downloadDump ,
123+ dumpDir : downloadConfig . DownloadDump ,
123124 minDurationForBlobsRequest : minDurationForBlobsRequest ,
124125 dlLatestReq : make (chan struct {}, 1 ),
125126 dlFinalizedReq : make (chan struct {}, 1 ),
126127 lg : lg ,
127128 done : make (chan struct {}),
128- lastDownloadBlock : downloadStart ,
129+ lastDownloadBlock : downloadConfig . DownloadStart ,
129130 downloadedBlobs : 0 ,
131+ emailConfig : downloadConfig .EmailConfig ,
130132 }
131133}
132134
@@ -386,33 +388,22 @@ func (s *Downloader) downloadRange(start int64, end int64, toCache bool) ([]blob
386388 )
387389 }
388390
389- var clBlobs map [common.Hash ]eth.Blob
390- if s .l1Beacon != nil {
391- clBlobs , err = s .l1Beacon .DownloadBlobs (s .l1Beacon .Timestamp2Slot (elBlock .timestamp ))
392- if err != nil {
393- s .lg .Error ("L1 beacon download blob error" , "err" , err )
394- return nil , err
395- }
396- } else if s .daClient != nil {
397- var hashes []common.Hash
398- for _ , blob := range elBlock .blobs {
399- hashes = append (hashes , blob .hash )
400- }
401-
402- clBlobs , err = s .daClient .DownloadBlobs (hashes )
403- if err != nil {
404- s .lg .Error ("DA client download blob error" , "err" , err )
405- return nil , err
406- }
407- } else {
408- return nil , fmt .Errorf ("no beacon client or DA client is available" )
391+ clBlobs , err := s .downloadBlobsWithRetry (elBlock , 3 )
392+ if err != nil {
393+ s .lg .Error ("Failed to download blobs for the block after 3 attempts" , "block" , elBlock .number , "err" , err )
394+ // Empty CL blob will be handled later in the EL blob loop
409395 }
410396
411397 for _ , elBlob := range elBlock .blobs {
398+ shard := elBlob .kvIndex .Uint64 () >> s .sm .KvEntriesBits ()
399+ if ! slices .Contains (s .sm .Shards (), shard ) {
400+ s .lg .Warn ("Shard not initialized locally for the kvIndex, skip this blob" , "kvIndex" , elBlob .kvIndex .Uint64 (), "shard" , shard )
401+ continue
402+ }
412403 clBlob , exists := clBlobs [elBlob .hash ]
413404 if ! exists {
414- s .lg . Error ( "Did not find the event specified blob in the CL" )
415-
405+ s .notifyBlobMissing ( elBlock . number , elBlob . kvIndex . Uint64 (), elBlob . hash )
406+ continue
416407 }
417408 // encode blobs so that miner can do sampling directly from cache
418409 elBlob .data = s .sm .EncodeBlob (clBlob .Data , elBlob .hash , elBlob .kvIndex .Uint64 (), s .sm .MaxKvSize ())
@@ -434,6 +425,49 @@ func (s *Downloader) downloadRange(start int64, end int64, toCache bool) ([]blob
434425 return blobs , nil
435426}
436427
428+ func (s * Downloader ) downloadBlobsWithRetry (elBlock * blockBlobs , maxAttempts int ) (map [common.Hash ]eth.Blob , error ) {
429+ var lastErr error
430+ for attempt := 1 ; attempt <= maxAttempts ; attempt ++ {
431+ clBlobs , err := s .downloadBlobs (elBlock )
432+ if err == nil {
433+ return clBlobs , nil
434+ }
435+ lastErr = err
436+ if attempt < maxAttempts {
437+ time .Sleep (3 * time .Second )
438+ }
439+ }
440+ return nil , lastErr
441+ }
442+
443+ func (s * Downloader ) downloadBlobs (elBlock * blockBlobs ) (map [common.Hash ]eth.Blob , error ) {
444+ if s .l1Beacon != nil {
445+ slot := s .l1Beacon .Timestamp2Slot (elBlock .timestamp )
446+ clBlobs , err := s .l1Beacon .DownloadBlobs (slot )
447+ if err != nil {
448+ s .lg .Error ("L1 beacon download blob error" , "block" , elBlock .number , "slot" , slot , "err" , err )
449+ return nil , err
450+ }
451+ return clBlobs , nil
452+ }
453+
454+ if s .daClient != nil {
455+ hashes := make ([]common.Hash , 0 , len (elBlock .blobs ))
456+ for _ , b := range elBlock .blobs {
457+ hashes = append (hashes , b .hash )
458+ }
459+
460+ clBlobs , err := s .daClient .DownloadBlobs (hashes )
461+ if err != nil {
462+ s .lg .Error ("DA client download blob error" , "err" , err )
463+ return nil , err
464+ }
465+ return clBlobs , nil
466+ }
467+
468+ return nil , fmt .Errorf ("no beacon client or DA client is available" )
469+ }
470+
437471func (s * Downloader ) dumpBlobsIfNeeded (blobs []blob ) {
438472 if s .dumpDir != "" {
439473 for _ , blob := range blobs {
@@ -484,3 +518,21 @@ func (s *Downloader) eventsToBlocks(events []types.Log) ([]*blockBlobs, error) {
484518
485519 return blocks , nil
486520}
521+
522+ func (s * Downloader ) notifyBlobMissing (blockNumber uint64 , kvIndex uint64 , hash common.Hash ) {
523+ title := "🛑 Fatal Error from es-node: Downloader Failed to Locate Blob in CL"
524+ msg := "The downloader couldn't locate the specified blob in the consensus layer. The node is stopped pending resolution. "
525+ msg += "Details from the EL event: \n "
526+ msg += fmt .Sprintf (" - blockNumber: %d\n " , blockNumber )
527+ msg += fmt .Sprintf (" - kvIndex: %d\n " , kvIndex )
528+ msg += fmt .Sprintf (" - hash: %s\n " , hash .Hex ())
529+ msg += "This may indicate a potential issue with blob availability on the consensus layer. \n "
530+
531+ if s .emailConfig != nil {
532+ if err := email .SendEmail (title , msg , * s .emailConfig , s .lg ); err == nil {
533+ return
534+ }
535+ }
536+ s .lg .Error (title )
537+ s .lg .Crit (msg )
538+ }
0 commit comments