Skip to content

feat: re-fork cometbft-bera onto the v0.39.x line - #51

Draft
fridrik01 wants to merge 4 commits into
bera-v0.39.xfrom
refork-cometbft-bera-to-0.39
Draft

feat: re-fork cometbft-bera onto the v0.39.x line#51
fridrik01 wants to merge 4 commits into
bera-v0.39.xfrom
refork-cometbft-bera-to-0.39

Conversation

@fridrik01

@fridrik01 fridrik01 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Berachain's CometBFT fork (bera-v1.x) is built on upstream's v1.x line, which has been wound down in favor of v0.39. Security fixes and improvements such as the new libp2p networking layer now land only on v0.39, while our v1.x base is effectively unmaintained and ours to secure alone. Migrating onto v0.39 puts us back on a maintained line, and keeping PBTS and BLS aggregation preserves the block format so the move can be a rolling binary upgrade rather than a coordinated halt-and-swap.

This PR re-forks the berachain CometBFT customizations from the bera-v1.x line (CometBFT v1.x) onto a fresh v0.39.x base, built on tag v0.39.3. That establishes the new bera-v0.39.x line.

It is designed to be forward-compatible, so an existing bera-v1.x network can restart directly on this binary with no state migration. That works because the on-disk state and the consensus sign and wire bytes are kept byte-identical to bera-v1.x, which we verified against a bera-v1.x build (see Testing).

PBTS and BLS signature aggregation are always enabled on this fork.

What's in it

  • PBTS (Proposer-Based Timestamps). The block timestamp now comes from the proposer and is validated against the synchrony parameters, rather than being derived from the median of vote times. The per-vote timestamps are removed from the signed bytes.
  • BLS12-381 signature aggregation. When a validator set is entirely BLS, the individual precommit signatures are collapsed into a single aggregated commit. That commit is gossiped as a whole and lets lagging nodes catch up quickly.
  • NextBlockDelay (ADR-115). The application now sets the delay before the next height starts, replacing the static timeout_commit from config.
  • NextProposerAddress. ProcessProposal now carries the address of the next block's proposer.

One thing is deliberately left out. The #5860 blocksync fix (sender binding plus a signature-count cap) is excluded from this PR. It already lives on upstream cometbft v0.39.x and lands in this fork on the v0.39.4 rebase, described in Base and upstream syncing below.

How to review

Most of the diff is a faithful port of code that already runs in bera-v1.x production, so the review effort is best spent on the parts that had to change specifically for v0.39.

There is one caveat on method. Provenance cannot be shown with a mechanical diff, because the bera-v1.x line moved its packages under internal/ and api/ and renamed the proto packages from cometbft.* to tendermint.*. A file-to-file diff therefore does not line up, and the split below is curated by hand.

The following can be skimmed, since they are faithful ports and need little review:

The following deserve close review, since they exist only because of the back-port and are where bugs would hide:

  • LastCommit is now an interface (VoteSetReader) that holds either a *VoteSet or a whole aggregated *Commit. This required guarded type assertions at roughly six call sites, plus a cmtjson registration so that the /dump_consensus_state endpoint still works. See consensus/state.go, consensus/types/round_state.go, types/block.go, and types/vote_set.go.
  • BLS is build-tag optional, because v0.39 must still compile without blst. An aggregation_none.go stub and a canAggregateCommits fallback gate the aggregated path. See crypto/bls12381/aggregation_none.go and consensus/state.go.
  • ADR-115 is grafted onto a scheduler that previously only knew timeout_commit. The commit timeout is now derived from state.NextBlockDelay, with a fallback to config.TimeoutCommit. See consensus/state.go, state/state.go, and state/execution.go.
  • The proto and toolchain changes include gogofaster codegen, a mapping from wrappers to gogo, moving the authority field from 6 to 8, and a hand-written Wrap and Unwrap for the new Commit message. See buf.gen.yaml and proto/tendermint/consensus/message.go.
  • The validator key wire format changed so that ToProto and FromProto now write pub_key_bytes and pub_key_type, with a fallback to the legacy pub_key. See types/validator.go and crypto/encoding/codec.go.
  • PBTS is plumbed into existing v0.39 call sites. NewProposal now takes the block time, which is threaded through about twenty callers. In addition, privval/file.go drops the obsolete path that re-signed a vote when only its timestamp differed, and a ProposalTimestampDifference metric was added. See types/proposal.go, privval/file.go, and consensus/metrics.go.

