Skip to content

Commit 9cfe41d

Browse files
(consensus/params): Set default value of BlobParams.maxBytes to 0 (#19)
* Set default value of blob params to 0 * Added maxBytes param update to e2e * Update ci.toml with proper blob_max_bytes value and fix check * fixed edge case when blobs are disabled, lowered the height they are activated at * Minor fixes to address initial comments * BlobMaxBytes update logic is the same as VE * Removed printf * Moved BlobMaxBytes var close to BlobMaxBytesUpdateHeight * Do not use blob in UTs * Fix UT to use blobs and set maxBlobBytes to 800kb * Fix typo * Applied review comments * Fix UTs * Update comment Co-authored-by: Sergio Mena <[email protected]> * Update comment Co-authored-by: Sergio Mena <[email protected]> --------- Co-authored-by: Sergio Mena <[email protected]>
1 parent 86f5774 commit 9cfe41d

File tree

14 files changed

+211
-49
lines changed

14 files changed

+211
-49
lines changed

consensus/byzantine_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ func TestByzantineConflictingProposalsWithPartition(t *testing.T) {
353353
require.NoError(t, err)
354354

355355
conR := NewReactor(css[i], true) // so we don't start the consensus states
356+
conR.conS.state.ConsensusParams.Blob.MaxBytes = types.MaxBlobSizeBytes
356357
conR.SetLogger(logger.With("validator", i))
357358
conR.SetEventBus(eventBus)
358359

consensus/common_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -513,13 +513,13 @@ func loadPrivValidator(config *cfg.Config) *privval.FilePV {
513513
}
514514

515515
func randState(nValidators int) (*State, []*validatorStub) {
516-
return randStateWithApp(nValidators, kvstore.NewInMemoryApplication())
516+
return randStateWithApp(nValidators, kvstore.NewInMemoryApplication(), false)
517517
}
518518

519519
func randStateWithBlob(nValidators int) (*State, []*validatorStub) {
520520
app := kvstore.NewInMemoryApplication()
521521
app.SetGenerateBlobs()
522-
return randStateWithApp(nValidators, app)
522+
return randStateWithApp(nValidators, app, true)
523523
}
524524

525525
func randStateWithAppWithHeight(
@@ -531,8 +531,11 @@ func randStateWithAppWithHeight(
531531
c.ABCI.VoteExtensionsEnableHeight = height
532532
return randStateWithAppImpl(nValidators, app, c)
533533
}
534-
func randStateWithApp(nValidators int, app abci.Application) (*State, []*validatorStub) {
534+
func randStateWithApp(nValidators int, app abci.Application, blob bool) (*State, []*validatorStub) {
535535
c := test.ConsensusParams()
536+
if blob {
537+
c.Blob.MaxBytes = 100
538+
}
536539
return randStateWithAppImpl(nValidators, app, c)
537540
}
538541

@@ -877,6 +880,7 @@ func randConsensusNetWithPeers(
877880
appFunc func(string) abci.Application,
878881
) ([]*State, *types.GenesisDoc, *cfg.Config, cleanupFunc) {
879882
c := test.ConsensusParams()
883+
c.Blob.MaxBytes = 100
880884
genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower, c)
881885
css := make([]*State, nPeers)
882886
logger := consensusLogger()

consensus/reactor_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,8 @@ func TestReactorRecordsVotesAndBlockPartsAndBlobParts(t *testing.T) {
421421
n := 4
422422

423423
css, _, _, cleanup := randConsensusNetWithPeers(t, n, n, "consensus_reactor_test", newMockTickerFunc(true), newPersistentKVStoreWithPathAndBlob)
424-
425424
defer cleanup()
425+
426426
reactors, blocksSubs, eventBuses := startConsensusNet(t, css, n)
427427
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
428428

consensus/state_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ func TestProcessProposalAccept(t *testing.T) {
16551655
}
16561656
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: status}, nil)
16571657
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil).Maybe()
1658-
cs1, _ := randStateWithApp(4, m)
1658+
cs1, _ := randStateWithApp(4, m, false)
16591659
height, round := cs1.Height, cs1.Round
16601660

16611661
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
@@ -1796,7 +1796,7 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
17961796
}, nil)
17971797
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
17981798
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
1799-
cs1, vss := randStateWithApp(4, m)
1799+
cs1, vss := randStateWithApp(4, m, false)
18001800
height, round := cs1.Height, cs1.Round
18011801
cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = cs1.Height
18021802

