Skip to content

Comments

feat(state): CometBFT does not store blobs#26

Open
alesforz wants to merge 30 commits intoberachain:bera-v1.xfrom
alesforz:alesforz/feature/blobs
Open

feat(state): CometBFT does not store blobs#26
alesforz wants to merge 30 commits intoberachain:bera-v1.xfrom
alesforz:alesforz/feature/blobs

Conversation

@alesforz
Copy link

@alesforz alesforz commented Mar 13, 2025

This feature branch stores the work we are doing to prevent CometBFT from storing blobs.
We will merge related PRs to this branch rather than bera-v1.x.

Context

Berachain’s ABCI app includes two transactions in each proposal, the second of which is a blob cryptographically linked to the first. Because these blobs are included in blocks as transactions, CometBFT stores them in its block database, leading to a significant storage footprint over time.

A better approach is to avoid storing the blobs in CometBFT’s storage layer at all. This means the ABCI app must not include them in the block itself. However, the app still needs access to these blobs during ProcessProposal for state validation. Therefore, we must ensure CometBFT gossips and delivers the blobs to the app alongside the corresponding block.

Changes merged so far

  • PrepareProposal returns the blob separately from the blocks' txs (PR)
  • updates to RoundState data structure (PR 5, PR 8)
  • add BlobID type (PR)
  • extend Proposal msg with BlobID (PR)
  • ProcessProposal delivers blob to ABCI application (PR)
  • add new BlobPartMessage (PR)
  • add Blob parts to PeerRoundState (PR)
  • split blob into multiple parts (PR)
  • blob size is a configurable parameter (PR)
  • basic blob metrics (PR)
  • consensus (PR)
    • update consensus reactor to gossip block parts
    • update consensus state to handle the reception of BlobPartMessage
    • updates to RoundState during consensus rounds
    • update unit tests
  • e2e testing (PR)

Note for Readers

All the code in this PR has already been reviewed and approved in the smaller PRs linked above. All commits are either merge commits of those PRs or merges from the bera-v1.x branch to keep this branch up to date.
Therefore, to review the changes, I suggest looking at the list above and going to the specific PR that implemented the changes you're interested in. This should be easier than trying to review all the changes at once in this PR.

* Add `blob` field to `PrepareProposalResponse`.

* Add `Blob` type as `[]byte`.

* `BlockExecutor.CreateProposalBlock` returns the blob as well.

* update supranational/blst to fix compile errors

* Add `TestCreateProposalWithBlob` unit test.

Tests that a call to `PrepareProposal` returns a blob inr
`PrepareProposalResponse`.

* update go.sum

* Update call sites of `State.createProposalBlock()`

* removed unnecessary line from docs

---------

Signed-off-by: Alessandro Sforzin <alesforz@gmail.com>
* Update `RoundState` with blob fields

* Added methods `Hash` and `String` to type `Blob`.

* Update `RoundStateSimple` creation

* Update `RoundState.StringIndented()`

* `RoundState` fields of type `Blob` are no longer pointers.

Type `Blob` is a `[]byte` slice, so there's no need for it to be a pointer.

* Update `String()` method of type `Blob`

It now returns a different string when the blob is nil, as type `Block` does.
* Move `Blob` type and methods to separate file

* Add `BlobID` type

* Added protobuf definitions for type `Blob`

* Add unit tests for `BlobID` methods

* Fix typo pointed out in code review

Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Signed-off-by: Alessandro Sforzin <alesforz@gmail.com>

* regenerated protobuf

---------

Signed-off-by: Alessandro Sforzin <alesforz@gmail.com>
Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Alessandro Sforzin added 2 commits March 18, 2025 12:44
* Extend type `Proposal` with `BlobID` field

* Update `Proposal` methods `ValidateBasic()` and `String()` to use `BlobID`

* Add `BlobID` to `Proposal` protobuf.

Regenerated the proto files as well.
Moved the proto definition of `BlobID` to the `types.proto` file to fix an
import cycle.

* Add function to canonalize `BlobID`

* Update `Proposal` to/from protobuf functions to include `BlobID`

