@@ -42,6 +42,11 @@ type client struct {
4242// Ensure client implements the FullClient interface (Client + BlobGetter + Verifier).
4343var _ FullClient = (* client )(nil )
4444
45+ const (
46+ blockTimestampFetchMaxAttempts = 3
47+ blockTimestampFetchBackoff = 100 * time .Millisecond
48+ )
49+
4550// NewClient creates a new blob client wrapper with pre-calculated namespace bytes.
4651func NewClient (cfg Config ) FullClient {
4752 if cfg .DA == nil {
@@ -184,15 +189,40 @@ func (c *client) Submit(ctx context.Context, data [][]byte, _ float64, namespace
184189
185190// getBlockTimestamp fetches the block timestamp from the DA layer header.
186191func (c * client ) getBlockTimestamp (ctx context.Context , height uint64 ) (time.Time , error ) {
187- headerCtx , cancel := context .WithTimeout (ctx , c .defaultTimeout )
188- defer cancel ()
192+ var lastErr error
193+ backoff := blockTimestampFetchBackoff
194+
195+ for attempt := 1 ; attempt <= blockTimestampFetchMaxAttempts ; attempt ++ {
196+ headerCtx , cancel := context .WithTimeout (ctx , c .defaultTimeout )
197+ header , err := c .headerAPI .GetByHeight (headerCtx , height )
198+ cancel ()
199+ if err == nil {
200+ return header .Time (), nil
201+ }
202+ lastErr = err
189203
190- header , err := c .headerAPI .GetByHeight (headerCtx , height )
191- if err != nil {
192- return time.Time {}, fmt .Errorf ("failed to get header timestamp for block %d: %w" , height , err )
204+ if attempt == blockTimestampFetchMaxAttempts {
205+ break
206+ }
207+
208+ c .logger .Info ().
209+ Uint64 ("height" , height ).
210+ Int ("attempt" , attempt ).
211+ Int ("max_attempts" , blockTimestampFetchMaxAttempts ).
212+ Dur ("retry_in" , backoff ).
213+ Err (err ).
214+ Msg ("failed to get block timestamp, retrying" )
215+
216+ select {
217+ case <- ctx .Done ():
218+ return time.Time {}, fmt .Errorf ("fetching header timestamp for block %d: %w" , height , ctx .Err ())
219+ case <- time .After (backoff ):
220+ }
221+
222+ backoff *= 2
193223 }
194224
195- return header .Time (), nil
225+ return time .Time {}, fmt . Errorf ( "get header timestamp for block %d after %d attempts: %w" , height , blockTimestampFetchMaxAttempts , lastErr )
196226}
197227
198228// Retrieve retrieves blobs from the DA layer at the specified height and namespace.
@@ -224,8 +254,13 @@ func (c *client) Retrieve(ctx context.Context, height uint64, namespace []byte)
224254 blockTime , err := c .getBlockTimestamp (ctx , height )
225255 if err != nil {
226256 c .logger .Error ().Uint64 ("height" , height ).Err (err ).Msg ("failed to get block timestamp" )
227- blockTime = time .Now ()
228- // TODO: we should retry fetching the timestamp. Current time may mess block time consistency for based sequencers.
257+ return datypes.ResultRetrieve {
258+ BaseResult : datypes.BaseResult {
259+ Code : datypes .StatusError ,
260+ Message : fmt .Sprintf ("failed to get block timestamp: %s" , err .Error ()),
261+ Height : height ,
262+ },
263+ }
229264 }
230265
231266 return datypes.ResultRetrieve {
@@ -262,8 +297,13 @@ func (c *client) Retrieve(ctx context.Context, height uint64, namespace []byte)
262297 blockTime , err := c .getBlockTimestamp (ctx , height )
263298 if err != nil {
264299 c .logger .Error ().Uint64 ("height" , height ).Err (err ).Msg ("failed to get block timestamp" )
265- blockTime = time .Now ()
266- // TODO: we should retry fetching the timestamp. Current time may mess block time consistency for based sequencers.
300+ return datypes.ResultRetrieve {
301+ BaseResult : datypes.BaseResult {
302+ Code : datypes .StatusError ,
303+ Message : fmt .Sprintf ("failed to get block timestamp: %s" , err .Error ()),
304+ Height : height ,
305+ },
306+ }
267307 }
268308
269309 if len (blobs ) == 0 {
0 commit comments