@@ -1890,7 +1890,7 @@ func TestPrepareProposalReceivesVoteExtensions(t *testing.T) {
18901890
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
18911891
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil)
18921892

1893-
cs1, vss := randStateWithApp(4, m)
1893+
cs1, vss := randStateWithApp(4, m, false)
18941894
height, round := cs1.Height, cs1.Round
18951895

18961896
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
@@ -1994,7 +1994,7 @@ func TestFinalizeBlockCalled(t *testing.T) {
19941994
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(r, nil).Maybe()
19951995
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
19961996

1997-
cs1, vss := randStateWithApp(4, m)
1997+
cs1, vss := randStateWithApp(4, m, false)
19981998
height, round := cs1.Height, cs1.Round
19991999

20002000
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)

test/e2e/app/app.go

Lines changed: 102 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

116128
func 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.
132144
func 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+
207240
func (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.
225258
func (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.
283320
func (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(
548599
func (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+
689760
func (app *Application) checkHeightAndExtensions(isPrepareProcessProposal bool, height int64, callsite string) (int64, bool) {
690761
appHeight := app.getAppHeight()
691762
if height != appHeight {

test/e2e/generator/generate.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/Masterminds/semver/v3"
1313
e2e "github.com/cometbft/cometbft/test/e2e/pkg"
14+
"github.com/cometbft/cometbft/types"
1415
"github.com/cometbft/cometbft/version"
1516
"github.com/go-git/go-git/v5"
1617
"github.com/go-git/go-git/v5/plumbing/object"
@@ -62,6 +63,8 @@ var (
6263
voteExtensionUpdateHeight = uniformChoice{int64(-1), int64(0), int64(1)} // -1: genesis, 0: InitChain, 1: (use offset)
6364
voteExtensionEnabled = weightedChoice{true: 3, false: 1}
6465
voteExtensionHeightOffset = uniformChoice{int64(0), int64(10), int64(100)}
66+
blobMaxBytesUpdateHeight = uniformChoice{int64(-1), int64(0), int64(1)}
67+
blobHeightOffset = uniformChoice{int64(0), int64(10), int64(100)}
6568
)
6669

6770
type generateConfig struct {
@@ -157,6 +160,16 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}, upgradeVersion st
157160
manifest.VoteExtensionsEnableHeight = baseHeight + voteExtensionHeightOffset.Choose(r).(int64)
158161
}
159162

163+
manifest.BlobMaxBytesUpdateHeight = blobMaxBytesUpdateHeight.Choose(r).(int64)
164+
165+
if manifest.BlobMaxBytesUpdateHeight == 1 {
166+
manifest.BlobMaxBytesUpdateHeight = manifest.InitialHeight + blobHeightOffset.Choose(r).(int64)
167+
manifest.BlobMaxBytes = types.MaxBlobSizeBytes
168+
}
169+
170+
if manifest.BlobMaxBytesUpdateHeight == 0 {
171+
manifest.BlobMaxBytes = types.MaxBlobSizeBytes
172+
}
160173
var numSeeds, numValidators, numFulls, numLightClients int
161174
switch opt["topology"].(string) {
162175
case "single":

test/e2e/networks/ci.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ ipv6 = true
55
initial_height = 1000
66
vote_extensions_update_height = -1
77
vote_extensions_enable_height = 1000
8+
blob_max_bytes_update_height = 1000
9+
blob_max_bytes = 819200
810
evidence = 5
911
initial_state = { initial01 = "a", initial02 = "b", initial03 = "c" }
1012
prepare_proposal_delay = "100ms"

0 commit comments

Comments
 (0)