@@ -24,6 +24,7 @@ import (
2424	"github.com/cometbft/cometbft/libs/protoio" 
2525	cryptoproto "github.com/cometbft/cometbft/proto/tendermint/crypto" 
2626	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 
27+ 
2728	cmttypes "github.com/cometbft/cometbft/types" 
2829	"github.com/cometbft/cometbft/version" 
2930)
@@ -36,6 +37,7 @@ const (
3637	suffixChainID        string  =  "ChainID" 
3738	suffixVoteExtHeight  string  =  "VoteExtensionsHeight" 
3839	suffixInitialHeight  string  =  "InitialHeight" 
40+ 	suffixBlobMaxBytes   string  =  "BlobMaxBytes" 
3941)
4042
4143// Application is an ABCI application for use by end-to-end tests. It is a 
@@ -111,6 +113,16 @@ type Config struct {
111113	// -1 denotes it is set at genesis. 
112114	// 0 denotes it is set at InitChain. 
113115	VoteExtensionsUpdateHeight  int64  `toml:"vote_extensions_update_height"` 
116+ 
117+ 	// BlobMaxBytesUpdateHeight configures the height at which the 
118+ 	// blob max bytes consensus parameters are updated 
119+ 	// -1 means the max_bytes value is set at genesis 
120+ 	// 0 means the max_bytes value is set at InitChain 
121+ 	// >0 means the max_bytes value is set at the given height 
122+ 	BlobMaxBytesUpdateHeight  int64  `toml:"blob_max_bytes_update_height"` 
123+ 
124+ 	// BlobMaxBytes is the value of max bytes for blobs 
125+ 	BlobMaxBytes  int64  `toml:"blob_max_bytes"` 
114126}
115127
116128func  DefaultConfig (dir  string ) * Config  {
@@ -123,14 +135,15 @@ func DefaultConfig(dir string) *Config {
123135
124136// blobOracle can tell you the expected blob on a specific height. 
125137// It can be used to add blobs to proposals or simulate network peer responses. 
126- // Blob properties: height modulo 4  
138+ // Blob properties: height modulo 5  
127139// 0 - no blob 
128140// 1 - blob is exactly 8 bytes long and contains the height truncated into a byte 8 times 
129141// 2 - blob is empty ([]byte{}) 
130142// 3 - blob is variable length string that contains "BLOBXXX" where XXX is height multiplied by 0x80 in hex 
131143// 4 - blob is the size of MaxBlobSizeBytes and contains height truncated into two bytes repeated. 
132144func  blobOracle (height  int64 ) ([]byte , bool ) {
133- 	switch  height  %  4  {
145+ 
146+ 	switch  height  %  5  {
134147	case  1 :
135148		truncatedHeight  :=  byte (height  %  0x100 )
136149		data  :=  bytes .Repeat ([]byte {truncatedHeight }, 8 )
@@ -141,7 +154,7 @@ func blobOracle(height int64) ([]byte, bool) {
141154		return  []byte (fmt .Sprintf ("BLOB%x" , height * 0x80 )), true 
142155	case  4 :
143156		truncatedHeight  :=  byte (height  %  0x10000 )
144- 		data  :=  bytes .Repeat ([]byte {truncatedHeight }, cmttypes .MaxBlobSizeBytes / 4 )
157+ 		data  :=  bytes .Repeat ([]byte {truncatedHeight }, cmttypes .MaxBlobSizeBytes )
145158		return  data , true 
146159	}
147160	return  nil , false 
@@ -204,6 +217,26 @@ func (app *Application) Info(context.Context, *abci.RequestInfo) (*abci.Response
204217	}, nil 
205218}
206219
220+ // Expected to be called with params set 
221+ func  (app  * Application ) updateBlobMaxBytes (currentHeight  int64 , params  * cmtproto.ConsensusParams ) * cmtproto.ConsensusParams  {
222+ 
223+ 	if  app .cfg .BlobMaxBytesUpdateHeight  ==  currentHeight  {
224+ 		app .logger .Info ("updating blob max bytes on the fly" ,
225+ 			"current_height" , currentHeight ,
226+ 			"blob_max_bytes_update_height" , app .cfg .BlobMaxBytesUpdateHeight )
227+ 		if  params  ==  nil  {
228+ 			params  =  & cmtproto.ConsensusParams {}
229+ 		}
230+ 		params .Blob  =  & cmtproto.BlobParams {
231+ 			MaxBytes : app .cfg .BlobMaxBytes ,
232+ 		}
233+ 
234+ 		app .logger .Info ("updating blob max bytes in app_state" , "height" , app .cfg .BlobMaxBytes )
235+ 		app .state .Set (prefixReservedKey + suffixBlobMaxBytes , strconv .FormatInt (app .cfg .BlobMaxBytes , 10 ))
236+ 	}
237+ 	return  params 
238+ }
239+ 
207240func  (app  * Application ) updateVoteExtensionEnableHeight (currentHeight  int64 ) * cmtproto.ConsensusParams  {
208241	var  params  * cmtproto.ConsensusParams 
209242	if  app .cfg .VoteExtensionsUpdateHeight  ==  currentHeight  {
@@ -224,6 +257,7 @@ func (app *Application) updateVoteExtensionEnableHeight(currentHeight int64) *cm
224257// Info implements ABCI. 
225258func  (app  * Application ) InitChain (_  context.Context , req  * abci.RequestInitChain ) (* abci.ResponseInitChain , error ) {
226259	var  err  error 
260+ 
227261	app .state .initialHeight  =  uint64 (req .InitialHeight )
228262	if  len (req .AppStateBytes ) >  0  {
229263		err  =  app .state .Import (0 , req .AppStateBytes )
@@ -237,6 +271,7 @@ func (app *Application) InitChain(_ context.Context, req *abci.RequestInitChain)
237271	app .state .Set (prefixReservedKey + suffixVoteExtHeight , strconv .FormatInt (req .ConsensusParams .Abci .VoteExtensionsEnableHeight , 10 ))
238272	app .logger .Info ("setting initial height in app_state" , "initial_height" , req .InitialHeight )
239273	app .state .Set (prefixReservedKey + suffixInitialHeight , strconv .FormatInt (req .InitialHeight , 10 ))
274+ 	app .state .Set (prefixReservedKey + suffixBlobMaxBytes , strconv .FormatInt (req .ConsensusParams .Blob .MaxBytes , 10 ))
240275	// Get validators from genesis 
241276	if  req .Validators  !=  nil  {
242277		for  _ , val  :=  range  req .Validators  {
@@ -249,6 +284,8 @@ func (app *Application) InitChain(_ context.Context, req *abci.RequestInitChain)
249284
250285	params  :=  app .updateVoteExtensionEnableHeight (0 )
251286
287+ 	params  =  app .updateBlobMaxBytes (0 , params )
288+ 
252289	resp  :=  & abci.ResponseInitChain {
253290		ConsensusParams : params ,
254291		AppHash :         app .state .GetHash (),
@@ -281,6 +318,10 @@ func (app *Application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*a
281318// It returns a boolean indicating if a blob exists (either retrieved from the simaulted network or from the 
282319// local cache) and the blob itself. 
283320func  (app  * Application ) GetBlob (height  int64 ) ([]byte , bool , error ) {
321+ 	if  ! app .checkBlobEnabled ("getBlob" ) {
322+ 		// Blob max bytes is 0 so we cannot send a blob 
323+ 		return  nil , false , nil 
324+ 	}
284325	// First check the local cache 
285326	if  blob , found  :=  app .blobCache [height ]; found  {
286327		if  ! isBlob (blob ) {
@@ -317,18 +358,20 @@ func (app *Application) FinalizeBlock(_ context.Context, req *abci.RequestFinali
317358	}
318359
319360	// Verify blob. 
320- 	blob , exists , err  :=  app .GetBlob (req .Height )
321- 	if  err  !=  nil  {
322- 		return  nil , fmt .Errorf ("could not fetch blob for height %d, %s" , req .Height , err .Error ())
323- 	}
324- 	if  exists  &&  ! VerifyBlob (req .Height , blob ) {
325- 		return  nil , fmt .Errorf ("blob verification failed for height %d" , req .Height )
326- 	}
361+ 	if  app .checkBlobEnabled ("FinalizeBlock" ) {
362+ 		blob , exists , err  :=  app .GetBlob (req .Height )
363+ 		if  err  !=  nil  {
364+ 			return  nil , fmt .Errorf ("could not fetch blob for height %d, %s" , req .Height , err .Error ())
365+ 		}
366+ 		if  exists  &&  ! VerifyBlob (req .Height , blob ) {
367+ 			return  nil , fmt .Errorf ("blob verification failed for height %d" , req .Height )
368+ 		}
327369
328- 	// This is a short-term cache so we delete the entry after we received it. 
329- 	delete (app .blobCache , req .Height )
330- 	if  len (app .blobCache ) !=  0  {
331- 		app .logger .Error ("blob cache should be empty" , "size" , len (app .blobCache ))
370+ 		// This is a short-term cache so we delete the entry after we received it. 
371+ 		delete (app .blobCache , req .Height )
372+ 		if  len (app .blobCache ) !=  0  {
373+ 			app .logger .Error ("blob cache should be empty" , "size" , len (app .blobCache ))
374+ 		}
332375	}
333376
334377	for  _ , ev  :=  range  req .Misbehavior  {
@@ -348,6 +391,8 @@ func (app *Application) FinalizeBlock(_ context.Context, req *abci.RequestFinali
348391
349392	params  :=  app .updateVoteExtensionEnableHeight (req .Height )
350393
394+ 	params  =  app .updateBlobMaxBytes (req .Height , params )
395+ 
351396	if  app .cfg .FinalizeBlockDelay  !=  0  {
352397		time .Sleep (app .cfg .FinalizeBlockDelay )
353398	}
@@ -527,17 +572,23 @@ func (app *Application) PrepareProposal(
527572		// Coherence: No need to call parseTx, as the check is stateless and has been performed by CheckTx 
528573		txs  =  append (txs , tx )
529574	}
575+ 	var  blob  []byte 
576+ 	var  exists  bool 
530577	// Generate blob for the current height. 
531- 	blob , exists  :=  CreateBlob (req .Height )
532- 
578+ 	if  app .checkBlobEnabled ("prepare_proposal" ) {
579+ 		blob , exists  =  CreateBlob (req .Height )
580+ 		if  len (blob ) !=  0  {
581+ 			app .logger .Debug ("Received blob" , "Blob: " , blob [min (len (blob )- 1 , 10 )])
582+ 		}
583+ 	}
533584	if  app .cfg .PrepareProposalDelay  !=  0  {
534585		time .Sleep (app .cfg .PrepareProposalDelay )
535586	}
536587
537588	if  ! exists  {
538589		return  & abci.ResponsePrepareProposal {Txs : txs }, nil 
539590	}
540- 	 fmt . Println ( "BLOB_1: " ,  blob ) 
591+ 
541592	return  & abci.ResponsePrepareProposal {Txs : txs , Blob : blob }, nil 
542593}
543594
@@ -548,23 +599,28 @@ func (app *Application) PrepareProposal(
548599func  (app  * Application ) ProcessProposal (_  context.Context , req  * abci.RequestProcessProposal ) (* abci.ResponseProcessProposal , error ) {
549600	r  :=  & abci.Request {Value : & abci.Request_ProcessProposal {ProcessProposal : & abci.RequestProcessProposal {}}}
550601	app .logger .Info ("ABCIRequest" , "request" , r )
602+ 	if  app .checkBlobEnabled ("ProcessProposal" ) {
551603
552- 	fmt .Println ("BLOB: " , req .Blob )
553- 
554- 	if  ! VerifyBlob (req .Height , req .Blob ) {
555- 		app .logger .Error ("invalid blob, rejecting proposal" , "received height" , req .Height , "received" , req .Blob )
556- 		return  & abci.ResponseProcessProposal {Status : abci .ResponseProcessProposal_REJECT }, nil 
557- 	}
558- 	// Blob verification 
559- 	if  len (req .Blob ) !=  0  {
560- 		// Proposal will be accepted by us and blob exists and valid, so we store it in the cache. 
561- 		app .blobCache [req .Height ] =  req .Blob 
604+ 		if  ! VerifyBlob (req .Height , req .Blob ) {
605+ 			app .logger .Error ("invalid blob, rejecting proposal" , "received height" , req .Height , "received" , req .Blob )
606+ 			return  & abci.ResponseProcessProposal {Status : abci .ResponseProcessProposal_REJECT }, nil 
607+ 		}
608+ 		// Blob verification 
609+ 		if  len (req .Blob ) !=  0  {
610+ 			app .logger .Debug ("Received blob" , "Blob: " , req .Blob [min (len (req .Blob )- 1 , 10 )])
611+ 			// Proposal will be accepted by us and blob exists and valid, so we store it in the cache. 
612+ 			app .blobCache [req .Height ] =  req .Blob 
613+ 		} else  {
614+ 			// Proposal will be accepted by us and there is no blob. 
615+ 			// We make a note of it in the cache so we can inform FinalizeBlock. A real application will have better methods for this. 
616+ 			app .blobCache [req .Height ] =  noBlobBytes ()
617+ 		}
562618	} else  {
563- 		// Proposal will be accepted by us and there is no blob. 
564- 		// We make a note of it in the cache so we can inform FinalizeBlock. A real application will have better methods for this. 
565- 		app .blobCache [req .Height ] =  noBlobBytes ()
619+ 		if  len (req .Blob ) !=  0  {
620+ 			app .logger .Error ("received a blob when blobs are disabled, rejecting proposal" , "received height" , req .Height , "received" , req .Blob )
621+ 			return  & abci.ResponseProcessProposal {Status : abci .ResponseProcessProposal_REJECT }, nil 
622+ 		}
566623	}
567- 
568624	_ , areExtensionsEnabled  :=  app .checkHeightAndExtensions (true , req .Height , "ProcessProposal" )
569625
570626	for  _ , tx  :=  range  req .Txs  {
@@ -686,6 +742,21 @@ func (app *Application) getAppHeight() int64 {
686742	return  appHeight  +  1 
687743}
688744
745+ func  (app  * Application ) checkBlobEnabled (callsite  string ) bool  {
746+ 	blobMaxBytesStr  :=  app .state .Get (prefixReservedKey  +  suffixBlobMaxBytes )
747+ 	if  len (blobMaxBytesStr ) ==  0  {
748+ 		panic ("blob max bytes not set in database" )
749+ 	}
750+ 	blobMaxBytes , err  :=  strconv .ParseInt (blobMaxBytesStr , 10 , 64 )
751+ 	if  err  !=  nil  {
752+ 		panic (fmt .Errorf ("malformed blob max bytes %q in database" , blobMaxBytesStr ))
753+ 	}
754+ 	if  blobMaxBytes  ==  0  {
755+ 		return  false 
756+ 	}
757+ 	return  true 
758+ }
759+ 
689760func  (app  * Application ) checkHeightAndExtensions (isPrepareProcessProposal  bool , height  int64 , callsite  string ) (int64 , bool ) {
690761	appHeight  :=  app .getAppHeight ()
691762	if  height  !=  appHeight  {
0 commit comments