@@ -26,6 +26,7 @@ import (
2626 "time"
2727
2828 ctypes "github.com/berachain/beacon-kit/consensus-types/types"
29+ "github.com/berachain/beacon-kit/consensus/cometbft/service/encoding"
2930 "github.com/berachain/beacon-kit/consensus/types"
3031 datypes "github.com/berachain/beacon-kit/da/types"
3132 "github.com/berachain/beacon-kit/primitives/crypto"
@@ -39,12 +40,27 @@ import (
3940func (s * Service ) FinalizeBlock (
4041 ctx sdk.Context ,
4142 req * cmtabci.FinalizeBlockRequest ,
43+ blobs datypes.BlobSidecars ,
4244) (transition.ValidatorUpdates , error ) {
45+ maxTxCount := MaxConsensusTxsCount
46+ if s .chainSpec .BlobConsensusEnableHeight () > 0 && req .Height >= s .chainSpec .BlobConsensusEnableHeight () {
47+ maxTxCount = 1
48+ }
49+
4350 // STEP 1: Decode block and blobs.
44- signedBlk , blobs , err := s .ParseBeaconBlock (req )
51+ if countTx := len (req .GetTxs ()); countTx > maxTxCount {
52+ return nil , fmt .Errorf ("invalid tx count in FinalizeBlock at height %d: expected max %d, got %d" ,
53+ req .Height , maxTxCount , countTx )
54+ }
55+
56+ forkVersion := s .chainSpec .ActiveForkVersionForTimestamp (math .U64 (req .GetTime ().Unix ())) //#nosec: G115
57+ signedBlk , err := encoding .UnmarshalBeaconBlockFromABCIRequest (
58+ req .GetTxs (),
59+ BeaconBlockTxIndex ,
60+ forkVersion ,
61+ )
4562 if err != nil {
46- s .logger .Error ("Failed to decode block and blobs" , "error" , err )
47- return nil , fmt .Errorf ("failed to decode block and blobs: %w" , err )
63+ return nil , fmt .Errorf ("failed to decode block at height %d with fork version %s: %w" , req .Height , forkVersion , err )
4864 }
4965 blk := signedBlk .GetBeaconBlock ()
5066 st := s .storageBackend .StateFromContext (ctx )
@@ -73,43 +89,95 @@ func (s *Service) FinalizeBlock(
7389 consensusBlk := types .NewConsensusBlock (blk , req .GetProposerAddress (), req .GetTime ())
7490 valUpdates , err := s .finalizeBeaconBlock (ctx , st , consensusBlk )
7591 if err != nil {
76- s .logger .Error ("Failed to process verified beacon block" ,
77- "error" , err ,
78- )
79- return nil , err
92+ return nil , fmt .Errorf ("failed finalizing beacon block at height %d: %w" , req .Height , err )
8093 }
8194
8295 // STEP 4: Post Finalizations cleanups.
8396 return valUpdates , s .PostFinalizeBlockOps (ctx , blk )
8497}
8598
99+ //nolint:gocognit // ok for now
86100func (s * Service ) FinalizeSidecars (
87101 ctx sdk.Context ,
88102 syncingToHeight int64 ,
89103 blk * ctypes.BeaconBlock ,
90104 blobs datypes.BlobSidecars ,
91105) error {
106+ // Check the block to see if there should be blobs
107+ expectedBlobs := len (blk .GetBody ().GetBlobKzgCommitments ())
108+ if expectedBlobs == 0 {
109+ return nil // No blobs expected for this block
110+ }
111+
112+ processBlobsFunc := func (blobs datypes.BlobSidecars ) error {
113+ // Process the blobs which saves them to storage
114+ if err := s .blobProcessor .ProcessSidecars (s .storageBackend .AvailabilityStore (), blobs ); err != nil {
115+ return fmt .Errorf ("failed to process %d blob sidecars for slot %d: %w" , len (blobs ), blk .GetSlot (), err )
116+ }
117+
118+ // Final verification
119+ if ! s .storageBackend .AvailabilityStore ().IsDataAvailable (ctx , blk .GetSlot (), blk .GetBody ()) {
120+ return fmt .Errorf ("data not available after processing blobs for slot %d: %w" , blk .GetSlot (), ErrDataNotAvailable )
121+ }
122+
123+ return nil
124+ }
125+
92126 // SyncingToHeight is always the tip of the chain both during sync and when
93127 // caught up. We don't need to process sidecars unless they are within DA period.
94128 //
95129 //#nosec: G115 // SyncingToHeight will never be negative.
130+ //nolint:nestif // ok for now
96131 if s .chainSpec .WithinDAPeriod (blk .GetSlot (), math .Slot (syncingToHeight )) {
97- err := s .blobProcessor .ProcessSidecars (
98- s .storageBackend .AvailabilityStore (),
99- blobs ,
100- )
132+ if s .storageBackend .AvailabilityStore ().IsDataAvailable (ctx , blk .GetSlot (), blk .GetBody ()) {
133+ return nil // Data already available
134+ }
135+
136+ // if we are before blob enable height then blobs should be in storage already
137+ if s .chainSpec .BlobConsensusEnableHeight () <= 0 || int64 (blk .GetSlot ()) < s .chainSpec .BlobConsensusEnableHeight () {
138+ return processBlobsFunc (blobs )
139+ }
140+
141+ // If blobs were passed in (normal consensus mode), use them
142+ if len (blobs ) > 0 {
143+ return processBlobsFunc (blobs )
144+ }
145+
146+ // Otherwise we need to fetch the blobs from peers (sync mode)
147+ if s .blobRequester == nil {
148+ return fmt .Errorf ("blob requester not initialized for slot %d after BlobEnableHeight %d" ,
149+ blk .GetSlot (), s .chainSpec .BlobConsensusEnableHeight ())
150+ }
151+
152+ // Check if we're at the current consensus height (switching from sync to consensus). In this case, we're finalizing a block
153+ // that was just agreed upon in consensus, and blob fetching would fail since peers are still processing the same height
154+ if int64 (blk .GetSlot ()) >= syncingToHeight {
155+ s .logger .Warn ("At consensus height during mode switch, skipping blob fetch" ,
156+ "slot" , blk .GetSlot (),
157+ "syncingToHeight" , syncingToHeight ,
158+ "expected_blobs" , expectedBlobs )
159+ return nil
160+ }
161+
162+ s .logger .Info ("Fetching missing blobs during sync" , "slot" , blk .GetSlot (), "expected_blobs" , expectedBlobs )
163+ now := time .Now ()
164+ fetchedBlobs , err := s .blobRequester .RequestBlobs (blk .GetSlot ().Unwrap ())
101165 if err != nil {
102- s .logger .Error ("Failed to process blob sidecars" , "error" , err )
103- return fmt .Errorf ("failed to process blob sidecars: %w" , err )
166+ return fmt .Errorf ("failed to fetch blobs, slot: %d: %w" , blk .GetSlot (), err )
104167 }
105168
106- // Ensure we can access the data using the commitments from the block.
107- if ! s .storageBackend .AvailabilityStore ().IsDataAvailable (
108- ctx , blk .GetSlot (), blk .GetBody (),
109- ) {
110- return ErrDataNotAvailable
169+ // Validate blobs fetched from peers before processing
170+ err = s .blobProcessor .VerifySidecars (ctx , fetchedBlobs , blk .GetHeader (), blk .GetBody ().GetBlobKzgCommitments ())
171+ if err != nil {
172+ return fmt .Errorf ("failed to verify fetched blobs from peers at slot %d: %w" , blk .GetSlot (), err )
111173 }
112- return nil
174+
175+ s .logger .Info ("Successfully fetched and verified blobs" ,
176+ "slot" , blk .GetSlot (),
177+ "count" , len (fetchedBlobs ),
178+ "elapsed" , time .Since (now ).String ())
179+
180+ return processBlobsFunc (fetchedBlobs )
113181 }
114182
115183 // Here outside Data Availability window. Just log if needed
@@ -141,6 +209,11 @@ func (s *Service) PostFinalizeBlockOps(ctx sdk.Context, blk *ctypes.BeaconBlock)
141209 return err
142210 }
143211
212+ // Update the BlobReactor with the current head slot so it can advertise to peers
213+ if s .blobRequester != nil {
214+ s .blobRequester .SetHeadSlot (slot .Unwrap ())
215+ }
216+
144217 // Prune the availability and deposit store.
145218 if err := s .processPruning (ctx , blk ); err != nil {
146219 s .logger .Error ("failed to processPruning" , "error" , err )
0 commit comments