@@ -19,8 +19,13 @@ import (
1919 datypes "github.com/evstack/ev-node/pkg/da/types"
2020)
2121
22- // DefaultMaxBlobSize is the default max blob size
23- const DefaultMaxBlobSize uint64 = 7 * 1024 * 1024 // 7MB
22+ const (
23+ // DefaultMaxBlobSize is the default max blob size
24+ DefaultMaxBlobSize uint64 = 7 * 1024 * 1024 // 7MB
25+
26+ // DefaultBlockTime is the default time between empty blocks
27+ DefaultBlockTime = 1 * time .Second
28+ )
2429
2530// LocalDA is a simple implementation of in-memory DA. Not production ready! Intended only for testing!
2631//
@@ -35,6 +40,8 @@ type LocalDA struct {
3540 height uint64
3641 privKey ed25519.PrivateKey
3742 pubKey ed25519.PublicKey
43+ blockTime time.Duration
44+ lastTime time.Time // tracks last timestamp to ensure monotonicity
3845
3946 logger zerolog.Logger
4047}
@@ -51,6 +58,8 @@ func NewLocalDA(logger zerolog.Logger, opts ...func(*LocalDA) *LocalDA) *LocalDA
5158 timestamps : make (map [uint64 ]time.Time ),
5259 blobData : make (map [uint64 ][]* blobrpc.Blob ),
5360 maxBlobSize : DefaultMaxBlobSize ,
61+ blockTime : DefaultBlockTime ,
62+ lastTime : time .Now (),
5463 logger : logger ,
5564 }
5665 for _ , f := range opts {
@@ -194,7 +203,7 @@ func (d *LocalDA) SubmitWithOptions(ctx context.Context, blobs []datypes.Blob, g
194203 defer d .mu .Unlock ()
195204 ids := make ([]datypes.ID , len (blobs ))
196205 d .height += 1
197- d .timestamps [d .height ] = time . Now ()
206+ d .timestamps [d .height ] = d . monotonicTime ()
198207 for i , blob := range blobs {
199208 ids [i ] = append (d .nextID (), d .getHash (blob )... )
200209
@@ -224,7 +233,7 @@ func (d *LocalDA) Submit(ctx context.Context, blobs []datypes.Blob, gasPrice flo
224233 defer d .mu .Unlock ()
225234 ids := make ([]datypes.ID , len (blobs ))
226235 d .height += 1
227- d .timestamps [d .height ] = time . Now ()
236+ d .timestamps [d .height ] = d . monotonicTime ()
228237 for i , blob := range blobs {
229238 ids [i ] = append (d .nextID (), d .getHash (blob )... )
230239
@@ -274,10 +283,57 @@ func (d *LocalDA) getProof(id, blob []byte) []byte {
274283 return sign
275284}
276285
286+ // monotonicTime returns a timestamp that is guaranteed to be after the last recorded timestamp.
287+ func (d * LocalDA ) monotonicTime () time.Time {
288+ now := time .Now ()
289+ if now .After (d .lastTime ) {
290+ d .lastTime = now
291+ return now
292+ }
293+ d .lastTime = d .lastTime .Add (1 )
294+ return d .lastTime
295+ }
296+
277297// WithMaxBlobSize returns a function that sets the max blob size of LocalDA
278298func WithMaxBlobSize (maxBlobSize uint64 ) func (* LocalDA ) * LocalDA {
279299 return func (da * LocalDA ) * LocalDA {
280300 da .maxBlobSize = maxBlobSize
281301 return da
282302 }
283303}
304+
305+ // WithBlockTime returns a function that sets the block time for empty block production
306+ func WithBlockTime (blockTime time.Duration ) func (* LocalDA ) * LocalDA {
307+ return func (da * LocalDA ) * LocalDA {
308+ da .blockTime = blockTime
309+ return da
310+ }
311+ }
312+
313+ // Start begins producing empty blocks at the configured block time interval.
314+ func (d * LocalDA ) Start (ctx context.Context ) {
315+ go func () {
316+ ticker := time .NewTicker (d .blockTime )
317+ defer ticker .Stop ()
318+
319+ for {
320+ select {
321+ case <- ctx .Done ():
322+ d .logger .Info ().Msg ("LocalDA: stopping empty block production" )
323+ return
324+ case <- ticker .C :
325+ d .produceEmptyBlock ()
326+ }
327+ }
328+ }()
329+ d .logger .Info ().Dur ("blockTime" , d .blockTime ).Msg ("LocalDA: started empty block production" )
330+ }
331+
332+ // produceEmptyBlock creates a new empty block at the next height.
333+ func (d * LocalDA ) produceEmptyBlock () {
334+ d .mu .Lock ()
335+ defer d .mu .Unlock ()
336+ d .height ++
337+ d .timestamps [d .height ] = d .monotonicTime ()
338+ d .logger .Debug ().Uint64 ("height" , d .height ).Msg ("produced empty block" )
339+ }
0 commit comments