@@ -504,3 +504,114 @@ func TestDARetriever_FetchForcedIncludedTxs_NotFound(t *testing.T) {
504504 require .NotNil (t , result )
505505 require .Empty (t , result .Txs )
506506}
507+
508+ func TestDARetriever_RetrieveForcedIncludedTxsFromDA_ExceedsMaxBlobSize (t * testing.T ) {
509+ ds := dssync .MutexWrap (datastore .NewMapDatastore ())
510+ st := store .New (ds )
511+ cm , err := cache .NewManager (config .DefaultConfig (), st , zerolog .Nop ())
512+ require .NoError (t , err )
513+
514+ addr , pub , signer := buildSyncTestSigner (t )
515+ gen := genesis.Genesis {ChainID : "tchain" , InitialHeight : 1 , StartTime : time .Now ().Add (- time .Second ), ProposerAddress : addr }
516+
517+ cfg := config .DefaultConfig ()
518+ cfg .DA .ForcedInclusionNamespace = "nsForcedInclusion"
519+ cfg .DA .ForcedInclusionDAEpoch = 3 // Test with multiple epochs
520+
521+ namespaceForcedInclusionBz := coreda .NamespaceFromString (cfg .DA .GetForcedInclusionNamespace ()).Bytes ()
522+
523+ // Create signed data blobs that will exceed DefaultMaxBlobSize when accumulated
524+ // DefaultMaxBlobSize is 1.5MB = 1,572,864 bytes
525+ // Each 700KB tx becomes ~719KB blob, so 2 blobs = ~1.44MB (fits), 3 blobs = ~2.16MB (exceeds)
526+ d1 := & types.Data {
527+ Metadata : & types.Metadata {ChainID : gen .ChainID , Height : 10 , Time : uint64 (time .Now ().UnixNano ())},
528+ Txs : make (types.Txs , 1 ),
529+ }
530+ d1 .Txs [0 ] = make ([]byte , 700 * 1024 ) // 700KB transaction
531+
532+ payload1 , err := d1 .MarshalBinary ()
533+ require .NoError (t , err )
534+ sig1 , err := signer .Sign (payload1 )
535+ require .NoError (t , err )
536+ sd1 := & types.SignedData {Data : * d1 , Signature : sig1 , Signer : types.Signer {PubKey : pub , Address : addr }}
537+ dataBin1 , err := sd1 .MarshalBinary ()
538+ require .NoError (t , err )
539+
540+ d2 := & types.Data {
541+ Metadata : & types.Metadata {ChainID : gen .ChainID , Height : 11 , Time : uint64 (time .Now ().UnixNano ())},
542+ Txs : make (types.Txs , 1 ),
543+ }
544+ d2 .Txs [0 ] = make ([]byte , 700 * 1024 ) // 700KB transaction
545+
546+ payload2 , err := d2 .MarshalBinary ()
547+ require .NoError (t , err )
548+ sig2 , err := signer .Sign (payload2 )
549+ require .NoError (t , err )
550+ sd2 := & types.SignedData {Data : * d2 , Signature : sig2 , Signer : types.Signer {PubKey : pub , Address : addr }}
551+ dataBin2 , err := sd2 .MarshalBinary ()
552+ require .NoError (t , err )
553+
554+ d3 := & types.Data {
555+ Metadata : & types.Metadata {ChainID : gen .ChainID , Height : 12 , Time : uint64 (time .Now ().UnixNano ())},
556+ Txs : make (types.Txs , 1 ),
557+ }
558+ d3 .Txs [0 ] = make ([]byte , 700 * 1024 ) // 700KB transaction
559+
560+ payload3 , err := d3 .MarshalBinary ()
561+ require .NoError (t , err )
562+ sig3 , err := signer .Sign (payload3 )
563+ require .NoError (t , err )
564+ sd3 := & types.SignedData {Data : * d3 , Signature : sig3 , Signer : types.Signer {PubKey : pub , Address : addr }}
565+ dataBin3 , err := sd3 .MarshalBinary ()
566+ require .NoError (t , err )
567+
568+ mockDA := testmocks .NewMockDA (t )
569+
570+ // First epoch - should succeed
571+ mockDA .EXPECT ().GetIDs (mock .Anything , uint64 (1001 ), mock .MatchedBy (func (ns []byte ) bool {
572+ return bytes .Equal (ns , namespaceForcedInclusionBz )
573+ })).Return (& coreda.GetIDsResult {IDs : [][]byte {[]byte ("fi1" )}, Timestamp : time .Now ()}, nil ).Once ()
574+
575+ mockDA .EXPECT ().Get (mock .Anything , mock .Anything , mock .MatchedBy (func (ns []byte ) bool {
576+ return bytes .Equal (ns , namespaceForcedInclusionBz )
577+ })).Return ([][]byte {dataBin1 }, nil ).Once ()
578+
579+ // Second epoch - should succeed
580+ mockDA .EXPECT ().GetIDs (mock .Anything , uint64 (1002 ), mock .MatchedBy (func (ns []byte ) bool {
581+ return bytes .Equal (ns , namespaceForcedInclusionBz )
582+ })).Return (& coreda.GetIDsResult {IDs : [][]byte {[]byte ("fi2" )}, Timestamp : time .Now ()}, nil ).Once ()
583+
584+ mockDA .EXPECT ().Get (mock .Anything , mock .Anything , mock .MatchedBy (func (ns []byte ) bool {
585+ return bytes .Equal (ns , namespaceForcedInclusionBz )
586+ })).Return ([][]byte {dataBin2 }, nil ).Once ()
587+
588+ // Third epoch - should be retrieved but cause error due to size limit
589+ mockDA .EXPECT ().GetIDs (mock .Anything , uint64 (1003 ), mock .MatchedBy (func (ns []byte ) bool {
590+ return bytes .Equal (ns , namespaceForcedInclusionBz )
591+ })).Return (& coreda.GetIDsResult {IDs : [][]byte {[]byte ("fi3" )}, Timestamp : time .Now ()}, nil ).Once ()
592+
593+ mockDA .EXPECT ().Get (mock .Anything , mock .Anything , mock .MatchedBy (func (ns []byte ) bool {
594+ return bytes .Equal (ns , namespaceForcedInclusionBz )
595+ })).Return ([][]byte {dataBin3 }, nil ).Once ()
596+
597+ r := NewDARetriever (mockDA , cm , cfg , gen , zerolog .Nop ())
598+
599+ result , err := r .RetrieveForcedIncludedTxsFromDA (context .Background (), 1000 )
600+
601+ // Should succeed but skip the third blob due to size limit (using continue)
602+ require .NoError (t , err )
603+ require .NotNil (t , result )
604+
605+ // Should only have 2 transactions, third is skipped due to size
606+ require .Len (t , result .Txs , 2 )
607+ assert .Equal (t , dataBin1 , result .Txs [0 ])
608+ assert .Equal (t , dataBin2 , result .Txs [1 ])
609+
610+ // Verify total size is within limits
611+ totalSize := len (dataBin1 ) + len (dataBin2 )
612+ assert .LessOrEqual (t , totalSize , int (common .DefaultMaxBlobSize ))
613+
614+ // Verify that adding the third would have exceeded the limit
615+ totalSizeWithThird := totalSize + len (dataBin3 )
616+ assert .Greater (t , totalSizeWithThird , int (common .DefaultMaxBlobSize ))
617+ }
0 commit comments