The three features are coupled through the VoteSetReader change, which is why they ship as a single PR rather than three.

Wire and upgrade compatibility

  • The on-disk state and the consensus sign and wire bytes are byte-identical to bera-v1.x, so an existing network can restart on this binary.
  • An upgrade is restart-style rather than a mixed-set rollout. The old and new binaries use different p2p type-URLs (cometbft.* versus tendermint.*) and cannot gossip consensus messages to each other.
  • The change is state-breaking relative to a pristine v0.39.3, owing to the new parameters and the removal of vote timestamps. That is the intended bera shape.

Base and upstream syncing

The bera-v0.39.x line is branched from a tagged upstream release, the CometBFT v0.39.3 tag, which gives it a fixed and well-known starting point on the upstream v0.39.x line. It was created with:

git remote add upstream https://github.com/cometbft/cometbft.git
git fetch upstream --tags
git checkout -b bera-v0.39.x v0.39.3

Future upstream releases are adopted by merging the release tag into bera-v0.39.x through a normal PR:

# fetch exactly this release tag, explicitly from upstream cometbft
git fetch upstream tag v0.39.4
# verify the local tag matches what upstream serves (the two SHAs must match)
git ls-remote upstream 'refs/tags/v0.39.4^{}'
git rev-parse 'v0.39.4^{commit}'
# start a sync branch from the current tip of bera-v0.39.x
git checkout -b sync-upstream-v0.39.4 origin/bera-v0.39.x
# merge the tag, recording its upstream origin in the commit message
git merge v0.39.4 -m "Merge upstream cometbft tag v0.39.4 into bera-v0.39.x"
# resolve conflicts, rebuild and test, then open a PR into bera-v0.39.x

Sync PRs must be merged with a merge commit, never squashed. Squashing breaks the shared ancestry with upstream, so every later sync re-conflicts on changes we already took. Merging rather than rebasing also keeps the branch append-only, so open PRs, clones, and go module consumers survive a sync, and each conflict is resolved once inside a reviewed PR.

The bera-specific changes stay easy to inspect. git diff v0.39.4 bera-v0.39.x shows everything we maintain on top of the latest upstream tag.

Urgent upstream fixes can be cherry-picked between releases, and the next sync merge supersedes them. v0.39.4 already contains the #5860 blocksync fix, so no cherry-pick is needed.

Testing

  • The fork builds both with and without -tags bls12381.
  • The test suite is at parity with a pristine v0.39.3. The one remaining flake reproduces on the pristine tag as well, so it is not a regression.
  • Compatibility with bera-v1.x was verified out of tree. A one-time harness captured the exact bytes and hashes a bera-v1.x build produces and confirmed this port produces identical output, covering the vote and proposal sign bytes, the aggregated-commit hash and its proto encoding, the block header hash, the consensus params hash, the persisted state proto, and a full real aggregated commit. That harness is not part of this PR, so a committed vectors test could be added later to make it reproducible in CI.

New tests added by this PR:

@fridrik01 fridrik01 self-assigned this Jun 26, 2026
@fridrik01
fridrik01 force-pushed the refork-cometbft-bera-to-0.39 branch 3 times, most recently from 518c996 to ecb80ec Compare June 26, 2026 10:27
@fridrik01
fridrik01 force-pushed the refork-cometbft-bera-to-0.39 branch from f39c0b5 to 9fe10d1 Compare July 23, 2026 11:08
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Protobuf Lint / lint (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedAug 13, 2026, 4:27 PM

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Re-forks Berachain’s CometBFT customizations onto v0.39.3 while preserving bera-v1.x consensus and persisted-state compatibility.

Changes:

  • Ports PBTS, BLS commit aggregation, and whole-commit catch-up.
  • Adds ADR-115 next-block delays and next-proposer ABCI data.
  • Updates protobuf formats, validator encoding, remote signing, tests, and tooling.

Reviewed changes

Copilot reviewed 91 out of 93 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.golangci.yml Allows BLST imports.
CHANGELOG.md Documents fork features and compatibility.
buf.gen.yaml Maps protobuf wrappers for generation.
version/version.go Sets Berachain fork version.
blocksync/reactor.go Uses feature-based vote-extension settings.
blocksync/reactor_test.go Updates blocksync fixtures for PBTS.
consensus/state.go Integrates PBTS, aggregation, delays, and catch-up.
consensus/state_test.go Adapts consensus state tests.
consensus/state_ingest.go Uses feature parameters during ingestion.
consensus/state_ingest_test.go Updates ingestion tests.
consensus/reactor.go Gossips block-part hints and aggregated commits.
consensus/reactor_test.go Updates reactor state expectations.
consensus/replay.go Replays aggregated commit messages.
consensus/replay_test.go Adapts proposal timestamps.
consensus/msgs.go Converts new consensus messages.
consensus/msgs_test.go Pins new message wire formats.
consensus/metrics.go Defines PBTS and delay metrics.
consensus/metrics.gen.go Generates metric implementations.
consensus/pbts_test.go Tests PBTS timing behavior.
consensus/mempool_test.go Passes proposal receive times.
consensus/invalid_test.go Removes vote timestamps.
consensus/common_test.go Updates shared proposal helpers.
consensus/byzantine_test.go Adapts Byzantine scenarios.
consensus/aggregation_reactor_test.go Tests BLS network commits.
consensus/aggregation_replay_test.go Tests aggregated WAL recovery.
consensus/aggregation_pbts_test.go Tests PBTS rejection with BLS.
consensus/aggregation_catchup_test.go Tests whole-commit catch-up.
consensus/types/round_state.go Supports commit-backed LastCommit.
consensus/types/peer_round_state.go Tracks peer catch-up commits.
consensus/types/height_vote_set.go Stores received aggregated commits.
crypto/bls12381/aggregation.go Implements BLS aggregation.
crypto/bls12381/aggregation_none.go Adds disabled-build stubs.
crypto/bls12381/aggregation_test.go Tests aggregate verification.
crypto/bls12381/key.go Updates disabled BLS API.
crypto/bls12381/key_bls12381.go Adds compression and corrected aggregation types.
crypto/bls12381/const.go Defines compressed key size.
crypto/encoding/codec.go Supports pointer and compressed BLS keys.
evidence/pool_test.go Updates encoded evidence size.
internal/test/commit.go Uses feature parameters.
internal/test/config.go Adds PBTS genesis parameters.
internal/test/params.go Enables extensions through feature parameters.
node/setup.go Shortens seed disconnect timing.
privval/file.go Removes timestamp re-signing and adds raw signing.
privval/file_test.go Updates timestamp signing tests.
privval/msgs.go Wraps raw-signing messages.
privval/retry_signer_client.go Retries raw signing.
privval/signer_client.go Adds remote raw signing.
privval/signer_client_test.go Tests remote raw signing.
privval/signer_requestHandler.go Handles raw-signing requests.
proto/buf.yaml Exempts intentional wire changes.
proto/tendermint/abci/types.proto Adds delay and next proposer fields.
proto/tendermint/consensus/message.go Wraps new consensus messages.
proto/tendermint/consensus/types.proto Defines commit and block-part messages.
proto/tendermint/privval/types.proto Defines raw-signing messages.
proto/tendermint/state/types.proto Persists next-block delay.
proto/tendermint/types/canonical.proto Removes signed vote timestamps.
proto/tendermint/types/canonical.pb.go Regenerates canonical types.
proto/tendermint/types/params.proto Adds feature and synchrony parameters.
proto/tendermint/types/validator.proto Adds aggregation flags and key fields.
rpc/client/evidence_test.go Uses committed PBTS block time.
state/execution.go Applies ADR-115 and next proposer data.
state/execution_test.go Updates execution expectations.
state/state.go Persists delays and creates PBTS blocks.
state/validation.go Validates PBTS block times.
state/validation_test.go Updates block validation tests.
store/store_test.go Adapts pruning tests for PBTS.
test/e2e/runner/setup.go Writes feature-based genesis settings.
types/block.go Adds aggregate flags and commit reader support.
types/block_test.go Updates commit tests.
types/canonical.go Excludes vote timestamps from sign bytes.
types/events.go Adds block-part event.
types/evidence.go Removes evidence vote timestamps.
types/params.go Implements feature and synchrony parameters.
types/params_test.go Tests new parameter representation.
types/priv_validator.go Adds BLS mocks and raw signing.
types/proposal.go Adds PBTS proposal timestamps and timeliness.
types/proposal_test.go Updates proposal tests.
types/test_util.go Removes vote timestamps from helpers.
types/time/time.go Adds injectable clock source.
types/time/mocks/source.go Provides generated clock mock.
types/validation.go Verifies aggregated commits.
types/validator.go Changes validator key serialization.
types/validator_set_test.go Updates validator protobuf fixtures.
types/vote.go Enforces zero vote timestamps.
types/vote_test.go Updates signed-byte vectors.
types/vote_set.go Creates aggregated BLS commits.
types/vote_set_test.go Updates vote-set tests.
Files not reviewed (2)
  • abci/types/types.pb.go: Generated file
  • consensus/metrics.gen.go: Generated file
Suppressed comments (2)

consensus/state.go:1440

  • This second aggregation path also runs when vote extensions are enabled, bypassing canAggregateCommits. That creates an aggregated extended commit which the restart and whole-commit catch-up paths cannot consume. Include !cs.isVoteExtensionsEnabled(lastCommitAsVs.GetHeight()) in canBeAggregated before calling MakeBLSCommit.
		if canBeAggregated {
			if !cs.isPBTSEnabled(cs.Height) {
				panic("Wanted to aggregate LastCommit, but PBTS is not enabled for height " + strconv.FormatInt(cs.Height, 10))
			}
			lastExtCommit = lastCommitAsVs.MakeBLSCommit()

consensus/state.go:1535

  • Aggregation is selected even when vote extensions are enabled, despite the new catch-up path explicitly not supporting that combination. The resulting extended commit cannot be reconstructed after restart because its individual consensus signatures were cleared, and AddCommit refuses it. Gate aggregation on vote extensions being disabled.
func (cs *State) canAggregateCommits() bool {
	proposerKey := cs.Validators.GetProposer().PubKey
	_, blsKey := proposerKey.(*bls12381.PubKey)
	_, blsKey2 := proposerKey.(bls12381.PubKey)
	return (blsKey || blsKey2) && cs.Validators.AllKeysHaveSameType()

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

for i, pubk := range pubks {
blsPubKeys[i] = pubk.pk
}
return agSig.FastAggregateVerify(true, blsPubKeys, msg, dstMinPk)
Comment on lines +91 to +93
case *privvalproto.Message_SignBytesRequest:
var signature []byte
signature, err = privVal.SignBytes(r.SignBytesRequest.Value)
Comment thread types/params.go
Comment on lines +319 to +324
if params.Feature.PbtsEnableHeight < 0 {
return fmt.Errorf("Feature.PbtsEnableHeight cannot be negative. Got: %d", params.Feature.PbtsEnableHeight)
}

// Synchrony params are only relevant when PBTS is enabled
if params.Feature.PbtsEnableHeight > 0 {
Comment thread consensus/state.go
Comment on lines +1510 to +1514
if cs.Proposal.Round > 10 {
return true
}

sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Proposal.Round)
Comment thread consensus/state.go
Comment on lines +699 to +702
// With BLS aggregation the individual precommit votes cannot be
// reconstructed from an aggregated seen commit, so the whole *Commit
// stands in for the vote set.
return commit, nil
Comment thread consensus/state.go
Comment on lines +1432 to +1435
_, blsKey := cs.privValidatorPubKey.(*bls12381.PubKey)
_, blsKey2 := cs.privValidatorPubKey.(bls12381.PubKey)
canBeAggregated := (blsKey || blsKey2) &&
cs.state.Validators.AllKeysHaveSameType()
Re-forks the berachain CometBFT customizations from the bera-v1.x line
onto a fresh v0.39.x base (tag v0.39.3), establishing the bera-v0.39.x
line.

Brings over:
 - Proposer-Based Timestamps (PBTS) + removal of per-vote timestamps
 - BLS12-381 signature aggregation (aggregated commits + fast catch-up)
 - NextBlockDelay (ADR-115) and NextProposerAddress in ProcessProposal
 - validator pub-key wire format, Synchrony/Feature params, and supporting
   proto

Forward-compatible: existing bera-v1.x networks can restart on this
binary (on-disk state and consensus sign/wire bytes are byte-identical).

Upstream cometbft#5860 is intentionally excluded (arrives via the v0.39.4 rebase).
@fridrik01
fridrik01 force-pushed the refork-cometbft-bera-to-0.39 branch from 71516da to 5ad4d72 Compare August 13, 2026 16:27
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.

2 participants