* Update `Proposal` unit tests to use `BlobID`

* Add method `IsNil()` to type `BlobID`

* `State.defaultDecideProposal() assigns an empty `BlobID` to `Proposal`.

Since `BlobID` is not used in the consensus logic yet, this change is harmless.
However, it allows us to keep this PR focused solely on `Proposal`-related
changes.

In a future PR, we’ll handle the proper creation of `BlobID` by implementing
blob splitting into multiple parts.

Additionally, this PR includes a refactor for improved readability.

* Add missing `BlobID` param to `decideProposal()`

* Add missing `BlobID` param in calls to `NewProposal()` in state_test.go

* Add missing `BlobID` param in calls to `NewProposal()` in replay_test.go

* Add missing `BlobID` param in calls to `NewProposal()` in pbts_test.go

* Add missing `BlobID` param in calls to `NewProposal()` in byzantine_test.go

* Fixed unit test TestConsMsgsVectors.

The `Proposal` sub-test was using an incorrect hex string to check the test's
results.
The string changed because we added the `BlobID` field to the `Proposal` type,
thus changing its encoding to protobuf.

Also rearrenged the code for better readability.

* Fixed unit test `TestWALEncoder`.

It was using an incorrect hex string to check the test's results.
The string changed because we added the `BlobID` field to the `Proposal` type,
thus changing its encoding to protobuf.

Also rearrenged the code for better readability.

* Fixed unit test `TestPrivvalVectors`.

The sub-tests "Proposal Request", "Proposal Response", and "Proposal Response
with error" were was using an incorrect hex string to check the test's results.

The string changed because we added the `BlobID` field to the `Proposal` type,
thus changing its encoding to protobuf.

Also rearrenged the code for better readability.

* Extend `EventDataCompleteProposal` with `BlobID` field

* `Hash()` method of type `Blob` checks return `nil` if empty blob.

* updated docs

* Update `State.isProposalComplete()`.

Rationale: If a block has an associated blob, we set the `BlobID` in `Proposal`
after returning from `PrepareProposal()`, causing the `IsNil()` check to
return false. If the block does not have a blob, the `BlobID` field in
`Proposal` will remain its zero value, making `IsNil()` return true.

If a blob is present, `cs.ProposalBlob.IsNil()` will always return true until
`cs.ProposalBlob` is assigned a non-empty `[]byte` slice. This assignment
happens only after we have received all the blob parts and we have successfully
reconstructed the blob.
Alessandro Sforzin added 5 commits March 19, 2025 11:43
* Update protobuf definition of `ProcessProposalRequest`

* Update calls to `ProcessProposal` to include the blob.

* Update test `TestProcessProposal`
…sus state and reactor (#9)

* Add `BlobPartMessage` in consensus reactor

* Add `BlobPartMessage` case in consensus state `handleMsg()`

* add blob part in protobuf messages

* Add `HasProposalBlobPartMessage` to consensus reactor

* Implement `Wrap()` method for types `BlobPart` and `HasProposalBlobPart`

* Added `BlobPartMessage` and `HasBlobPartMessage` to functions
serializing/deserializing protobuf messages to/from their corresponding
Go types

* Add `ProposalBlobPart` to events

* Add `BlobParts` to `peerStateStats`.

Does it make sense to mark a peer as good if it has sent enough blob parts?
See `switch` in `Reactor.peerStatsRoutine()`.

* Add `BlobPartMessage` to WAL replay after crash.

* Fix unit test `TestMarshalJSONPeerState`

* Update unit test `TestMsgToProto`

* Add test `TestBlobPartMessageValidateBasic`

* Small refactor of `State.readReplayMessage()`

* Add a blob ID to some WAL tests

* Address comments from code review

* Register types `BlobPartMessage` and `HasProposalBlobPartMessage` in consensus
reactor `init`()

* Add `TODO` in consensus reactor `subscribeToBroadcastEvents()`.

In this PR we don't call `FireEvent` for `BlobPartMessage`.
We must add this call in a future PR implementing consensus state updates for
blobs. Because this code isn't there yet, there isn't a "correct spot" to place
the call to `FireEvent` yet.
Alessandro Sforzin and others added 3 commits March 19, 2025 16:07
feat(state): Split `Blob` into blob parts and send it to internal message queue
feat(state): Add blob-related fields to `PeerRoundState`
* Handle reception of BlobPartMessage

* Gossip blob part to connecting peers (current round only)

* Receive routine fixes

* Clear blob on new round

* add basic test

* different way of checking for complete block

* lint

* lint2

* Properly test whether partset is complete, fixes tests

* lint3

* Added SendBlobPartSetHasBlobPart

* lint4

* lint5

* Renamed BlockPartSizeBytes to PartSizeBytes to indicate it is also impacting blobs

* block/blob completeness check fixes

* trying alternative blob completion check

* blob completion check fixes

* Reverting to IsComplete when nil

* Revert "blob completion check fixes"

This reverts commit de950b1.

* Revert "trying alternative blob completion check"

This reverts commit 35a2819.

* Revert "block/blob completeness check fixes"

This reverts commit 9cb911f.

* Revert "Reverting to IsComplete when nil"

This reverts commit dd16f7d.

* Attempt to fix the checks

* Small fix

* PartSetType for blocks and blobs

* Use partsettype

* supress similar tests as duplicates

* Remove proto decoding for blobs

* Add unit test with blob

* Fix build errors

* Use randStateWithBlob

* Set ProposalBlobPartSetHeader; note this commit also resets them , that is to be revisited

* Unit test to send blob between peers

* Reintroduced MaxBlobSizeBytes

* lint: removed unused function

* blob existence fixes

* Revert "blob existence fixes"

This reverts commit 499371d.

* condition simplified, updateHeight clears blob

* feat(consensus): Configurable max blob size bytes (#17)

* added blob parameters to params.proto

* make proto-gen

* extended ConsensusParams in types/params.go

* test app fixes for configurable blob size

* comment updated in auto-generated protobuf

* unit test fix for blob config

* typo fix

* unit tests for blob bytes config

* add detail to BlobParams doc

* regenerate protobuf files

---------

Co-authored-by: Alessandro Sforzin <alessandro@informal.systems>

* removed PartSetType

* removed unused function, lint

* removed MaxBlobSizeBytes

* No BlobID pointer in tests

* lint

* Set `ProposalBlob` to empty slice rather than `nil` in `enterNewRound()`

* Add `TestStateEnterProposeWithBlob()`

Checks that we finish the proposal stage without timing out (i.e., we have the
proposal) for a block with a corresponding blob.

* Fix incorrect formula computing `MaxBlockPartsCount`.

The previous formula always added one extra part, even when the block size is an
exact multiple of the part size.

* Rename `BlockPartSizeBytes` to `PartSizeBytes`.

* Fix `TestNewValidBlockMessageValidateBasic`

* Add blob in `TestStateFullRound1`

* Add blob to `TestStateFullRoundNil`

* Add blob to TestStateFullRound2

* Fix race condition in test

* Update util `decideProposal()` with blob.

- Adds a non-empty `BlobID` to proposal
- Returns the blob

* Add blob to `TestStateLock_NoPOL`

* Add blob to `TestStateLock_POLUpdateLock`

* Add blob to `TestStateLock_POLRelock`

* Add blob to `TestStateLock_PrevoteNilWhenLockedAndMissProposal`

* Add blob to `TestStateLock_PrevoteNilWhenLockedAndDifferentProposal`

* Add blob to `TestStateLock_POLDoesNotUnlock`

* Add blob to `TestStateLock_MissingProposalWhenPOLSeenDoesNotUpdateLock`

* Add blob to `TestStateLock_MissingProposalWhenPOLForLockedBlock`

* Add blob to `TestStateLock_DoesNotLockOnOldProposal`

* Add blob to `TestStateLock_POLSafety1`

* Add `TestBlob()` to kvstore example application

* Add blob to `TestStateLock_POLSafety1`

* Add blob to `TestStateLock_POLSafety2`

* lintwq

* Add blob to `TestState_PrevotePOLFromPreviousRound`

* Add blob to `TestProposeValidBlock`

* Add blob to `TestSetValidBlockOnDelayedPrevote`

* `ensureProposalWithTimeout` error string prints BlobID instead of BlockID.

* Add blob to `TestSetValidBlockOnDelayedProposal`

* Add blob to `TestResetTimeoutPrecommitUponNewHeight`

* Add blob to `TestState_MissingProposalValidBlockReceivedTimeout`.

* Improve blob testing in `TestState_MissingProposalValidBlockReceivedTimeout`

* Add blob to `TestState_MissingProposalValidBlockReceivedPrecommit`

* Improve blob testing in `TestStateLock_POLSafety1`

* Improve blob testing in `TestStateLock_POLSafety2`

* Add blob to `TestStateTimestamp_ProposalNotMatch`

* Add blob to `TestStateBadProposal`.

Improve blob testing in `TestStateTimestamp_ProposalNotMatch`

* Test func `decideProposal` returns app blob instead of generating one.

* Add blob `nil` checks in several tests

* Fix `TestResetTimeoutPrecommitUponNewHeight`

* Add blob to `TestByzantineConflictingProposalsWithPartition`

* minor refactor and fix incorrect msg

---------

Co-authored-by: Jasmina Malicevic <jasmina@informal.systems>
Co-authored-by: Alessandro Sforzin <alessandro@informal.systems>
* Handle reception of BlobPartMessage

* Gossip blob part to connecting peers (current round only)

* Receive routine fixes

* Clear blob on new round

* add basic test

* different way of checking for complete block

* lint

* lint2

* Properly test whether partset is complete, fixes tests

* lint3

* Added SendBlobPartSetHasBlobPart

* lint4

* lint5

* Renamed BlockPartSizeBytes to PartSizeBytes to indicate it is also impacting blobs

* e2e test app update to generate and verify blobs

* block/blob completeness check fixes

* lint1

* trying alternative blob completion check

* blob completion check fixes

* Reverting to IsComplete when nil

* Revert "blob completion check fixes"

This reverts commit de950b1.

* Revert "trying alternative blob completion check"

This reverts commit 35a2819.

* Revert "block/blob completeness check fixes"

This reverts commit 9cb911f.

* Revert "Reverting to IsComplete when nil"

This reverts commit dd16f7d.

* Attempt to fix the checks

* Small fix

* PartSetType for blocks and blobs

* Use partsettype

* supress similar tests as duplicates

* Remove proto decoding for blobs

* Add unit test with blob

* Fix build errors

* Use randStateWithBlob

* Set ProposalBlobPartSetHeader; note this commit also resets them , that is to be revisited

* Unit test to send blob between peers

* Reintroduced MaxBlobSizeBytes

* e2e app MaxBlobByteSize

* e2e app half size blobs

* lint: removed unused function

* simpler blob generation algorithm

* blob fix

* blob existence fixes

* Revert "blob existence fixes"

This reverts commit 499371d.

* condition simplified, updateHeight clears blob

* feat(consensus): Configurable max blob size bytes (#17)

* added blob parameters to params.proto

* make proto-gen

* extended ConsensusParams in types/params.go

* test app fixes for configurable blob size

* comment updated in auto-generated protobuf

* unit test fix for blob config

* typo fix

* unit tests for blob bytes config

* add detail to BlobParams doc

* regenerate protobuf files

---------

Co-authored-by: Alessandro Sforzin <alessandro@informal.systems>

* removed PartSetType

* removed unused function, lint

* removed MaxBlobSizeBytes

* No BlobID pointer in tests

* lint

* Set `ProposalBlob` to empty slice rather than `nil` in `enterNewRound()`

* Add `TestStateEnterProposeWithBlob()`

Checks that we finish the proposal stage without timing out (i.e., we have the
proposal) for a block with a corresponding blob.

* Fix incorrect formula computing `MaxBlockPartsCount`.

The previous formula always added one extra part, even when the block size is an
exact multiple of the part size.

* Rename `BlockPartSizeBytes` to `PartSizeBytes`.

* Fix `TestNewValidBlockMessageValidateBasic`

* Fixed check for empty blob

* Add blob in `TestStateFullRound1`

* Add blob to `TestStateFullRoundNil`

* Add blob to TestStateFullRound2

* Fix race condition in test

* Update util `decideProposal()` with blob.

- Adds a non-empty `BlobID` to proposal
- Returns the blob

* Add blob to `TestStateLock_NoPOL`

* Add blob to `TestStateLock_POLUpdateLock`

* Add blob to `TestStateLock_POLRelock`

* Add blob to `TestStateLock_PrevoteNilWhenLockedAndMissProposal`

* Add blob to `TestStateLock_PrevoteNilWhenLockedAndDifferentProposal`

* Add blob to `TestStateLock_POLDoesNotUnlock`

* Add blob to `TestStateLock_MissingProposalWhenPOLSeenDoesNotUpdateLock`

* Add blob to `TestStateLock_MissingProposalWhenPOLForLockedBlock`

* Add blob to `TestStateLock_DoesNotLockOnOldProposal`

* Add blob to `TestStateLock_POLSafety1`

* Add `TestBlob()` to kvstore example application

* Add blob to `TestStateLock_POLSafety1`

* Add blob to `TestStateLock_POLSafety2`

* lintwq

* Add blob to `TestState_PrevotePOLFromPreviousRound`

* Add blob to `TestProposeValidBlock`

* Add blob to `TestSetValidBlockOnDelayedPrevote`

* `ensureProposalWithTimeout` error string prints BlobID instead of BlockID.

* Add blob to `TestSetValidBlockOnDelayedProposal`

* Add blob to `TestResetTimeoutPrecommitUponNewHeight`

* Add blob to `TestState_MissingProposalValidBlockReceivedTimeout`.

* Revert "Add blob to `TestState_MissingProposalValidBlockReceivedTimeout`."

This reverts commit a17ad30.

Wrong branch.

* Add blob to `TestState_MissingProposalValidBlockReceivedTimeout`.

* Improve blob testing in `TestState_MissingProposalValidBlockReceivedTimeout`

* Add blob to `TestState_MissingProposalValidBlockReceivedPrecommit`

* Improve blob testing in `TestStateLock_POLSafety1`

* Improve blob testing in `TestStateLock_POLSafety2`

* Add blob to `TestStateTimestamp_ProposalNotMatch`

* Add blob to `TestStateBadProposal`.

Improve blob testing in `TestStateTimestamp_ProposalNotMatch`

* Test func `decideProposal` returns app blob instead of generating one.

* Add blob `nil` checks in several tests

* Fix `TestResetTimeoutPrecommitUponNewHeight`

* Add blob to `TestByzantineConflictingProposalsWithPartition`

* minor refactor and fix incorrect msg

---------

Co-authored-by: Jasmina Malicevic <jasmina@informal.systems>
Co-authored-by: Alessandro Sforzin <alessandro@informal.systems>
* Handle reception of BlobPartMessage

* Gossip blob part to connecting peers (current round only)

* Receive routine fixes

* Clear blob on new round

* add basic test

* different way of checking for complete block

* lint

* lint2

* Properly test whether partset is complete, fixes tests

* lint3

* Added SendBlobPartSetHasBlobPart

* lint4

* lint5

* Renamed BlockPartSizeBytes to PartSizeBytes to indicate it is also impacting blobs

* block/blob completeness check fixes

* trying alternative blob completion check

* blob completion check fixes

* Reverting to IsComplete when nil

* Revert "blob completion check fixes"

This reverts commit de950b1.

* Revert "trying alternative blob completion check"

This reverts commit 35a2819.

* Revert "block/blob completeness check fixes"

This reverts commit 9cb911f.

* Revert "Reverting to IsComplete when nil"

This reverts commit dd16f7d.

* Attempt to fix the checks

* Small fix

* PartSetType for blocks and blobs

* Use partsettype

* supress similar tests as duplicates

* Remove proto decoding for blobs

* Add unit test with blob

* Fix build errors

* Use randStateWithBlob

* Set ProposalBlobPartSetHeader; note this commit also resets them , that is to be revisited

* Unit test to send blob between peers

* Reintroduced MaxBlobSizeBytes

* lint: removed unused function

* implement basic blob metrics

* blob existence fixes

* Revert "blob existence fixes"

This reverts commit 499371d.

* condition simplified, updateHeight clears blob

* feat(consensus): Configurable max blob size bytes (#17)

* added blob parameters to params.proto

* make proto-gen

* extended ConsensusParams in types/params.go

* test app fixes for configurable blob size

* comment updated in auto-generated protobuf

* unit test fix for blob config

* typo fix

* unit tests for blob bytes config

* add detail to BlobParams doc

* regenerate protobuf files

---------

Co-authored-by: Alessandro Sforzin <alessandro@informal.systems>

* removed PartSetType

* removed unused function, lint

* removed MaxBlobSizeBytes

* No BlobID pointer in tests

* lint

* Set `ProposalBlob` to empty slice rather than `nil` in `enterNewRound()`

* Add `TestStateEnterProposeWithBlob()`

Checks that we finish the proposal stage without timing out (i.e., we have the
proposal) for a block with a corresponding blob.

* Fix incorrect formula computing `MaxBlockPartsCount`.

The previous formula always added one extra part, even when the block size is an
exact multiple of the part size.

* Rename `BlockPartSizeBytes` to `PartSizeBytes`.

* Fix `TestNewValidBlockMessageValidateBasic`

* Add blob in `TestStateFullRound1`

* Add blob to `TestStateFullRoundNil`

* Add blob to TestStateFullRound2

* Fix race condition in test

* Update util `decideProposal()` with blob.

- Adds a non-empty `BlobID` to proposal
- Returns the blob

* Add blob to `TestStateLock_NoPOL`

* Add blob to `TestStateLock_POLUpdateLock`

* Add blob to `TestStateLock_POLRelock`

* Add blob to `TestStateLock_PrevoteNilWhenLockedAndMissProposal`

* Add blob to `TestStateLock_PrevoteNilWhenLockedAndDifferentProposal`

* Add blob to `TestStateLock_POLDoesNotUnlock`

* Add blob to `TestStateLock_MissingProposalWhenPOLSeenDoesNotUpdateLock`

* Add blob to `TestStateLock_MissingProposalWhenPOLForLockedBlock`

* Add blob to `TestStateLock_DoesNotLockOnOldProposal`

* Add blob to `TestStateLock_POLSafety1`

* Add `TestBlob()` to kvstore example application

* Add blob to `TestStateLock_POLSafety1`

* Add blob to `TestStateLock_POLSafety2`

* lintwq

* Add blob to `TestState_PrevotePOLFromPreviousRound`

* Add blob to `TestProposeValidBlock`

* Add blob to `TestSetValidBlockOnDelayedPrevote`

* `ensureProposalWithTimeout` error string prints BlobID instead of BlockID.

* Add blob to `TestSetValidBlockOnDelayedProposal`

* Add blob to `TestResetTimeoutPrecommitUponNewHeight`

* Add blob to `TestState_MissingProposalValidBlockReceivedTimeout`.

* Improve blob testing in `TestState_MissingProposalValidBlockReceivedTimeout`

* Add blob to `TestState_MissingProposalValidBlockReceivedPrecommit`

* Improve blob testing in `TestStateLock_POLSafety1`

* Improve blob testing in `TestStateLock_POLSafety2`

* Add blob to `TestStateTimestamp_ProposalNotMatch`

* Add blob to `TestStateBadProposal`.

Improve blob testing in `TestStateTimestamp_ProposalNotMatch`

* Test func `decideProposal` returns app blob instead of generating one.

* Add blob `nil` checks in several tests

* Fix `TestResetTimeoutPrecommitUponNewHeight`

* Add blob to `TestByzantineConflictingProposalsWithPartition`

* minor refactor and fix incorrect msg

* linting

---------

Signed-off-by: Alessandro Sforzin <alessandro@informal.systems>
Co-authored-by: Jasmina Malicevic <jasmina.dustinac@gmail.com>
Co-authored-by: Alessandro Sforzin <alessandro@informal.systems>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants