@@ -28,6 +28,8 @@ import (
2828 "github.com/evstack/ev-node/types"
2929)
3030
31+ var _ BlockSyncer = (* Syncer )(nil )
32+
3133// forcedInclusionGracePeriodConfig contains internal configuration for forced inclusion grace periods.
3234type forcedInclusionGracePeriodConfig struct {
3335 // basePeriod is the base number of additional epochs allowed for including forced inclusion transactions
@@ -118,6 +120,10 @@ type Syncer struct {
118120
119121 // P2P wait coordination
120122 p2pWaitState atomic.Value // stores p2pWaitState
123+
124+ // blockSyncer is the interface used for block sync operations.
125+ // defaults to self, but can be wrapped with tracing.
126+ blockSyncer BlockSyncer
121127}
122128
123129// pendingForcedInclusionTx represents a forced inclusion transaction that hasn't been included yet
@@ -155,7 +161,7 @@ func NewSyncer(
155161 blockFullnessEMA := & atomic.Pointer [float64 ]{}
156162 blockFullnessEMA .Store (& initialFullness )
157163
158- return & Syncer {
164+ s := & Syncer {
159165 store : store ,
160166 exec : exec ,
161167 cache : cache ,
@@ -175,6 +181,14 @@ func NewSyncer(
175181 blockFullnessEMA : blockFullnessEMA ,
176182 gracePeriodConfig : newForcedInclusionGracePeriodConfig (),
177183 }
184+ s .blockSyncer = s
185+ return s
186+ }
187+
188+ // SetBlockSyncer sets the block syncer interface, allowing injection of
189+ // a tracing wrapper or other decorator.
190+ func (s * Syncer ) SetBlockSyncer (bs BlockSyncer ) {
191+ s .blockSyncer = bs
178192}
179193
180194// Start begins the syncing component
@@ -515,7 +529,7 @@ func (s *Syncer) processHeightEvent(event *common.DAHeightEvent) {
515529 s .cancelP2PWait (height )
516530
517531 // Try to sync the next block
518- if err := s .trySyncNextBlock ( event ); err != nil {
532+ if err := s .blockSyncer . TrySyncNextBlock ( s . ctx , event ); err != nil {
519533 s .logger .Error ().Err (err ).Msg ("failed to sync next block" )
520534 // If the error is not due to an validation error, re-store the event as pending
521535 switch {
@@ -559,12 +573,12 @@ var (
559573 errInvalidState = errors .New ("invalid state" )
560574)
561575
562- // trySyncNextBlock attempts to sync the next available block
576+ // TrySyncNextBlock attempts to sync the next available block
563577// the event is always the next block in sequence as processHeightEvent ensures it.
564- func (s * Syncer ) trySyncNextBlock ( event * common.DAHeightEvent ) error {
578+ func (s * Syncer ) TrySyncNextBlock ( ctx context. Context , event * common.DAHeightEvent ) error {
565579 select {
566- case <- s . ctx .Done ():
567- return s . ctx .Err ()
580+ case <- ctx .Done ():
581+ return ctx .Err ()
568582 default :
569583 }
570584
@@ -579,7 +593,7 @@ func (s *Syncer) trySyncNextBlock(event *common.DAHeightEvent) error {
579593 // Compared to the executor logic where the current block needs to be applied first,
580594 // here only the previous block needs to be applied to proceed to the verification.
581595 // The header validation must be done before applying the block to avoid executing gibberish
582- if err := s .validateBlock ( currentState , data , header ); err != nil {
596+ if err := s .ValidateBlock ( ctx , currentState , data , header ); err != nil {
583597 // remove header as da included (not per se needed, but keep cache clean)
584598 s .cache .RemoveHeaderDAIncluded (headerHash )
585599 if ! errors .Is (err , errInvalidState ) && ! errors .Is (err , errInvalidBlock ) {
@@ -590,7 +604,7 @@ func (s *Syncer) trySyncNextBlock(event *common.DAHeightEvent) error {
590604
591605 // Verify forced inclusion transactions if configured
592606 if event .Source == common .SourceDA {
593- if err := s .verifyForcedInclusionTxs ( currentState , data ); err != nil {
607+ if err := s .VerifyForcedInclusionTxs ( ctx , currentState , data ); err != nil {
594608 s .logger .Error ().Err (err ).Uint64 ("height" , nextHeight ).Msg ("forced inclusion verification failed" )
595609 if errors .Is (err , errMaliciousProposer ) {
596610 s .cache .RemoveHeaderDAIncluded (headerHash )
@@ -600,7 +614,7 @@ func (s *Syncer) trySyncNextBlock(event *common.DAHeightEvent) error {
600614 }
601615
602616 // Apply block
603- newState , err := s .applyBlock ( header .Header , data , currentState )
617+ newState , err := s .ApplyBlock ( ctx , header .Header , data , currentState )
604618 if err != nil {
605619 return fmt .Errorf ("failed to apply block: %w" , err )
606620 }
@@ -650,16 +664,16 @@ func (s *Syncer) trySyncNextBlock(event *common.DAHeightEvent) error {
650664 return nil
651665}
652666
653- // applyBlock applies a block to get the new state
654- func (s * Syncer ) applyBlock ( header types.Header , data * types.Data , currentState types.State ) (types.State , error ) {
667+ // ApplyBlock applies a block to get the new state
668+ func (s * Syncer ) ApplyBlock ( ctx context. Context , header types.Header , data * types.Data , currentState types.State ) (types.State , error ) {
655669 // Prepare transactions
656670 rawTxs := make ([][]byte , len (data .Txs ))
657671 for i , tx := range data .Txs {
658672 rawTxs [i ] = []byte (tx )
659673 }
660674
661675 // Execute transactions
662- ctx : = context .WithValue (s . ctx , types .HeaderContextKey , header )
676+ ctx = context .WithValue (ctx , types .HeaderContextKey , header )
663677 newAppHash , err := s .executeTxsWithRetry (ctx , rawTxs , header , currentState )
664678 if err != nil {
665679 s .sendCriticalError (fmt .Errorf ("failed to execute transactions: %w" , err ))
@@ -705,11 +719,11 @@ func (s *Syncer) executeTxsWithRetry(ctx context.Context, rawTxs [][]byte, heade
705719 return nil , nil
706720}
707721
708- // validateBlock validates a synced block
722+ // ValidateBlock validates a synced block
709723// NOTE: if the header was gibberish and somehow passed all validation prior but the data was correct
710724// or if the data was gibberish and somehow passed all validation prior but the header was correct
711725// we are still losing both in the pending event. This should never happen.
712- func (s * Syncer ) validateBlock ( currState types.State , data * types.Data , header * types.SignedHeader ) error {
726+ func (s * Syncer ) ValidateBlock ( _ context. Context , currState types.State , data * types.Data , header * types.SignedHeader ) error {
713727 // Set custom verifier for aggregator node signature
714728 header .SetCustomVerifierForSyncNode (s .options .SyncNodeSignatureBytesProvider )
715729
@@ -804,11 +818,11 @@ func (s *Syncer) getEffectiveGracePeriod() uint64 {
804818 return uint64 (max (effectivePeriod , minPeriod ))
805819}
806820
807- // verifyForcedInclusionTxs verifies that forced inclusion transactions from DA are properly handled.
821+ // VerifyForcedInclusionTxs verifies that forced inclusion transactions from DA are properly handled.
808822// Note: Due to block size constraints (MaxBytes), sequencers may defer forced inclusion transactions
809823// to future blocks (smoothing). This is legitimate behavior within an epoch.
810824// However, ALL forced inclusion txs from an epoch MUST be included before the next epoch begins or grace boundary (whichever comes later).
811- func (s * Syncer ) verifyForcedInclusionTxs ( currentState types.State , data * types.Data ) error {
825+ func (s * Syncer ) VerifyForcedInclusionTxs ( _ context. Context , currentState types.State , data * types.Data ) error {
812826 if s .fiRetriever == nil {
813827 return nil
814828 }
0 commit comments